Documentation
¶
Overview ¶
Package daemon implements the job queue for overkill's daemon mode (§8.7.3).
Jobs arrive via the ACP /v1/jobs endpoint and are persisted in PostgreSQL so the queue survives restarts. A Worker pool picks them up, calls a pluggable RunFunc, and transitions the job through the state machine:
queued → running → completed
→ failed
queued → cancelled (explicit cancel before pickup)
running → cancelled (explicit cancel during run)
running → suspended (waiting for remote approval)
Package daemon — UNIX-socket RPC between the running daemon and CLI or TUI clients. Line-delimited JSON over a stream socket; one request per line, one response per line. Small surface on purpose — anything richer than ping/list/cancel either grows the protocol explicitly or reads/writes the Postgres DB directly (the daemon and clients share the same database).
Why UNIX socket and not gRPC / HTTP?
- Zero dependencies; net package only
- Filesystem permissions act as the access boundary (0600 on the socket file → same user only)
- No port allocation, no localhost binding to fight firewalls
- Tests use a short private socket directory on macOS
Index ¶
- Variables
- func SocketPath() (string, error)
- type Client
- type Handler
- type Job
- type JobStatus
- type JobStore
- func (s *JobStore) Cancel(ctx context.Context, id string) error
- func (s *JobStore) Close() error
- func (s *JobStore) Create(ctx context.Context, job Job) error
- func (s *JobStore) Get(ctx context.Context, id string) (*Job, error)
- func (s *JobStore) List(ctx context.Context) ([]Job, error)
- func (s *JobStore) UpdateStatus(ctx context.Context, id string, status JobStatus, errMsg string) error
- type RPCError
- type Request
- type Response
- type RunFunc
- type Server
- type Worker
Constants ¶
This section is empty.
Variables ¶
var ErrDaemonDown = errors.New("daemon: not running (socket not reachable)")
ErrDaemonDown signals "couldn't reach the daemon socket at all" — as opposed to "the daemon reached returned an error". Lets the CLI tell the user "start the daemon first" instead of leaking a socket-not- found error from net.Dial.
Functions ¶
func SocketPath ¶
SocketPath returns the canonical daemon socket path. Distinct from the pidfile because some platforms (macOS) restrict socket paths to 104 chars; we keep the directory shallow.
Types ¶
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client is a one-shot RPC caller. Each Call opens a fresh connection — the daemon socket is not heavily used and pooling adds complexity (idle conn timeouts, broken-pipe detection) for negligible win.
func NewClient ¶
NewClient returns a Client bound to the standard daemon socket path or a custom path (tests). Default timeout 5s; raise if a downstream op is known to be slow.
type Handler ¶
Handler resolves one request to a response. Returning a non-nil error short-circuits to a Response with the error's Error() as Err and Code "internal".
type Job ¶
type Job struct {
ID string `json:"id"`
SessionID string `json:"session_id"`
Intent string `json:"intent"` // the user prompt
Status JobStatus `json:"status"`
Channel string `json:"channel"` // originating bridge channel
ChatKey string `json:"chat_key"`
Profile string `json:"profile"` // "remote" for bridge-originated
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
Error string `json:"error,omitempty"`
}
Job is one unit of deferred work submitted via the ACP bridge.
type JobStore ¶
type JobStore struct {
// contains filtered or unexported fields
}
JobStore persists jobs to PostgreSQL.
func NewJobStore ¶
NewJobStore wraps a *sql.DB. Caller owns DB lifecycle.
func (*JobStore) Create ¶
Create persists a new Job. The job must have a non-empty ID; callers should set it with uuid.New().String() before calling Create.
type RPCError ¶
RPCError wraps a daemon-returned error. Code is the machine-readable kind; Message is the human description.
type Request ¶
type Request struct {
Op string `json:"op"`
Params json.RawMessage `json:"params,omitempty"`
}
Request is one inbound RPC. Op is required; Params is op-specific.
type Response ¶
type Response struct {
Result json.RawMessage `json:"result,omitempty"`
Err string `json:"error,omitempty"`
Code string `json:"code,omitempty"`
}
Response is one outbound RPC reply. Either Result or Err is set; never both. Code lets clients branch on machine-readable error kinds without parsing the human Err string.
type Server ¶
type Server struct {
// contains filtered or unexported fields
}
Server owns the listener + accept loop. Stop() is idempotent so shutdown paths (signal + explicit) can both call it without racing.
func NewServer ¶
NewServer returns an unstarted server bound to path. The path is removed first if a stale socket exists from a prior process; we don't try to detect liveness — the caller's pidfile check is the authority for "another daemon is running."
func (*Server) Register ¶
Register binds op → handler. Last write wins so the daemon's wiring can override the built-in ping if it ever wants to.
type Worker ¶
type Worker struct {
// contains filtered or unexported fields
}
Worker is a bounded pool that picks up queued jobs, runs them, and updates their status in JobStore.
func (*Worker) Start ¶
Start launches the background dispatch loop and recovers any orphaned jobs from a previous daemon crash (queued or running jobs that never finished). Also starts a TTL watcher that periodically resets stuck "running" jobs (M18).