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,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Callout signing-key management (Stage A, PR1).
|
|
3
|
+
*
|
|
4
|
+
* The auth-callout requires two nkey pairs:
|
|
5
|
+
* - An **account key pair** — the callout issuer account (public key goes into
|
|
6
|
+
* the nats-server config; the seed is held by the responder to sign user JWTs).
|
|
7
|
+
* - An **auth-service key pair** — the user listed in `auth_users` whose
|
|
8
|
+
* connection bypasses the callout (required by NATS auth-callout spec).
|
|
9
|
+
*
|
|
10
|
+
* Both seeds are stored alongside `nats.token` in NATS_DATA_DIR (same on-disk
|
|
11
|
+
* trust boundary; gitignored). The server reads them at startup; key rotation
|
|
12
|
+
* is a documented follow-up.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { join } from "node:path"
|
|
16
|
+
import { mkdirSync, renameSync, chmodSync } from "node:fs"
|
|
17
|
+
import { createAccount, createUser, fromSeed } from "@nats-io/nkeys"
|
|
18
|
+
import type { KeyPair } from "@nats-io/nkeys"
|
|
19
|
+
|
|
20
|
+
const ACCOUNT_SEED_FILE = "nats.callout-account.seed"
|
|
21
|
+
const AUTH_USER_SEED_FILE = "nats.callout-auth-user.seed"
|
|
22
|
+
const TOKEN_SECRET_FILE = "nats.callout-token.secret"
|
|
23
|
+
|
|
24
|
+
/** Loaded callout key material. */
|
|
25
|
+
export interface CalloutKeys {
|
|
26
|
+
/** KeyPair for the callout issuer account (signs user JWTs). */
|
|
27
|
+
accountKp: KeyPair
|
|
28
|
+
/** Public key of the account (goes into nats-server config). */
|
|
29
|
+
accountPublicKey: string
|
|
30
|
+
/** KeyPair for the auth-service user (bypasses callout). */
|
|
31
|
+
authUserKp: KeyPair
|
|
32
|
+
/** Public key of the auth-service user (goes into nats-server config). */
|
|
33
|
+
authUserPublicKey: string
|
|
34
|
+
/**
|
|
35
|
+
* 32-byte shared secret used to mint and verify stateless credential tokens
|
|
36
|
+
* (Stage B). Both the server process and the daemon child load this from disk.
|
|
37
|
+
*/
|
|
38
|
+
tokenSecret: Uint8Array
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async function readOrCreateSeed(
|
|
42
|
+
seedPath: string,
|
|
43
|
+
generator: () => KeyPair
|
|
44
|
+
): Promise<string> {
|
|
45
|
+
const file = Bun.file(seedPath)
|
|
46
|
+
if (await file.exists()) {
|
|
47
|
+
const seed = (await file.text()).trim()
|
|
48
|
+
if (seed.length > 0) return seed
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const kp = generator()
|
|
52
|
+
const seed = Buffer.from(kp.getSeed()).toString()
|
|
53
|
+
const tmp = `${seedPath}.tmp.${process.pid}`
|
|
54
|
+
await Bun.write(tmp, seed + "\n")
|
|
55
|
+
renameSync(tmp, seedPath) // atomic
|
|
56
|
+
chmodSync(seedPath, 0o600) // owner-read/write only
|
|
57
|
+
return seed
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async function readOrCreateTokenSecret(secretPath: string): Promise<Uint8Array> {
|
|
61
|
+
const file = Bun.file(secretPath)
|
|
62
|
+
if (await file.exists()) {
|
|
63
|
+
const hex = (await file.text()).trim()
|
|
64
|
+
if (hex.length === 64) return new Uint8Array(Buffer.from(hex, "hex"))
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const secret = new Uint8Array(32)
|
|
68
|
+
crypto.getRandomValues(secret)
|
|
69
|
+
const hex = Buffer.from(secret).toString("hex")
|
|
70
|
+
const tmp = `${secretPath}.tmp.${process.pid}`
|
|
71
|
+
await Bun.write(tmp, hex + "\n")
|
|
72
|
+
renameSync(tmp, secretPath) // atomic
|
|
73
|
+
chmodSync(secretPath, 0o600) // owner-read/write only
|
|
74
|
+
return secret
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Ensure callout key material exists in `dataDir`.
|
|
79
|
+
* Generates both pairs and the token secret on first call; subsequent calls load from disk.
|
|
80
|
+
* Safe for single-writer use (the NATS daemon); not safe for concurrent writers.
|
|
81
|
+
*/
|
|
82
|
+
export async function ensureCalloutKeys(dataDir: string): Promise<CalloutKeys> {
|
|
83
|
+
mkdirSync(dataDir, { recursive: true })
|
|
84
|
+
|
|
85
|
+
const accountSeedPath = join(dataDir, ACCOUNT_SEED_FILE)
|
|
86
|
+
const authUserSeedPath = join(dataDir, AUTH_USER_SEED_FILE)
|
|
87
|
+
const tokenSecretPath = join(dataDir, TOKEN_SECRET_FILE)
|
|
88
|
+
|
|
89
|
+
const accountSeed = await readOrCreateSeed(accountSeedPath, createAccount)
|
|
90
|
+
const authUserSeed = await readOrCreateSeed(authUserSeedPath, createUser)
|
|
91
|
+
const tokenSecret = await readOrCreateTokenSecret(tokenSecretPath)
|
|
92
|
+
|
|
93
|
+
const accountKp = fromSeed(Buffer.from(accountSeed.trim()))
|
|
94
|
+
const authUserKp = fromSeed(Buffer.from(authUserSeed.trim()))
|
|
95
|
+
|
|
96
|
+
return {
|
|
97
|
+
accountKp,
|
|
98
|
+
accountPublicKey: accountKp.getPublicKey(),
|
|
99
|
+
authUserKp,
|
|
100
|
+
authUserPublicKey: authUserKp.getPublicKey(),
|
|
101
|
+
tokenSecret,
|
|
102
|
+
}
|
|
103
|
+
}
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auth-callout responder (Stage B, PR1).
|
|
3
|
+
*
|
|
4
|
+
* Subscribes to $SYS.REQ.USER.AUTH. For each request:
|
|
5
|
+
* 1. Decode the AuthorizationRequest JWT from NATS.
|
|
6
|
+
* 2. Validate the credential in connect_opts.auth_token via verifyCredentialToken.
|
|
7
|
+
* 3. Resolve the connection class (and runnerId for runner class).
|
|
8
|
+
* 4. Mint a scoped user JWT and respond with a signed AuthorizationResponse.
|
|
9
|
+
* 5. Audit every decision (grant/deny).
|
|
10
|
+
*
|
|
11
|
+
* The responding connection itself uses the auth-service nkey (authUserKp) and
|
|
12
|
+
* bypasses the callout, per the NATS auth_callout spec.
|
|
13
|
+
*
|
|
14
|
+
* Stage B: replaced in-memory credential registry with stateless HMAC-signed
|
|
15
|
+
* tokens (verifyCredentialToken). No registration step; both the server process
|
|
16
|
+
* (issuer) and this daemon child (verifier) share only the on-disk token secret.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { connect, type NatsConnection, type Subscription } from "@nats-io/transport-node"
|
|
20
|
+
import { nkeyAuthenticator } from "@nats-io/nats-core"
|
|
21
|
+
import {
|
|
22
|
+
encodeUser,
|
|
23
|
+
encodeAuthorizationResponse,
|
|
24
|
+
decode,
|
|
25
|
+
Algorithms,
|
|
26
|
+
fromPublic,
|
|
27
|
+
createUser,
|
|
28
|
+
} from "@nats-io/jwt"
|
|
29
|
+
import type { AuthorizationRequest } from "@nats-io/jwt"
|
|
30
|
+
import type { KeyPair } from "@nats-io/nkeys"
|
|
31
|
+
import { permissionsFor } from "./scope-policy"
|
|
32
|
+
import type { ResolvedIdentity } from "./scope-policy"
|
|
33
|
+
import { verifyCredentialToken } from "./token"
|
|
34
|
+
|
|
35
|
+
const CALLOUT_SUBJECT = "$SYS.REQ.USER.AUTH"
|
|
36
|
+
|
|
37
|
+
// ── Responder ─────────────────────────────────────────────────────────────────
|
|
38
|
+
|
|
39
|
+
export interface ResponderOptions {
|
|
40
|
+
/** NATS URL to connect to (TCP). */
|
|
41
|
+
natsUrl: string
|
|
42
|
+
/** KeyPair for the auth-service user (bypasses callout). */
|
|
43
|
+
authUserKp: KeyPair
|
|
44
|
+
/** KeyPair for the callout issuer account (signs user JWTs). */
|
|
45
|
+
accountKp: KeyPair
|
|
46
|
+
/** Public key of the callout account (needed for encodeUser issuer_account). */
|
|
47
|
+
accountPublicKey: string
|
|
48
|
+
/**
|
|
49
|
+
* Account name as declared in the nats-server config accounts block.
|
|
50
|
+
* This becomes the `aud` (audience) of the issued user JWT so NATS knows
|
|
51
|
+
* which account to place the connection in.
|
|
52
|
+
*/
|
|
53
|
+
accountName?: string
|
|
54
|
+
/**
|
|
55
|
+
* 32-byte shared secret loaded from NATS_DATA_DIR by the daemon child.
|
|
56
|
+
* Used to verify stateless credential tokens without any in-memory registry.
|
|
57
|
+
*/
|
|
58
|
+
tokenSecret: Uint8Array
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export class CalloutResponder {
|
|
62
|
+
private nc: NatsConnection | null = null
|
|
63
|
+
private sub: Subscription | null = null
|
|
64
|
+
private readonly opts: ResponderOptions
|
|
65
|
+
|
|
66
|
+
constructor(opts: ResponderOptions) {
|
|
67
|
+
this.opts = opts
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Connect to NATS as the auth-service user (bypasses callout) and
|
|
72
|
+
* start the subscription loop.
|
|
73
|
+
*/
|
|
74
|
+
async start(): Promise<void> {
|
|
75
|
+
const { authUserKp, natsUrl } = this.opts
|
|
76
|
+
|
|
77
|
+
// Connect using the auth-service nkey (bypasses the callout per NATS spec).
|
|
78
|
+
// nkeyAuthenticator takes the seed bytes; the server verifies the public key
|
|
79
|
+
// against auth_users in the config.
|
|
80
|
+
this.nc = await connect({
|
|
81
|
+
servers: natsUrl,
|
|
82
|
+
authenticator: nkeyAuthenticator(authUserKp.getSeed()),
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
this.sub = this.nc.subscribe(CALLOUT_SUBJECT)
|
|
86
|
+
void this.listenLoop().catch((err) => {
|
|
87
|
+
console.error("[nats-callout] listenLoop fatal:", err)
|
|
88
|
+
})
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** Stop the responder and drain the NATS connection. */
|
|
92
|
+
async stop(): Promise<void> {
|
|
93
|
+
this.sub?.unsubscribe()
|
|
94
|
+
if (this.nc) {
|
|
95
|
+
try { await this.nc.drain() } catch { /* ignore drain errors on shutdown */ }
|
|
96
|
+
this.nc = null
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
private async listenLoop(): Promise<void> {
|
|
101
|
+
if (!this.sub) return
|
|
102
|
+
for await (const msg of this.sub) {
|
|
103
|
+
await this.handleCallout(msg)
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
private async handleCallout(msg: { data: Uint8Array; respond: (data: Uint8Array) => void }): Promise<void> {
|
|
108
|
+
const { accountKp, accountPublicKey, accountName = "CALLOUT_ACCOUNT" } = this.opts
|
|
109
|
+
|
|
110
|
+
// The callout request body is a JWT (not plain JSON).
|
|
111
|
+
// Decode it to get the AuthorizationRequest payload from the `nats` claim.
|
|
112
|
+
let req: AuthorizationRequest
|
|
113
|
+
try {
|
|
114
|
+
const jwt = new TextDecoder().decode(msg.data)
|
|
115
|
+
const claims = decode<AuthorizationRequest>(jwt)
|
|
116
|
+
req = claims.nats as unknown as AuthorizationRequest
|
|
117
|
+
} catch (err) {
|
|
118
|
+
this.audit("deny", "unparseable-request", null, `decode error: ${err}`)
|
|
119
|
+
await this.respondError(msg, accountKp, null, "invalid authorization request")
|
|
120
|
+
return
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const userNkey = req.user_nkey
|
|
124
|
+
const serverNkey = req.server_id.id
|
|
125
|
+
const presentedToken = req.connect_opts?.auth_token
|
|
126
|
+
|
|
127
|
+
// Verify the stateless signed credential token.
|
|
128
|
+
const identity = await verifyCredentialToken(presentedToken, this.opts.tokenSecret)
|
|
129
|
+
if (!identity) {
|
|
130
|
+
this.audit("deny", userNkey, null, "unknown or invalid credential")
|
|
131
|
+
await this.respondError(msg, accountKp, serverNkey, "unknown credential")
|
|
132
|
+
return
|
|
133
|
+
}
|
|
134
|
+
const scope = permissionsFor(identity)
|
|
135
|
+
|
|
136
|
+
// Mint a fresh user keypair for this connection (the JWT subject).
|
|
137
|
+
// The user nkey from the request is the connecting client's key;
|
|
138
|
+
// we use fromPublic to get a Key that encodeUser can use as ukp.
|
|
139
|
+
let userKey: ReturnType<typeof fromPublic>
|
|
140
|
+
try {
|
|
141
|
+
userKey = fromPublic(userNkey)
|
|
142
|
+
} catch (err) {
|
|
143
|
+
this.audit("deny", userNkey, identity, `invalid user nkey: ${err}`)
|
|
144
|
+
await this.respondError(msg, accountKp, serverNkey, "invalid user nkey")
|
|
145
|
+
return
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Encode the scoped user JWT.
|
|
149
|
+
// The `aud` field must be the account name from the nats-server config so
|
|
150
|
+
// NATS knows which account to place this connection in.
|
|
151
|
+
let userJwt: string
|
|
152
|
+
try {
|
|
153
|
+
userJwt = await encodeUser(
|
|
154
|
+
connectionClassName(identity),
|
|
155
|
+
userKey,
|
|
156
|
+
accountKp, // issuer — the account key pair
|
|
157
|
+
{
|
|
158
|
+
pub: scope.pub,
|
|
159
|
+
sub: scope.sub,
|
|
160
|
+
},
|
|
161
|
+
{ algorithm: Algorithms.v2, aud: accountName }
|
|
162
|
+
)
|
|
163
|
+
} catch (err) {
|
|
164
|
+
this.audit("deny", userNkey, identity, `jwt encode failed: ${err}`)
|
|
165
|
+
await this.respondError(msg, accountKp, serverNkey, "internal error minting JWT")
|
|
166
|
+
return
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Build and sign the authorization response.
|
|
170
|
+
try {
|
|
171
|
+
const serverKp = fromPublic(serverNkey)
|
|
172
|
+
const responseJwt = await encodeAuthorizationResponse(
|
|
173
|
+
userKey,
|
|
174
|
+
serverKp,
|
|
175
|
+
accountKp,
|
|
176
|
+
{ jwt: userJwt },
|
|
177
|
+
{ algorithm: Algorithms.v2 }
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
this.audit("grant", userNkey, identity, "ok")
|
|
181
|
+
msg.respond(new TextEncoder().encode(responseJwt))
|
|
182
|
+
} catch (err) {
|
|
183
|
+
this.audit("deny", userNkey, identity, `response encode failed: ${err}`)
|
|
184
|
+
await this.respondError(msg, accountKp, serverNkey, "internal error building response")
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
private async respondError(
|
|
189
|
+
msg: { respond: (data: Uint8Array) => void },
|
|
190
|
+
accountKp: KeyPair,
|
|
191
|
+
serverNkey: string | null,
|
|
192
|
+
reason: string
|
|
193
|
+
): Promise<void> {
|
|
194
|
+
try {
|
|
195
|
+
// For error responses we need a server key; if we don't have one, create
|
|
196
|
+
// a throwaway keypair (NATS only needs a valid structure for the error path).
|
|
197
|
+
const serverKp = serverNkey
|
|
198
|
+
? fromPublic(serverNkey)
|
|
199
|
+
: createUser()
|
|
200
|
+
|
|
201
|
+
// The user key in an error response is also a throwaway.
|
|
202
|
+
const throwawayUser = createUser()
|
|
203
|
+
|
|
204
|
+
const responseJwt = await encodeAuthorizationResponse(
|
|
205
|
+
throwawayUser,
|
|
206
|
+
serverKp,
|
|
207
|
+
accountKp,
|
|
208
|
+
{ error: reason },
|
|
209
|
+
{ algorithm: Algorithms.v2 }
|
|
210
|
+
)
|
|
211
|
+
msg.respond(new TextEncoder().encode(responseJwt))
|
|
212
|
+
} catch {
|
|
213
|
+
// Last resort: send empty bytes; NATS will treat as rejection.
|
|
214
|
+
msg.respond(new Uint8Array(0))
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
private audit(
|
|
219
|
+
decision: "grant" | "deny",
|
|
220
|
+
userNkey: string | null,
|
|
221
|
+
identity: ResolvedIdentity | null,
|
|
222
|
+
detail: string
|
|
223
|
+
): void {
|
|
224
|
+
const ts = new Date().toISOString()
|
|
225
|
+
const cls = identity ? connectionClassName(identity) : "unknown"
|
|
226
|
+
const rid = identity?.class === "runner" ? identity.runnerId : "-"
|
|
227
|
+
console.warn(
|
|
228
|
+
`[nats-callout] ${ts} decision=${decision} class=${cls} runnerId=${rid}`,
|
|
229
|
+
`nkey=${userNkey?.slice(0, 12) ?? "?"}… detail=${detail}`
|
|
230
|
+
)
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// ── Helpers ───────────────────────────────────────────────────────────────────
|
|
235
|
+
|
|
236
|
+
function connectionClassName(identity: ResolvedIdentity): string {
|
|
237
|
+
return identity.class === "runner"
|
|
238
|
+
? `runner:${identity.runnerId}`
|
|
239
|
+
: identity.class
|
|
240
|
+
}
|
|
241
|
+
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { describe, test, expect } from "bun:test"
|
|
2
|
+
import { permissionsFor, runnerKvKeySubject, runnerCmdWildcard } from "./scope-policy"
|
|
3
|
+
|
|
4
|
+
// Helper: check whether a subject is covered by an allow list.
|
|
5
|
+
// NATS uses prefix-match with ">" wildcard and exact ">" suffix.
|
|
6
|
+
function matchesAny(subject: string, patterns: string[]): boolean {
|
|
7
|
+
for (const pattern of patterns) {
|
|
8
|
+
if (matchesNats(subject, pattern)) return true
|
|
9
|
+
}
|
|
10
|
+
return false
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function matchesNats(subject: string, pattern: string): boolean {
|
|
14
|
+
if (pattern === subject) return true
|
|
15
|
+
if (pattern.endsWith(".>")) {
|
|
16
|
+
const prefix = pattern.slice(0, -1) // "foo.>" -> "foo."
|
|
17
|
+
if (subject.startsWith(prefix)) return true
|
|
18
|
+
}
|
|
19
|
+
return false
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// ── server-admin ──────────────────────────────────────────────────────────────
|
|
23
|
+
|
|
24
|
+
describe("server-admin scope", () => {
|
|
25
|
+
const scope = permissionsFor({ class: "server-admin" })
|
|
26
|
+
|
|
27
|
+
test("may publish to runtime.>", () => {
|
|
28
|
+
expect(matchesAny("runtime.runner.cmd.A.start", scope.pub.allow)).toBe(true)
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
test("may publish JetStream ACKs (regression: $JS.ACK is a separate tree from $JS.API)", () => {
|
|
32
|
+
expect(matchesAny("$JS.ACK.KANNA_RUNNER_EVENTS.consumer.1.1.1.1.0", scope.pub.allow)).toBe(true)
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
test("may publish to JetStream API", () => {
|
|
36
|
+
expect(matchesAny("$JS.API.STREAM.INFO", scope.pub.allow)).toBe(true)
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
test("may publish to KV", () => {
|
|
40
|
+
expect(matchesAny("$KV.runtime_runner_registry.someRunner", scope.pub.allow)).toBe(true)
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
test("may subscribe to callout subject", () => {
|
|
44
|
+
expect(matchesAny("$SYS.REQ.USER.AUTH", scope.sub.allow)).toBe(true)
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
test("no deny rules", () => {
|
|
48
|
+
expect(scope.pub.deny).toHaveLength(0)
|
|
49
|
+
expect(scope.sub.deny).toHaveLength(0)
|
|
50
|
+
})
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
// ── ui-client ─────────────────────────────────────────────────────────────────
|
|
54
|
+
|
|
55
|
+
describe("ui-client scope", () => {
|
|
56
|
+
const scope = permissionsFor({ class: "ui-client" })
|
|
57
|
+
|
|
58
|
+
test("may publish to runtime.cmd.>", () => {
|
|
59
|
+
expect(matchesAny("runtime.cmd.someCommand", scope.pub.allow)).toBe(true)
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
test("may subscribe to runner events", () => {
|
|
63
|
+
expect(matchesAny("runtime.runner.evt.chat123", scope.sub.allow)).toBe(true)
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
test("may pull chat messages via JS CONSUMER.MSG.NEXT (regression: was denied)", () => {
|
|
67
|
+
// The browser uses a pull consumer; without this it got Publish Violations
|
|
68
|
+
// and could not read chat messages. See intake #21.
|
|
69
|
+
expect(
|
|
70
|
+
matchesAny("$JS.API.CONSUMER.MSG.NEXT.KANNA_CHAT_MESSAGE_EVENTS.someConsumer_1", scope.pub.allow),
|
|
71
|
+
).toBe(true)
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
test("pull fetch is still scoped to the chat stream only (not other streams)", () => {
|
|
75
|
+
expect(
|
|
76
|
+
matchesAny("$JS.API.CONSUMER.MSG.NEXT.KANNA_RUNNER_EVENTS.c", scope.pub.allow),
|
|
77
|
+
).toBe(false)
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
test("may subscribe to snapshots", () => {
|
|
81
|
+
expect(matchesAny("runtime.snap.chat.abc", scope.sub.allow)).toBe(true)
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
test("runtime.runner.cmd.> is NOT in pub allow list (excluded by default)", () => {
|
|
85
|
+
// NATS: not in allow list means denied. No explicit deny needed.
|
|
86
|
+
expect(matchesAny("runtime.runner.cmd.A.start", scope.pub.allow)).toBe(false)
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
test("may NOT subscribe to runner cmd subjects (not in allow list)", () => {
|
|
90
|
+
expect(matchesAny("runtime.runner.cmd.A.>", scope.sub.allow)).toBe(false)
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
test("no deny rules (allow-only policy)", () => {
|
|
94
|
+
expect(scope.pub.deny).toHaveLength(0)
|
|
95
|
+
expect(scope.sub.deny).toHaveLength(0)
|
|
96
|
+
})
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
// ── runner (per-runnerId) ─────────────────────────────────────────────────────
|
|
100
|
+
|
|
101
|
+
describe("runner scope", () => {
|
|
102
|
+
const runnerId = "runner-A"
|
|
103
|
+
const otherRunnerId = "runner-B"
|
|
104
|
+
const scope = permissionsFor({ class: "runner", runnerId })
|
|
105
|
+
|
|
106
|
+
test("may subscribe to its own cmd subject", () => {
|
|
107
|
+
expect(matchesAny(runnerCmdWildcard(runnerId), scope.sub.allow)).toBe(true)
|
|
108
|
+
expect(matchesAny(`runtime.runner.cmd.${runnerId}.start`, scope.sub.allow)).toBe(true)
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
test("another runner cmd subject is NOT in sub allow (excluded by default)", () => {
|
|
112
|
+
// NATS: not in allow list means denied. No explicit deny needed.
|
|
113
|
+
expect(matchesAny(runnerCmdWildcard(otherRunnerId), scope.sub.allow)).toBe(false)
|
|
114
|
+
expect(matchesAny(`runtime.runner.cmd.${otherRunnerId}.start`, scope.sub.allow)).toBe(false)
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
test("may publish its own heartbeat", () => {
|
|
118
|
+
expect(matchesAny(`runtime.runner.heartbeat.${runnerId}`, scope.pub.allow)).toBe(true)
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
test("another runner heartbeat is NOT in pub allow (excluded by default)", () => {
|
|
122
|
+
expect(matchesAny(`runtime.runner.heartbeat.${otherRunnerId}`, scope.pub.allow)).toBe(false)
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
test("may publish runner events", () => {
|
|
126
|
+
expect(matchesAny("runtime.runner.evt.chat1", scope.pub.allow)).toBe(true)
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
test("may publish its own KV registry key", () => {
|
|
130
|
+
const ownKey = runnerKvKeySubject(runnerId)
|
|
131
|
+
expect(matchesAny(ownKey, scope.pub.allow)).toBe(true)
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
test("another runner KV key is NOT in pub allow (excluded by default)", () => {
|
|
135
|
+
// NATS: not in allow means denied. We use specific-allow only (no wildcard allow,
|
|
136
|
+
// no deny list) so other keys are automatically excluded.
|
|
137
|
+
const foreignKey = runnerKvKeySubject(otherRunnerId)
|
|
138
|
+
expect(matchesAny(foreignKey, scope.pub.allow)).toBe(false)
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
test("no deny rules (allow-only policy)", () => {
|
|
142
|
+
expect(scope.pub.deny).toHaveLength(0)
|
|
143
|
+
expect(scope.sub.deny).toHaveLength(0)
|
|
144
|
+
})
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
// ── runnerKvKeySubject helper ─────────────────────────────────────────────────
|
|
148
|
+
|
|
149
|
+
describe("runnerKvKeySubject", () => {
|
|
150
|
+
test("returns expected KV subject", () => {
|
|
151
|
+
expect(runnerKvKeySubject("abc")).toBe("$KV.runtime_runner_registry.abc")
|
|
152
|
+
})
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
describe("runnerCmdWildcard", () => {
|
|
156
|
+
test("returns expected wildcard", () => {
|
|
157
|
+
expect(runnerCmdWildcard("xyz")).toBe("runtime.runner.cmd.xyz.>")
|
|
158
|
+
})
|
|
159
|
+
})
|