// package-registry: the forge module's Go bundle for its package registry (novox/hq ADR 0251 §4, design 51): its // retention tools, and the verbs of the two package seats it holds (seats.go). // A process the node's runtime launches and speaks MCP over stdio to, beside the module's TypeScript // bundle. It says which versions of each npm package the registry holds, what keeps each one, and — // asked with a why — deletes the versions nothing keeps. stdout is the protocol; it says what it says on // stderr, and never the forge's credential. package main import ( "context" "fmt" "math" "os" "strings" stdio "git.novox.be/novox/mesh-sdk/go" "gitea/internal/packageseats" ) // ToolNames are the tools this bundle serves. The module's manifest lists no `tools`: its TypeScript // bundle's tools are served without one, and a claim without `serves` would offer a `tools` list to both // of the module's seats as their verbs. The README names these two, and a test holds the two together. var ToolNames = []string{"go_packages", "go_retention", "go_incoming", "npm_packages", "npm_retention", "npm_incoming"} func main() { forge := func() (*Forge, error) { return ForgeFromEnv() } holder := func() (*packageseats.Holder, error) { return busHolder(forge) } go followIdle(holder) tools := append(Tools(forge, holder), SeatTools(holder)...) if err := stdio.Serve("", tools); err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } } // Tools are the bundle's tools over the forge the function gives; asked per call, so a manifest that // does not set the environment is a refusal naming what is missing, not a bundle that will not start. func Tools(forge func() (*Forge, error), holder func() (*packageseats.Holder, error)) []stdio.Tool { out := append(typeTools(TypeGo, forge), incomingTool(TypeGo, packageseats.GoSeat, holder)) return append(append(out, typeTools(TypeNpm, forge)...), incomingTool(TypeNpm, packageseats.NpmSeat, holder)) } // typeTools are one package type's two tools, _packages and _retention, over the forge reading that type. func typeTools(typ string, forgeOf func() (*Forge, error)) []stdio.Tool { forge := func() (*Forge, error) { f, err := forgeOf() if err != nil { return nil, err } return f.As(typ), nil } pkg := map[string]any{"type": "string", "description": "one package, by its name (default every package)"} keep := map[string]any{"type": "integer", "description": fmt.Sprintf("how many of each package's newest versions are kept whatever names them (default %d, at least 1)", DefaultKeep)} listed := "Every npm package in the forge's package registry, each version newest first with when it was published, " + "its size and what keeps it: a lockfile on a repository's default branch that names it, the highest version " + "satisfying a range a package.json there names, a dist-tag, or being among the newest `keep` of its package " + "(novox/hq ADR 0251). Repositories and files that could not be read are named. Changes nothing. Replaces " + "npm view and npm dist-tag ls. (r)" deletes := "What retention would delete from the forge's package registry: every version nothing keeps — no lockfile " + "on a repository's default branch names it, it is not the highest version satisfying any range a package.json " + "there names, no dist-tag names it, and it is not among the newest `keep` of its package — with the bytes. " + "A dry run unless dry_run is false; a real run needs why, and deletes nothing at all when any repository or " + "file could not be read (novox/hq ADR 0251). Each deletion says removed on the npm seat. Replaces npm unpublish. (a)" if typ == TypeGo { listed = "Every Go module in the forge's package registry (the Go seat's), each version newest first with when it " + "was published, its size and what keeps it: a go.mod on a repository's default branch that requires it, or " + "being among the newest `keep` of its module (design 51 *Retention*). Repositories and go.mod files that " + "could not be read are named. Changes nothing. Replaces go list -m -versions. (r)" deletes = "What retention would delete from the forge's Go registry: every module version no go.mod on a " + "repository's default branch requires and not among the newest `keep` of its module, with the bytes. A dry " + "run unless dry_run is false; a real run needs why, and deletes nothing at all when any repository or go.mod " + "could not be read. Each deletion says removed on the Go seat. (a)" } return []stdio.Tool{ { Name: typ + "_packages", Description: listed, Input: map[string]any{"package": pkg, "keep": keep}, Run: func(args map[string]any) (any, error) { f, err := forge() if err != nil { return nil, err } k, err := count(args, "keep", DefaultKeep, 1) if err != nil { return nil, err } s, err := survey(context.Background(), f, text(args, "package"), k, func(Item) bool { return true }) if err != nil { return nil, err } kept, total := 0, 0 for _, p := range s.Packages { for _, v := range p.Versions { total++ if v.Kept { kept++ } } } s.Said = append(s.Said, fmt.Sprintf("%d packages, %d versions, %d kept; %d repositories read, %d unread", len(s.Packages), total, kept, s.Repositories, len(s.Unread))) s.Said = append(s.Said, sizesSaid(s)...) return s, nil }, }, { Name: typ + "_retention", Description: deletes, Input: map[string]any{ "package": pkg, "keep": keep, "dry_run": map[string]any{"type": "boolean", "description": "false to delete; default true"}, "why": map[string]any{"type": "string", "description": "why the versions are deleted; required for a real run"}, }, Run: func(args map[string]any) (any, error) { f, err := forge() if err != nil { return nil, err } k, err := count(args, "keep", DefaultKeep, 1) if err != nil { return nil, err } dry := true if v, given := args["dry_run"]; given && v != nil { b, ok := v.(bool) if !ok { if s, isText := v.(string); isText && (s == "true" || s == "false") { b, ok = s == "true", true } } if !ok { return nil, fmt.Errorf("dry_run is true or false, not %v", v) } dry = b } return retention(context.Background(), f, text(args, "package"), k, dry, text(args, "why")) }, }, } } func text(args map[string]any, key string) string { s, _ := args[key].(string) return strings.TrimSpace(s) } func count(args map[string]any, key string, fallback, least int) (int, error) { v, given := args[key] if !given || v == nil { return fallback, nil } x, ok := v.(float64) if !ok || x != math.Trunc(x) { return 0, fmt.Errorf("%s must be a whole number", key) } if int(x) < least { return 0, fmt.Errorf("%s must be at least %d", key, least) } return int(x), nil }