daemon

package
v0.22.157 Latest Latest
Warning

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

Go to latest
Published: May 23, 2026 License: MIT Imports: 16 Imported by: 0

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

Constants

This section is empty.

Variables

This section is empty.

Functions

func FormatStatus

func FormatStatus(s *State) string

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

func HTTPRequest(method, path string, body *bytes.Reader, out any) error

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

func IsRunning(s *State) bool

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 LogPath

func LogPath() string

LogPath returns the daemon's combined-output log path.

func ReadToken

func ReadToken() (string, error)

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 StatePath

func StatePath() string

StatePath returns the file Ensure / Stop persist to. Honors $XDG_CONFIG_HOME, else ~/.config/clawtool/daemon.json.

func Stop

func Stop() error

Stop sends SIGTERM, waits up to 5s, escalates to SIGKILL, then removes the state file. No-op if no daemon is recorded.

func TokenPath

func TokenPath() string

TokenPath returns the bearer-token file the daemon and adapters share. Same XDG conventions as StatePath.

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

func Ensure(ctx context.Context) (*State, error)

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 state, poll /v1/health for up to 5s.

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

func EnsureFrom(ctx context.Context, exePath string) (*State, error)

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

func ReadState() (*State, error)

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.

func (*State) HealthURL

func (s *State) HealthURL() string

HealthURL is the unauthenticated probe URL the daemon exposes for readiness checks.

func (*State) URL

func (s *State) URL() string

URL is the MCP-over-HTTP endpoint hosts dial.

Jump to

Keyboard shortcuts

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