toolbelt

package module
v1.0.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 17, 2026 License: GPL-2.0, GPL-3.0 Imports: 32 Imported by: 0

README

toolbelt

Go Reference Go version Test coverage Mutation OpenSSF Scorecard

Declarative dev-tool provisioning for container dev boxes: manifest + catalog + reconciler engine

A standalone Go library that provisions developer tools (language servers, CLIs, runtimes, linters) onto a persistent volume, declaratively. A JSON manifest records intent: which tools, at which versions, enabled or disabled. A compiled catalog carries install knowledge for ~700 tools, sourced from the mise and aqua registries plus curated overlays. The engine reconciles installed state against intent through a single-flight job queue: enabled-but-missing tools are installed (checksum-verified when the registry definition declares a source), disabled-but-installed tools are uninstalled with their template kept, and unmanaged files are never touched.

Built for headless and UI consumers alike: everything is driven through the Go API, an optional REST projection (toolbelt/httpapi), or hand edits of the manifest file itself, which the engine picks up on its next operation.

The model

Three data artifacts, all on the consumer's persistent volume:

File Owner Purpose
tools.json user intent (engine-written, hand-editable) which tools exist, versions, pin, disabled
tools-state.json engine what is actually installed, owned bin names, last error
tool-catalog.json image build (cmd/toolcatalog) install knowledge: sources, artifact templates, checksum locations, shims, dependencies

Tool lifecycle is a three-state machine the reconciler enforces in both directions:

  • absent: not in the manifest. Unmanaged files under the tools dir are never touched.
  • disabled ("disabled": true): a template. Recorded intent, nothing on disk; the engine uninstalls its owned footprint if present.
  • enabled (present, no flag): installed; updated when unpinned.

Sources: aqua:owner/repo (binary artifacts with upstream checksum verification), npm:pkg, pip:pkg (via uv), cargo:crate, go:module, and a manual bash escape hatch. Ecosystem backends are themselves tools: an npm: install pulls node from the catalog automatically, go: pulls the Go toolchain, and so on.

Install

go get github.com/cplieger/toolbelt@latest

Usage

engine, err := toolbelt.New(&toolbelt.Config{
    ConfigDir:   "/config",                        // tools.json + tools-state.json
    ToolsDir:    "/config/tools",                  // bin/, opt/, npm/, python/
    CatalogPath: "/opt/app/tool-catalog.json",     // compiled at image build
    Seed:        toolbelt.DefaultSeed(),           // LSP templates, disabled
})
if err != nil {
    log.Fatal(err)
}
defer engine.Close()

// Boot: converge disk to intent, then gate whatever needs tools on the job.
if job, _ := engine.Reconcile(toolbelt.ReconcileMissing); job != nil {
    _, _ = engine.Wait(ctx, job.ID)
}

// Add a tool by name; the catalog supplies source, version, shims, deps.
job, err := engine.Add(ctx, &toolbelt.AddRequest{Name: "gopls"})

// The enable/disable toggle: disabling uninstalls, the template stays.
on := true
job, err = engine.Patch("gopls", toolbelt.PatchRequest{Disabled: &on})

DefaultSeed() ships four disabled templates (gopls, tsc-native, pyrefly, gh): nothing downloads until a template is enabled, and install knowledge hydrates from the catalog at enable time, so the seed never goes stale.

The REST projection

toolbelt/httpapi serves the engine over HTTP: inventory, catalog search, add, patch (the toggle verb), install, update, remove, jobs, cancel. It is a pure projection: no auth, no middleware; wrap it in your own stack (an origin-checked chain, a loopback-only gate).

h := httpapi.Handler(engine, "/api/tools")
mux.Handle("/api/tools", h)
mux.Handle("/api/tools/", h)

Mutations return 202 {"job": ...}; refusals are 409 with a coded envelope (has_dependents names the blockers, disabled marks install-on-a-template). Stream job progress via the Config callbacks or poll GET .../jobs.

The catalog compiler

cmd/toolcatalog (a nested module, so its registry-parsing dependencies never enter your go.sum) compiles the catalog at image build and verifies it against your required tool set:

go run github.com/cplieger/toolbelt/cmd/toolcatalog@latest \
    -mise mise-checkout/registry -aqua aqua-registry-checkout/pkgs \
    -overlay overlays.json -refs mise=<ref>,aqua=<ref> -out tool-catalog.json

go run github.com/cplieger/toolbelt/cmd/toolcatalog@latest \
    verify -catalog tool-catalog.json -require required-tools.txt

verify fails the build when a required name is missing from the catalog or its definition is unusable (no source, unparseable templates, no linux amd64/arm64 support), so registry drift surfaces at image build instead of in a boot job. The lane ships a base overlay set covering runtimes, forge CLIs, and language servers with the wrapper-script shims agent CLIs probe for (typescript-language-server, pyright, pyright-langserver).

Configuration reference

Config field Purpose
ConfigDir directory holding tools.json + tools-state.json (required)
ToolsDir install tree root: bin/ (the single PATH dir), opt/<name>/<version>/, npm/, python/ (required)
CatalogPath compiled catalog path; missing degrades to manual/ecosystem sources with named errors for catalog-dependent entries
Seed manifest written when none exists (fresh volume or retired-format backup); nil seeds empty
System image-baked binaries reported read-only in Inventory
OnJobChanged / OnJobOutput job lifecycle + coalesced output callbacks (must not block); nil is silent
Logger slog logger; nil uses the default

Manifest entry fields: source, version, pin (freeze version), disabled (template), requires, shims (wrapper scripts, full command lines), and install/uninstall/probe for manual sources. Every field except the name is optional; the catalog completes the rest. A _comment array survives engine rewrites.

Security model

  • Aqua-sourced artifacts verify against the checksum source their registry definition declares (upstream checksums.txt and equivalents); an install without one is logged as unverified.
  • Fetches ride an SSRF-guarded client (public-IP enforcement at the dial boundary, redirect policy, port allowlist) with transient-failure retry and rate-limit handling via cplieger/httpx.
  • Downloads are size-capped (1 GiB); archive extraction rejects symlink members that escape the install tree; installs land in versioned directories swapped atomically.
  • The manual source runs arbitrary bash by design: it is an operator escape hatch for single-principal volumes, equivalent in trust to editing the manifest itself.

Scope notes

  • Linux only (amd64, arm64). The aqua evaluator resolves definitions for linux and ignores other platforms.
  • No apt/system-package backend: OS packages belong to the image or the consumer's entrypoint, not volume intent.
  • The manifest store's single-writer guarantee is in-process. Run one engine per data directory; other processes go through the consumer's server.

Disclaimer

This project is built with care and follows security best practices, but it is intended for personal / self-hosted use. No guarantees of fitness for production environments. Use at your own risk.

This project was built with AI-assisted tooling using Claude Opus and Kiro. The human maintainer defines architecture, supervises implementation, and makes all final decisions.

License

GPL-3.0 — see LICENSE.

Documentation

Overview

Package toolbelt provisions developer tools onto a persistent volume, declaratively. A manifest (tools.json) records intent: which tools, at which versions, enabled or disabled. A compiled catalog carries install knowledge (sources, artifact templates, checksum locations, shims, dependencies). The Engine reconciles installed state against intent through a single-flight job queue: enabled-but-missing tools are installed, disabled-but-installed tools are uninstalled (their template kept), unmanaged files are never touched.

Data files (all under the consumer's persistent config volume):

<ConfigDir>/tools.json       — the manifest: user intent. Toolbelt is
                               the only writer, but out-of-band hand
                               edits are supported by design (the file
                               is re-read on every operation).
<ConfigDir>/tools-state.json — engine-owned machine state (installed
                               version, owned bin names, last error).
<ToolsDir>/opt/<name>/<ver>/ — versioned install trees.
<ToolsDir>/bin               — the single PATH dir: symlinks + shims.

The catalog (CatalogPath, compiled at image build by cmd/toolcatalog) is read-only environment data; a missing catalog degrades to manual and ecosystem sources only, and entries that need catalog knowledge fail their jobs with a named error.

Install sources: aqua-registry artifact definitions (with upstream checksum verification when the definition declares a source), npm, pip (via uv), cargo, go install, and a manual bash escape hatch. No external package-manager binary ships with the library; ecosystem backends are themselves installable tools (npm needs node, pip needs uv, ...), and the engine adopts those backend dependencies automatically.

Index

Constants

View Source
const (
	SourceAqua   = "aqua"   // aqua:owner/repo — evaluated aqua-registry definition
	SourceNpm    = "npm"    // npm:package
	SourcePip    = "pip"    // pip:package (installed via uv)
	SourceCargo  = "cargo"  // cargo:crate
	SourceGo     = "go"     // go:module/path
	SourceManual = "manual" // user-provided install command
)

Source prefixes for Tool.Source. A source is "<kind>:<ref>" except SourceManual which stands alone.

View Source
const (
	JobQueued    = "queued"
	JobRunning   = "running"
	JobDone      = "done"
	JobFailed    = "failed"
	JobCancelled = "cancelled"
)

Job states.

View Source
const (
	JobKindInstall   = "install"
	JobKindUninstall = "uninstall" // Remove: footprint removed, entry deleted
	JobKindDisable   = "disable"   // Patch disabled:true: footprint removed, entry kept
	JobKindUpdate    = "update"
	JobKindReconcile = "reconcile" // converge disk to intent (install missing + disable extras)
)

Job kinds.

View Source
const ManifestVersion = 2

ManifestVersion is the manifest schema version this engine reads and writes. Files without it (the retired sectioned v1 shape) are backed up and replaced with the configured seed at engine start.

Variables

View Source
var (
	// ErrNotFound marks an operation on a tool the manifest doesn't have.
	ErrNotFound = errors.New("tool not found")
	// ErrHasDependents marks a refused remove/disable: enabled entries
	// still require the tool (directly or as an implied backend).
	ErrHasDependents = errors.New("tool has dependents")
	// ErrDisabled marks an install attempt on a disabled template.
	// Enabling is an explicit state change (Patch Disabled=false), never
	// a side effect of a retry.
	ErrDisabled = errors.New("tool is disabled")
)

Sentinel errors. Compare with errors.Is; *DependentsError additionally carries the dependent names (errors.As).

Functions

func VerifyCatalog

func VerifyCatalog(c *Catalog, require []string) []error

VerifyCatalog checks that every required name resolves in the catalog to install knowledge the engine can act on, offline: a non-empty source; for aqua sources an embedded definition that parses its templates and claims linux support on both amd64 and arm64; for manual sources an install command. One error per failing name; nil means the catalog satisfies the requirement set. Consumers run this at image build (cmd/toolcatalog verify) over their seed + migration names so a registry drift fails the build instead of a boot job.

Types

type AddRequest

type AddRequest struct {
	Shims       map[string]string `json:"shims,omitempty"`
	Name        string            `json:"name"`
	Source      string            `json:"source,omitempty"`  // optional when the catalog knows the name
	Version     string            `json:"version,omitempty"` // optional: resolve latest
	Description string            `json:"description,omitempty"`
	Origin      string            `json:"origin,omitempty"`
	Install     string            `json:"install,omitempty"`
	Uninstall   string            `json:"uninstall,omitempty"`
	Probe       string            `json:"probe,omitempty"`
	Requires    []string          `json:"requires,omitempty"`
	Pin         bool              `json:"pin,omitempty"`
	// Disabled adds the entry as a template: recorded, not installed,
	// no job enqueued (Add then returns a nil Job).
	Disabled bool `json:"disabled,omitempty"`
}

AddRequest is the Add call's body: intent for a new tool. Every field except Name is optional when the catalog knows the name.

type AquaChecksum

type AquaChecksum struct {
	Enabled   *bool  `json:"enabled,omitempty" yaml:"enabled"`
	Type      string `json:"type,omitempty" yaml:"type"` // github_release | http
	Asset     string `json:"asset,omitempty" yaml:"asset"`
	URL       string `json:"url,omitempty" yaml:"url"`
	Algorithm string `json:"algorithm,omitempty" yaml:"algorithm"` // sha256 | sha512 | ...
}

AquaChecksum describes where the artifact's checksum lives.

type AquaFile

type AquaFile struct {
	Name string `json:"name" yaml:"name"`
	Src  string `json:"src,omitempty" yaml:"src"` // template; default = Name
}

AquaFile names one binary inside the extracted artifact.

type AquaOverride

type AquaOverride struct {
	Replacements map[string]string `json:"replacements,omitempty" yaml:"replacements"`
	Checksum     *AquaChecksum     `json:"checksum,omitempty" yaml:"checksum"`
	GOOS         string            `json:"goos,omitempty" yaml:"goos"`
	GOArch       string            `json:"goarch,omitempty" yaml:"goarch"`
	Asset        string            `json:"asset,omitempty" yaml:"asset"`
	URL          string            `json:"url,omitempty" yaml:"url"`
	Format       string            `json:"format,omitempty" yaml:"format"`
	Files        []AquaFile        `json:"files,omitempty" yaml:"files"`
}

AquaOverride adjusts fields for a specific GOOS/GOARCH.

type AquaPackage

type AquaPackage struct {
	Replacements     map[string]string     `json:"replacements,omitempty" yaml:"replacements"`
	Checksum         *AquaChecksum         `json:"checksum,omitempty" yaml:"checksum"`
	VersionConstr    string                `json:"version_constraint,omitempty" yaml:"version_constraint"`
	VersionSource    string                `json:"version_source,omitempty" yaml:"version_source"`
	Asset            string                `json:"asset,omitempty" yaml:"asset"`
	URL              string                `json:"url,omitempty" yaml:"url"`
	Path             string                `json:"path,omitempty" yaml:"path"`
	Format           string                `json:"format,omitempty" yaml:"format"`
	RepoOwner        string                `json:"repo_owner,omitempty" yaml:"repo_owner"`
	RepoName         string                `json:"repo_name,omitempty" yaml:"repo_name"`
	VersionPrefix    string                `json:"version_prefix,omitempty" yaml:"version_prefix"`
	Description      string                `json:"description,omitempty" yaml:"description"`
	Type             string                `json:"type" yaml:"type"`
	VersionFilter    string                `json:"version_filter,omitempty" yaml:"version_filter"`
	VersionOverrides []AquaVersionOverride `json:"version_overrides,omitempty" yaml:"version_overrides"`
	SupportedEnvs    []string              `json:"supported_envs,omitempty" yaml:"supported_envs"`
	Overrides        []AquaOverride        `json:"overrides,omitempty" yaml:"overrides"`
	Files            []AquaFile            `json:"files,omitempty" yaml:"files"`
	NoAsset          bool                  `json:"no_asset,omitempty" yaml:"no_asset"`
}

AquaPackage is the subset of an aqua-registry package definition the engine evaluates. Field names mirror the upstream YAML/JSON schema (https://aquaproj.github.io/docs/reference/registry-config), which the catalog compiler (cmd/toolcatalog) relies on to unmarshal registry files directly. Windows- and darwin-only concerns (rosetta2, windows_arm_emulation, complete_windows_ext) are intentionally absent: the engine resolves for linux only, and the evaluator resolves everything for linux/GOARCH.

func (*AquaPackage) CheckTemplates

func (p *AquaPackage) CheckTemplates() error

CheckTemplates parses (without executing) every Go template the definition carries — asset, url, path, checksum asset/url, file srcs, across version overrides — so catalog verification can catch template syntax drift offline. Returns the first parse error.

func (*AquaPackage) ResolveSpec

func (p *AquaPackage) ResolveSpec(version string) (*InstallSpec, error)

ResolveSpec evaluates the package definition for the given version on linux/GOARCH and returns the download plan. version is the upstream tag (with any version_prefix still attached).

func (*AquaPackage) SupportsLinux

func (p *AquaPackage) SupportsLinux(goarch string) bool

SupportsLinux reports whether the definition claims support for linux/goarch at the root level (supported_envs; empty = everywhere). A static, offline check — no version rendering. Used by catalog verification.

type AquaVersionOverride

type AquaVersionOverride struct {
	Asset         *string            `json:"asset,omitempty" yaml:"asset"`
	URL           *string            `json:"url,omitempty" yaml:"url"`
	Format        *string            `json:"format,omitempty" yaml:"format"`
	Replacements  *map[string]string `json:"replacements,omitempty" yaml:"replacements"`
	Checksum      *AquaChecksum      `json:"checksum,omitempty" yaml:"checksum"`
	NoAsset       *bool              `json:"no_asset,omitempty" yaml:"no_asset"`
	VersionPrefix *string            `json:"version_prefix,omitempty" yaml:"version_prefix"`
	VersionConstr string             `json:"version_constraint,omitempty" yaml:"version_constraint"`
	Files         []AquaFile         `json:"files,omitempty" yaml:"files"`
	Overrides     []AquaOverride     `json:"overrides,omitempty" yaml:"overrides"`
	SupportedEnvs []string           `json:"supported_envs,omitempty" yaml:"supported_envs"`
}

AquaVersionOverride switches definition fields per version range. A nil pointer field means "inherit from the package root".

type Catalog

type Catalog struct {
	// Refs records the upstream registry refs this catalog was
	// compiled from (informational).
	Refs    map[string]string       `json:"refs,omitempty"`
	Entries map[string]CatalogEntry `json:"entries"`
}

Catalog is the compiled tool-catalog.json document.

func (*Catalog) Featured

func (c *Catalog) Featured() []CatalogEntry

Featured returns the curated starter set (empty-state content), sorted by name.

func (*Catalog) Lookup

func (c *Catalog) Lookup(name string) (CatalogEntry, bool)

Lookup finds a catalog entry by name or alias.

func (*Catalog) Search

func (c *Catalog) Search(query string) []CatalogEntry

Search ranks catalog entries against a query: exact name, name prefix, alias, name substring, then description substring. Empty query returns the featured set.

type CatalogEntry

type CatalogEntry struct {
	Shims       map[string]string `json:"shims,omitempty"`
	Aqua        *AquaPackage      `json:"aqua,omitempty"`
	Name        string            `json:"name"`
	Description string            `json:"description,omitempty"`
	Source      string            `json:"source"`
	// Version is the default pinned version for entries without an
	// upstream version source (manual installs).
	Version   string   `json:"version,omitempty"`
	Install   string   `json:"install,omitempty"`   // manual-source entries
	Uninstall string   `json:"uninstall,omitempty"` // manual-source entries
	Probe     string   `json:"probe,omitempty"`     // manual-source entries
	Aliases   []string `json:"aliases,omitempty"`
	Requires  []string `json:"requires,omitempty"`
	Featured  bool     `json:"featured,omitempty"`
	// Lsp marks language-server entries; drives the consumers'
	// no-LSP-enabled warning and UI badges.
	Lsp bool `json:"lsp,omitempty"`
}

CatalogEntry is one tool in the compiled catalog: the mise-registry name/description joined with the preferred install source and, for aqua sources, the embedded aqua package definition. Overlay entries (curated) may add requires/shims/manual install commands and the lsp marker.

type Config

type Config struct {
	// OnJobChanged, when non-nil, receives every job state transition
	// (queued, running, done, failed, cancelled). Views carry no output
	// tail; stream output via OnJobOutput or poll Jobs().
	OnJobChanged func(*Job)
	// OnJobOutput, when non-nil, receives coalesced batches of a running
	// job's output lines (~150 ms cadence).
	OnJobOutput func(jobID string, lines []string)
	// Logger receives engine-level log lines. Nil means slog.Default().
	Logger *slog.Logger
	// Seed is the manifest written when no valid one exists (fresh
	// volume, or a retired-format file that was just backed up). Nil
	// seeds an empty manifest. See DefaultSeed.
	Seed *Manifest
	// ConfigDir holds tools.json + tools-state.json (the persistent
	// config volume root).
	ConfigDir string
	// ToolsDir is the install tree root (bin/, opt/, npm/, python/).
	ToolsDir string
	// CatalogPath is the compiled catalog baked into the consumer's
	// image (optional; missing = degraded search + named install errors
	// for catalog-dependent entries).
	CatalogPath string
	// System names image-baked binaries surfaced read-only in
	// Inventory's System group (informational; not managed).
	System []string
}

Config wires an Engine. ConfigDir and ToolsDir are required.

type DependentsError

type DependentsError struct {
	Dependents []string
}

DependentsError is the ErrHasDependents shape that names the enabled entries blocking a remove/disable.

func (*DependentsError) Error

func (e *DependentsError) Error() string

func (*DependentsError) Is

func (e *DependentsError) Is(target error) bool

Is makes errors.Is(err, ErrHasDependents) match.

type Engine

type Engine struct {
	// contains filtered or unexported fields
}

Engine is the tools subsystem: the single owner of the manifest and install tree, the job queue, and the catalog. Construct with New.

The manifest store's single-writer guarantee is an in-process lock: every other process (a CLI, an agent) must go through the consumer's server rather than linking toolbelt against the same data dirs.

func New

func New(cfg *Config) (*Engine, error)

New constructs and starts an Engine: initializes the manifest files (seeding when absent, backing up a retired-format file) and launches the job worker.

func (*Engine) Add

func (e *Engine) Add(ctx context.Context, req *AddRequest) (*Job, error)

Add records a new tool in the manifest and, unless the request marks it disabled, enqueues its install. Present-and-enabled is the default intent: adding means "have this installed".

func (*Engine) CancelJob

func (e *Engine) CancelJob(id string) bool

CancelJob aborts a queued or running job.

func (*Engine) Close

func (e *Engine) Close()

Close stops the job worker (cancelling any running job).

func (*Engine) EnsureInstalled

func (e *Engine) EnsureInstalled(ctx context.Context, name string) error

EnsureInstalled synchronously guarantees a tool: present in the manifest (created from the catalog when missing), enabled, installed, and on PATH. This is the programmatic "a product action needs this binary now" path (a forge login installing gh, an MCP flow installing node); unlike Install it DOES enable a disabled template, because the user explicitly invoked the feature that needs the tool.

func (*Engine) Install

func (e *Engine) Install(name string) (*Job, error)

Install re-enqueues an install for an existing, enabled tool (retry / install-missing). Installing a disabled template is refused with ErrDisabled: install is policy-neutral, enabling rides Patch.

func (*Engine) Inventory

func (e *Engine) Inventory() (*Inventory, error)

Inventory assembles the full read-side snapshot: every manifest entry joined with install state, the system group, and the active job.

func (*Engine) Jobs

func (e *Engine) Jobs() (active *Job, recent []*Job)

Jobs returns the active job (with output tail) and recent history.

func (*Engine) Patch

func (e *Engine) Patch(name string, req PatchRequest) (*Job, error)

Patch merges fields into an existing tool and enqueues the follow-up job the transition needs: enable → install (when missing), disable → footprint uninstall (template kept), version change on an enabled tool → reinstall. Returns nil when no job is needed.

func (*Engine) Reconcile

func (e *Engine) Reconcile(mode ReconcileMode) (*Job, error)

Reconcile enqueues the convergence job: install missing enabled entries, uninstall the engine-owned footprint of disabled ones. ReconcileFull additionally enqueues an update pass over unpinned entries. Returns (nil, nil) when the manifest is empty (nothing to converge). The returned job is the reconcile job; Wait on it to gate consumer readiness (session creation, UI states).

func (*Engine) Remove

func (e *Engine) Remove(name string, force bool) (*Job, []string, error)

Remove uninstalls a tool and deletes its template. Without force, a tool that enabled entries require is refused and the dependents are returned. The removed manifest entries travel on the uninstall job so source-specific cleanup (npm/pip uninstalls, manual uninstall commands) still knows the sources after the manifest rows are gone.

func (*Engine) Search

func (e *Engine) Search(query string) []CatalogEntry

Search queries the catalog (empty query = featured set), hiding entries already in the manifest.

func (*Engine) Update

func (e *Engine) Update(names ...string) (*Job, error)

Update enqueues an update job over every unpinned, enabled tool (or the given names).

func (*Engine) Wait

func (e *Engine) Wait(ctx context.Context, jobID string) (*Job, error)

Wait blocks until the job reaches a terminal state and returns its final view.

type InstallSpec

type InstallSpec struct {
	URL         string
	Format      string // tar.gz | tar.xz | tar.zst | zip | gz | xz | raw
	ChecksumURL string // empty = no verification
	ChecksumAlg string
	Files       []AquaFile
}

InstallSpec is the fully resolved plan for downloading one tool version on this machine: a URL, an archive format, the binaries to link, and an optional checksum source.

type Inventory

type Inventory struct {
	Job    *Job         `json:"job,omitempty"`
	Tools  []ToolInfo   `json:"tools"`
	System []SystemTool `json:"system"`
}

Inventory is the full read-side snapshot: every manifest entry joined with state, the system group, and the active job.

type Job

type Job struct {
	ID    string   `json:"id"`
	Kind  string   `json:"kind"`
	State string   `json:"state"`
	Error string   `json:"error,omitempty"`
	Names []string `json:"names,omitempty"`
	// OutputTail carries the job's most recent output lines; populated
	// by Jobs() snapshots only (live output streams via the
	// Config.OnJobOutput callback).
	OutputTail []string `json:"output_tail,omitempty"`
	// Timestamps are Unix milliseconds.
	CreatedAt int64 `json:"created_at"`
	StartedAt int64 `json:"started_at,omitempty"`
	EndedAt   int64 `json:"ended_at,omitempty"`
}

Job is one queued/running/finished unit of engine work.

type Manifest

type Manifest struct {
	Tools map[string]Tool `json:"tools"`
	// Comment is a single reserved documentation key preserved across
	// engine rewrites (seed how-to text). Other unknown JSON keys are
	// NOT preserved; the manifest is not a general round-tripping
	// document.
	Comment []string `json:"_comment,omitempty"`
	Version int      `json:"version"`
}

Manifest is the tools.json document (schema ManifestVersion).

func DefaultSeed

func DefaultSeed() *Manifest

DefaultSeed returns the shared starter manifest: language-server templates for Go, TypeScript, and Python plus the GitHub CLI, all disabled. Nothing downloads until an entry is enabled; install knowledge (source, shims, dependencies, version) hydrates from the catalog at enable time. Returns a fresh copy on every call.

type PatchRequest

type PatchRequest struct {
	Version     *string            `json:"version,omitempty"`
	Pin         *bool              `json:"pin,omitempty"`
	Disabled    *bool              `json:"disabled,omitempty"`
	Description *string            `json:"description,omitempty"`
	Requires    *[]string          `json:"requires,omitempty"`
	Shims       *map[string]string `json:"shims,omitempty"`
	Install     *string            `json:"install,omitempty"`
	Uninstall   *string            `json:"uninstall,omitempty"`
	// Force permits disabling a tool that enabled entries require
	// (mirrors Remove's force).
	Force bool `json:"force,omitempty"`
}

PatchRequest edits an existing tool. Pointer fields distinguish "absent" from zero values. Disabled is the enable/disable toggle: false→true uninstalls the engine-owned footprint and keeps the template; true→false installs.

type ReconcileMode

type ReconcileMode int

ReconcileMode selects how much a Reconcile job does.

const (
	// ReconcileMissing converges intent without network: installs
	// missing enabled entries, uninstalls the engine-owned footprint of
	// disabled ones. Zero fetches when already converged.
	ReconcileMissing ReconcileMode = iota
	// ReconcileFull is ReconcileMissing plus an update pass over
	// unpinned entries (enqueued as a separate update job).
	ReconcileFull
)

type State

type State struct {
	Tools map[string]ToolStatus `json:"tools"`
}

State is the tools-state.json document.

type SystemTool

type SystemTool struct {
	Name      string `json:"name"`
	Installed bool   `json:"installed"`
}

SystemTool is one image-baked binary surfaced read-only (Config.System).

type Tool

type Tool struct {
	// Shims maps extra bin names to command lines, written as wrapper
	// scripts in the bin dir (e.g. typescript-language-server ->
	// "tsc --lsp --stdio"). Shims are full command lines, not symlinks.
	Shims map[string]string `json:"shims,omitempty"`
	// Source locates the install definition: "aqua:cli/cli",
	// "npm:pyright", "pip:x", "cargo:x", "go:golang.org/x/tools/gopls",
	// or "manual". Empty = hydrate from the catalog.
	Source string `json:"source,omitempty"`
	// Version is the concrete upstream version, exactly as upstream
	// tags it (may or may not carry a leading v). Never a range.
	// Empty = resolve latest when the tool is actively installed.
	Version string `json:"version,omitempty"`
	// Description is display text (catalog-provided or user-written).
	Description string `json:"description,omitempty"`
	// Origin records provenance for linked entries, e.g. "mcp:<name>"
	// for a tool created from an MCP add flow.
	Origin string `json:"origin,omitempty"`
	// Install is the shell command for Source == "manual". It runs via
	// bash with VERSION, BIN, TOOLS, OPT and ARCH_* in the environment.
	Install string `json:"install,omitempty"`
	// Uninstall optionally overrides cleanup for Source == "manual".
	Uninstall string `json:"uninstall,omitempty"`
	// Probe is the bin name whose presence marks the tool installed
	// (manual installs only; other sources derive it). Defaults to the
	// tool name.
	Probe string `json:"probe,omitempty"`
	// Requires lists other manifest/catalog tool names that must be
	// installed before (or alongside) this one, e.g. jdtls -> java.
	// Backend-level needs (npm->node, pip->uv, cargo->rust, go->go)
	// are implied and need not be listed.
	Requires []string `json:"requires,omitempty"`
	// Pin freezes the version: update runs skip this tool.
	Pin bool `json:"pin,omitempty"`
	// Disabled marks the entry a template: recorded intent whose
	// install is explicitly bypassed. The reconciler uninstalls a
	// disabled tool's engine-owned footprint and keeps the entry.
	// Absent (false) means enabled — presence in the manifest is
	// intent to have the tool installed.
	Disabled bool `json:"disabled,omitempty"`
}

Tool is one manifest entry: the user's intent for a single tool. Every field except the map key (the tool name) is optional; empty Source/Version/Shims/Requires hydrate from the catalog when the tool is installed or updated.

type ToolInfo

type ToolInfo struct {
	Shims            map[string]string `json:"shims,omitempty"`
	Name             string            `json:"name"`
	Source           string            `json:"source,omitempty"`
	Version          string            `json:"version,omitempty"`
	Description      string            `json:"description,omitempty"`
	Origin           string            `json:"origin,omitempty"`
	InstalledVersion string            `json:"installed_version,omitempty"`
	Latest           string            `json:"latest,omitempty"`
	LastError        string            `json:"last_error,omitempty"`
	Requires         []string          `json:"requires,omitempty"`
	Pin              bool              `json:"pin,omitempty"`
	Disabled         bool              `json:"disabled,omitempty"`
	// Lsp marks a language-server entry (catalog knowledge); consumers
	// use it for the no-LSP-enabled warning and UI badges.
	Lsp        bool `json:"lsp,omitempty"`
	Installed  bool `json:"installed"`
	Installing bool `json:"installing"`
}

ToolInfo is one tool row in Inventory: the manifest entry joined with the engine's install state.

type ToolStatus

type ToolStatus struct {
	// UpdatedAt is when this status last changed.
	UpdatedAt time.Time `json:"updated_at"`
	// InstalledVersion is the version last installed successfully.
	InstalledVersion string `json:"installed_version,omitempty"`
	// LastError is the failure message of the most recent install
	// attempt; cleared on success.
	LastError string `json:"last_error,omitempty"`
	// Bins are the names this tool owns in the bin dir (symlinks and
	// shim wrappers), removed on uninstall.
	Bins []string `json:"bins,omitempty"`
	// PMBins are package-manager bin names discovered by diffing the
	// pm's bin dir (npm/pip), symlinked into the bin dir.
	PMBins []string `json:"pm_bins,omitempty"`
}

ToolStatus is the engine-owned per-tool machine state.

Directories

Path Synopsis
cmd
toolcatalog module
Package httpapi is the HTTP projection of a toolbelt Engine: a REST surface over the Engine's Go API, one route per method, JSON in and out.
Package httpapi is the HTTP projection of a toolbelt Engine: a REST surface over the Engine's Go API, one route per method, JSON in and out.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL