Documentation
¶
Overview ¶
Package daemon — HTTP client helper. One canonical dial path for everything that wants to call the local daemon's HTTP listener: CLI subcommands (`clawtool peer …`, `clawtool a2a peers`) and the orchestrator TUI's peers panel both pump through here.
Centralizing this avoids three near-identical copies of "read state, read token, build request, set bearer + Content-Type, do it with a 5s timeout, decode JSON, surface daemon errors as Go errors" — and keeps timeout/auth invariants in one spot when we want to tune them.
Package daemon manages a single persistent `clawtool serve --listen --mcp-http` process the operator's hosts (Codex / OpenCode / Gemini / Claude Code) all fan into. Per ADR-014 (recursive) and the operator's design call: every host that registers clawtool as an MCP server should connect to the SAME backend so BIAM identity, task store, and notify channels are shared. Stdio-spawning a child per host would create N independent identities and N independent BIAM stores — cross-host notify cannot work that way.
State lives at $XDG_CONFIG_HOME/clawtool/daemon.json (LF-delimited, 0600). Token file (bearer) lives at $XDG_CONFIG_HOME/clawtool/ listener-token. Ensure starts the daemon if missing, returns the existing state otherwise; Stop SIGTERMs and cleans up.
This package is the only place that knows the daemon's process lifecycle. Adapters (mcp_host.go) and CLI (`clawtool daemon …`) drive it through Ensure / Stop / Status — they don't touch the state file directly.
Index ¶
- func FormatStatus(s *State) string
- func HTTPRequest(method, path string, body *bytes.Reader, out any) error
- func IsRunning(s *State) bool
- func LanExposureEnabled() bool
- func LanExposurePath() string
- func LogPath() string
- func ReadToken() (string, error)
- func SetLanExposure(on bool) error
- func StatePath() string
- func Stop() error
- func TokenPath() string
- type State
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func FormatStatus ¶
FormatStatus renders the daemon state as a multi-line human string for `clawtool daemon status`. Used by the CLI; tests assert on substrings not whole layout.
func HTTPRequest ¶ added in v0.22.36
HTTPRequest dials the local daemon's HTTP listener with the shared bearer token. body may be nil for GET/DELETE; out may be nil when the caller doesn't care about the response payload. Daemon-side errors (HTTP >= 300) are surfaced as Go errors with the daemon's JSON {"error": "..."} string when present.
func IsRunning ¶
IsRunning returns true when the recorded PID is alive AND the port still answers /v1/health within a short timeout. Both checks matter: a stale state file from a crashed daemon must not look healthy, and a port that no longer belongs to us (recycled by some other process) must not look ours.
func LanExposureEnabled ¶ added in v0.22.170
func LanExposureEnabled() bool
LanExposureEnabled reports whether LAN exposure has been opted into.
func LanExposurePath ¶ added in v0.22.170
func LanExposurePath() string
LanExposurePath is the marker file that opts this device's shared daemon into LAN exposure. Present ⇒ Ensure binds all interfaces with --allow-lan + token auth so circle peers can reach the read-only endpoints; absent ⇒ the default 127.0.0.1 --no-auth loopback daemon (the machine is the trust boundary). Same XDG conventions as StatePath.
func ReadToken ¶
ReadToken returns the bearer token contents (whitespace-trimmed). Empty string + nil error if the file is missing — Ensure ensures the file exists before exposing the token to callers.
func SetLanExposure ¶ added in v0.22.170
SetLanExposure toggles the LAN-exposure marker. The caller restarts the daemon afterwards so Ensure re-reads it and rebinds.
func StatePath ¶
func StatePath() string
StatePath returns the file Ensure / Stop persist to. Honors $XDG_CONFIG_HOME, else ~/.config/clawtool/daemon.json.
Types ¶
type State ¶
type State struct {
Version int `json:"version"`
PID int `json:"pid"`
Port int `json:"port"`
StartedAt time.Time `json:"started_at"`
TokenFile string `json:"token_file"`
LogFile string `json:"log_file"`
}
State is the persisted snapshot of a running daemon.
func Ensure ¶
Ensure starts the daemon if it isn't already running and returns the live State. Idempotent: if the daemon is already healthy, the existing state is returned without spawning.
Spawn flow: pick a free port, ensure the bearer token, fork the detached process, write the candidate state to daemon.json.pending, poll /v1/health with exponential backoff up to readinessDeadline (60 s), and only then atomically promote .pending → daemon.json. Order matters: daemon.json's appearance is the on-disk signal of "daemon is reachable", not "daemon process was forked." A spawn that crashes before bind leaves no daemon.json behind so the next Ensure invocation cleanly re-spawns instead of trusting a stale PID record. On readiness timeout, the returned error includes a tail of the daemon log so the install verb / operator sees the underlying bind failure without digging through XDG_STATE_HOME.
Concurrency: two CLI invocations within the spawn window (read-state → IsRunning → spawn → write-state) would both see "no daemon" and both fork, leaving an orphan racing for the state file + ports. We bracket the whole sequence with an OS advisory lock on a sibling .lock file (flock on POSIX, LockFileEx on Windows via fileLockExclusive). The fast path — a healthy daemon already running — does not need the lock; we re-check IsRunning inside the lock so a concurrent winner's state is observed before we duplicate-spawn.
func EnsureFrom ¶ added in v0.22.36
EnsureFrom is Ensure with an explicit binary path. Use this when the caller knows where the canonical clawtool binary lives and can't trust os.Executable() to resolve to the right inode — e.g. `clawtool upgrade` after the install-path swap, where the upgrading CLI process is running from the freshly-renamed `.clawtool.old` backup that may already have been unlinked. An empty exePath falls back to os.Executable() which is correct for every non-upgrade caller.
func ReadState ¶
ReadState returns the persisted state, or (nil, nil) if no daemon has been started yet. Parse errors are returned verbatim so callers can decide whether to wipe + retry.