version

package
v0.22.95 Latest Latest
Warning

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

Go to latest
Published: May 1, 2026 License: MIT Imports: 17 Imported by: 0

Documentation

Overview

Package version — `clawtool version --check` runner.

Wraps CheckForUpdate with CLI-shaped output + exit codes so a monitoring script (CI cron, system-package wrapper, fleet inventory) can pipeline-detect out-of-date clawtool installs without parsing human banners. The cache + GitHub-API logic lives in update.go; this file is purely the rendering / exit-code layer.

Exit-code contract (stable, scripts will gate on it):

0 — up-to-date (running binary's version >= latest tag)
1 — newer release available
2 — check itself failed (network, parse, rate-limit)

Anyone changing these values needs to rev the wiki/decisions doc that promises them — they're now part of the public CLI contract.

Package version — daemon-side periodic update poller. Every `Interval` ticks (default 1h) the poller calls `CheckForUpdate`; when a transition from no-update → update-available is detected it broadcasts a SystemNotification onto the supplied publisher (typically biam.WatchHub.BroadcastSystem). Connected watchers — orchestrator, dashboard, `task watch`, MCP clients dialling the watch socket — render the inline banner immediately, no polling.

Why daemon-side rather than per-CLI: the CLI is short-lived; the daemon (`clawtool serve`) is the long-running process the operator already keeps up. One canonical poller, single GitHub round-trip per host per hour, push to every active surface.

Telemetry: each transition emits a `clawtool.update_check` event with the same allow-listed payload SessionStart uses, so the operator gets a unified PostHog view of update detection across surfaces.

Package version — update-check primitive. Hits the GitHub Releases API for the latest tag and reports whether the local build is older. Cached for 24h in ~/.cache/clawtool/update.json so a `clawtool doctor` (or any other surface that calls CheckForUpdate) doesn't pay a network roundtrip on every invocation.

No background polling, no telemetry — this is a stateless, user-initiated check. The cache exists purely to avoid being rude to the GitHub API.

Package version exposes the clawtool build version.

`Info()` and `InfoJSON()` extend the surface for shell-pipeline consumers that want a structured snapshot (`clawtool version --json`) instead of the human banner. Single source of truth so telemetry / health-probe / version-gate scripts all read the same shape.

Three layers, picked in order:

  1. ldflags override — `go build -ldflags='-X github.com/cogitave/clawtool/internal/version.Version=v…'`. goreleaser sets this on every release tarball, so installed binaries always carry the exact tag.

  2. runtime/debug.ReadBuildInfo — module-cached `go install` binaries surface the tag here. Local `go build` from a working tree returns "(devel)".

  3. The release-please-tracked constant below — fallback for dev workflows where neither (1) nor (2) yields a real version.

`Resolved()` is the single function every caller (overview, upgrade, claude-bootstrap, telemetry) must use to read the effective version. Reading `Version` directly (the constant) will diverge from what the binary actually is when goreleaser stamped a different value via ldflags.

Index

Constants

View Source
const Name = "clawtool"

x-release-please-start-version

View Source
const UpdateCheckURL = "https://api.github.com/repos/cogitave/clawtool/releases/latest"

UpdateCheckURL is the GitHub Releases API endpoint we hit. The public API permits ~60 unauthenticated requests/hour per IP; the 24h cache keeps us well under that even on shared CI runners.

Variables

View Source
var Version = "0.21.7" // x-release-please-version

Version is the build-stamped semver string. Declared as `var` (not `const`) so goreleaser can override it via `-ldflags='-X github.com/cogitave/clawtool/internal/version.Version=…'` at link time. `-X` cannot patch constants; that's why this is a var even though it's effectively immutable at runtime.

Functions

func InfoJSON added in v0.22.40

func InfoJSON() (string, error)

InfoJSON renders Info() as indented JSON, ready to print. The indented form is the same shape Marshal would emit; we use MarshalIndent because operators inspecting the output by eye expect a pretty document, and shell pipelines (`jq`) handle either form transparently.

func Resolved added in v0.22.8

func Resolved() string

Resolved returns the authoritative installed-binary version. First-call computation is cached for the process lifetime — the binary's version doesn't change while it's running.

Output strips any leading "v" so callers can pass it straight into Compare() without normalising at every call site.

**Every external surface MUST use this** — telemetry events, hook payloads, /v1/health JSON, A2A card, doctor banner, orchestrator probe, MCP serverInfo. The literal `Version` var holds the pre-build fallback ("0.21.7") and reads of it outside this package are an anti-pattern: a goreleaser-baked binary at v0.22.34 emitting the const looks like v0.21.7 to every consumer (operator's PostHog filter, A2A peer, /v1/health probe — all silently wrong). The bug repeated across 9 sites before the operator caught it on 2026-04-29 ("12 hours, no telemetry events"). Don't repeat it — call Resolved().

func RunCheck added in v0.22.40

func RunCheck(ctx context.Context, jsonOutput bool, w io.Writer) int

RunCheck performs an update probe, renders the result to w, and returns the CLI exit code. When jsonOutput is true the rendered form is the indented JSON marshal of CheckResult; otherwise a short human banner that names the current + latest versions and points at `clawtool upgrade` when there's drift.

The probe routes through CheckForUpdate, which honours the 5-minute on-disk cache so scripts that gate on this every loop don't hammer the GitHub API.

func String

func String() string

String is the formatted "clawtool X.Y.Z" banner the CLI prints.

Types

type BuildInfo added in v0.22.40

type BuildInfo struct {
	Name      string `json:"name"`
	Version   string `json:"version"`
	GoVersion string `json:"go_version"`
	Platform  string `json:"platform"` // GOOS/GOARCH
	// Commit is the git revision baked in by `go build` via
	// debug.ReadBuildInfo. Empty when the binary was built without
	// VCS info (e.g. via the goreleaser pipeline that strips it).
	Commit string `json:"commit,omitempty"`
	// Modified reports whether the working tree was dirty when the
	// binary was built. Best-effort — only populated when the build
	// captured VCS info.
	Modified bool `json:"modified,omitempty"`
}

BuildInfo is the structured snapshot emitted by `clawtool version --json`. Single source of truth for any external probe (telemetry, /v1/health, monitoring scripts) that wants to reason about the running binary's identity. snake_case JSON tags follow the project's convention (mirrors the agents.Status / agentListEntry shape from earlier ticks).

func Info added in v0.22.40

func Info() BuildInfo

Info returns a structured snapshot of the running binary. Wraps Resolved() so the version string respects the same ldflags / debug.BuildInfo / fallback hierarchy.

type CheckResult added in v0.22.40

type CheckResult struct {
	Up        bool      `json:"up_to_date"`
	Current   string    `json:"current"`
	Latest    string    `json:"latest,omitempty"`
	FetchedAt time.Time `json:"fetched_at"`
	Error     string    `json:"error,omitempty"`
}

CheckResult is the structured payload of a `version --check` invocation. snake_case JSON tags follow the project-wide wire convention (mirrors BuildInfo / agents.Status / agentListEntry).

`Up` collapses HasUpdate + Err into the binary "are we current?" signal a script actually wants — a check failure is *not* an affirmative "up to date" answer (Up=false, Error!="" → exit 2).

func (CheckResult) ExitCode added in v0.22.40

func (r CheckResult) ExitCode() int

ExitCode classifies a result into the documented CLI exit codes (0 / 1 / 2). Centralised here so both RunCheck and any future callers (HTTP probe, scripted wrapper) agree on the mapping.

type Poller added in v0.22.9

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

Poller wraps the periodic update probe + publisher. Lifetime = daemon process. Stop via ctx cancellation.

func NewPoller added in v0.22.9

func NewPoller(pub PublishFn, cfg PollerConfig, track func(outcome string)) *Poller

NewPoller constructs the poller with the given publisher and optional telemetry tracker. `track` is called on every check with the outcome enum ("up_to_date" | "update_available" | "check_failed"); pass nil to skip telemetry.

func (*Poller) Run added in v0.22.9

func (p *Poller) Run(ctx context.Context)

Run blocks until ctx cancels, ticking once per Interval. The first check fires immediately so a fresh daemon catches an already-pending update without waiting an hour.

type PollerConfig added in v0.22.9

type PollerConfig struct {
	// Interval between checks. Default 1h. Tests pass 50ms.
	Interval time.Duration
	// Timeout per HTTP round-trip. Default 5s.
	Timeout time.Duration
	// Now overrides time.Now for deterministic testing of
	// transitions. Production passes nil.
	Now func() time.Time
}

PollerConfig overrides the defaults — useful for tests that need a tighter tick. Empty struct = production defaults.

type PublishFn added in v0.22.9

type PublishFn func(kind, severity, title, body, actionHint string)

PublishFn is the slim function shape the poller needs from the caller. server.go wraps biam.WatchHub.BroadcastSystem; tests pass a recorder closure. Keeping this as a function instead of an interface avoids dragging biam into the version package's import graph (version stays a leaf).

type UpdateInfo added in v0.9.0

type UpdateInfo struct {
	// HasUpdate is true when the upstream tag is newer than the
	// local build. Defaults to false on any error so we never nag
	// the user about a non-confirmed update.
	HasUpdate bool

	// Latest is the tag string from GitHub (e.g. "v0.10.0"). Empty
	// on error.
	Latest string

	// Current is the local build version as Resolved() returns it
	// — i.e. the goreleaser-stamped tag when present, else the
	// debug.BuildInfo Main.Version, else the package fallback.
	// Reading the bare `Version` var here would mis-report on every
	// production binary (it carries the dev-fallback "0.21.7"); a
	// HasUpdate=true would fire for already-current installs.
	Current string

	// FetchedAt is when we last asked GitHub. Surfaced so the user
	// knows how stale the answer is. UTC.
	FetchedAt time.Time

	// Err is non-nil when the check itself failed (network, parse,
	// rate-limit). Callers display "could not check" instead of
	// pretending no update exists.
	Err error
}

UpdateInfo is the result a caller surfaces in the UI.

func CheckForUpdate added in v0.9.0

func CheckForUpdate(ctx context.Context) UpdateInfo

CheckForUpdate is the entry point. Returns the cached result when fresh; otherwise hits the API, persists, returns. Never blocks longer than the HTTP client's timeout.

Jump to

Keyboard shortcuts

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