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,112 @@
|
|
|
1
|
+
import { afterEach, describe, test, expect } from "bun:test"
|
|
2
|
+
import { NatsServer } from "@lagz0ne/nats-embedded"
|
|
3
|
+
import { connect, type NatsConnection } from "@nats-io/transport-node"
|
|
4
|
+
import { jetstreamManager } from "@nats-io/jetstream"
|
|
5
|
+
import { Kvm } from "@nats-io/kv"
|
|
6
|
+
import {
|
|
7
|
+
ensureTerminalEventsStream,
|
|
8
|
+
TERMINAL_EVENTS_STREAM,
|
|
9
|
+
} from "./nats-streams"
|
|
10
|
+
import { KV_BUCKET, snapshotKvKey } from "../shared/nats-subjects"
|
|
11
|
+
|
|
12
|
+
let server: NatsServer | null = null
|
|
13
|
+
let nc: NatsConnection | null = null
|
|
14
|
+
|
|
15
|
+
afterEach(async () => {
|
|
16
|
+
if (nc) {
|
|
17
|
+
await nc.drain()
|
|
18
|
+
nc = null
|
|
19
|
+
}
|
|
20
|
+
if (server) {
|
|
21
|
+
await server.stop()
|
|
22
|
+
server = null
|
|
23
|
+
}
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
describe("nats-streams", () => {
|
|
27
|
+
test("ensureTerminalEventsStream creates the stream", async () => {
|
|
28
|
+
server = await NatsServer.start({ jetstream: true })
|
|
29
|
+
nc = await connect({ servers: server.url })
|
|
30
|
+
|
|
31
|
+
await ensureTerminalEventsStream(nc)
|
|
32
|
+
|
|
33
|
+
const jsm = await jetstreamManager(nc)
|
|
34
|
+
const info = await jsm.streams.info(TERMINAL_EVENTS_STREAM)
|
|
35
|
+
expect(info.config.name).toBe(TERMINAL_EVENTS_STREAM)
|
|
36
|
+
expect(info.config.subjects).toContain("runtime.evt.terminal.>")
|
|
37
|
+
expect(info.config.storage).toBe("memory")
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
test("ensureTerminalEventsStream is idempotent", async () => {
|
|
41
|
+
server = await NatsServer.start({ jetstream: true })
|
|
42
|
+
nc = await connect({ servers: server.url })
|
|
43
|
+
|
|
44
|
+
await ensureTerminalEventsStream(nc)
|
|
45
|
+
await ensureTerminalEventsStream(nc)
|
|
46
|
+
|
|
47
|
+
const jsm = await jetstreamManager(nc)
|
|
48
|
+
const info = await jsm.streams.info(TERMINAL_EVENTS_STREAM)
|
|
49
|
+
expect(info.config.name).toBe(TERMINAL_EVENTS_STREAM)
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
test("stream retains published terminal events", async () => {
|
|
53
|
+
server = await NatsServer.start({ jetstream: true })
|
|
54
|
+
nc = await connect({ servers: server.url })
|
|
55
|
+
|
|
56
|
+
await ensureTerminalEventsStream(nc)
|
|
57
|
+
|
|
58
|
+
const jsm = await jetstreamManager(nc)
|
|
59
|
+
const encoder = new TextEncoder()
|
|
60
|
+
|
|
61
|
+
// Publish a terminal event
|
|
62
|
+
const { jetstream } = await import("@nats-io/jetstream")
|
|
63
|
+
const js = jetstream(nc)
|
|
64
|
+
await js.publish(
|
|
65
|
+
"runtime.evt.terminal.term-1",
|
|
66
|
+
encoder.encode(JSON.stringify({ type: "terminal.output", terminalId: "term-1", data: "hello" }))
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
const info = await jsm.streams.info(TERMINAL_EVENTS_STREAM)
|
|
70
|
+
expect(info.state.messages).toBe(1)
|
|
71
|
+
})
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
describe("KV snapshot bucket", () => {
|
|
75
|
+
test("can create and read from runtime_snapshots bucket", async () => {
|
|
76
|
+
server = await NatsServer.start({ jetstream: true })
|
|
77
|
+
nc = await connect({ servers: server.url })
|
|
78
|
+
|
|
79
|
+
const kvm = new Kvm(nc)
|
|
80
|
+
const kv = await kvm.create(KV_BUCKET)
|
|
81
|
+
const encoder = new TextEncoder()
|
|
82
|
+
|
|
83
|
+
await kv.put("sidebar", encoder.encode(JSON.stringify({ workspaceGroups: [] })))
|
|
84
|
+
const entry = await kv.get("sidebar")
|
|
85
|
+
expect(entry).not.toBeNull()
|
|
86
|
+
expect(entry!.json() as Record<string, unknown>).toEqual({ workspaceGroups: [] })
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
test("kv dedup: identical puts create new revisions", async () => {
|
|
90
|
+
server = await NatsServer.start({ jetstream: true })
|
|
91
|
+
nc = await connect({ servers: server.url })
|
|
92
|
+
|
|
93
|
+
const kvm = new Kvm(nc)
|
|
94
|
+
const kv = await kvm.create(KV_BUCKET)
|
|
95
|
+
const encoder = new TextEncoder()
|
|
96
|
+
const data = encoder.encode(JSON.stringify({ test: true }))
|
|
97
|
+
|
|
98
|
+
const rev1 = await kv.put("test-key", data)
|
|
99
|
+
const rev2 = await kv.put("test-key", data)
|
|
100
|
+
// NATS KV creates new revision even for identical values
|
|
101
|
+
// (server-side dedup in publisher prevents this)
|
|
102
|
+
expect(rev2).toBeGreaterThan(rev1)
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
test("snapshotKvKey maps topics correctly", () => {
|
|
106
|
+
expect(snapshotKvKey({ type: "sidebar" })).toBe("sidebar")
|
|
107
|
+
expect(snapshotKvKey({ type: "local-workspaces" })).toBe("local-workspaces")
|
|
108
|
+
expect(snapshotKvKey({ type: "chat", chatId: "abc" })).toBe("chat.abc")
|
|
109
|
+
expect(snapshotKvKey({ type: "terminal", terminalId: "t1" })).toBe("terminal.t1")
|
|
110
|
+
})
|
|
111
|
+
})
|
|
112
|
+
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { jetstreamManager, RetentionPolicy, StorageType } from "@nats-io/jetstream"
|
|
2
|
+
import { Kvm } from "@nats-io/kv"
|
|
3
|
+
import type { NatsConnection } from "@nats-io/transport-node"
|
|
4
|
+
import { ALL_TERMINAL_EVENTS, ALL_CHAT_MESSAGE_EVENTS, CHAT_MESSAGE_EVENTS_STREAM_NAME, ALL_WORKSPACE_COORDINATION_EVENTS, WORKSPACE_COORDINATION_EVENTS_STREAM_NAME, ALL_SANDBOX_EVENTS, SANDBOX_EVENTS_STREAM_NAME } from "../shared/nats-subjects"
|
|
5
|
+
import { RUNNER_EVENTS_STREAM, ALL_RUNNER_EVENTS, RUNNER_REGISTRY_BUCKET } from "../shared/runner-protocol"
|
|
6
|
+
import { LOG_PREFIX } from "../shared/branding"
|
|
7
|
+
|
|
8
|
+
export const TERMINAL_EVENTS_STREAM = "KANNA_TERMINAL_EVENTS"
|
|
9
|
+
export const CHAT_MESSAGE_EVENTS_STREAM = CHAT_MESSAGE_EVENTS_STREAM_NAME
|
|
10
|
+
|
|
11
|
+
interface StreamConfig {
|
|
12
|
+
name: string
|
|
13
|
+
subjects: string[]
|
|
14
|
+
max_age_ns: number
|
|
15
|
+
max_msgs: number
|
|
16
|
+
max_bytes: number
|
|
17
|
+
storage?: StorageType
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const FIVE_MINUTES_NS = 5 * 60 * 1_000_000_000
|
|
21
|
+
const THIRTY_MINUTES_NS = 30 * 60 * 1_000_000_000
|
|
22
|
+
|
|
23
|
+
async function ensureStream(nc: NatsConnection, config: StreamConfig): Promise<void> {
|
|
24
|
+
const jsm = await jetstreamManager(nc)
|
|
25
|
+
|
|
26
|
+
const streamConfig = {
|
|
27
|
+
name: config.name,
|
|
28
|
+
subjects: config.subjects,
|
|
29
|
+
retention: RetentionPolicy.Limits,
|
|
30
|
+
storage: config.storage ?? StorageType.Memory,
|
|
31
|
+
max_age: config.max_age_ns,
|
|
32
|
+
max_msgs: config.max_msgs,
|
|
33
|
+
max_bytes: config.max_bytes,
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
try {
|
|
37
|
+
await jsm.streams.info(config.name)
|
|
38
|
+
await jsm.streams.update(config.name, streamConfig)
|
|
39
|
+
console.warn(LOG_PREFIX, `JetStream stream ${config.name} updated`)
|
|
40
|
+
} catch {
|
|
41
|
+
// info() throws when stream doesn't exist — safe to create.
|
|
42
|
+
// Any other error will surface from add().
|
|
43
|
+
await jsm.streams.add(streamConfig)
|
|
44
|
+
console.warn(LOG_PREFIX, `JetStream stream ${config.name} created`)
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Creates or updates the JetStream stream for terminal events (memory-backed, 5 min / 10K msg retention). */
|
|
49
|
+
export function ensureTerminalEventsStream(nc: NatsConnection): Promise<void> {
|
|
50
|
+
return ensureStream(nc, {
|
|
51
|
+
name: TERMINAL_EVENTS_STREAM,
|
|
52
|
+
subjects: [ALL_TERMINAL_EVENTS],
|
|
53
|
+
max_age_ns: FIVE_MINUTES_NS,
|
|
54
|
+
max_msgs: 10_000,
|
|
55
|
+
max_bytes: 64 * 1024 * 1024,
|
|
56
|
+
})
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** Creates or updates the JetStream stream for chat message events (memory-backed, 30 min / 50K msg retention). */
|
|
60
|
+
export function ensureChatMessageStream(nc: NatsConnection): Promise<void> {
|
|
61
|
+
return ensureStream(nc, {
|
|
62
|
+
name: CHAT_MESSAGE_EVENTS_STREAM,
|
|
63
|
+
subjects: [ALL_CHAT_MESSAGE_EVENTS],
|
|
64
|
+
max_age_ns: THIRTY_MINUTES_NS,
|
|
65
|
+
max_msgs: 50_000,
|
|
66
|
+
max_bytes: 128 * 1024 * 1024,
|
|
67
|
+
})
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** Creates or updates the JetStream stream for runner turn events (file-backed, 30 min / 50K msg retention). */
|
|
71
|
+
export function ensureRunnerEventsStream(nc: NatsConnection): Promise<void> {
|
|
72
|
+
return ensureStream(nc, {
|
|
73
|
+
name: RUNNER_EVENTS_STREAM,
|
|
74
|
+
subjects: [ALL_RUNNER_EVENTS],
|
|
75
|
+
max_age_ns: THIRTY_MINUTES_NS,
|
|
76
|
+
max_msgs: 50_000,
|
|
77
|
+
max_bytes: 128 * 1024 * 1024,
|
|
78
|
+
storage: StorageType.File,
|
|
79
|
+
})
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** Creates or updates the JetStream stream for project coordination events (file-backed, 24h / 100K msg retention). */
|
|
83
|
+
export function ensureWorkspaceCoordinationStream(nc: NatsConnection): Promise<void> {
|
|
84
|
+
return ensureStream(nc, {
|
|
85
|
+
name: WORKSPACE_COORDINATION_EVENTS_STREAM_NAME,
|
|
86
|
+
subjects: [ALL_WORKSPACE_COORDINATION_EVENTS],
|
|
87
|
+
max_age_ns: 24 * 60 * 60 * 1_000_000_000,
|
|
88
|
+
max_msgs: 100_000,
|
|
89
|
+
max_bytes: 256 * 1024 * 1024,
|
|
90
|
+
storage: StorageType.File,
|
|
91
|
+
})
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** Creates or updates the JetStream stream for sandbox events (memory-backed, 5 min / 5K msg retention). */
|
|
95
|
+
export function ensureSandboxEventsStream(nc: NatsConnection): Promise<void> {
|
|
96
|
+
return ensureStream(nc, {
|
|
97
|
+
name: SANDBOX_EVENTS_STREAM_NAME,
|
|
98
|
+
subjects: [ALL_SANDBOX_EVENTS],
|
|
99
|
+
max_age_ns: FIVE_MINUTES_NS,
|
|
100
|
+
max_msgs: 5_000,
|
|
101
|
+
max_bytes: 10 * 1024 * 1024,
|
|
102
|
+
})
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Ensure the runner registry KV bucket exists.
|
|
107
|
+
*
|
|
108
|
+
* Called by the server (server-admin connection) at startup so that spawned
|
|
109
|
+
* runners — whose NATS scope does not include STREAM.CREATE — can open the
|
|
110
|
+
* bucket immediately without needing to create it themselves.
|
|
111
|
+
*
|
|
112
|
+
* Stage D: narrow the runner's $JS.API.> pub allow to the specific subjects
|
|
113
|
+
* needed for kvm.open + kvStore.put once the exact set is empirically confirmed.
|
|
114
|
+
*/
|
|
115
|
+
export async function ensureRunnerRegistryBucket(nc: NatsConnection): Promise<void> {
|
|
116
|
+
const kvm = new Kvm(nc)
|
|
117
|
+
try {
|
|
118
|
+
// Create with idempotent semantics: if the bucket already exists the
|
|
119
|
+
// underlying STREAM.CREATE call is treated as an update by nats-server.
|
|
120
|
+
await kvm.create(RUNNER_REGISTRY_BUCKET, {
|
|
121
|
+
max_bytes: 1024 * 1024,
|
|
122
|
+
})
|
|
123
|
+
console.warn(LOG_PREFIX, `Runner registry KV bucket '${RUNNER_REGISTRY_BUCKET}' ready`)
|
|
124
|
+
} catch (err) {
|
|
125
|
+
const message = err instanceof Error ? err.message : String(err)
|
|
126
|
+
console.warn(LOG_PREFIX, `Runner registry KV bucket ensure failed: ${message}`)
|
|
127
|
+
throw err
|
|
128
|
+
}
|
|
129
|
+
}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OAuth pool NATS responders.
|
|
3
|
+
*
|
|
4
|
+
* Subjects (request/reply under `runtime.cmd.oauth.*`):
|
|
5
|
+
* - oauth.list -> { ok, settings }
|
|
6
|
+
* - oauth.add { label, token, maxConcurrent? } -> { ok, entry }
|
|
7
|
+
* - oauth.remove { id } -> { ok, removed }
|
|
8
|
+
* - oauth.update { id, label?, maxConcurrent? } -> { ok, entry }
|
|
9
|
+
* - oauth.setConcurrencyDefault { value } -> { ok }
|
|
10
|
+
*
|
|
11
|
+
* Change broadcast: `runtime.evt.oauth.changed` carries snapshot after
|
|
12
|
+
* mutations so clients can refresh without re-requesting.
|
|
13
|
+
*
|
|
14
|
+
* Token values are returned with full secret material — UI only renders
|
|
15
|
+
* masked previews. Same posture as kanna; tinkaria runs on operator's own
|
|
16
|
+
* machine.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import type { NatsConnection, Subscription } from "@nats-io/transport-node"
|
|
20
|
+
import { compressPayload, decompressPayload } from "../../shared/compression"
|
|
21
|
+
import {
|
|
22
|
+
ALL_OAUTH_COMMANDS,
|
|
23
|
+
oauthChangedSubject,
|
|
24
|
+
} from "../../shared/nats-subjects"
|
|
25
|
+
import type { ClaudeAuthSettings, OAuthTokenEntry } from "../../shared/types"
|
|
26
|
+
import {
|
|
27
|
+
OAUTH_TOKEN_LABEL_MAX,
|
|
28
|
+
OAUTH_TOKEN_MAX_CONCURRENT_MAX,
|
|
29
|
+
OAUTH_TOKEN_MAX_CONCURRENT_MIN,
|
|
30
|
+
OAUTH_TOKEN_VALUE_MAX,
|
|
31
|
+
} from "../../shared/types"
|
|
32
|
+
import type { OAuthSettingsStore } from "./oauth-settings-store"
|
|
33
|
+
|
|
34
|
+
const LOG_PREFIX = "[oauth-pool]"
|
|
35
|
+
const encoder = new TextEncoder()
|
|
36
|
+
const decoder = new TextDecoder()
|
|
37
|
+
|
|
38
|
+
function encode(data: unknown): Uint8Array {
|
|
39
|
+
return compressPayload(encoder.encode(JSON.stringify(data)))
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async function decode(data: Uint8Array): Promise<unknown> {
|
|
43
|
+
const raw = await decompressPayload(data)
|
|
44
|
+
return JSON.parse(decoder.decode(raw))
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface OAuthResponderDeps {
|
|
48
|
+
nc: NatsConnection
|
|
49
|
+
store: OAuthSettingsStore
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export interface OAuthResponderHandle {
|
|
53
|
+
dispose(): void
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
interface CommandResult {
|
|
57
|
+
ok: boolean
|
|
58
|
+
error?: string
|
|
59
|
+
settings?: ClaudeAuthSettings
|
|
60
|
+
entry?: OAuthTokenEntry
|
|
61
|
+
removed?: boolean
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function registerOAuthResponders(deps: OAuthResponderDeps): OAuthResponderHandle {
|
|
65
|
+
const { nc, store } = deps
|
|
66
|
+
const sub: Subscription = nc.subscribe(ALL_OAUTH_COMMANDS)
|
|
67
|
+
|
|
68
|
+
;(async () => {
|
|
69
|
+
for await (const msg of sub) {
|
|
70
|
+
const subject = msg.subject
|
|
71
|
+
const type = subject.replace(/^runtime\.cmd\./, "")
|
|
72
|
+
let payload: Record<string, unknown> = {}
|
|
73
|
+
try {
|
|
74
|
+
payload = (await decode(msg.data)) as Record<string, unknown>
|
|
75
|
+
} catch {
|
|
76
|
+
msg.respond?.(encode({ ok: false, error: "invalid JSON payload" } satisfies CommandResult))
|
|
77
|
+
continue
|
|
78
|
+
}
|
|
79
|
+
try {
|
|
80
|
+
const result = await dispatch(type, payload, store)
|
|
81
|
+
if (result.ok && type !== "oauth.list") {
|
|
82
|
+
try {
|
|
83
|
+
nc.publish(oauthChangedSubject(), encode({ settings: store.getSnapshot() }))
|
|
84
|
+
} catch (err) {
|
|
85
|
+
console.warn(LOG_PREFIX, "changed publish failed:", err instanceof Error ? err.message : String(err))
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
msg.respond?.(encode(result))
|
|
89
|
+
} catch (err) {
|
|
90
|
+
const message = err instanceof Error ? err.message : String(err)
|
|
91
|
+
console.warn(LOG_PREFIX, `responder error for ${type}: ${message}`)
|
|
92
|
+
msg.respond?.(encode({ ok: false, error: message } satisfies CommandResult))
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
})().catch((err) => {
|
|
96
|
+
console.warn(LOG_PREFIX, "responder loop terminated:", err instanceof Error ? err.message : String(err))
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
return {
|
|
100
|
+
dispose() {
|
|
101
|
+
sub.unsubscribe()
|
|
102
|
+
},
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
async function dispatch(
|
|
107
|
+
type: string,
|
|
108
|
+
payload: Record<string, unknown>,
|
|
109
|
+
store: OAuthSettingsStore,
|
|
110
|
+
): Promise<CommandResult> {
|
|
111
|
+
switch (type) {
|
|
112
|
+
case "oauth.list":
|
|
113
|
+
return { ok: true, settings: store.getSnapshot() }
|
|
114
|
+
|
|
115
|
+
case "oauth.add": {
|
|
116
|
+
const label = requireString(payload, "label").trim()
|
|
117
|
+
const token = requireString(payload, "token")
|
|
118
|
+
if (label.length === 0 || label.length > OAUTH_TOKEN_LABEL_MAX) {
|
|
119
|
+
return { ok: false, error: `label must be 1..${OAUTH_TOKEN_LABEL_MAX} chars` }
|
|
120
|
+
}
|
|
121
|
+
if (token.length === 0 || token.length > OAUTH_TOKEN_VALUE_MAX) {
|
|
122
|
+
return { ok: false, error: `token must be 1..${OAUTH_TOKEN_VALUE_MAX} chars` }
|
|
123
|
+
}
|
|
124
|
+
const maxConcurrent = normalizeMaxConcurrent(payload.maxConcurrent)
|
|
125
|
+
if (maxConcurrent === "invalid") {
|
|
126
|
+
return { ok: false, error: `maxConcurrent must be in [${OAUTH_TOKEN_MAX_CONCURRENT_MIN}, ${OAUTH_TOKEN_MAX_CONCURRENT_MAX}]` }
|
|
127
|
+
}
|
|
128
|
+
const entry = await store.addToken({
|
|
129
|
+
label,
|
|
130
|
+
token,
|
|
131
|
+
...(maxConcurrent !== undefined ? { maxConcurrent } : {}),
|
|
132
|
+
})
|
|
133
|
+
return { ok: true, entry }
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
case "oauth.remove": {
|
|
137
|
+
const id = requireString(payload, "id")
|
|
138
|
+
const removed = await store.removeToken(id)
|
|
139
|
+
return { ok: true, removed }
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
case "oauth.update": {
|
|
143
|
+
const id = requireString(payload, "id")
|
|
144
|
+
const patch: { label?: string; maxConcurrent?: number | null } = {}
|
|
145
|
+
if (typeof payload.label === "string") patch.label = payload.label
|
|
146
|
+
if (payload.maxConcurrent === null) patch.maxConcurrent = null
|
|
147
|
+
else if (typeof payload.maxConcurrent === "number") {
|
|
148
|
+
const v = normalizeMaxConcurrent(payload.maxConcurrent)
|
|
149
|
+
if (v === "invalid") return { ok: false, error: "maxConcurrent out of range" }
|
|
150
|
+
if (v !== undefined) patch.maxConcurrent = v
|
|
151
|
+
}
|
|
152
|
+
const entry = await store.updateToken(id, patch)
|
|
153
|
+
if (!entry) return { ok: false, error: `unknown token id: ${id}` }
|
|
154
|
+
return { ok: true, entry }
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
case "oauth.setConcurrencyDefault": {
|
|
158
|
+
const value = payload.value
|
|
159
|
+
if (typeof value !== "number" || value < OAUTH_TOKEN_MAX_CONCURRENT_MIN || value > OAUTH_TOKEN_MAX_CONCURRENT_MAX) {
|
|
160
|
+
return { ok: false, error: `value must be in [${OAUTH_TOKEN_MAX_CONCURRENT_MIN}, ${OAUTH_TOKEN_MAX_CONCURRENT_MAX}]` }
|
|
161
|
+
}
|
|
162
|
+
await store.setConcurrencyDefault(value)
|
|
163
|
+
return { ok: true }
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
default:
|
|
167
|
+
return { ok: false, error: `unknown oauth command: ${type}` }
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function requireString(payload: Record<string, unknown>, field: string): string {
|
|
172
|
+
const value = payload[field]
|
|
173
|
+
if (typeof value !== "string") {
|
|
174
|
+
throw new Error(`missing required string field: ${field}`)
|
|
175
|
+
}
|
|
176
|
+
return value
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function normalizeMaxConcurrent(raw: unknown): number | undefined | "invalid" {
|
|
180
|
+
if (raw === undefined || raw === null) return undefined
|
|
181
|
+
if (typeof raw !== "number" || !Number.isFinite(raw)) return "invalid"
|
|
182
|
+
const rounded = Math.round(raw)
|
|
183
|
+
if (rounded < OAUTH_TOKEN_MAX_CONCURRENT_MIN || rounded > OAUTH_TOKEN_MAX_CONCURRENT_MAX) return "invalid"
|
|
184
|
+
return rounded
|
|
185
|
+
}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { promises as fs } from "fs"
|
|
2
|
+
import path from "path"
|
|
3
|
+
import { homedir } from "os"
|
|
4
|
+
import {
|
|
5
|
+
CLAUDE_AUTH_DEFAULTS,
|
|
6
|
+
type ClaudeAuthSettings,
|
|
7
|
+
type OAuthTokenEntry,
|
|
8
|
+
type OAuthTokenStatus,
|
|
9
|
+
} from "../../shared/types"
|
|
10
|
+
import type { TokenStatusPatch } from "./oauth-token-pool"
|
|
11
|
+
|
|
12
|
+
const FILE_NAME = "oauth-tokens.json"
|
|
13
|
+
|
|
14
|
+
function defaultDir(): string {
|
|
15
|
+
return path.join(homedir(), ".tinkaria")
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function isValidStatus(value: unknown): value is OAuthTokenStatus {
|
|
19
|
+
return value === "active" || value === "limited" || value === "error" || value === "disabled"
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function normalizeEntry(raw: unknown): OAuthTokenEntry | null {
|
|
23
|
+
if (!raw || typeof raw !== "object") return null
|
|
24
|
+
const r = raw as Record<string, unknown>
|
|
25
|
+
if (typeof r.id !== "string" || typeof r.label !== "string" || typeof r.token !== "string") return null
|
|
26
|
+
const status = isValidStatus(r.status) ? r.status : "active"
|
|
27
|
+
const out: OAuthTokenEntry = {
|
|
28
|
+
id: r.id,
|
|
29
|
+
label: r.label,
|
|
30
|
+
token: r.token,
|
|
31
|
+
status,
|
|
32
|
+
limitedUntil: typeof r.limitedUntil === "number" ? r.limitedUntil : null,
|
|
33
|
+
lastUsedAt: typeof r.lastUsedAt === "number" ? r.lastUsedAt : null,
|
|
34
|
+
lastErrorAt: typeof r.lastErrorAt === "number" ? r.lastErrorAt : null,
|
|
35
|
+
lastErrorMessage: typeof r.lastErrorMessage === "string" ? r.lastErrorMessage : null,
|
|
36
|
+
addedAt: typeof r.addedAt === "number" ? r.addedAt : Date.now(),
|
|
37
|
+
}
|
|
38
|
+
if (typeof r.maxConcurrent === "number" && Number.isFinite(r.maxConcurrent)) {
|
|
39
|
+
out.maxConcurrent = r.maxConcurrent
|
|
40
|
+
}
|
|
41
|
+
return out
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function normalizeSettings(raw: unknown): ClaudeAuthSettings {
|
|
45
|
+
if (!raw || typeof raw !== "object") return { ...CLAUDE_AUTH_DEFAULTS, tokens: [] }
|
|
46
|
+
const r = raw as Record<string, unknown>
|
|
47
|
+
const tokensRaw = Array.isArray(r.tokens) ? r.tokens : []
|
|
48
|
+
const tokens = tokensRaw.map(normalizeEntry).filter((t): t is OAuthTokenEntry => t !== null)
|
|
49
|
+
const concurrencyDefault = typeof r.concurrencyDefault === "number" && Number.isFinite(r.concurrencyDefault)
|
|
50
|
+
? r.concurrencyDefault
|
|
51
|
+
: CLAUDE_AUTH_DEFAULTS.concurrencyDefault
|
|
52
|
+
return { tokens, concurrencyDefault }
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export class OAuthSettingsStore {
|
|
56
|
+
private readonly filePath: string
|
|
57
|
+
private cached: ClaudeAuthSettings = { ...CLAUDE_AUTH_DEFAULTS, tokens: [] }
|
|
58
|
+
private writeChain: Promise<void> = Promise.resolve()
|
|
59
|
+
private loaded = false
|
|
60
|
+
|
|
61
|
+
constructor(dataDir = defaultDir()) {
|
|
62
|
+
this.filePath = path.join(dataDir, FILE_NAME)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async load(): Promise<void> {
|
|
66
|
+
try {
|
|
67
|
+
const raw = await fs.readFile(this.filePath, "utf8")
|
|
68
|
+
const parsed = JSON.parse(raw)
|
|
69
|
+
this.cached = normalizeSettings(parsed)
|
|
70
|
+
} catch (err) {
|
|
71
|
+
const e = err as NodeJS.ErrnoException
|
|
72
|
+
if (e.code !== "ENOENT") {
|
|
73
|
+
console.warn("[oauth-pool] failed to load tokens:", e.message)
|
|
74
|
+
}
|
|
75
|
+
this.cached = { ...CLAUDE_AUTH_DEFAULTS, tokens: [] }
|
|
76
|
+
}
|
|
77
|
+
this.loaded = true
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
getSnapshot(): ClaudeAuthSettings {
|
|
81
|
+
return { tokens: [...this.cached.tokens], concurrencyDefault: this.cached.concurrencyDefault }
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
getTokens(): OAuthTokenEntry[] {
|
|
85
|
+
return [...this.cached.tokens]
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
getConcurrencyDefault(): number {
|
|
89
|
+
return this.cached.concurrencyDefault
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async addToken(input: { label: string; token: string; maxConcurrent?: number }): Promise<OAuthTokenEntry> {
|
|
93
|
+
if (!this.loaded) await this.load()
|
|
94
|
+
const entry: OAuthTokenEntry = {
|
|
95
|
+
id: cryptoRandomId(),
|
|
96
|
+
label: input.label.trim(),
|
|
97
|
+
token: input.token,
|
|
98
|
+
status: "active",
|
|
99
|
+
limitedUntil: null,
|
|
100
|
+
lastUsedAt: null,
|
|
101
|
+
lastErrorAt: null,
|
|
102
|
+
lastErrorMessage: null,
|
|
103
|
+
addedAt: Date.now(),
|
|
104
|
+
}
|
|
105
|
+
if (typeof input.maxConcurrent === "number") entry.maxConcurrent = input.maxConcurrent
|
|
106
|
+
this.cached = { ...this.cached, tokens: [...this.cached.tokens, entry] }
|
|
107
|
+
await this.persist()
|
|
108
|
+
return entry
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
async removeToken(id: string): Promise<boolean> {
|
|
112
|
+
if (!this.loaded) await this.load()
|
|
113
|
+
const next = this.cached.tokens.filter((t) => t.id !== id)
|
|
114
|
+
if (next.length === this.cached.tokens.length) return false
|
|
115
|
+
this.cached = { ...this.cached, tokens: next }
|
|
116
|
+
await this.persist()
|
|
117
|
+
return true
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
async updateToken(id: string, patch: { label?: string; maxConcurrent?: number | null }): Promise<OAuthTokenEntry | null> {
|
|
121
|
+
if (!this.loaded) await this.load()
|
|
122
|
+
let updated: OAuthTokenEntry | null = null
|
|
123
|
+
const next = this.cached.tokens.map((t) => {
|
|
124
|
+
if (t.id !== id) return t
|
|
125
|
+
const merged: OAuthTokenEntry = { ...t }
|
|
126
|
+
if (typeof patch.label === "string") merged.label = patch.label.trim()
|
|
127
|
+
if (patch.maxConcurrent === null) delete merged.maxConcurrent
|
|
128
|
+
else if (typeof patch.maxConcurrent === "number") merged.maxConcurrent = patch.maxConcurrent
|
|
129
|
+
updated = merged
|
|
130
|
+
return merged
|
|
131
|
+
})
|
|
132
|
+
if (!updated) return null
|
|
133
|
+
this.cached = { ...this.cached, tokens: next }
|
|
134
|
+
await this.persist()
|
|
135
|
+
return updated
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
async setConcurrencyDefault(value: number): Promise<void> {
|
|
139
|
+
if (!this.loaded) await this.load()
|
|
140
|
+
this.cached = { ...this.cached, concurrencyDefault: value }
|
|
141
|
+
await this.persist()
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/** Used by OAuthTokenPool writeStatus callback. Persists asynchronously. */
|
|
145
|
+
mutateTokenStatus(id: string, patch: TokenStatusPatch): void {
|
|
146
|
+
const next = this.cached.tokens.map((t) => {
|
|
147
|
+
if (t.id !== id) return t
|
|
148
|
+
return { ...t, ...patch }
|
|
149
|
+
})
|
|
150
|
+
this.cached = { ...this.cached, tokens: next }
|
|
151
|
+
this.writeChain = this.writeChain.then(() => this.writeFile()).catch((err) => {
|
|
152
|
+
console.warn("[oauth-pool] status write failed:", err instanceof Error ? err.message : String(err))
|
|
153
|
+
})
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
private async persist(): Promise<void> {
|
|
157
|
+
this.writeChain = this.writeChain.then(() => this.writeFile())
|
|
158
|
+
await this.writeChain
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
private async writeFile(): Promise<void> {
|
|
162
|
+
await fs.mkdir(path.dirname(this.filePath), { recursive: true })
|
|
163
|
+
const tmp = `${this.filePath}.tmp`
|
|
164
|
+
await fs.writeFile(tmp, JSON.stringify(this.cached, null, 2), "utf8")
|
|
165
|
+
await fs.rename(tmp, this.filePath)
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function cryptoRandomId(): string {
|
|
170
|
+
const arr = new Uint8Array(16)
|
|
171
|
+
crypto.getRandomValues(arr)
|
|
172
|
+
return Array.from(arr, (b) => b.toString(16).padStart(2, "0")).join("")
|
|
173
|
+
}
|