airaknit 1.1.2-rc.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +84 -0
- package/README.md +202 -0
- package/bin/airaknit +9 -0
- package/bin/airaknit-project +14 -0
- package/bin/kanna +9 -0
- package/dist/client/assets/CompactSummaryMessage-Yw0BDWEJ.js +1 -0
- package/dist/client/assets/ExitPlanModeMessage-DIdkQ4uF.js +1 -0
- package/dist/client/assets/LocalFilePreviewDialog-DQx2eiCc.js +3 -0
- package/dist/client/assets/LocalProjectsSection-C4xlWkgS.js +1 -0
- package/dist/client/assets/TextMessage-B5G39DEJ.js +1 -0
- package/dist/client/assets/UserMessage-CIkWk-0L.js +1 -0
- package/dist/client/assets/_basePickBy-CVrAFfnZ.js +1 -0
- package/dist/client/assets/_baseUniq-JL-aaF4P.js +1 -0
- package/dist/client/assets/arc-B07zg7ol.js +1 -0
- package/dist/client/assets/architecture-YZFGNWBL-PSLVJL3p.js +1 -0
- package/dist/client/assets/architectureDiagram-Q4EWVU46-DfWIF1G_.js +36 -0
- package/dist/client/assets/array-BGFCBI0e.js +1 -0
- package/dist/client/assets/blockDiagram-DXYQGD6D-CzwIeo_B.js +132 -0
- package/dist/client/assets/bundle-mjs-BDE2gWbQ.js +1 -0
- package/dist/client/assets/button-DO50qOGv.js +1 -0
- package/dist/client/assets/c4Diagram-AHTNJAMY-CR8DCQRE.js +10 -0
- package/dist/client/assets/channel-Dj-UUfaF.js +1 -0
- package/dist/client/assets/chunk-2KRD3SAO-dznP-cn8.js +1 -0
- package/dist/client/assets/chunk-336JU56O-Dqss5Vu6.js +2 -0
- package/dist/client/assets/chunk-426QAEUC-CEKis_0_.js +1 -0
- package/dist/client/assets/chunk-4BX2VUAB-BYOv0Gm1.js +1 -0
- package/dist/client/assets/chunk-4TB4RGXK-BxzubH5S.js +206 -0
- package/dist/client/assets/chunk-55IACEB6-BSlTj03a.js +1 -0
- package/dist/client/assets/chunk-5FUZZQ4R-9Au93Bi1.js +62 -0
- package/dist/client/assets/chunk-5PVQY5BW-BhksHFEZ.js +2 -0
- package/dist/client/assets/chunk-67CJDMHE-BFwhz-8t.js +1 -0
- package/dist/client/assets/chunk-7N4EOEYR-BDUPds87.js +1 -0
- package/dist/client/assets/chunk-AA7GKIK3-CEWTdyXO.js +1 -0
- package/dist/client/assets/chunk-BO2N2NFS-D0LvxnhU.js +103 -0
- package/dist/client/assets/chunk-BSJP7CBP-BNJnK6sq.js +1 -0
- package/dist/client/assets/chunk-Bj-mKKzh.js +1 -0
- package/dist/client/assets/chunk-CIAEETIT-CYhfoCeN.js +1 -0
- package/dist/client/assets/chunk-EDXVE4YY-C5ovJLc0.js +1 -0
- package/dist/client/assets/chunk-ENJZ2VHE-HOhYaeGr.js +10 -0
- package/dist/client/assets/chunk-FMBD7UC4-BLCiKcAQ.js +15 -0
- package/dist/client/assets/chunk-FOC6F5B3-B6GtY2ek.js +1 -0
- package/dist/client/assets/chunk-ICPOFSXX-DPoIZoC5.js +122 -0
- package/dist/client/assets/chunk-K5T4RW27-BsKN63rv.js +94 -0
- package/dist/client/assets/chunk-KGLVRYIC-BUGn9uuY.js +1 -0
- package/dist/client/assets/chunk-LIHQZDEY-DhaZyo03.js +1 -0
- package/dist/client/assets/chunk-ORNJ4GCN-DlpeeJyi.js +1 -0
- package/dist/client/assets/chunk-OYMX7WX6-Dc9q7aYA.js +231 -0
- package/dist/client/assets/chunk-QZHKN3VN-BEdrPoSb.js +1 -0
- package/dist/client/assets/chunk-U2HBQHQK-CIB3Bjjd.js +70 -0
- package/dist/client/assets/chunk-X2U36JSP-CtB-o8Yp.js +1 -0
- package/dist/client/assets/chunk-XPW4576I-C6iHhX_8.js +32 -0
- package/dist/client/assets/chunk-YZCP3GAM-CTmKr6ZH.js +1 -0
- package/dist/client/assets/chunk-ZZ45TVLE-BgU8A2RF.js +1 -0
- package/dist/client/assets/classDiagram-6PBFFD2Q-Bqk5e679.js +1 -0
- package/dist/client/assets/classDiagram-v2-HSJHXN6E-6pSaZOkC.js +1 -0
- package/dist/client/assets/client-BrKWI4CM.js +1 -0
- package/dist/client/assets/client-CGgNRU9w.js +1 -0
- package/dist/client/assets/client-DMSLRzg9.js +6 -0
- package/dist/client/assets/clone-DWcL7whJ.js +1 -0
- package/dist/client/assets/cose-bilkent-S5V4N54A-CrV5wsV_.js +1 -0
- package/dist/client/assets/cytoscape.esm--aLzKuep.js +321 -0
- package/dist/client/assets/dagre-CuRxWcrj.js +1 -0
- package/dist/client/assets/dagre-KV5264BT-BIDiVnkA.js +4 -0
- package/dist/client/assets/defaultLocale-CRZydyG6.js +1 -0
- package/dist/client/assets/diagram-5BDNPKRD-i1kjKRCB.js +10 -0
- package/dist/client/assets/diagram-G4DWMVQ6-9ZSLuhbl.js +24 -0
- package/dist/client/assets/diagram-MMDJMWI5-B4_CUjgv.js +43 -0
- package/dist/client/assets/diagram-TYMM5635-Ct5eTGS8.js +24 -0
- package/dist/client/assets/dist-CuB4kiSK.js +1 -0
- package/dist/client/assets/erDiagram-SMLLAGMA-Cy38ercc.js +85 -0
- package/dist/client/assets/flowDiagram-DWJPFMVM-CZKuYl0V.js +162 -0
- package/dist/client/assets/ganttDiagram-T4ZO3ILL-DLPjCh7a.js +292 -0
- package/dist/client/assets/gitGraph-7Q5UKJZL-DqbrtEp9.js +1 -0
- package/dist/client/assets/gitGraphDiagram-UUTBAWPF-BoRBkDhQ.js +106 -0
- package/dist/client/assets/graphlib-BcQ6qlQh.js +1 -0
- package/dist/client/assets/highlighted-body-OFNGDK62-BEpBVDTX.js +1 -0
- package/dist/client/assets/index-CetCiuqP.js +105 -0
- package/dist/client/assets/index-N29Mip7A.css +1 -0
- package/dist/client/assets/info-OMHHGYJF-D98DRBJX.js +1 -0
- package/dist/client/assets/infoDiagram-42DDH7IO-BAcdTWbt.js +2 -0
- package/dist/client/assets/init-B8gtcn7T.js +1 -0
- package/dist/client/assets/isArrayLikeObject-D8SJFmkN.js +1 -0
- package/dist/client/assets/isEmpty-BF3YX5Jk.js +1 -0
- package/dist/client/assets/ishikawaDiagram-UXIWVN3A-Ynu2VKdC.js +70 -0
- package/dist/client/assets/journeyDiagram-VCZTEJTY-BjfhQaN3.js +139 -0
- package/dist/client/assets/jsx-runtime-CyI9ICYU.js +1 -0
- package/dist/client/assets/kanban-definition-6JOO6SKY-JLXH9zUJ.js +89 -0
- package/dist/client/assets/katex-B94qP8b6.js +265 -0
- package/dist/client/assets/lib--QVjyxmL.js +29 -0
- package/dist/client/assets/lib-B6rgJiZ9.js +1 -0
- package/dist/client/assets/line-DCrYfLBn.js +1 -0
- package/dist/client/assets/linear-_4upLmeo.js +1 -0
- package/dist/client/assets/mermaid-GHXKKRXX-rwJHYUmW.js +1 -0
- package/dist/client/assets/mermaid-parser.core-KZinfW8o.js +4 -0
- package/dist/client/assets/mermaid.core-QqY9gSNe.js +11 -0
- package/dist/client/assets/mindmap-definition-QFDTVHPH-TWgHDAzp.js +96 -0
- package/dist/client/assets/ordinal-CCj7PWgZ.js +1 -0
- package/dist/client/assets/packet-4T2RLAQJ-DEvfkn3F.js +1 -0
- package/dist/client/assets/path-DZF-JdEe.js +1 -0
- package/dist/client/assets/pie-ZZUOXDRM-72e6WVjb.js +1 -0
- package/dist/client/assets/pieDiagram-DEJITSTG-Cl8PCsoj.js +30 -0
- package/dist/client/assets/preload-helper-rov5CBGT.js +1 -0
- package/dist/client/assets/pty-client-DZ27IS00.js +1 -0
- package/dist/client/assets/ptyInstancesStore-D9ag7SYd.js +1 -0
- package/dist/client/assets/quadrantDiagram-34T5L4WZ-CHyVGp9E.js +7 -0
- package/dist/client/assets/radar-PYXPWWZC-Cp7xd_EY.js +1 -0
- package/dist/client/assets/react-CClhXMB2.js +1 -0
- package/dist/client/assets/react-Dd6D81m0.js +1 -0
- package/dist/client/assets/react-dom--G6_6fQ_.js +1 -0
- package/dist/client/assets/requirementDiagram-MS252O5E-DaanG2iM.js +84 -0
- package/dist/client/assets/rough.esm-BsmKo2S5.js +1 -0
- package/dist/client/assets/sankeyDiagram-XADWPNL6-B_fhLY36.js +10 -0
- package/dist/client/assets/sequenceDiagram-FGHM5R23-C5FNrveI.js +157 -0
- package/dist/client/assets/src-DeTlMJAU.js +1 -0
- package/dist/client/assets/stateDiagram-FHFEXIEX-nTTcdjjQ.js +1 -0
- package/dist/client/assets/stateDiagram-v2-QKLJ7IA2-Dw0632j_.js +1 -0
- package/dist/client/assets/timeline-definition-GMOUNBTQ-DkQV1yP8.js +120 -0
- package/dist/client/assets/treeView-SZITEDCU-ZLIgC7_K.js +1 -0
- package/dist/client/assets/treemap-W4RFUUIX-BqaMbB6N.js +1 -0
- package/dist/client/assets/uiIdentityOverlay-Ba7GNj7m.js +1 -0
- package/dist/client/assets/vennDiagram-DHZGUBPP-DbZ2xgs6.js +34 -0
- package/dist/client/assets/wardley-RL74JXVD-DXQS8zf4.js +1 -0
- package/dist/client/assets/wardleyDiagram-NUSXRM2D-BzCJ6MAu.js +20 -0
- package/dist/client/assets/xychartDiagram-5P7HB3ND-BSlFecop.js +7 -0
- package/dist/client/favicon.png +0 -0
- package/dist/client/favicon.svg +15 -0
- package/dist/client/fonts/body-medium.woff2 +0 -0
- package/dist/client/fonts/body-regular-italic.woff2 +0 -0
- package/dist/client/fonts/body-regular.woff2 +0 -0
- package/dist/client/fonts/body-semibold.woff2 +0 -0
- package/dist/client/index.html +31 -0
- package/dist/client/manifest.webmanifest +12 -0
- package/dist/client/sw.js +32 -0
- package/package.json +122 -0
- package/src/nats/auth-callout/callout-config.test.ts +93 -0
- package/src/nats/auth-callout/callout-config.ts +109 -0
- package/src/nats/auth-callout/callout.integration.test.ts +332 -0
- package/src/nats/auth-callout/keys.ts +103 -0
- package/src/nats/auth-callout/responder.ts +241 -0
- package/src/nats/auth-callout/scope-policy.test.ts +159 -0
- package/src/nats/auth-callout/scope-policy.ts +210 -0
- package/src/nats/auth-callout/token.test.ts +163 -0
- package/src/nats/auth-callout/token.ts +157 -0
- package/src/nats/nats-daemon-callout.ts +194 -0
- package/src/nats/nats-daemon.test.ts +77 -0
- package/src/nats/nats-daemon.ts +50 -0
- package/src/nats/nats-token.test.ts +61 -0
- package/src/nats/nats-token.ts +59 -0
- package/src/runner/coordination-mcp-integration.test.ts +134 -0
- package/src/runner/nats-coordination-client.test.ts +49 -0
- package/src/runner/nats-coordination-client.ts +94 -0
- package/src/runner/runner-agent.test.ts +469 -0
- package/src/runner/runner-agent.ts +453 -0
- package/src/runner/runner-credential.test.ts +93 -0
- package/src/runner/runner-credential.ts +82 -0
- package/src/runner/runner-nats.test.ts +495 -0
- package/src/runner/runner-nats.ts +323 -0
- package/src/runner/runner-pair.test.ts +107 -0
- package/src/runner/runner-pair.ts +81 -0
- package/src/runner/runner.test.ts +135 -0
- package/src/runner/runner.ts +212 -0
- package/src/runner/turn-factories.test.ts +97 -0
- package/src/runner/turn-factories.ts +475 -0
- package/src/server/agent-config-journey.test.ts +106 -0
- package/src/server/agent.ts +8 -0
- package/src/server/auto-continue/auth-error-detector.ts +66 -0
- package/src/server/auto-continue/limit-detector.ts +194 -0
- package/src/server/bm25.test.ts +92 -0
- package/src/server/bm25.ts +101 -0
- package/src/server/chat-events-jetstream.test.ts +135 -0
- package/src/server/claude-harness.ts +360 -0
- package/src/server/claude-pty/agent-normalizers.ts +309 -0
- package/src/server/claude-pty/auth.test.ts +38 -0
- package/src/server/claude-pty/auth.ts +32 -0
- package/src/server/claude-pty/claude-session-registry.adapter.ts +81 -0
- package/src/server/claude-pty/claude-session-registry.test.ts +149 -0
- package/src/server/claude-pty/driver.test.ts +902 -0
- package/src/server/claude-pty/driver.ts +807 -0
- package/src/server/claude-pty/jsonl-path.adapter.ts +57 -0
- package/src/server/claude-pty/jsonl-path.test.ts +114 -0
- package/src/server/claude-pty/jsonl-to-event.test.ts +241 -0
- package/src/server/claude-pty/jsonl-to-event.ts +174 -0
- package/src/server/claude-pty/output-ring.test.ts +35 -0
- package/src/server/claude-pty/output-ring.ts +25 -0
- package/src/server/claude-pty/parity-matrix.test.ts +227 -0
- package/src/server/claude-pty/pid-registry.adapter.ts +135 -0
- package/src/server/claude-pty/pid-registry.test.ts +122 -0
- package/src/server/claude-pty/preflight/binary-fingerprint.adapter.ts +20 -0
- package/src/server/claude-pty/preflight/binary-fingerprint.test.ts +32 -0
- package/src/server/claude-pty/pty-instance-registry.test.ts +177 -0
- package/src/server/claude-pty/pty-instance-registry.ts +166 -0
- package/src/server/claude-pty/pty-memory-sampler.adapter.test.ts +103 -0
- package/src/server/claude-pty/pty-memory-sampler.adapter.ts +85 -0
- package/src/server/claude-pty/pty-process.adapter.ts +66 -0
- package/src/server/claude-pty/pty-process.test.ts +49 -0
- package/src/server/claude-pty/resolve-binary.adapter.ts +106 -0
- package/src/server/claude-pty/resolve-binary.test.ts +118 -0
- package/src/server/claude-pty/runtime-dir.adapter.ts +19 -0
- package/src/server/claude-pty/settings-writer.adapter.ts +27 -0
- package/src/server/claude-pty/settings-writer.test.ts +22 -0
- package/src/server/claude-pty/smoke-test-io.adapter.ts +28 -0
- package/src/server/claude-pty/smoke-test.test.ts +191 -0
- package/src/server/claude-pty/smoke-test.ts +185 -0
- package/src/server/claude-pty/subagent-orchestrator.ts +887 -0
- package/src/server/claude-pty/tool-callback.ts +274 -0
- package/src/server/claude-pty/tui-control.test.ts +272 -0
- package/src/server/claude-pty/tui-control.ts +182 -0
- package/src/server/claude-pty/tui-source.adapter.test.ts +360 -0
- package/src/server/claude-pty/tui-source.adapter.ts +343 -0
- package/src/server/claude-pty/tunnel-gateway.ts +12 -0
- package/src/server/claude-pty-mcp/canonical-args.ts +15 -0
- package/src/server/claude-pty-mcp/fs-stat.adapter.ts +8 -0
- package/src/server/claude-pty-mcp/history-primer.ts +90 -0
- package/src/server/claude-pty-mcp/http-server.adapter.ts +33 -0
- package/src/server/claude-pty-mcp/mcp-http.ts +177 -0
- package/src/server/claude-pty-mcp/mcp.ts +412 -0
- package/src/server/claude-pty-mcp/mention-parser.ts +25 -0
- package/src/server/claude-pty-mcp/paths.ts +24 -0
- package/src/server/claude-pty-mcp/permission-gate.ts +243 -0
- package/src/server/claude-pty-mcp/terminal-pid-registry.adapter.ts +107 -0
- package/src/server/claude-pty-mcp/tools/ask-user-question.test.ts +119 -0
- package/src/server/claude-pty-mcp/tools/ask-user-question.ts +61 -0
- package/src/server/claude-pty-mcp/tools/bash.adapter.ts +76 -0
- package/src/server/claude-pty-mcp/tools/bash.test.ts +56 -0
- package/src/server/claude-pty-mcp/tools/delegate-subagent.test.ts +155 -0
- package/src/server/claude-pty-mcp/tools/delegate-subagent.ts +111 -0
- package/src/server/claude-pty-mcp/tools/edit.adapter.ts +95 -0
- package/src/server/claude-pty-mcp/tools/edit.test.ts +93 -0
- package/src/server/claude-pty-mcp/tools/exit-plan-mode.test.ts +61 -0
- package/src/server/claude-pty-mcp/tools/exit-plan-mode.ts +50 -0
- package/src/server/claude-pty-mcp/tools/glob.adapter.ts +86 -0
- package/src/server/claude-pty-mcp/tools/glob.test.ts +61 -0
- package/src/server/claude-pty-mcp/tools/grep.adapter.ts +126 -0
- package/src/server/claude-pty-mcp/tools/grep.test.ts +62 -0
- package/src/server/claude-pty-mcp/tools/read.adapter.ts +58 -0
- package/src/server/claude-pty-mcp/tools/read.test.ts +62 -0
- package/src/server/claude-pty-mcp/tools/tool-callback-shim.ts +42 -0
- package/src/server/claude-pty-mcp/tools/webfetch.test.ts +81 -0
- package/src/server/claude-pty-mcp/tools/webfetch.ts +82 -0
- package/src/server/claude-pty-mcp/tools/websearch.test.ts +40 -0
- package/src/server/claude-pty-mcp/tools/websearch.ts +42 -0
- package/src/server/claude-pty-mcp/tools/write.adapter.ts +60 -0
- package/src/server/claude-pty-mcp/tools/write.test.ts +52 -0
- package/src/server/claude-pty-mcp/uploads.adapter.ts +98 -0
- package/src/server/claude-pty-mcp/uploads.ts +38 -0
- package/src/server/claude-turn.test.ts +176 -0
- package/src/server/cli-runtime.test.ts +456 -0
- package/src/server/cli-runtime.ts +374 -0
- package/src/server/cli-supervisor.ts +81 -0
- package/src/server/cli.ts +78 -0
- package/src/server/client-log-forwarder.test.ts +74 -0
- package/src/server/client-log-forwarder.ts +75 -0
- package/src/server/codex-app-server-protocol.ts +449 -0
- package/src/server/codex-app-server.test.ts +2990 -0
- package/src/server/codex-app-server.ts +1713 -0
- package/src/server/coordination-integration.test.ts +63 -0
- package/src/server/coordination-mcp.test.ts +149 -0
- package/src/server/coordination-mcp.ts +197 -0
- package/src/server/delegation-coordinator.test.ts +675 -0
- package/src/server/delegation-coordinator.ts +454 -0
- package/src/server/discovery.test.ts +211 -0
- package/src/server/discovery.ts +301 -0
- package/src/server/event-store-agent-config.test.ts +124 -0
- package/src/server/event-store-coordination.test.ts +149 -0
- package/src/server/event-store-profile.test.ts +132 -0
- package/src/server/event-store-repo.test.ts +154 -0
- package/src/server/event-store-runner-team.test.ts +104 -0
- package/src/server/event-store.test.ts +342 -0
- package/src/server/event-store.ts +2208 -0
- package/src/server/events.ts +379 -0
- package/src/server/extension-router.test.ts +183 -0
- package/src/server/extension-router.ts +114 -0
- package/src/server/extensions/agents/server.test.ts +191 -0
- package/src/server/extensions/agents/server.ts +108 -0
- package/src/server/extensions/c3/server.test.ts +284 -0
- package/src/server/extensions/c3/server.ts +212 -0
- package/src/server/extensions/code/server.test.ts +200 -0
- package/src/server/extensions/code/server.ts +150 -0
- package/src/server/extensions.config.ts +10 -0
- package/src/server/external-open.ts +69 -0
- package/src/server/generate-fork-context.ts +58 -0
- package/src/server/generate-merge-context.test.ts +290 -0
- package/src/server/generate-merge-context.ts +141 -0
- package/src/server/generate-title.ts +36 -0
- package/src/server/git-clone-policy.test.ts +138 -0
- package/src/server/git-clone-policy.ts +27 -0
- package/src/server/harness-types.ts +1 -0
- package/src/server/journey-verification.test.ts +640 -0
- package/src/server/journey-verification.ts +195 -0
- package/src/server/machine-name.ts +22 -0
- package/src/server/nats-auth.test.ts +92 -0
- package/src/server/nats-auth.ts +6 -0
- package/src/server/nats-bind-guard.test.ts +71 -0
- package/src/server/nats-bind-guard.ts +42 -0
- package/src/server/nats-bridge.test.ts +141 -0
- package/src/server/nats-bridge.ts +111 -0
- package/src/server/nats-connector.test.ts +56 -0
- package/src/server/nats-connector.ts +71 -0
- package/src/server/nats-daemon-manager.test.ts +76 -0
- package/src/server/nats-daemon-manager.ts +174 -0
- package/src/server/nats-publisher.test.ts +356 -0
- package/src/server/nats-publisher.ts +271 -0
- package/src/server/nats-responders.test.ts +1018 -0
- package/src/server/nats-responders.ts +925 -0
- package/src/server/nats-streams.test.ts +112 -0
- package/src/server/nats-streams.ts +129 -0
- package/src/server/oauth-pool/oauth-responders.ts +185 -0
- package/src/server/oauth-pool/oauth-settings-store.ts +173 -0
- package/src/server/oauth-pool/oauth-token-pool.ts +303 -0
- package/src/server/orchestration.test.ts +1063 -0
- package/src/server/orchestration.ts +716 -0
- package/src/server/pairing-endpoints.test.ts +171 -0
- package/src/server/pairing-store.test.ts +154 -0
- package/src/server/pairing-store.ts +124 -0
- package/src/server/paths.ts +27 -0
- package/src/server/pr3-liveness.test.ts +252 -0
- package/src/server/process-utils.ts +10 -0
- package/src/server/project-cli.ts +180 -0
- package/src/server/provider-catalog.test.ts +177 -0
- package/src/server/provider-catalog.ts +146 -0
- package/src/server/pty-responders.ts +345 -0
- package/src/server/push-notifications.test.ts +234 -0
- package/src/server/push-notifications.ts +188 -0
- package/src/server/quick-response.test.ts +196 -0
- package/src/server/quick-response.ts +154 -0
- package/src/server/read-models-agent-config.test.ts +59 -0
- package/src/server/read-models-coordination.test.ts +69 -0
- package/src/server/read-models.test.ts +332 -0
- package/src/server/read-models.ts +258 -0
- package/src/server/repo-journey.test.ts +96 -0
- package/src/server/repo-manager.test.ts +118 -0
- package/src/server/repo-manager.ts +97 -0
- package/src/server/repo-status.test.ts +54 -0
- package/src/server/repo-status.ts +82 -0
- package/src/server/restart.test.ts +27 -0
- package/src/server/restart.ts +30 -0
- package/src/server/runner-incompatible-gate.test.ts +383 -0
- package/src/server/runner-manager.test.ts +72 -0
- package/src/server/runner-manager.ts +312 -0
- package/src/server/runner-pairing-urls.test.ts +59 -0
- package/src/server/runner-pairing-urls.ts +67 -0
- package/src/server/runner-proxy.test.ts +456 -0
- package/src/server/runner-proxy.ts +494 -0
- package/src/server/runner-router.test.ts +429 -0
- package/src/server/runner-router.ts +212 -0
- package/src/server/runner-routing.test.ts +584 -0
- package/src/server/runtime-registry.test.ts +436 -0
- package/src/server/runtime-registry.ts +307 -0
- package/src/server/sandbox-health.test.ts +127 -0
- package/src/server/sandbox-health.ts +94 -0
- package/src/server/sandbox-journey.test.ts +232 -0
- package/src/server/sandbox-manager.test.ts +146 -0
- package/src/server/sandbox-manager.ts +159 -0
- package/src/server/server.test.ts +287 -0
- package/src/server/server.ts +1108 -0
- package/src/server/session-discovery.test.ts +129 -0
- package/src/server/session-discovery.ts +475 -0
- package/src/server/session-index.test.ts +362 -0
- package/src/server/session-index.ts +119 -0
- package/src/server/session-seed.ts +288 -0
- package/src/server/share.test.ts +108 -0
- package/src/server/share.ts +113 -0
- package/src/server/skill-discovery.test.ts +201 -0
- package/src/server/skill-discovery.ts +77 -0
- package/src/server/storage/test-helpers.ts +67 -0
- package/src/server/terminal-manager.test.ts +309 -0
- package/src/server/terminal-manager.ts +354 -0
- package/src/server/transcript-consumer.test.ts +339 -0
- package/src/server/transcript-consumer.ts +162 -0
- package/src/server/transcript-search.test.ts +193 -0
- package/src/server/transcript-search.ts +83 -0
- package/src/server/transcript-utils.ts +52 -0
- package/src/server/update-manager.test.ts +107 -0
- package/src/server/update-manager.ts +230 -0
- package/src/server/workflow-engine.test.ts +251 -0
- package/src/server/workflow-engine.ts +169 -0
- package/src/server/workflow-mcp.ts +49 -0
- package/src/server/workflow-store.test.ts +155 -0
- package/src/server/workflow-store.ts +139 -0
- package/src/server/workspace-agent-integration.test.ts +167 -0
- package/src/server/workspace-agent-routes.test.ts +127 -0
- package/src/server/workspace-agent-routes.ts +89 -0
- package/src/server/workspace-agent.test.ts +103 -0
- package/src/server/workspace-agent.ts +102 -0
- package/src/server/workspace-cli.test.ts +79 -0
- package/src/server/workspace-config-manager.test.ts +109 -0
- package/src/server/workspace-config-manager.ts +83 -0
- package/src/server/workspace-directory-policy.test.ts +109 -0
- package/src/server/workspace-directory-policy.ts +56 -0
- package/src/shared/agent-config-types.ts +25 -0
- package/src/shared/branding.test.ts +42 -0
- package/src/shared/branding.ts +54 -0
- package/src/shared/compression.test.ts +85 -0
- package/src/shared/compression.ts +42 -0
- package/src/shared/coordination-store.test.ts +24 -0
- package/src/shared/coordination-store.ts +26 -0
- package/src/shared/dev-ports.test.ts +84 -0
- package/src/shared/dev-ports.ts +100 -0
- package/src/shared/extension-types.ts +45 -0
- package/src/shared/fork-presets.ts +54 -0
- package/src/shared/harness-types.test.ts +15 -0
- package/src/shared/harness-types.ts +21 -0
- package/src/shared/log-sink.test.ts +112 -0
- package/src/shared/log-sink.ts +185 -0
- package/src/shared/mention-pattern.ts +7 -0
- package/src/shared/merge-presets.ts +41 -0
- package/src/shared/nats-subjects.test.ts +61 -0
- package/src/shared/nats-subjects.ts +131 -0
- package/src/shared/permission-policy.ts +136 -0
- package/src/shared/ports.ts +3 -0
- package/src/shared/preset-types.ts +15 -0
- package/src/shared/profile-types.ts +49 -0
- package/src/shared/projectFileUrl.ts +36 -0
- package/src/shared/protocol.ts +153 -0
- package/src/shared/pty-instance.ts +43 -0
- package/src/shared/puggy/diagnostics/result.ts +18 -0
- package/src/shared/puggy/expressions/evaluate.ts +292 -0
- package/src/shared/puggy/index.test.ts +101 -0
- package/src/shared/puggy/index.ts +69 -0
- package/src/shared/puggy/parser/ast.ts +110 -0
- package/src/shared/puggy/parser/parser.ts +624 -0
- package/src/shared/puggy/renderer/html.ts +447 -0
- package/src/shared/runner-protocol.test.ts +277 -0
- package/src/shared/runner-protocol.ts +210 -0
- package/src/shared/runner-team-types.ts +73 -0
- package/src/shared/runtime-types.ts +48 -0
- package/src/shared/sandbox-types.ts +53 -0
- package/src/shared/tailwind-build.test.ts +12 -0
- package/src/shared/tinkaria-system-prompt.ts +101 -0
- package/src/shared/tools.test.ts +335 -0
- package/src/shared/tools.ts +397 -0
- package/src/shared/transcript-entries.ts +27 -0
- package/src/shared/transcript-render.test.ts +225 -0
- package/src/shared/transcript-render.ts +467 -0
- package/src/shared/types.ts +1031 -0
- package/src/shared/vite-config.test.ts +47 -0
- package/src/shared/web-context.test.ts +110 -0
- package/src/shared/web-context.ts +76 -0
- package/src/shared/workflow-types.ts +51 -0
- package/src/shared/workspace-types.ts +100 -0
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NATS daemon — callout mode (Stage A, PR1).
|
|
3
|
+
*
|
|
4
|
+
* Spawns the bundled nats-server binary directly (via resolveBinary()) with a
|
|
5
|
+
* self-generated auth-callout config, discovers the TCP + WS ports from stderr,
|
|
6
|
+
* starts the CalloutResponder as the auth-service connection, then writes the
|
|
7
|
+
* JSON handshake to stdout (same shape as nats-daemon.ts so NatsDaemonManager
|
|
8
|
+
* can parse it).
|
|
9
|
+
*
|
|
10
|
+
* Invoked by NatsDaemonManager when NATS_AUTH_MODE=callout.
|
|
11
|
+
* NOT used in token mode — that path stays in nats-daemon.ts unchanged.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { spawn } from "node:child_process"
|
|
15
|
+
import { mkdtempSync, writeFileSync, rmSync } from "node:fs"
|
|
16
|
+
import { join } from "node:path"
|
|
17
|
+
import { tmpdir } from "node:os"
|
|
18
|
+
import { resolveBinary } from "@lagz0ne/nats-embedded"
|
|
19
|
+
import { ensureCalloutKeys } from "./auth-callout/keys"
|
|
20
|
+
import { buildCalloutConfig } from "./auth-callout/callout-config"
|
|
21
|
+
import { CalloutResponder } from "./auth-callout/responder"
|
|
22
|
+
|
|
23
|
+
const LOG_PREFIX = "[nats-daemon-callout]"
|
|
24
|
+
|
|
25
|
+
const host = process.env.NATS_HOST ?? "127.0.0.1"
|
|
26
|
+
const port = process.env.NATS_PORT ? Number(process.env.NATS_PORT) : -1
|
|
27
|
+
const wsPort = process.env.NATS_WS_PORT ? Number(process.env.NATS_WS_PORT) : -1
|
|
28
|
+
const storeDir = process.env.NATS_STORE_DIR
|
|
29
|
+
const dataDir = process.env.NATS_DATA_DIR
|
|
30
|
+
// Opt-in localhost-only HTTP monitoring for diagnostics (/connz, /subsz, /varz).
|
|
31
|
+
const monitorPort = process.env.NATS_MONITOR_PORT ? Number(process.env.NATS_MONITOR_PORT) : undefined
|
|
32
|
+
|
|
33
|
+
if (!dataDir) {
|
|
34
|
+
console.error(LOG_PREFIX, "NATS_DATA_DIR is required in callout mode (key storage)")
|
|
35
|
+
process.exit(1)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// ── 1. Ensure signing keys ────────────────────────────────────────────────────
|
|
39
|
+
|
|
40
|
+
const keys = await ensureCalloutKeys(dataDir)
|
|
41
|
+
console.warn(LOG_PREFIX, `Callout account public key: ${keys.accountPublicKey}`)
|
|
42
|
+
console.warn(LOG_PREFIX, `Auth-service user public key: ${keys.authUserPublicKey}`)
|
|
43
|
+
|
|
44
|
+
// ── 2. Generate config and write to temp file ─────────────────────────────────
|
|
45
|
+
|
|
46
|
+
const configContent = buildCalloutConfig({
|
|
47
|
+
host,
|
|
48
|
+
port,
|
|
49
|
+
wsPort,
|
|
50
|
+
storeDir,
|
|
51
|
+
monitorPort,
|
|
52
|
+
accountPublicKey: keys.accountPublicKey,
|
|
53
|
+
authUserPublicKey: keys.authUserPublicKey,
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
const configDir = mkdtempSync(join(tmpdir(), "nats-callout-"))
|
|
57
|
+
const configPath = join(configDir, "callout.conf")
|
|
58
|
+
writeFileSync(configPath, configContent)
|
|
59
|
+
|
|
60
|
+
// ── 3. Spawn binary directly, parse ports from stderr ─────────────────────────
|
|
61
|
+
|
|
62
|
+
const binaryPath = resolveBinary()
|
|
63
|
+
console.warn(LOG_PREFIX, `Spawning binary: ${binaryPath}`)
|
|
64
|
+
|
|
65
|
+
const child = spawn(binaryPath, ["-c", configPath], {
|
|
66
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
interface PortResult {
|
|
70
|
+
tcpPort: number
|
|
71
|
+
wsPort: number
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const portResult = await new Promise<PortResult>((resolve, reject) => {
|
|
75
|
+
const timeout = setTimeout(() => {
|
|
76
|
+
child.kill("SIGKILL")
|
|
77
|
+
reject(new Error("nats-server (callout mode) did not start within 10s"))
|
|
78
|
+
}, 10_000)
|
|
79
|
+
|
|
80
|
+
child.on("error", (err) => {
|
|
81
|
+
clearTimeout(timeout)
|
|
82
|
+
reject(err)
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
child.on("exit", (code) => {
|
|
86
|
+
clearTimeout(timeout)
|
|
87
|
+
reject(new Error(`nats-server exited with code ${code} before ready`))
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
let tcpPort: number | null = null
|
|
91
|
+
let wsPort: number | null = null
|
|
92
|
+
let buffer = ""
|
|
93
|
+
|
|
94
|
+
const onStderr = (chunk: Buffer) => {
|
|
95
|
+
const text = chunk.toString()
|
|
96
|
+
buffer += text
|
|
97
|
+
const lines = buffer.split("\n")
|
|
98
|
+
buffer = lines.pop() ?? ""
|
|
99
|
+
|
|
100
|
+
for (const line of lines) {
|
|
101
|
+
// "Listening for client connections on host:port"
|
|
102
|
+
const tcpMatch = line.match(/Listening for client connections on .+:(\d+)/)
|
|
103
|
+
if (tcpMatch) tcpPort = Number(tcpMatch[1])
|
|
104
|
+
|
|
105
|
+
// "Listening for websocket clients on host:port"
|
|
106
|
+
const wsMatch = line.match(/Listening for websocket clients on .+:(\d+)/)
|
|
107
|
+
if (wsMatch) wsPort = Number(wsMatch[1])
|
|
108
|
+
|
|
109
|
+
if (tcpPort !== null && wsPort !== null) {
|
|
110
|
+
clearTimeout(timeout)
|
|
111
|
+
child.removeAllListeners("exit")
|
|
112
|
+
child.stderr.removeListener("data", onStderr)
|
|
113
|
+
resolve({ tcpPort, wsPort })
|
|
114
|
+
return
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
child.stderr.on("data", onStderr)
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
const natsUrl = `nats://${host}:${portResult.tcpPort}`
|
|
123
|
+
const wsUrl = `ws://${host}:${portResult.wsPort}`
|
|
124
|
+
|
|
125
|
+
// Observability (decision 0012): forward nats-server's own runtime logs onward
|
|
126
|
+
// (to this process's stderr, which the daemon manager pipes into VictoriaLogs).
|
|
127
|
+
// Previously the stderr listener was removed after port parsing and stdout was
|
|
128
|
+
// discarded, hiding the entire NATS layer — slow-consumer drops, per-connection
|
|
129
|
+
// delivery errors, auth violations, and disconnects. Continuously draining also
|
|
130
|
+
// prevents the pipe buffer from backing up.
|
|
131
|
+
function forwardNatsServerOutput(stream: NodeJS.ReadableStream | null): void {
|
|
132
|
+
if (!stream) return
|
|
133
|
+
let buf = ""
|
|
134
|
+
stream.on("data", (chunk: Buffer) => {
|
|
135
|
+
buf += chunk.toString()
|
|
136
|
+
const lines = buf.split("\n")
|
|
137
|
+
buf = lines.pop() ?? ""
|
|
138
|
+
for (const line of lines) {
|
|
139
|
+
if (line.trim()) console.warn("[nats-server]", line)
|
|
140
|
+
}
|
|
141
|
+
})
|
|
142
|
+
}
|
|
143
|
+
forwardNatsServerOutput(child.stdout)
|
|
144
|
+
forwardNatsServerOutput(child.stderr)
|
|
145
|
+
|
|
146
|
+
console.warn(LOG_PREFIX, `nats-server ready — url: ${natsUrl}, ws: ${wsUrl}`)
|
|
147
|
+
|
|
148
|
+
// ── 4. Start the callout responder ────────────────────────────────────────────
|
|
149
|
+
|
|
150
|
+
const responder = new CalloutResponder({
|
|
151
|
+
natsUrl,
|
|
152
|
+
authUserKp: keys.authUserKp,
|
|
153
|
+
accountKp: keys.accountKp,
|
|
154
|
+
accountPublicKey: keys.accountPublicKey,
|
|
155
|
+
accountName: "CALLOUT_ACCOUNT",
|
|
156
|
+
tokenSecret: keys.tokenSecret,
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
await responder.start()
|
|
160
|
+
console.warn(LOG_PREFIX, "Callout responder started")
|
|
161
|
+
|
|
162
|
+
// ── 5. Emit handshake JSON to stdout (same shape as nats-daemon.ts) ───────────
|
|
163
|
+
|
|
164
|
+
const info = {
|
|
165
|
+
url: natsUrl,
|
|
166
|
+
wsUrl,
|
|
167
|
+
wsPort: portResult.wsPort,
|
|
168
|
+
pid: process.pid,
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
process.stdout.write(JSON.stringify(info) + "\n")
|
|
172
|
+
|
|
173
|
+
// ── 6. Lifecycle ──────────────────────────────────────────────────────────────
|
|
174
|
+
|
|
175
|
+
async function shutdown(signal: string) {
|
|
176
|
+
console.warn(LOG_PREFIX, `Received ${signal}, shutting down`)
|
|
177
|
+
await responder.stop()
|
|
178
|
+
child.kill("SIGTERM")
|
|
179
|
+
try {
|
|
180
|
+
rmSync(configDir, { recursive: true })
|
|
181
|
+
} catch { /* best-effort */ }
|
|
182
|
+
process.exit(0)
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
process.on("SIGTERM", () => void shutdown("SIGTERM"))
|
|
186
|
+
process.on("SIGINT", () => void shutdown("SIGINT"))
|
|
187
|
+
|
|
188
|
+
// Keep the process alive until nats-server exits.
|
|
189
|
+
await new Promise<void>((resolve) => {
|
|
190
|
+
child.on("exit", () => resolve())
|
|
191
|
+
})
|
|
192
|
+
|
|
193
|
+
// Clean up config dir on abnormal exit.
|
|
194
|
+
try { rmSync(configDir, { recursive: true }) } catch { /* best-effort */ }
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { describe, test, expect, afterEach } from "bun:test"
|
|
2
|
+
import { connect } from "@nats-io/transport-node"
|
|
3
|
+
|
|
4
|
+
function createDaemonTestEnv(token: string): NodeJS.ProcessEnv {
|
|
5
|
+
const {
|
|
6
|
+
NATS_DATA_DIR: _natsDataDir,
|
|
7
|
+
NATS_URL: _natsUrl,
|
|
8
|
+
NATS_MODE: _natsMode,
|
|
9
|
+
NATS_WS_PORT: _natsWsPort,
|
|
10
|
+
NATS_PORT: _natsPort,
|
|
11
|
+
NATS_STORE_DIR: _natsStoreDir,
|
|
12
|
+
NATS_HTTP_PORT: _natsHttpPort,
|
|
13
|
+
...spawnEnv
|
|
14
|
+
} = process.env
|
|
15
|
+
|
|
16
|
+
return {
|
|
17
|
+
...spawnEnv,
|
|
18
|
+
NATS_TOKEN: token,
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
describe("nats-daemon", () => {
|
|
23
|
+
let proc: ReturnType<typeof Bun.spawn> | null = null
|
|
24
|
+
|
|
25
|
+
afterEach(async () => {
|
|
26
|
+
if (proc) {
|
|
27
|
+
proc.kill("SIGTERM")
|
|
28
|
+
await proc.exited
|
|
29
|
+
proc = null
|
|
30
|
+
}
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
test("starts and outputs JSON with url, wsUrl, wsPort, pid", async () => {
|
|
34
|
+
const token = "test-token-" + Date.now()
|
|
35
|
+
proc = Bun.spawn(["bun", "run", "src/nats/nats-daemon.ts"], {
|
|
36
|
+
env: createDaemonTestEnv(token),
|
|
37
|
+
stdio: ["ignore", "pipe", "inherit"],
|
|
38
|
+
})
|
|
39
|
+
const reader = proc.stdout.getReader()
|
|
40
|
+
const { value } = await reader.read()
|
|
41
|
+
const info = JSON.parse(new TextDecoder().decode(value))
|
|
42
|
+
expect(info).toHaveProperty("url")
|
|
43
|
+
expect(info).toHaveProperty("wsUrl")
|
|
44
|
+
expect(info).toHaveProperty("wsPort")
|
|
45
|
+
expect(info).toHaveProperty("pid")
|
|
46
|
+
expect(typeof info.pid).toBe("number")
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
test("accepts NATS connections with token auth", async () => {
|
|
50
|
+
const token = "test-token-" + Date.now()
|
|
51
|
+
proc = Bun.spawn(["bun", "run", "src/nats/nats-daemon.ts"], {
|
|
52
|
+
env: createDaemonTestEnv(token),
|
|
53
|
+
stdio: ["ignore", "pipe", "inherit"],
|
|
54
|
+
})
|
|
55
|
+
const reader = proc.stdout.getReader()
|
|
56
|
+
const { value } = await reader.read()
|
|
57
|
+
const info = JSON.parse(new TextDecoder().decode(value))
|
|
58
|
+
|
|
59
|
+
const nc = await connect({ servers: info.url, token })
|
|
60
|
+
expect(nc).toBeDefined()
|
|
61
|
+
await nc.drain()
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
test("shuts down cleanly on SIGTERM", async () => {
|
|
65
|
+
proc = Bun.spawn(["bun", "run", "src/nats/nats-daemon.ts"], {
|
|
66
|
+
env: createDaemonTestEnv("test"),
|
|
67
|
+
stdio: ["ignore", "pipe", "inherit"],
|
|
68
|
+
})
|
|
69
|
+
const reader = proc.stdout.getReader()
|
|
70
|
+
await reader.read() // wait for startup
|
|
71
|
+
|
|
72
|
+
proc.kill("SIGTERM")
|
|
73
|
+
const exitCode = await proc.exited
|
|
74
|
+
expect(exitCode).toBe(0)
|
|
75
|
+
proc = null // already exited
|
|
76
|
+
})
|
|
77
|
+
})
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { NatsServer } from "@lagz0ne/nats-embedded"
|
|
2
|
+
import { ensureToken } from "./nats-token"
|
|
3
|
+
|
|
4
|
+
const LOG_PREFIX = "[nats-daemon]"
|
|
5
|
+
|
|
6
|
+
const host = process.env.NATS_HOST ?? "127.0.0.1"
|
|
7
|
+
const port = process.env.NATS_PORT ? Number(process.env.NATS_PORT) : -1
|
|
8
|
+
const wsPort = process.env.NATS_WS_PORT ? Number(process.env.NATS_WS_PORT) : undefined
|
|
9
|
+
const httpPort = process.env.NATS_HTTP_PORT ? Number(process.env.NATS_HTTP_PORT) : undefined
|
|
10
|
+
const storeDir = process.env.NATS_STORE_DIR
|
|
11
|
+
const dataDir = process.env.NATS_DATA_DIR
|
|
12
|
+
|
|
13
|
+
// Token resolution: NATS_DATA_DIR (file-based) > NATS_TOKEN (env) > none
|
|
14
|
+
const token = dataDir
|
|
15
|
+
? await ensureToken(dataDir)
|
|
16
|
+
: process.env.NATS_TOKEN || undefined
|
|
17
|
+
|
|
18
|
+
const server = await NatsServer.start({
|
|
19
|
+
host,
|
|
20
|
+
port,
|
|
21
|
+
websocket: wsPort ? { port: wsPort, no_tls: true } : true,
|
|
22
|
+
jetstream: true,
|
|
23
|
+
...(httpPort ? { httpPort } : {}),
|
|
24
|
+
...(storeDir ? { storeDir } : {}),
|
|
25
|
+
...(token ? { token } : {}),
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
if (!server.wsUrl || !server.wsPort) {
|
|
29
|
+
console.warn(LOG_PREFIX, "NATS server started without WebSocket support")
|
|
30
|
+
await server.stop()
|
|
31
|
+
process.exit(1)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const info = {
|
|
35
|
+
url: server.url,
|
|
36
|
+
wsUrl: server.wsUrl,
|
|
37
|
+
wsPort: server.wsPort,
|
|
38
|
+
pid: process.pid,
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
process.stdout.write(JSON.stringify(info) + "\n")
|
|
42
|
+
|
|
43
|
+
process.on("SIGTERM", async () => {
|
|
44
|
+
console.warn(LOG_PREFIX, "Received SIGTERM, shutting down")
|
|
45
|
+
await server.stop()
|
|
46
|
+
process.exit(0)
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
// Keep process alive until NATS server exits
|
|
50
|
+
await server.exited
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { describe, test, expect, beforeEach, afterEach } from "bun:test"
|
|
2
|
+
import { ensureToken, readToken } from "./nats-token"
|
|
3
|
+
import { mkdtempSync, rmSync } from "node:fs"
|
|
4
|
+
import { join } from "node:path"
|
|
5
|
+
import { tmpdir } from "node:os"
|
|
6
|
+
|
|
7
|
+
describe("nats-token", () => {
|
|
8
|
+
let dataDir: string
|
|
9
|
+
|
|
10
|
+
beforeEach(() => {
|
|
11
|
+
dataDir = mkdtempSync(join(tmpdir(), "nats-token-test-"))
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
afterEach(() => {
|
|
15
|
+
rmSync(dataDir, { recursive: true, force: true })
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
describe("ensureToken", () => {
|
|
19
|
+
test("generates and persists a token when none exists", async () => {
|
|
20
|
+
const token = await ensureToken(dataDir)
|
|
21
|
+
expect(token).toBeString()
|
|
22
|
+
expect(token.length).toBeGreaterThan(20)
|
|
23
|
+
|
|
24
|
+
// File should exist now
|
|
25
|
+
const file = Bun.file(join(dataDir, "nats.token"))
|
|
26
|
+
expect(await file.exists()).toBe(true)
|
|
27
|
+
expect((await file.text()).trim()).toBe(token)
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
test("returns existing token on subsequent calls", async () => {
|
|
31
|
+
const first = await ensureToken(dataDir)
|
|
32
|
+
const second = await ensureToken(dataDir)
|
|
33
|
+
expect(second).toBe(first)
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
test("creates data directory if it does not exist", async () => {
|
|
37
|
+
const nested = join(dataDir, "sub", "deep")
|
|
38
|
+
const token = await ensureToken(nested)
|
|
39
|
+
expect(token).toBeString()
|
|
40
|
+
expect(token.length).toBeGreaterThan(20)
|
|
41
|
+
})
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
describe("readToken", () => {
|
|
45
|
+
test("reads token written by ensureToken", async () => {
|
|
46
|
+
const written = await ensureToken(dataDir)
|
|
47
|
+
const read = await readToken(dataDir)
|
|
48
|
+
expect(read).toBe(written)
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
test("throws when token file does not exist", async () => {
|
|
52
|
+
expect(readToken(dataDir)).rejects.toThrow()
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
test("trims whitespace from token file", async () => {
|
|
56
|
+
await Bun.write(join(dataDir, "nats.token"), " my-token-value \n")
|
|
57
|
+
const token = await readToken(dataDir)
|
|
58
|
+
expect(token).toBe("my-token-value")
|
|
59
|
+
})
|
|
60
|
+
})
|
|
61
|
+
})
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { join } from "node:path"
|
|
2
|
+
import { mkdirSync } from "node:fs"
|
|
3
|
+
|
|
4
|
+
const TOKEN_FILE = "nats.token"
|
|
5
|
+
|
|
6
|
+
/** Generate a cryptographically random token string. */
|
|
7
|
+
function generateToken(): string {
|
|
8
|
+
const bytes = new Uint8Array(32)
|
|
9
|
+
crypto.getRandomValues(bytes)
|
|
10
|
+
return Buffer.from(bytes).toString("base64url")
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Ensure a NATS auth token exists in `dataDir/nats.token`.
|
|
15
|
+
* If missing, generates one with atomic write (tmp + rename).
|
|
16
|
+
* Called by the NATS daemon only (single owner).
|
|
17
|
+
*/
|
|
18
|
+
export async function ensureToken(dataDir: string): Promise<string> {
|
|
19
|
+
const tokenPath = join(dataDir, TOKEN_FILE)
|
|
20
|
+
const file = Bun.file(tokenPath)
|
|
21
|
+
|
|
22
|
+
if (await file.exists()) {
|
|
23
|
+
const existing = (await file.text()).trim()
|
|
24
|
+
if (existing.length > 0) return existing
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
mkdirSync(dataDir, { recursive: true })
|
|
28
|
+
|
|
29
|
+
const token = generateToken()
|
|
30
|
+
const tmpPath = `${tokenPath}.tmp.${process.pid}`
|
|
31
|
+
await Bun.write(tmpPath, token + "\n")
|
|
32
|
+
|
|
33
|
+
// Atomic rename — prevents partial reads by other processes
|
|
34
|
+
const { renameSync } = await import("node:fs")
|
|
35
|
+
renameSync(tmpPath, tokenPath)
|
|
36
|
+
|
|
37
|
+
return token
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Read the NATS auth token from `dataDir/nats.token`.
|
|
42
|
+
* Called by server and runner processes.
|
|
43
|
+
* Throws if the file does not exist (daemon not started yet).
|
|
44
|
+
*/
|
|
45
|
+
export async function readToken(dataDir: string): Promise<string> {
|
|
46
|
+
const tokenPath = join(dataDir, TOKEN_FILE)
|
|
47
|
+
const file = Bun.file(tokenPath)
|
|
48
|
+
|
|
49
|
+
if (!(await file.exists())) {
|
|
50
|
+
throw new Error(`NATS token file not found: ${tokenPath} — is the NATS daemon running?`)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const content = (await file.text()).trim()
|
|
54
|
+
if (content.length === 0) {
|
|
55
|
+
throw new Error(`NATS token file is empty: ${tokenPath}`)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return content
|
|
59
|
+
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { describe, test, expect, afterEach, beforeEach } from "bun:test"
|
|
2
|
+
import { mkdtempSync, rmSync } from "node:fs"
|
|
3
|
+
import { join } from "node:path"
|
|
4
|
+
import { tmpdir } from "node:os"
|
|
5
|
+
import { NatsServer } from "@lagz0ne/nats-embedded"
|
|
6
|
+
import { connect, type NatsConnection } from "@nats-io/transport-node"
|
|
7
|
+
import { EventStore } from "../server/event-store"
|
|
8
|
+
import { registerCommandResponders } from "../server/nats-responders"
|
|
9
|
+
import { NatsCoordinationClient } from "./nats-coordination-client"
|
|
10
|
+
import { createCoordinationMcpServer } from "../server/coordination-mcp"
|
|
11
|
+
|
|
12
|
+
describe("coordination MCP via runner NATS client", () => {
|
|
13
|
+
let natsServer: NatsServer
|
|
14
|
+
let serverNc: NatsConnection
|
|
15
|
+
let clientNc: NatsConnection
|
|
16
|
+
let store: EventStore
|
|
17
|
+
let tmpDir: string
|
|
18
|
+
let dispose: (() => void) | null = null
|
|
19
|
+
|
|
20
|
+
beforeEach(async () => {
|
|
21
|
+
tmpDir = mkdtempSync(join(tmpdir(), "coord-mcp-int-test-"))
|
|
22
|
+
natsServer = await NatsServer.start()
|
|
23
|
+
serverNc = await connect({ servers: natsServer.url })
|
|
24
|
+
clientNc = await connect({ servers: natsServer.url })
|
|
25
|
+
|
|
26
|
+
store = new EventStore(tmpDir)
|
|
27
|
+
await store.initialize()
|
|
28
|
+
|
|
29
|
+
const { dispose: d } = registerCommandResponders({
|
|
30
|
+
nc: serverNc,
|
|
31
|
+
store,
|
|
32
|
+
agent: {
|
|
33
|
+
send: async () => ({ chatId: "c" }),
|
|
34
|
+
cancel: async () => {},
|
|
35
|
+
disposeChat: async () => {},
|
|
36
|
+
respondTool: async () => {},
|
|
37
|
+
getActiveStatuses: () => new Map(),
|
|
38
|
+
} as never,
|
|
39
|
+
terminals: {
|
|
40
|
+
createTerminal: () => ({ terminalId: "t1", title: "bash", cwd: "/tmp", shell: "/bin/bash", cols: 80, rows: 24, scrollback: 0, serializedState: "", status: "running", exitCode: null }),
|
|
41
|
+
write: () => {},
|
|
42
|
+
resize: () => {},
|
|
43
|
+
close: () => {},
|
|
44
|
+
closeByCwd: () => {},
|
|
45
|
+
getSnapshot: () => null,
|
|
46
|
+
onEvent: () => () => {},
|
|
47
|
+
} as never,
|
|
48
|
+
refreshDiscovery: async () => [],
|
|
49
|
+
updateManager: null,
|
|
50
|
+
publisher: {
|
|
51
|
+
addSubscription: () => {},
|
|
52
|
+
removeSubscription: () => {},
|
|
53
|
+
getSnapshot: async () => null,
|
|
54
|
+
broadcastSnapshots: async () => {},
|
|
55
|
+
publishChatMessage: () => {},
|
|
56
|
+
refreshSessions: async () => {},
|
|
57
|
+
dispose: () => {},
|
|
58
|
+
} as never,
|
|
59
|
+
onStateChange: () => {},
|
|
60
|
+
directoryPolicy: null,
|
|
61
|
+
repoManager: null,
|
|
62
|
+
clonePolicy: null,
|
|
63
|
+
})
|
|
64
|
+
dispose = d
|
|
65
|
+
await serverNc.flush()
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
afterEach(async () => {
|
|
69
|
+
dispose?.()
|
|
70
|
+
dispose = null
|
|
71
|
+
await clientNc?.drain()
|
|
72
|
+
await serverNc?.drain()
|
|
73
|
+
await natsServer?.stop()
|
|
74
|
+
rmSync(tmpDir, { recursive: true, force: true })
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
test("NatsCoordinationClient can add a todo via NATS and retrieve via getSnapshot", async () => {
|
|
78
|
+
const project = await store.openProject("/tmp/test-coord-mcp-runner", "Test")
|
|
79
|
+
const workspaceId = project.id
|
|
80
|
+
|
|
81
|
+
const client = new NatsCoordinationClient(clientNc)
|
|
82
|
+
|
|
83
|
+
await client.addTodo(workspaceId, "t-1", "Implement feature X", "high", "session-runner")
|
|
84
|
+
|
|
85
|
+
// Verify via direct store (server-side truth)
|
|
86
|
+
const serverSnapshot = await store.state.coordinationByWorkspace.get(workspaceId)
|
|
87
|
+
expect(serverSnapshot?.todos.get("t-1")?.description).toBe("Implement feature X")
|
|
88
|
+
|
|
89
|
+
// Verify via getSnapshot (NATS round-trip)
|
|
90
|
+
const snapshot = await client.getSnapshot(workspaceId)
|
|
91
|
+
expect(snapshot.todos).toHaveLength(1)
|
|
92
|
+
expect(snapshot.todos[0].description).toBe("Implement feature X")
|
|
93
|
+
expect(snapshot.todos[0].status).toBe("open")
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
test("createCoordinationMcpServer works with NatsCoordinationClient", () => {
|
|
97
|
+
const client = new NatsCoordinationClient(clientNc)
|
|
98
|
+
const mcpServer = createCoordinationMcpServer(client)
|
|
99
|
+
// The MCP server should be defined with tools
|
|
100
|
+
expect(mcpServer).toBeDefined()
|
|
101
|
+
expect(typeof mcpServer).toBe("object")
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
test("NatsCoordinationClient full coordination lifecycle via NATS", async () => {
|
|
105
|
+
const project = await store.openProject("/tmp/test-coord-lifecycle", "Lifecycle")
|
|
106
|
+
const workspaceId = project.id
|
|
107
|
+
|
|
108
|
+
const client = new NatsCoordinationClient(clientNc)
|
|
109
|
+
|
|
110
|
+
// Add and claim a todo
|
|
111
|
+
await client.addTodo(workspaceId, "t-1", "Build feature", "normal", "session-a")
|
|
112
|
+
await client.claimTodo(workspaceId, "t-1", "session-b")
|
|
113
|
+
|
|
114
|
+
let snapshot = await client.getSnapshot(workspaceId)
|
|
115
|
+
expect(snapshot.todos[0].status).toBe("claimed")
|
|
116
|
+
|
|
117
|
+
// Complete the todo
|
|
118
|
+
await client.completeTodo(workspaceId, "t-1", ["feature.ts"])
|
|
119
|
+
snapshot = await client.getSnapshot(workspaceId)
|
|
120
|
+
expect(snapshot.todos[0].status).toBe("complete")
|
|
121
|
+
|
|
122
|
+
// Create a file claim
|
|
123
|
+
await client.createClaim(workspaceId, "c-1", "Refactor auth", ["src/auth.ts"], "session-a")
|
|
124
|
+
snapshot = await client.getSnapshot(workspaceId)
|
|
125
|
+
expect(snapshot.claims).toHaveLength(1)
|
|
126
|
+
expect(snapshot.claims[0].status).toBe("active")
|
|
127
|
+
|
|
128
|
+
// Set a rule
|
|
129
|
+
await client.setRule(workspaceId, "r-1", "No any types", "session-a")
|
|
130
|
+
snapshot = await client.getSnapshot(workspaceId)
|
|
131
|
+
expect(snapshot.rules).toHaveLength(1)
|
|
132
|
+
expect(snapshot.rules[0].content).toBe("No any types")
|
|
133
|
+
})
|
|
134
|
+
})
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { describe, test, expect, mock } from "bun:test"
|
|
2
|
+
import { NatsCoordinationClient } from "./nats-coordination-client"
|
|
3
|
+
|
|
4
|
+
describe("NatsCoordinationClient", () => {
|
|
5
|
+
test("addTodo sends NATS command and returns", async () => {
|
|
6
|
+
const mockNc = {
|
|
7
|
+
request: mock(() => Promise.resolve({
|
|
8
|
+
data: new TextEncoder().encode(JSON.stringify({ ok: true, result: { ok: true } })),
|
|
9
|
+
})),
|
|
10
|
+
}
|
|
11
|
+
const client = new NatsCoordinationClient(mockNc as any)
|
|
12
|
+
|
|
13
|
+
await client.addTodo("proj-1", "todo-1", "Fix bug", "normal", "session-1")
|
|
14
|
+
|
|
15
|
+
expect(mockNc.request).toHaveBeenCalled()
|
|
16
|
+
const callArgs = mockNc.request.mock.calls[0]
|
|
17
|
+
expect(callArgs[0]).toBe("runtime.cmd.workspace.todo.add")
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
test("state.coordinationByWorkspace returns empty map initially", () => {
|
|
21
|
+
const client = new NatsCoordinationClient({} as any)
|
|
22
|
+
expect(client.state.coordinationByWorkspace.size).toBe(0)
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
test("throws when server returns error", async () => {
|
|
26
|
+
const mockNc = {
|
|
27
|
+
request: mock(() => Promise.resolve({
|
|
28
|
+
data: new TextEncoder().encode(JSON.stringify({ ok: false, error: "Project not found" })),
|
|
29
|
+
})),
|
|
30
|
+
}
|
|
31
|
+
const client = new NatsCoordinationClient(mockNc as any)
|
|
32
|
+
await expect(client.addTodo("proj-1", "todo-1", "Fix bug", "normal", "session-1")).rejects.toThrow("Project not found")
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
test("implements CoordinationStore interface", () => {
|
|
36
|
+
const client = new NatsCoordinationClient({} as any)
|
|
37
|
+
expect(typeof client.addTodo).toBe("function")
|
|
38
|
+
expect(typeof client.claimTodo).toBe("function")
|
|
39
|
+
expect(typeof client.completeTodo).toBe("function")
|
|
40
|
+
expect(typeof client.abandonTodo).toBe("function")
|
|
41
|
+
expect(typeof client.createClaim).toBe("function")
|
|
42
|
+
expect(typeof client.releaseClaim).toBe("function")
|
|
43
|
+
expect(typeof client.createWorktree).toBe("function")
|
|
44
|
+
expect(typeof client.assignWorktree).toBe("function")
|
|
45
|
+
expect(typeof client.removeWorktree).toBe("function")
|
|
46
|
+
expect(typeof client.setRule).toBe("function")
|
|
47
|
+
expect(typeof client.removeRule).toBe("function")
|
|
48
|
+
})
|
|
49
|
+
})
|