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
package/package.json
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "airaknit",
|
|
3
|
+
"type": "module",
|
|
4
|
+
"version": "1.1.2-rc.9",
|
|
5
|
+
"description": "A playful web workbench for Claude Code and Codex",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"keywords": [
|
|
8
|
+
"claude",
|
|
9
|
+
"claude-code",
|
|
10
|
+
"ai",
|
|
11
|
+
"chat",
|
|
12
|
+
"ui",
|
|
13
|
+
"agent",
|
|
14
|
+
"anthropic",
|
|
15
|
+
"bun",
|
|
16
|
+
"react"
|
|
17
|
+
],
|
|
18
|
+
"repository": {
|
|
19
|
+
"type": "git",
|
|
20
|
+
"url": "git+https://github.com/lagz0ne/tinkaria.git"
|
|
21
|
+
},
|
|
22
|
+
"bin": {
|
|
23
|
+
"airaknit": "bin/airaknit",
|
|
24
|
+
"airaknit-project": "bin/airaknit-project"
|
|
25
|
+
},
|
|
26
|
+
"packageManager": "bun@1.3.11",
|
|
27
|
+
"files": [
|
|
28
|
+
"bin/",
|
|
29
|
+
"src/nats/",
|
|
30
|
+
"src/runner/",
|
|
31
|
+
"src/server/",
|
|
32
|
+
"src/shared/",
|
|
33
|
+
"dist/client/"
|
|
34
|
+
],
|
|
35
|
+
"engines": {
|
|
36
|
+
"bun": ">=1.3.5"
|
|
37
|
+
},
|
|
38
|
+
"scripts": {
|
|
39
|
+
"build": "vite build",
|
|
40
|
+
"check": "bunx @typescript/native-preview --noEmit -p tsconfig.json && vite build",
|
|
41
|
+
"lint:rules": "bash .git/hooks/pre-commit --all",
|
|
42
|
+
"dev": "bun run ./scripts/dev.ts",
|
|
43
|
+
"dev:client": "vite --host 0.0.0.0 --port 5174",
|
|
44
|
+
"dev:server": "bun run ./scripts/dev-server.ts --no-open --port 5175",
|
|
45
|
+
"install:dev": "bun install && bun run build && bun install -g .",
|
|
46
|
+
"start": "bun run ./src/server/cli.ts",
|
|
47
|
+
"test": "bun test",
|
|
48
|
+
"verify:journey": "bun run ./scripts/verify-journey.ts",
|
|
49
|
+
"copy:legacy-transcripts": "bun run ./scripts/copy-legacy-transcripts.ts",
|
|
50
|
+
"prepublishOnly": "vite build"
|
|
51
|
+
},
|
|
52
|
+
"dependencies": {
|
|
53
|
+
"@anthropic-ai/claude-agent-sdk": "^0.2.91",
|
|
54
|
+
"@chenglou/pretext": "^0.0.4",
|
|
55
|
+
"@lagz0ne/nats-embedded": "^0.3.1",
|
|
56
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
57
|
+
"@nats-io/jetstream": "^3.3.1",
|
|
58
|
+
"@nats-io/jwt": "^0.0.10-5",
|
|
59
|
+
"@nats-io/kv": "^3.3.1",
|
|
60
|
+
"@nats-io/nats-core": "^3.3.1",
|
|
61
|
+
"@nats-io/transport-node": "^3.3.1",
|
|
62
|
+
"@radix-ui/react-alert-dialog": "^1.1.15",
|
|
63
|
+
"@radix-ui/react-context-menu": "^2.2.16",
|
|
64
|
+
"@radix-ui/react-dropdown-menu": "^2.1.16",
|
|
65
|
+
"@radix-ui/react-select": "^2.2.6",
|
|
66
|
+
"@tanstack/react-virtual": "^3.13.23",
|
|
67
|
+
"@xterm/addon-fit": "^0.11.0",
|
|
68
|
+
"@xterm/addon-serialize": "^0.14.0",
|
|
69
|
+
"@xterm/addon-web-links": "^0.12.0",
|
|
70
|
+
"@xterm/headless": "^6.0.0",
|
|
71
|
+
"@xterm/xterm": "^6.0.0",
|
|
72
|
+
"cloudflared": "^0.7.1",
|
|
73
|
+
"default-shell": "^2.2.0",
|
|
74
|
+
"diff": "^8.0.4",
|
|
75
|
+
"file-type": "^22.0.1",
|
|
76
|
+
"js-yaml": "^4.1.1",
|
|
77
|
+
"mermaid": "^11.14.0",
|
|
78
|
+
"minimatch": "^10.2.5",
|
|
79
|
+
"qrcode": "^1.5.4",
|
|
80
|
+
"react-resizable-panels": "^4.9.0",
|
|
81
|
+
"shell-quote": "^1.8.4",
|
|
82
|
+
"sonner": "^2.0.7",
|
|
83
|
+
"sugar-high": "^1.0.0",
|
|
84
|
+
"web-push": "^3.6.7",
|
|
85
|
+
"zod": "^4.3.6"
|
|
86
|
+
},
|
|
87
|
+
"devDependencies": {
|
|
88
|
+
"@radix-ui/react-dialog": "^1.1.15",
|
|
89
|
+
"@radix-ui/react-popover": "^1.1.15",
|
|
90
|
+
"@radix-ui/react-tooltip": "^1.2.8",
|
|
91
|
+
"@resvg/resvg-js": "^2.6.2",
|
|
92
|
+
"@tailwindcss/postcss": "^4.2.2",
|
|
93
|
+
"@tailwindcss/typography": "^0.5.19",
|
|
94
|
+
"@types/bun": "^1.3.11",
|
|
95
|
+
"@types/diff": "^8.0.0",
|
|
96
|
+
"@types/js-yaml": "^4.0.9",
|
|
97
|
+
"@types/node": "^25.5.0",
|
|
98
|
+
"@types/qrcode": "^1.5.6",
|
|
99
|
+
"@types/react": "19.2.14",
|
|
100
|
+
"@types/react-dom": "19.2.3",
|
|
101
|
+
"@types/shell-quote": "^1.7.5",
|
|
102
|
+
"@types/web-push": "^3.6.4",
|
|
103
|
+
"@vitejs/plugin-react": "6.0.1",
|
|
104
|
+
"autoprefixer": "^10.4.27",
|
|
105
|
+
"class-variance-authority": "^0.7.1",
|
|
106
|
+
"clsx": "^2.1.1",
|
|
107
|
+
"lucide-react": "^1.7.0",
|
|
108
|
+
"png-to-ico": "^3.0.1",
|
|
109
|
+
"react": "19.2.4",
|
|
110
|
+
"react-dom": "19.2.4",
|
|
111
|
+
"react-markdown": "^10.1.0",
|
|
112
|
+
"react-router-dom": "^7.14.0",
|
|
113
|
+
"remark-gfm": "^4.0.1",
|
|
114
|
+
"sharp": "^0.34.5",
|
|
115
|
+
"streamdown": "^2.5.0",
|
|
116
|
+
"tailwind-merge": "^3.5.0",
|
|
117
|
+
"tailwindcss": "^4.2.2",
|
|
118
|
+
"typescript": "6.0.2",
|
|
119
|
+
"vite": "^8.0.3",
|
|
120
|
+
"zustand": "^5.0.12"
|
|
121
|
+
}
|
|
122
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { describe, test, expect } from "bun:test"
|
|
2
|
+
import { buildCalloutConfig, validateCalloutConfigShape } from "./callout-config"
|
|
3
|
+
|
|
4
|
+
const FAKE_ACCOUNT_KEY = "ACPJ3QKPKLBKL6X4FAKEACCOUNTPUBLIC"
|
|
5
|
+
const FAKE_AUTH_USER_KEY = "UDXFAKEUSERKEY3FAKEAUTHUSERPUBLIC"
|
|
6
|
+
|
|
7
|
+
describe("buildCalloutConfig", () => {
|
|
8
|
+
test("includes required auth_callout block with provided keys", () => {
|
|
9
|
+
const config = buildCalloutConfig({
|
|
10
|
+
accountPublicKey: FAKE_ACCOUNT_KEY,
|
|
11
|
+
authUserPublicKey: FAKE_AUTH_USER_KEY,
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
expect(config).toContain("auth_callout")
|
|
15
|
+
expect(config).toContain(`issuer: "${FAKE_ACCOUNT_KEY}"`)
|
|
16
|
+
expect(config).toContain(`"${FAKE_AUTH_USER_KEY}"`)
|
|
17
|
+
expect(config).toContain("jetstream")
|
|
18
|
+
expect(config).toContain("websocket")
|
|
19
|
+
expect(config).toContain("no_tls: true")
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
test("uses default host 127.0.0.1 when not specified", () => {
|
|
23
|
+
const config = buildCalloutConfig({
|
|
24
|
+
accountPublicKey: FAKE_ACCOUNT_KEY,
|
|
25
|
+
authUserPublicKey: FAKE_AUTH_USER_KEY,
|
|
26
|
+
})
|
|
27
|
+
expect(config).toContain('"127.0.0.1"')
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
test("uses provided host", () => {
|
|
31
|
+
const config = buildCalloutConfig({
|
|
32
|
+
accountPublicKey: FAKE_ACCOUNT_KEY,
|
|
33
|
+
authUserPublicKey: FAKE_AUTH_USER_KEY,
|
|
34
|
+
host: "100.64.1.1",
|
|
35
|
+
})
|
|
36
|
+
expect(config).toContain('"100.64.1.1"')
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
test("includes storeDir when provided", () => {
|
|
40
|
+
const config = buildCalloutConfig({
|
|
41
|
+
accountPublicKey: FAKE_ACCOUNT_KEY,
|
|
42
|
+
authUserPublicKey: FAKE_AUTH_USER_KEY,
|
|
43
|
+
storeDir: "/tmp/nats-test-store",
|
|
44
|
+
})
|
|
45
|
+
expect(config).toContain('store_dir: "/tmp/nats-test-store"')
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
test("uses provided port", () => {
|
|
49
|
+
const config = buildCalloutConfig({
|
|
50
|
+
accountPublicKey: FAKE_ACCOUNT_KEY,
|
|
51
|
+
authUserPublicKey: FAKE_AUTH_USER_KEY,
|
|
52
|
+
port: 4567,
|
|
53
|
+
wsPort: 4568,
|
|
54
|
+
})
|
|
55
|
+
expect(config).toContain("port: 4567")
|
|
56
|
+
expect(config).toContain("port: 4568")
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
test("uses custom account name when provided", () => {
|
|
60
|
+
const config = buildCalloutConfig({
|
|
61
|
+
accountPublicKey: FAKE_ACCOUNT_KEY,
|
|
62
|
+
authUserPublicKey: FAKE_AUTH_USER_KEY,
|
|
63
|
+
accountName: "MY_ACCOUNT",
|
|
64
|
+
})
|
|
65
|
+
expect(config).toContain("MY_ACCOUNT")
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
test("no include directives (avoids wrapper mis-resolution bug)", () => {
|
|
69
|
+
const config = buildCalloutConfig({
|
|
70
|
+
accountPublicKey: FAKE_ACCOUNT_KEY,
|
|
71
|
+
authUserPublicKey: FAKE_AUTH_USER_KEY,
|
|
72
|
+
})
|
|
73
|
+
expect(config).not.toContain("include")
|
|
74
|
+
})
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
describe("validateCalloutConfigShape", () => {
|
|
78
|
+
test("passes for a valid config", () => {
|
|
79
|
+
const config = buildCalloutConfig({
|
|
80
|
+
accountPublicKey: FAKE_ACCOUNT_KEY,
|
|
81
|
+
authUserPublicKey: FAKE_AUTH_USER_KEY,
|
|
82
|
+
})
|
|
83
|
+
const { ok, missing } = validateCalloutConfigShape(config)
|
|
84
|
+
expect(ok).toBe(true)
|
|
85
|
+
expect(missing).toHaveLength(0)
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
test("fails when sections are missing", () => {
|
|
89
|
+
const { ok, missing } = validateCalloutConfigShape("just some text")
|
|
90
|
+
expect(ok).toBe(false)
|
|
91
|
+
expect(missing.length).toBeGreaterThan(0)
|
|
92
|
+
})
|
|
93
|
+
})
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generate a self-contained nats-server config string for auth-callout mode
|
|
3
|
+
* (Stage A, PR1).
|
|
4
|
+
*
|
|
5
|
+
* Key points mirroring the spike caveat in decision 0007:
|
|
6
|
+
* - We do NOT use the wrapper's websocket+config combination (causes include
|
|
7
|
+
* mis-resolution). This config is written to a temp file and passed via -c
|
|
8
|
+
* to the binary directly.
|
|
9
|
+
* - The config includes the websocket{} block, JetStream, and the full
|
|
10
|
+
* accounts + authorization { auth_callout { … } } block.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
export interface CalloutConfigOptions {
|
|
14
|
+
/** TCP listen port; -1 = nats-server picks a random one. */
|
|
15
|
+
port?: number
|
|
16
|
+
/** WebSocket listen port; -1 = nats-server picks a random one. */
|
|
17
|
+
wsPort?: number
|
|
18
|
+
/** Host to bind. Default: 127.0.0.1 */
|
|
19
|
+
host?: string
|
|
20
|
+
/** JetStream store directory. */
|
|
21
|
+
storeDir?: string
|
|
22
|
+
/**
|
|
23
|
+
* Optional HTTP monitoring port (bound to 127.0.0.1 only). When set, enables
|
|
24
|
+
* the nats-server monitoring endpoints (/connz, /subsz, /varz) for diagnostics.
|
|
25
|
+
* Localhost-only — never exposed beyond the server host. Off when undefined.
|
|
26
|
+
*/
|
|
27
|
+
monitorPort?: number
|
|
28
|
+
/** Public key of the callout issuer account. */
|
|
29
|
+
accountPublicKey: string
|
|
30
|
+
/** Public key of the auth-service user (bypasses the callout). */
|
|
31
|
+
authUserPublicKey: string
|
|
32
|
+
/** Name used for the callout account and auth_users entry. */
|
|
33
|
+
accountName?: string
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Return a complete nats-server configuration string for callout mode.
|
|
38
|
+
* The config is self-contained (no `include` directives) so it can be written
|
|
39
|
+
* to any path and passed to nats-server via `-c`.
|
|
40
|
+
*/
|
|
41
|
+
export function buildCalloutConfig(opts: CalloutConfigOptions): string {
|
|
42
|
+
const host = opts.host ?? "127.0.0.1"
|
|
43
|
+
const port = opts.port ?? -1
|
|
44
|
+
const wsPort = opts.wsPort ?? -1
|
|
45
|
+
const accountName = opts.accountName ?? "CALLOUT_ACCOUNT"
|
|
46
|
+
|
|
47
|
+
const lines: string[] = [
|
|
48
|
+
`# nats-server config — auth-callout mode (generated by Tinkaria)`,
|
|
49
|
+
`host: "${host}"`,
|
|
50
|
+
`port: ${port}`,
|
|
51
|
+
``,
|
|
52
|
+
`# JetStream`,
|
|
53
|
+
`jetstream: true`,
|
|
54
|
+
...(opts.storeDir ? [`store_dir: "${opts.storeDir}"`] : []),
|
|
55
|
+
``,
|
|
56
|
+
// HTTP monitoring (diagnostics only, localhost-bound). Enables /connz, /subsz,
|
|
57
|
+
// /varz so we can inspect connections + per-connection subscriptions. Off
|
|
58
|
+
// unless monitorPort is provided.
|
|
59
|
+
...(opts.monitorPort ? [`http: "127.0.0.1:${opts.monitorPort}"`, ``] : []),
|
|
60
|
+
`# WebSocket`,
|
|
61
|
+
`websocket {`,
|
|
62
|
+
` host: "${host}"`,
|
|
63
|
+
` port: ${wsPort}`,
|
|
64
|
+
` no_tls: true`,
|
|
65
|
+
`}`,
|
|
66
|
+
``,
|
|
67
|
+
`# Accounts — one account holds all connections`,
|
|
68
|
+
`accounts {`,
|
|
69
|
+
` ${accountName} {`,
|
|
70
|
+
` jetstream: enabled`,
|
|
71
|
+
` users [`,
|
|
72
|
+
` # auth_users: this user bypasses the callout (the responder itself)`,
|
|
73
|
+
` { nkey: "${opts.authUserPublicKey}" }`,
|
|
74
|
+
` ]`,
|
|
75
|
+
` }`,
|
|
76
|
+
`}`,
|
|
77
|
+
``,
|
|
78
|
+
`# Auth-callout — delegate authorization to the responder`,
|
|
79
|
+
`authorization {`,
|
|
80
|
+
` auth_callout {`,
|
|
81
|
+
` issuer: "${opts.accountPublicKey}"`,
|
|
82
|
+
` auth_users: [ "${opts.authUserPublicKey}" ]`,
|
|
83
|
+
` account: "${accountName}"`,
|
|
84
|
+
` }`,
|
|
85
|
+
`}`,
|
|
86
|
+
]
|
|
87
|
+
|
|
88
|
+
return lines.join("\n") + "\n"
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Validate the generated config shape by checking for required sections.
|
|
93
|
+
* Used in tests; not called in production code.
|
|
94
|
+
*/
|
|
95
|
+
export function validateCalloutConfigShape(config: string): {
|
|
96
|
+
ok: boolean
|
|
97
|
+
missing: string[]
|
|
98
|
+
} {
|
|
99
|
+
const required = [
|
|
100
|
+
"auth_callout",
|
|
101
|
+
"issuer:",
|
|
102
|
+
"auth_users:",
|
|
103
|
+
"account:",
|
|
104
|
+
"websocket",
|
|
105
|
+
"jetstream",
|
|
106
|
+
]
|
|
107
|
+
const missing = required.filter((s) => !config.includes(s))
|
|
108
|
+
return { ok: missing.length === 0, missing }
|
|
109
|
+
}
|
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Integration test — auth-callout isolation (Stage B, PR1).
|
|
3
|
+
*
|
|
4
|
+
* Proves:
|
|
5
|
+
* 1. A runner-A credential connects and can pub/sub its own scoped subjects.
|
|
6
|
+
* 2. The same runner-A credential is DENIED (permissions violation) on
|
|
7
|
+
* runtime.runner.cmd.B.> — the subject scoped to runner-B.
|
|
8
|
+
* 3. The same runner-A credential is DENIED on runner-B's KV registry key.
|
|
9
|
+
* 4. A ui-client credential is denied on any runtime.runner.cmd subject.
|
|
10
|
+
* 5. Unknown/garbage token is refused at connect time.
|
|
11
|
+
*
|
|
12
|
+
* Stage B: credentials are minted via mintCredentialToken (stateless HMAC-signed
|
|
13
|
+
* tokens) rather than registered in the in-memory registry. All isolation
|
|
14
|
+
* assertions are preserved unchanged.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { describe, test, expect, afterAll, beforeAll } from "bun:test"
|
|
18
|
+
import { spawn, type ChildProcess } from "node:child_process"
|
|
19
|
+
import { mkdtempSync, writeFileSync, rmSync } from "node:fs"
|
|
20
|
+
import { join } from "node:path"
|
|
21
|
+
import { tmpdir } from "node:os"
|
|
22
|
+
import { connect, tokenAuthenticator } from "@nats-io/transport-node"
|
|
23
|
+
import { nkeyAuthenticator } from "@nats-io/nats-core"
|
|
24
|
+
import type { NatsConnection } from "@nats-io/transport-node"
|
|
25
|
+
import { resolveBinary } from "@lagz0ne/nats-embedded"
|
|
26
|
+
import { ensureCalloutKeys } from "./keys"
|
|
27
|
+
import { buildCalloutConfig } from "./callout-config"
|
|
28
|
+
import { CalloutResponder } from "./responder"
|
|
29
|
+
import { runnerKvKeySubject } from "./scope-policy"
|
|
30
|
+
import { mintCredentialToken } from "./token"
|
|
31
|
+
|
|
32
|
+
// ── Test setup ────────────────────────────────────────────────────────────────
|
|
33
|
+
|
|
34
|
+
const RUNNER_A = "runner-A"
|
|
35
|
+
const RUNNER_B = "runner-B"
|
|
36
|
+
|
|
37
|
+
// Tokens are minted from the shared secret in startCalloutServer(); declared
|
|
38
|
+
// here so tests can reference them after setup.
|
|
39
|
+
let SERVER_ADMIN_TOKEN: string
|
|
40
|
+
let UI_CLIENT_TOKEN: string
|
|
41
|
+
let RUNNER_A_TOKEN: string
|
|
42
|
+
let RUNNER_B_TOKEN: string
|
|
43
|
+
|
|
44
|
+
interface ServerInfo {
|
|
45
|
+
natsUrl: string
|
|
46
|
+
wsUrl: string
|
|
47
|
+
wsPort: number
|
|
48
|
+
tcpPort: number
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
let serverProcess: ChildProcess | null = null
|
|
52
|
+
let responder: CalloutResponder | null = null
|
|
53
|
+
let configDir: string | null = null
|
|
54
|
+
let serverInfo: ServerInfo | null = null
|
|
55
|
+
|
|
56
|
+
async function startCalloutServer(): Promise<ServerInfo> {
|
|
57
|
+
const dataDir = mkdtempSync(join(tmpdir(), "nats-callout-test-data-"))
|
|
58
|
+
configDir = mkdtempSync(join(tmpdir(), "nats-callout-test-conf-"))
|
|
59
|
+
|
|
60
|
+
const keys = await ensureCalloutKeys(dataDir)
|
|
61
|
+
|
|
62
|
+
// Stage B: mint stateless signed tokens — no registration needed.
|
|
63
|
+
SERVER_ADMIN_TOKEN = await mintCredentialToken({ class: "server-admin" }, keys.tokenSecret)
|
|
64
|
+
UI_CLIENT_TOKEN = await mintCredentialToken({ class: "ui-client" }, keys.tokenSecret)
|
|
65
|
+
RUNNER_A_TOKEN = await mintCredentialToken({ class: "runner", runnerId: RUNNER_A }, keys.tokenSecret)
|
|
66
|
+
RUNNER_B_TOKEN = await mintCredentialToken({ class: "runner", runnerId: RUNNER_B }, keys.tokenSecret)
|
|
67
|
+
|
|
68
|
+
const config = buildCalloutConfig({
|
|
69
|
+
host: "127.0.0.1",
|
|
70
|
+
port: -1, // random
|
|
71
|
+
wsPort: -1, // random
|
|
72
|
+
accountPublicKey: keys.accountPublicKey,
|
|
73
|
+
authUserPublicKey: keys.authUserPublicKey,
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
const configPath = join(configDir, "test-callout.conf")
|
|
77
|
+
writeFileSync(configPath, config)
|
|
78
|
+
|
|
79
|
+
const binaryPath = resolveBinary()
|
|
80
|
+
|
|
81
|
+
return new Promise<ServerInfo>((resolve, reject) => {
|
|
82
|
+
const proc = spawn(binaryPath, ["-c", configPath], {
|
|
83
|
+
stdio: ["ignore", "ignore", "pipe"],
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
serverProcess = proc
|
|
87
|
+
|
|
88
|
+
const timeout = setTimeout(() => {
|
|
89
|
+
proc.kill("SIGKILL")
|
|
90
|
+
reject(new Error("nats-server did not start within 10s"))
|
|
91
|
+
}, 10_000)
|
|
92
|
+
|
|
93
|
+
proc.on("error", (err) => { clearTimeout(timeout); reject(err) })
|
|
94
|
+
proc.on("exit", (code) => {
|
|
95
|
+
clearTimeout(timeout)
|
|
96
|
+
reject(new Error(`nats-server exited early with code ${code}`))
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
let tcpPort: number | null = null
|
|
100
|
+
let wsPort: number | null = null
|
|
101
|
+
let buffer = ""
|
|
102
|
+
|
|
103
|
+
proc.stderr!.on("data", (chunk: Buffer) => {
|
|
104
|
+
buffer += chunk.toString()
|
|
105
|
+
const lines = buffer.split("\n")
|
|
106
|
+
buffer = lines.pop() ?? ""
|
|
107
|
+
|
|
108
|
+
for (const line of lines) {
|
|
109
|
+
const tcpMatch = line.match(/Listening for client connections on .+:(\d+)/)
|
|
110
|
+
if (tcpMatch) tcpPort = Number(tcpMatch[1])
|
|
111
|
+
|
|
112
|
+
const wsMatch = line.match(/Listening for websocket clients on .+:(\d+)/)
|
|
113
|
+
if (wsMatch) wsPort = Number(wsMatch[1])
|
|
114
|
+
|
|
115
|
+
if (tcpPort !== null && wsPort !== null) {
|
|
116
|
+
clearTimeout(timeout)
|
|
117
|
+
proc.removeAllListeners("exit")
|
|
118
|
+
const natsUrl = `nats://127.0.0.1:${tcpPort}`
|
|
119
|
+
resolve({ natsUrl, wsUrl: `ws://127.0.0.1:${wsPort}`, wsPort, tcpPort })
|
|
120
|
+
return
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
})
|
|
124
|
+
}).then(async (info) => {
|
|
125
|
+
// Start the responder — tokenSecret is sufficient; no registration step.
|
|
126
|
+
responder = new CalloutResponder({
|
|
127
|
+
natsUrl: info.natsUrl,
|
|
128
|
+
authUserKp: keys.authUserKp,
|
|
129
|
+
accountKp: keys.accountKp,
|
|
130
|
+
accountPublicKey: keys.accountPublicKey,
|
|
131
|
+
accountName: "CALLOUT_ACCOUNT",
|
|
132
|
+
tokenSecret: keys.tokenSecret,
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
await responder.start()
|
|
136
|
+
|
|
137
|
+
// Brief settle for the responder subscription to be active
|
|
138
|
+
await new Promise((r) => setTimeout(r, 100))
|
|
139
|
+
|
|
140
|
+
return info
|
|
141
|
+
})
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
async function connectWithToken(natsUrl: string, token: string): Promise<NatsConnection> {
|
|
145
|
+
return connect({
|
|
146
|
+
servers: natsUrl,
|
|
147
|
+
authenticator: tokenAuthenticator(token),
|
|
148
|
+
// Short timeout so permission errors surface quickly
|
|
149
|
+
timeout: 5000,
|
|
150
|
+
})
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// ── Lifecycle ─────────────────────────────────────────────────────────────────
|
|
154
|
+
|
|
155
|
+
beforeAll(async () => {
|
|
156
|
+
serverInfo = await startCalloutServer()
|
|
157
|
+
}, 15_000)
|
|
158
|
+
|
|
159
|
+
afterAll(async () => {
|
|
160
|
+
await responder?.stop()
|
|
161
|
+
serverProcess?.kill("SIGTERM")
|
|
162
|
+
if (configDir) {
|
|
163
|
+
try { rmSync(configDir, { recursive: true }) } catch { /* best-effort */ }
|
|
164
|
+
}
|
|
165
|
+
})
|
|
166
|
+
|
|
167
|
+
// ── Tests ─────────────────────────────────────────────────────────────────────
|
|
168
|
+
|
|
169
|
+
describe("auth-callout isolation", () => {
|
|
170
|
+
|
|
171
|
+
// ── Happy path ──────────────────────────────────────────────────────────────
|
|
172
|
+
|
|
173
|
+
test("runner-A connects with scoped credential", async () => {
|
|
174
|
+
const nc = await connectWithToken(serverInfo!.natsUrl, RUNNER_A_TOKEN)
|
|
175
|
+
expect(nc.isClosed()).toBe(false)
|
|
176
|
+
await nc.drain()
|
|
177
|
+
})
|
|
178
|
+
|
|
179
|
+
test("runner-A can publish its own heartbeat", async () => {
|
|
180
|
+
const nc = await connectWithToken(serverInfo!.natsUrl, RUNNER_A_TOKEN)
|
|
181
|
+
// If the subject is not allowed, NATS will close the connection with a
|
|
182
|
+
// permissions violation. No error here = allowed.
|
|
183
|
+
nc.publish(`runtime.runner.heartbeat.${RUNNER_A}`, new TextEncoder().encode("hb"))
|
|
184
|
+
await nc.flush()
|
|
185
|
+
await nc.drain()
|
|
186
|
+
})
|
|
187
|
+
|
|
188
|
+
test("runner-A can subscribe to its own cmd subject", async () => {
|
|
189
|
+
const nc = await connectWithToken(serverInfo!.natsUrl, RUNNER_A_TOKEN)
|
|
190
|
+
const sub = nc.subscribe(`runtime.runner.cmd.${RUNNER_A}.>`)
|
|
191
|
+
expect(sub).toBeDefined()
|
|
192
|
+
sub.unsubscribe()
|
|
193
|
+
await nc.drain()
|
|
194
|
+
})
|
|
195
|
+
|
|
196
|
+
test("ui-client connects and can subscribe to runner events", async () => {
|
|
197
|
+
const nc = await connectWithToken(serverInfo!.natsUrl, UI_CLIENT_TOKEN)
|
|
198
|
+
const sub = nc.subscribe("runtime.runner.evt.>")
|
|
199
|
+
expect(sub).toBeDefined()
|
|
200
|
+
sub.unsubscribe()
|
|
201
|
+
await nc.drain()
|
|
202
|
+
})
|
|
203
|
+
|
|
204
|
+
// ── Negative — the decisive isolation assertions ────────────────────────────
|
|
205
|
+
|
|
206
|
+
test("runner-A is DENIED on runtime.runner.cmd.B.> (permissions violation)", async () => {
|
|
207
|
+
const nc = await connectWithToken(serverInfo!.natsUrl, RUNNER_A_TOKEN)
|
|
208
|
+
|
|
209
|
+
// Collect status events and the closed reason.
|
|
210
|
+
const statusEvents: string[] = []
|
|
211
|
+
let closedError: Error | null = null
|
|
212
|
+
|
|
213
|
+
// Subscribe before triggering the violation.
|
|
214
|
+
void (async () => {
|
|
215
|
+
for await (const s of nc.status()) {
|
|
216
|
+
statusEvents.push(`${s.type}:${String(s.data)}`)
|
|
217
|
+
}
|
|
218
|
+
})()
|
|
219
|
+
|
|
220
|
+
const closedPromise = nc.closed().then((err) => {
|
|
221
|
+
closedError = err instanceof Error ? err : new Error(String(err ?? "connection closed"))
|
|
222
|
+
})
|
|
223
|
+
|
|
224
|
+
// Attempt to publish to runner-B's command subject (not in runner-A's pub allow list).
|
|
225
|
+
// NATS sends -ERR 'Permissions Violation for Publish' and closes the connection.
|
|
226
|
+
nc.publish(`runtime.runner.cmd.${RUNNER_B}.start`, new TextEncoder().encode("{}"))
|
|
227
|
+
await nc.flush().catch(() => { /* may fail if already closing */ })
|
|
228
|
+
|
|
229
|
+
// Wait for the connection to be closed (NATS closes it on permissions violation).
|
|
230
|
+
await Promise.race([
|
|
231
|
+
closedPromise,
|
|
232
|
+
new Promise((r) => setTimeout(r, 4000)),
|
|
233
|
+
])
|
|
234
|
+
|
|
235
|
+
// Drain/close cleanup (may already be closed)
|
|
236
|
+
try { await nc.drain() } catch { /* expected */ }
|
|
237
|
+
|
|
238
|
+
// The decisive assertion: NATS must have closed the connection after the
|
|
239
|
+
// permissions violation, or we must have received a permissionsError status.
|
|
240
|
+
const hasPermissionsViolation =
|
|
241
|
+
closedError !== null ||
|
|
242
|
+
statusEvents.some((s) =>
|
|
243
|
+
s.toLowerCase().includes("permission") || s.toLowerCase().includes("violation")
|
|
244
|
+
)
|
|
245
|
+
|
|
246
|
+
const evidenceMsg = closedError
|
|
247
|
+
? `connection closed by NATS: ${closedError.message}`
|
|
248
|
+
: `status events: ${statusEvents.join(", ")}`
|
|
249
|
+
|
|
250
|
+
console.log(`[TEST] runner-A denied on runtime.runner.cmd.${RUNNER_B}.start — ${evidenceMsg}`)
|
|
251
|
+
|
|
252
|
+
expect(hasPermissionsViolation).toBe(true)
|
|
253
|
+
}, 10_000)
|
|
254
|
+
|
|
255
|
+
test("runner-A is DENIED on runner-B KV registry key", async () => {
|
|
256
|
+
const nc = await connectWithToken(serverInfo!.natsUrl, RUNNER_A_TOKEN)
|
|
257
|
+
|
|
258
|
+
let denied = false
|
|
259
|
+
const errorPromise = new Promise<void>((resolve) => {
|
|
260
|
+
void (async () => {
|
|
261
|
+
for await (const s of nc.status()) {
|
|
262
|
+
if (s.type === "permissionsError" || String(s.data).includes("Permissions Violation")) {
|
|
263
|
+
denied = true
|
|
264
|
+
resolve()
|
|
265
|
+
return
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
})()
|
|
269
|
+
nc.closed().then(() => { denied = true; resolve() })
|
|
270
|
+
})
|
|
271
|
+
|
|
272
|
+
// Attempt to write runner-B's KV key directly via NATS publish.
|
|
273
|
+
const runnerBKvSubject = runnerKvKeySubject(RUNNER_B)
|
|
274
|
+
nc.publish(runnerBKvSubject, new TextEncoder().encode("{}"))
|
|
275
|
+
|
|
276
|
+
await Promise.race([
|
|
277
|
+
errorPromise,
|
|
278
|
+
nc.closed(),
|
|
279
|
+
new Promise((r) => setTimeout(r, 3000)),
|
|
280
|
+
])
|
|
281
|
+
|
|
282
|
+
try { await nc.drain() } catch { /* expected */ }
|
|
283
|
+
|
|
284
|
+
expect(denied).toBe(true)
|
|
285
|
+
console.log(`[TEST] runner-A denied on runner-B KV subject (${runnerBKvSubject})`)
|
|
286
|
+
}, 10_000)
|
|
287
|
+
|
|
288
|
+
test("ui-client is DENIED on runtime.runner.cmd subject", async () => {
|
|
289
|
+
const nc = await connectWithToken(serverInfo!.natsUrl, UI_CLIENT_TOKEN)
|
|
290
|
+
|
|
291
|
+
let denied = false
|
|
292
|
+
const errorPromise = new Promise<void>((resolve) => {
|
|
293
|
+
void (async () => {
|
|
294
|
+
for await (const s of nc.status()) {
|
|
295
|
+
if (s.type === "permissionsError" || String(s.data).includes("Permissions Violation")) {
|
|
296
|
+
denied = true
|
|
297
|
+
resolve()
|
|
298
|
+
return
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
})()
|
|
302
|
+
nc.closed().then(() => { denied = true; resolve() })
|
|
303
|
+
})
|
|
304
|
+
|
|
305
|
+
nc.publish(`runtime.runner.cmd.${RUNNER_A}.start`, new TextEncoder().encode("{}"))
|
|
306
|
+
|
|
307
|
+
await Promise.race([
|
|
308
|
+
errorPromise,
|
|
309
|
+
nc.closed(),
|
|
310
|
+
new Promise((r) => setTimeout(r, 3000)),
|
|
311
|
+
])
|
|
312
|
+
|
|
313
|
+
try { await nc.drain() } catch { /* expected */ }
|
|
314
|
+
|
|
315
|
+
expect(denied).toBe(true)
|
|
316
|
+
console.log("[TEST] ui-client denied on runtime.runner.cmd")
|
|
317
|
+
}, 10_000)
|
|
318
|
+
|
|
319
|
+
// ── Unknown credential is rejected at connect ──────────────────────────────
|
|
320
|
+
|
|
321
|
+
test("unknown token is refused at connect time", async () => {
|
|
322
|
+
let connectError: Error | null = null
|
|
323
|
+
try {
|
|
324
|
+
const nc = await connectWithToken(serverInfo!.natsUrl, "bogus-token-not-registered")
|
|
325
|
+
await nc.drain()
|
|
326
|
+
} catch (err) {
|
|
327
|
+
connectError = err instanceof Error ? err : new Error(String(err))
|
|
328
|
+
}
|
|
329
|
+
expect(connectError).not.toBeNull()
|
|
330
|
+
console.log("[TEST] unknown credential refused:", connectError?.message)
|
|
331
|
+
}, 10_000)
|
|
332
|
+
})
|