{"_id":"@airtrafficcontrol/daemon","name":"@airtrafficcontrol/daemon","dist-tags":{"latest":"0.0.1"},"versions":{"0.0.1":{"name":"@airtrafficcontrol/daemon","version":"0.0.1","private":false,"type":"module","main":"./dist/index.js","types":"./dist/index.d.ts","dependencies":{"@fastify/websocket":"^11.2.0","fastify":"^5.3.3","zod":"^3.25.76","@airtrafficcontrol/adapter-claude-agent-sdk":"0.1.0","@airtrafficcontrol/tower":"0.0.1","@airtrafficcontrol/core":"0.1.0","@airtrafficcontrol/checklist":"0.0.1","@airtrafficcontrol/errors":"0.0.1","@airtrafficcontrol/types":"0.0.1"},"devDependencies":{"@types/node":"^25.5.0","@types/ws":"^8.18.1","tsx":"^4.21.0","ws":"^8.20.0"},"scripts":{"build":"tsc --build","start":"node --import tsx src/start.ts","seed:demo":"node --import tsx src/seed-demo-cli.ts"},"_id":"@airtrafficcontrol/daemon@0.0.1","description":"Long-running ATC daemon process. Provides a Fastify-based REST API and WebSocket channels for managing agents, crafts, vectors, pilots, and tower operations. Handles configuration loading, state persistence, git worktree management, adapter registration, ","_integrity":"sha512-vph6ljx0j8FmAVnrTaiLyRtmGr2FBSUXzx67QNjyiICcUZPQeAbrqnChCxGp84FmK6JKSijOo4tPydHziob90A==","_resolved":"/private/var/folders/n5/tzqmrds50218kyrpz8166v3w0000gn/T/2366cc7fe007bb22e40d418d97b53e7a/airtrafficcontrol-daemon-0.0.1.tgz","_from":"file:airtrafficcontrol-daemon-0.0.1.tgz","_nodeVersion":"24.11.1","_npmVersion":"11.6.2","dist":{"integrity":"sha512-vph6ljx0j8FmAVnrTaiLyRtmGr2FBSUXzx67QNjyiICcUZPQeAbrqnChCxGp84FmK6JKSijOo4tPydHziob90A==","shasum":"ee17b094003d01d81a6ab21b0664f7052e8448f4","tarball":"https://registry.npmjs.org/@airtrafficcontrol/daemon/-/daemon-0.0.1.tgz","fileCount":480,"unpackedSize":1852313,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCICUlKAtJ46huKbtyzQ5BdZbi6z1yoCxKZ3Dwu+X//LVDAiA27CAzvmzWzrq0bPvgh+RVEtWryjsw14IRZ8+cvYgvWA=="}]},"_npmUser":{"name":"mfoulks200","email":"mfoulks1@gmail.com"},"directories":{},"maintainers":[{"name":"mfoulks200","email":"mfoulks1@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/daemon_0.0.1_1776908447431_0.14875828172365058"},"_hasShrinkwrap":false}},"time":{"created":"2026-04-23T01:40:47.231Z","0.0.1":"2026-04-23T01:40:47.619Z","modified":"2026-04-23T01:40:47.952Z"},"maintainers":[{"name":"mfoulks200","email":"mfoulks1@gmail.com"}],"description":"Long-running ATC daemon process. Provides a Fastify-based REST API and WebSocket channels for managing agents, crafts, vectors, pilots, and tower operations. Handles configuration loading, state persistence, git worktree management, adapter registration, ","readme":"# @airtrafficcontrol/daemon\n\nLong-running ATC daemon process. Provides a Fastify-based REST API and WebSocket channels for managing agents, crafts, vectors, pilots, and tower operations. Handles configuration loading, state persistence, git worktree management, adapter registration, PID file lifecycle, and graceful shutdown.\n\n## Installation\n\n```bash\npnpm add @airtrafficcontrol/daemon\n```\n\nThis is an internal workspace package (`workspace:*`).\n\n## API Reference\n\n### `Daemon` Class\n\nTop-level orchestrator for a running ATC daemon instance. Create one per process.\n\n```typescript\nimport { Daemon } from \"@airtrafficcontrol/daemon\";\n\nconst daemon = new Daemon(\"/home/user/.atc/profiles/default\");\nawait daemon.start();\nconsole.log(`Listening on port ${daemon.port}`);\n```\n\n#### `constructor(profileDir: string)`\n\nCreates a daemon instance for the given profile directory (must contain `config.json`).\n\n#### `start(): Promise<void>`\n\nStarts the daemon. Sequence:\n1. Load profile config\n2. Initialize state stores (agent, craft, tower)\n3. Create Fastify app with REST routes and WebSocket channels\n4. Bind to configured host/port\n5. Start periodic state flush scheduler\n6. Write PID file\n7. Register SIGTERM/SIGINT handlers for graceful shutdown\n\n#### `stop(): Promise<void>`\n\nStops the daemon gracefully. Flushes state to disk, stops the scheduler, closes the server, and removes the PID file.\n\n#### Properties\n\n| Property | Type | Description |\n|---|---|---|\n| `isRunning` | `boolean` | Whether the daemon is currently accepting connections |\n| `port` | `number` | The bound TCP port (meaningful after `start()` resolves) |\n\n### Server\n\n#### `createApp(options: AppOptions): FastifyInstance`\n\nCreates the Fastify application with all REST routes and WebSocket support.\n\n#### REST Routes\n\n| Route Group | Prefix | Purpose |\n|---|---|---|\n| Health | `/health` | Liveness and readiness checks |\n| Projects | `/projects` | Project metadata |\n| Crafts | `/crafts` | Craft CRUD and lifecycle |\n| Vectors | `/vectors` | Vector reporting and status |\n| Tower | `/tower` | Landing clearance and merge queue |\n| Agents | `/agents` | Agent lifecycle management |\n| Pilots | `/pilots` | Pilot registration and assignment |\n| Intercom | `/intercom` | Inter-pilot messaging |\n| Black Box | `/blackbox` | Append-only event logging |\n| Config | `/api/v1/config/global` | Read, replace, patch, and unset keys on the global config store |\n\n### Adapter System\n\n#### `AgentAdapter` Interface\n\nContract for pluggable agent runtime backends. Adapters abstract how the daemon launches, communicates with, and controls agent processes.\n\n| Method | Description |\n|---|---|\n| `launch(options)` | Launch a new agent, return a handle |\n| `pause(handle)` | Pause without terminating |\n| `resume(handle, context)` | Resume a paused agent |\n| `terminate(handle)` | Permanently terminate |\n| `isAlive(handle)` | Check liveness |\n| `sendMessage(handle, message)` | Send intercom message |\n| `onMessage(handle, callback)` | Listen for messages |\n| `onStatusChange(handle, callback)` | Listen for status changes |\n| `onUsageReport(handle, callback)` | Listen for usage reports |\n\n#### `AgentHandle`\n\nOpaque handle returned when an agent is launched.\n\n| Property | Type | Description |\n|---|---|---|\n| `agentId` | `string` | Unique agent identifier (UUID) |\n| `pid` | `number?` | OS process ID if subprocess-backed |\n| `adapterMeta` | `Record<string, unknown>` | Adapter-specific metadata |\n\n#### `AgentLaunchOptions`\n\n| Property | Type | Description |\n|---|---|---|\n| `agentId` | `string` | UUID to assign |\n| `worktreePath` | `string` | Absolute path to git worktree |\n| `craft` | `CraftState` | Full persisted craft state |\n| `systemPrompt` | `string` | System prompt for agent context |\n| `intercomHistory` | `IntercomMessage[]` | Prior messages to replay |\n| `adapterConfig` | `Record<string, unknown>` | Adapter-specific config |\n| `mcpServers` | `Record<string, McpServerConfig>` | MCP server configurations |\n\n#### `AdapterRegistry`\n\nRegistry for managing adapter implementations by name.\n\n### State Stores\n\n#### `AgentStore`\n\nPersists agent records to disk. Supports `load()` and `save()`.\n\n#### `CraftStore`\n\nPersists craft state to disk. Supports `saveAll()`.\n\n#### `TowerStore`\n\nPersists tower/merge queue state.\n\n### Configuration\n\nConfig schemas are validated with [Zod](https://zod.dev). Global, profile, project, and agent scopes share the same layered-store abstraction: in-memory merged view, sparse on-disk diff against defaults, atomic writes, and a single apply path for REST, WebSocket, and file-watch mutations.\n\n#### `LayeredConfigStore<T>`\n\nGeneric, runtime-editable config store for a single scope. Owns in-memory state, persists only the sparse diff against defaults, and funnels all mutations through one internal apply path so REST, WebSocket, and file-watcher writes stay consistent. Free of daemon-specific wiring — project and agent scopes can reuse it by instantiating with a different schema, defaults, file path, and channel.\n\n| Method | Description |\n|---|---|\n| `load()` | One-shot read of the backing file. Populates in-memory state and emits a single `change` event with source `\"init\"`. |\n| `get()` | Returns the fully-merged view (defaults + overrides). |\n| `getOverrides()` | Returns only the sparse on-disk shape. |\n| `replace(next)` | Full replace. Missing known fields revert to default. Throws `ConfigValidationError` on schema failure. |\n| `patch(partial)` | Partial merge. Omitted fields are left untouched. |\n| `unset(key)` | Revert one known key to its default. Throws `UnknownConfigKeyError` if the key is not in the schema. |\n| `start()` | Begin watching the backing file for external edits (debounced `fs.watch` with mtime + sha256 fingerprinting to skip self-writes). |\n| `stop()` | Stop watching and await any in-flight write. |\n| `on(\"change\", fn)` | Subscribe to change events: `(merged, source: \"api\" \\| \"file\" \\| \"init\") => void`. |\n| `on(\"invalid_external_edit\", fn)` | Subscribe to invalid-external-edit events; fires when a file-watch reload fails validation. In-memory state is preserved. |\n\nEvery mutation is also published on the store's configured pub/sub channel (e.g. `config:global`) so WebSocket subscribers see the same change stream.\n\n#### `createGlobalConfigStore(atcDir, publish, logger)`\n\nFactory that wires a `LayeredConfigStore<GlobalConfig>` to `GLOBAL_CONFIG_SCHEMA`, `GLOBAL_CONFIG_DEFAULTS`, `<atcDir>/config.json`, and the `config:global` channel. The daemon constructs one during bootstrap and exposes it on the Fastify instance as `app.globalConfigStore` so REST and WebSocket handlers share a single instance.\n\n#### Legacy Loaders\n\n- `loadGlobalConfig(): Promise<GlobalConfig>` — one-shot read of the global config, used at bootstrap before the store is constructed.\n- `loadProfileConfig(profileDir: string): Promise<ProfileConfig>` — loads profile-specific configuration from a profile directory.\n- `loadProjectMetadata(): Promise<ProjectMetadata>` — loads project metadata for the current working directory.\n- `resolveProfilePath(profileName: string): string` — resolves the filesystem path for a named profile.\n\nAll loaders parse through Zod and surface structured issues via `ConfigValidationError` from `@airtrafficcontrol/errors`.\n\n#### Config Defaults\n\n- `GLOBAL_CONFIG_DEFAULTS` — Default values for global configuration\n- `PROFILE_CONFIG_DEFAULTS` — Default values for profile configuration\n\n#### WebSocket Config Dispatch\n\nClients can mutate the global config through the WebSocket surface as well as REST. Messages are routed to the same `app.globalConfigStore` and receive a `config.ack` reply.\n\n| Client message | Handler |\n|---|---|\n| `config.patch` | `store.patch(body)` |\n| `config.replace` | `store.replace(body)` |\n| `config.unset` | `store.unset(key)` |\n\nValidation failures return `config.ack` frames with structured error details (`INVALID_CONFIG` or `UNKNOWN_CONFIG_KEY`).\n\n### Git Utilities\n\n#### `initBareRepo(path: string): Promise<void>`\n\nInitializes a bare git repository at the given path.\n\n#### `cloneBareRepo(url: string, path: string): Promise<void>`\n\nClones a repository as a bare repo.\n\n#### `fetchBareRepo(path: string): Promise<void>`\n\nFetches updates into a bare repository.\n\n#### `createWorktree(bareRepoPath: string, worktreePath: string, branch: string): Promise<void>`\n\nCreates a git worktree from a bare repository.\n\n#### `removeWorktree(bareRepoPath: string, worktreePath: string): Promise<void>`\n\nRemoves a git worktree.\n\n### WebSocket Channels\n\n#### `ChannelRegistry`\n\nManages WebSocket channels for real-time event streaming.\n\n### Checklist Runner\n\n#### `runChecklist(items): Promise<ChecklistResult>`\n\nRuns the landing checklist pipeline within the daemon context.\n\n### Process Utilities\n\n#### `writePidFile(path: string): Promise<void>`\n\nWrites the current process ID to a file.\n\n#### `readPidFile(path: string): Promise<number | null>`\n\nReads a PID from a file.\n\n#### `removePidFile(path: string): Promise<void>`\n\nRemoves a PID file.\n\n#### `isProcessAlive(pid: number): boolean`\n\nChecks whether a process is alive by PID.\n\n### Key Types\n\n| Type | Description |\n|---|---|\n| `GlobalConfig` | Global ATC configuration |\n| `ProfileConfig` | Profile-specific configuration |\n| `AdapterConfig` | Adapter configuration |\n| `ProjectMetadata` | Project metadata |\n| `CraftState` | Full persisted craft state |\n| `PilotRecord` | Persisted pilot record |\n| `AgentRecord` | Persisted agent record |\n| `AgentStatus` | Agent lifecycle status |\n| `IntercomMessage` | Inter-pilot message |\n| `WsEvent` | WebSocket event |\n| `WsClientMessage` | Client-to-server WebSocket message |\n| `WsServerMessage` | Server-to-client WebSocket message |\n| `TokenUsage` | Token consumption report |\n| `ToolUsageEntry` | Tool usage tracking |\n| `SkillUsageEntry` | Skill usage tracking |\n| `AgentUsageReport` | Aggregate agent usage report |\n\n## Dependencies\n\n| Package | Purpose |\n|---|---|\n| `@airtrafficcontrol/types` | Domain types and enums |\n| `@airtrafficcontrol/errors` | Error classes |\n| `@airtrafficcontrol/core` | Craft lifecycle and controls logic |\n| `@airtrafficcontrol/tower` | Merge coordination |\n| `@airtrafficcontrol/checklist` | Landing checklist |\n| `fastify` | HTTP server framework |\n| `@fastify/websocket` | WebSocket support |\n| `zod` | Config schema validation |\n\n## Related Packages\n\n- [`@airtrafficcontrol/adapter-claude-agent-sdk`](../adapter-claude-agent-sdk/) — Claude Agent SDK adapter implementing `AgentAdapter`\n- [`@airtrafficcontrol/core`](../core/) — Runtime logic consumed by daemon routes\n- [`@airtrafficcontrol/tower`](../tower/) — Tower logic managed by the daemon\n","readmeFilename":"README.md","_rev":"1-a9f0a580ec544ad3fa01fad5a92b914b"}