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,374 @@
|
|
|
1
|
+
import process from "node:process"
|
|
2
|
+
import { spawnSync } from "node:child_process"
|
|
3
|
+
import { hasCommand, spawnDetached } from "./process-utils"
|
|
4
|
+
import { APP_NAME, CLI_COMMAND, getDataDirDisplay, LOG_PREFIX, PACKAGE_NAME } from "../shared/branding"
|
|
5
|
+
import type { UpdateInstallErrorCode } from "../shared/types"
|
|
6
|
+
import { PROD_SERVER_PORT } from "../shared/ports"
|
|
7
|
+
import { CLI_SUPPRESS_OPEN_ONCE_ENV_VAR } from "./restart"
|
|
8
|
+
import { logShareDetails, renderTerminalQr, startShareTunnel, type StartedShareTunnel } from "./share"
|
|
9
|
+
|
|
10
|
+
export interface CliOptions {
|
|
11
|
+
port: number
|
|
12
|
+
host: string
|
|
13
|
+
openBrowser: boolean
|
|
14
|
+
share: boolean
|
|
15
|
+
strictPort: boolean
|
|
16
|
+
sandbox: boolean
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface CliUpdateOptions {
|
|
20
|
+
version: string
|
|
21
|
+
fetchLatestVersion: (packageName: string) => Promise<string>
|
|
22
|
+
installVersion: (packageName: string, version: string) => UpdateInstallAttemptResult
|
|
23
|
+
argv: string[]
|
|
24
|
+
command: string
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface StartedCli {
|
|
28
|
+
kind: "started"
|
|
29
|
+
stop: () => Promise<void>
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface RestartingCli {
|
|
33
|
+
kind: "restarting"
|
|
34
|
+
reason: "startup_update" | "ui_update"
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface ExitedCli {
|
|
38
|
+
kind: "exited"
|
|
39
|
+
code: number
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export type CliRunResult = StartedCli | RestartingCli | ExitedCli
|
|
43
|
+
|
|
44
|
+
export interface CliRuntimeDeps {
|
|
45
|
+
version: string
|
|
46
|
+
bunVersion: string
|
|
47
|
+
startServer: (options: CliOptions & {
|
|
48
|
+
update: CliUpdateOptions
|
|
49
|
+
onMigrationProgress?: (message: string) => void
|
|
50
|
+
}) => Promise<{ port: number; stop: () => Promise<void> }>
|
|
51
|
+
fetchLatestVersion: (packageName: string) => Promise<string>
|
|
52
|
+
installVersion: (packageName: string, version: string) => UpdateInstallAttemptResult
|
|
53
|
+
openUrl: (url: string) => void
|
|
54
|
+
log: (message: string) => void
|
|
55
|
+
warn: (message: string) => void
|
|
56
|
+
renderShareQr?: (url: string) => Promise<string>
|
|
57
|
+
startShareTunnel?: (localUrl: string) => Promise<StartedShareTunnel>
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export interface UpdateInstallAttemptResult {
|
|
61
|
+
ok: boolean
|
|
62
|
+
errorCode: UpdateInstallErrorCode | null
|
|
63
|
+
userTitle: string | null
|
|
64
|
+
userMessage: string | null
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
type ParsedArgs =
|
|
68
|
+
| { kind: "run"; options: CliOptions }
|
|
69
|
+
| { kind: "help" }
|
|
70
|
+
| { kind: "version" }
|
|
71
|
+
|
|
72
|
+
const MINIMUM_BUN_VERSION = "1.3.5"
|
|
73
|
+
|
|
74
|
+
function printHelp() {
|
|
75
|
+
console.log(`${APP_NAME} — local-only project chat UI
|
|
76
|
+
|
|
77
|
+
Usage:
|
|
78
|
+
${CLI_COMMAND} [options]
|
|
79
|
+
|
|
80
|
+
Options:
|
|
81
|
+
--port <number> Port to listen on (default: ${PROD_SERVER_PORT})
|
|
82
|
+
--host <host> Bind to a specific host or IP
|
|
83
|
+
--remote Shortcut for --host 0.0.0.0
|
|
84
|
+
--share Create a public Cloudflare share URL with terminal QR
|
|
85
|
+
--strict-port Fail instead of trying another port
|
|
86
|
+
--no-open Don't open browser automatically
|
|
87
|
+
--sandbox Enable Docker sandbox isolation for workspaces
|
|
88
|
+
--version Print version and exit
|
|
89
|
+
--help Show this help message`)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export function parseArgs(argv: string[]): ParsedArgs {
|
|
93
|
+
let port = PROD_SERVER_PORT
|
|
94
|
+
let host = "127.0.0.1"
|
|
95
|
+
let openBrowser = true
|
|
96
|
+
let share = false
|
|
97
|
+
let sawHost = false
|
|
98
|
+
let sawRemote = false
|
|
99
|
+
let strictPort = false
|
|
100
|
+
let sandbox = false
|
|
101
|
+
|
|
102
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
103
|
+
const arg = argv[index]
|
|
104
|
+
if (arg === "--version" || arg === "-v") {
|
|
105
|
+
return { kind: "version" }
|
|
106
|
+
}
|
|
107
|
+
if (arg === "--help" || arg === "-h") {
|
|
108
|
+
return { kind: "help" }
|
|
109
|
+
}
|
|
110
|
+
if (arg === "--port") {
|
|
111
|
+
const next = argv[index + 1]
|
|
112
|
+
if (!next) throw new Error("Missing value for --port")
|
|
113
|
+
const parsed = Number(next)
|
|
114
|
+
if (!Number.isFinite(parsed)) throw new Error(`Invalid port: ${next}`)
|
|
115
|
+
port = parsed
|
|
116
|
+
index += 1
|
|
117
|
+
continue
|
|
118
|
+
}
|
|
119
|
+
if (arg === "--host") {
|
|
120
|
+
const next = argv[index + 1]
|
|
121
|
+
if (!next || next.startsWith("-")) throw new Error("Missing value for --host")
|
|
122
|
+
if (share) throw new Error("--share cannot be used with --host")
|
|
123
|
+
host = next
|
|
124
|
+
sawHost = true
|
|
125
|
+
index += 1
|
|
126
|
+
continue
|
|
127
|
+
}
|
|
128
|
+
if (arg === "--remote") {
|
|
129
|
+
if (share) throw new Error("--share cannot be used with --remote")
|
|
130
|
+
host = "0.0.0.0"
|
|
131
|
+
sawRemote = true
|
|
132
|
+
continue
|
|
133
|
+
}
|
|
134
|
+
if (arg === "--share") {
|
|
135
|
+
if (sawHost) throw new Error("--share cannot be used with --host")
|
|
136
|
+
if (sawRemote) throw new Error("--share cannot be used with --remote")
|
|
137
|
+
share = true
|
|
138
|
+
continue
|
|
139
|
+
}
|
|
140
|
+
if (arg === "--no-open") {
|
|
141
|
+
openBrowser = false
|
|
142
|
+
continue
|
|
143
|
+
}
|
|
144
|
+
if (arg === "--strict-port") {
|
|
145
|
+
strictPort = true
|
|
146
|
+
continue
|
|
147
|
+
}
|
|
148
|
+
if (arg === "--sandbox") {
|
|
149
|
+
sandbox = true
|
|
150
|
+
continue
|
|
151
|
+
}
|
|
152
|
+
if (!arg.startsWith("-")) throw new Error(`Unexpected positional argument: ${arg}`)
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return {
|
|
156
|
+
kind: "run",
|
|
157
|
+
options: {
|
|
158
|
+
port,
|
|
159
|
+
host,
|
|
160
|
+
openBrowser,
|
|
161
|
+
share,
|
|
162
|
+
strictPort,
|
|
163
|
+
sandbox,
|
|
164
|
+
},
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export function compareVersions(currentVersion: string, latestVersion: string) {
|
|
169
|
+
const currentParts = normalizeVersion(currentVersion)
|
|
170
|
+
const latestParts = normalizeVersion(latestVersion)
|
|
171
|
+
const length = Math.max(currentParts.length, latestParts.length)
|
|
172
|
+
|
|
173
|
+
for (let index = 0; index < length; index += 1) {
|
|
174
|
+
const current = currentParts[index] ?? 0
|
|
175
|
+
const latest = latestParts[index] ?? 0
|
|
176
|
+
if (current === latest) continue
|
|
177
|
+
return current < latest ? -1 : 1
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return 0
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function normalizeVersion(version: string) {
|
|
184
|
+
return version
|
|
185
|
+
.trim()
|
|
186
|
+
.replace(/^v/i, "")
|
|
187
|
+
.split("-")[0]
|
|
188
|
+
.split(".")
|
|
189
|
+
.map((part) => Number.parseInt(part, 10))
|
|
190
|
+
.filter((part) => Number.isFinite(part))
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
async function maybeSelfUpdate(_argv: string[], deps: CliRuntimeDeps) {
|
|
194
|
+
if (process.env.KANNA_DISABLE_SELF_UPDATE === "1") {
|
|
195
|
+
return null
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
deps.log(`${LOG_PREFIX} checking for updates`)
|
|
199
|
+
|
|
200
|
+
let latestVersion: string
|
|
201
|
+
try {
|
|
202
|
+
latestVersion = await deps.fetchLatestVersion(PACKAGE_NAME)
|
|
203
|
+
}
|
|
204
|
+
catch (error) {
|
|
205
|
+
deps.warn(`${LOG_PREFIX} update check failed, continuing current version`)
|
|
206
|
+
if (error instanceof Error && error.message) {
|
|
207
|
+
deps.warn(`${LOG_PREFIX} ${error.message}`)
|
|
208
|
+
}
|
|
209
|
+
return null
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
if (!latestVersion || compareVersions(deps.version, latestVersion) >= 0) {
|
|
213
|
+
return null
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
deps.log(`${LOG_PREFIX} installing ${PACKAGE_NAME}@${latestVersion}`)
|
|
217
|
+
const installResult = deps.installVersion(PACKAGE_NAME, latestVersion)
|
|
218
|
+
if (!installResult.ok) {
|
|
219
|
+
deps.warn(`${LOG_PREFIX} update failed, continuing current version`)
|
|
220
|
+
if (installResult.userMessage) {
|
|
221
|
+
deps.warn(`${LOG_PREFIX} ${installResult.userMessage}`)
|
|
222
|
+
}
|
|
223
|
+
return null
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
deps.log(`${LOG_PREFIX} restarting into updated version`)
|
|
227
|
+
return "startup_update"
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
export async function runCli(argv: string[], deps: CliRuntimeDeps): Promise<CliRunResult> {
|
|
231
|
+
const parsedArgs = parseArgs(argv)
|
|
232
|
+
if (parsedArgs.kind === "version") {
|
|
233
|
+
deps.log(deps.version)
|
|
234
|
+
return { kind: "exited", code: 0 }
|
|
235
|
+
}
|
|
236
|
+
if (parsedArgs.kind === "help") {
|
|
237
|
+
printHelp()
|
|
238
|
+
return { kind: "exited", code: 0 }
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
if (compareVersions(deps.bunVersion, MINIMUM_BUN_VERSION) < 0) {
|
|
242
|
+
deps.warn(`${LOG_PREFIX} Bun ${MINIMUM_BUN_VERSION}+ is required for the embedded terminal. Current Bun: ${deps.bunVersion}`)
|
|
243
|
+
return { kind: "exited", code: 1 }
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
const shouldRestart = await maybeSelfUpdate(argv, deps)
|
|
247
|
+
if (shouldRestart !== null) {
|
|
248
|
+
return { kind: "restarting", reason: shouldRestart }
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
const { port, stop } = await deps.startServer({
|
|
252
|
+
...parsedArgs.options,
|
|
253
|
+
onMigrationProgress: deps.log,
|
|
254
|
+
update: {
|
|
255
|
+
version: deps.version,
|
|
256
|
+
fetchLatestVersion: deps.fetchLatestVersion,
|
|
257
|
+
installVersion: deps.installVersion,
|
|
258
|
+
argv,
|
|
259
|
+
command: CLI_COMMAND,
|
|
260
|
+
},
|
|
261
|
+
})
|
|
262
|
+
const bindHost = parsedArgs.options.host
|
|
263
|
+
const displayHost = parsedArgs.options.share || bindHost === "127.0.0.1" || bindHost === "0.0.0.0" ? "localhost" : bindHost
|
|
264
|
+
const launchUrl = `http://${displayHost}:${port}`
|
|
265
|
+
let shareTunnelStop: (() => void) | null = null
|
|
266
|
+
|
|
267
|
+
deps.log(`${LOG_PREFIX} listening on http://${bindHost}:${port}`)
|
|
268
|
+
deps.log(`${LOG_PREFIX} data dir: ${getDataDirDisplay()}`)
|
|
269
|
+
|
|
270
|
+
const suppressOpenBrowser = process.env[CLI_SUPPRESS_OPEN_ONCE_ENV_VAR] === "1"
|
|
271
|
+
if (parsedArgs.options.share) {
|
|
272
|
+
try {
|
|
273
|
+
const shareTunnel = await (deps.startShareTunnel ?? ((localUrl) => startShareTunnel(localUrl, {
|
|
274
|
+
log: (message) => deps.log(`${LOG_PREFIX} ${message}`),
|
|
275
|
+
})))(launchUrl)
|
|
276
|
+
shareTunnelStop = shareTunnel.stop
|
|
277
|
+
await logShareDetails(deps.log, shareTunnel.publicUrl, launchUrl, deps.renderShareQr ?? renderTerminalQr)
|
|
278
|
+
} catch (error) {
|
|
279
|
+
await stop()
|
|
280
|
+
deps.warn(`${LOG_PREFIX} failed to start Cloudflare share tunnel`)
|
|
281
|
+
if (error instanceof Error && error.message) {
|
|
282
|
+
deps.warn(`${LOG_PREFIX} ${error.message}`)
|
|
283
|
+
}
|
|
284
|
+
return { kind: "exited", code: 1 }
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
if (parsedArgs.options.openBrowser && !parsedArgs.options.share && !suppressOpenBrowser) {
|
|
289
|
+
deps.openUrl(launchUrl)
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
return {
|
|
293
|
+
kind: "started",
|
|
294
|
+
stop: async () => {
|
|
295
|
+
shareTunnelStop?.()
|
|
296
|
+
await stop()
|
|
297
|
+
},
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
export function openUrl(url: string) {
|
|
302
|
+
const platform = process.platform
|
|
303
|
+
if (platform === "darwin") {
|
|
304
|
+
spawnDetached("open", [url])
|
|
305
|
+
} else if (platform === "win32") {
|
|
306
|
+
spawnDetached("cmd", ["/c", "start", "", url])
|
|
307
|
+
} else {
|
|
308
|
+
spawnDetached("xdg-open", [url])
|
|
309
|
+
}
|
|
310
|
+
console.log(`${LOG_PREFIX} opened in default browser`)
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
export async function fetchLatestPackageVersion(packageName: string) {
|
|
314
|
+
const response = await fetch(`https://registry.npmjs.org/${encodeURIComponent(packageName)}/latest`)
|
|
315
|
+
if (!response.ok) {
|
|
316
|
+
throw new Error(`registry returned ${response.status}`)
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
const payload = await response.json() as { version?: unknown }
|
|
320
|
+
if (typeof payload.version !== "string" || !payload.version.trim()) {
|
|
321
|
+
throw new Error("registry response did not include a version")
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
return payload.version
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
export function classifyInstallVersionFailure(output: string): UpdateInstallAttemptResult {
|
|
328
|
+
const normalizedOutput = output.trim()
|
|
329
|
+
if (/No version matching .* found|failed to resolve/i.test(normalizedOutput)) {
|
|
330
|
+
return {
|
|
331
|
+
ok: false,
|
|
332
|
+
errorCode: "version_not_live_yet",
|
|
333
|
+
userTitle: "Update not live yet",
|
|
334
|
+
userMessage: "This update is still propagating. Try again in a few minutes.",
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
return {
|
|
339
|
+
ok: false,
|
|
340
|
+
errorCode: "install_failed",
|
|
341
|
+
userTitle: "Update failed",
|
|
342
|
+
userMessage: `${APP_NAME} could not install the update. Try again later.`,
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
export function installPackageVersion(packageName: string, version: string) {
|
|
347
|
+
if (!hasCommand("bun")) {
|
|
348
|
+
return {
|
|
349
|
+
ok: false,
|
|
350
|
+
errorCode: "command_missing",
|
|
351
|
+
userTitle: "Bun not found",
|
|
352
|
+
userMessage: `${APP_NAME} could not find Bun to install the update.`,
|
|
353
|
+
} satisfies UpdateInstallAttemptResult
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
const result = spawnSync("bun", ["install", "-g", `${packageName}@${version}`], {
|
|
357
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
358
|
+
encoding: "utf8",
|
|
359
|
+
})
|
|
360
|
+
const stdout = result.stdout ?? ""
|
|
361
|
+
const stderr = result.stderr ?? ""
|
|
362
|
+
if (stdout) process.stdout.write(stdout)
|
|
363
|
+
if (stderr) process.stderr.write(stderr)
|
|
364
|
+
if (result.status === 0) {
|
|
365
|
+
return {
|
|
366
|
+
ok: true,
|
|
367
|
+
errorCode: null,
|
|
368
|
+
userTitle: null,
|
|
369
|
+
userMessage: null,
|
|
370
|
+
} satisfies UpdateInstallAttemptResult
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
return classifyInstallVersionFailure(`${stdout}\n${stderr}`)
|
|
374
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import process from "node:process"
|
|
2
|
+
import { spawn } from "node:child_process"
|
|
3
|
+
import { CLI_COMMAND, LOG_PREFIX } from "../shared/branding"
|
|
4
|
+
import {
|
|
5
|
+
CLI_CHILD_ARGS_ENV_VAR,
|
|
6
|
+
CLI_CHILD_COMMAND_ENV_VAR,
|
|
7
|
+
CLI_CHILD_MODE,
|
|
8
|
+
CLI_CHILD_MODE_ENV_VAR,
|
|
9
|
+
CLI_SUPPRESS_OPEN_ONCE_ENV_VAR,
|
|
10
|
+
isUiUpdateRestart,
|
|
11
|
+
parseChildArgsEnv,
|
|
12
|
+
shouldRestartCliProcess,
|
|
13
|
+
} from "./restart"
|
|
14
|
+
|
|
15
|
+
interface ChildExit {
|
|
16
|
+
code: number | null
|
|
17
|
+
signal: NodeJS.Signals | null
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function getChildProcessSpec() {
|
|
21
|
+
const command = process.env[CLI_CHILD_COMMAND_ENV_VAR] || CLI_COMMAND
|
|
22
|
+
const args = parseChildArgsEnv(process.env[CLI_CHILD_ARGS_ENV_VAR])
|
|
23
|
+
return { command, args }
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function spawnChild(argv: string[]) {
|
|
27
|
+
const childProcess = getChildProcessSpec()
|
|
28
|
+
const suppressOpenThisChild = suppressOpenOnNextChild
|
|
29
|
+
suppressOpenOnNextChild = false
|
|
30
|
+
return new Promise<ChildExit>((resolve, reject) => {
|
|
31
|
+
const child = spawn(childProcess.command, [...childProcess.args, ...argv], {
|
|
32
|
+
stdio: "inherit",
|
|
33
|
+
env: {
|
|
34
|
+
...process.env,
|
|
35
|
+
[CLI_CHILD_MODE_ENV_VAR]: CLI_CHILD_MODE,
|
|
36
|
+
...(suppressOpenThisChild ? { [CLI_SUPPRESS_OPEN_ONCE_ENV_VAR]: "1" } : {}),
|
|
37
|
+
},
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
const forwardSignal = (signal: NodeJS.Signals) => {
|
|
41
|
+
if (child.exitCode !== null) return
|
|
42
|
+
child.kill(signal)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const onSigint = () => {
|
|
46
|
+
forwardSignal("SIGINT")
|
|
47
|
+
}
|
|
48
|
+
const onSigterm = () => {
|
|
49
|
+
forwardSignal("SIGTERM")
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
process.on("SIGINT", onSigint)
|
|
53
|
+
process.on("SIGTERM", onSigterm)
|
|
54
|
+
|
|
55
|
+
child.once("error", (error) => {
|
|
56
|
+
process.off("SIGINT", onSigint)
|
|
57
|
+
process.off("SIGTERM", onSigterm)
|
|
58
|
+
reject(error)
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
child.once("exit", (code, signal) => {
|
|
62
|
+
process.off("SIGINT", onSigint)
|
|
63
|
+
process.off("SIGTERM", onSigterm)
|
|
64
|
+
resolve({ code, signal })
|
|
65
|
+
})
|
|
66
|
+
})
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const argv = process.argv.slice(2)
|
|
70
|
+
let suppressOpenOnNextChild = false
|
|
71
|
+
|
|
72
|
+
while (true) {
|
|
73
|
+
const result = await spawnChild(argv)
|
|
74
|
+
if (shouldRestartCliProcess(result.code, result.signal)) {
|
|
75
|
+
suppressOpenOnNextChild = isUiUpdateRestart(result.code, result.signal)
|
|
76
|
+
console.log(`${LOG_PREFIX} supervisor restarting ${CLI_COMMAND} in the same terminal session`)
|
|
77
|
+
continue
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
process.exit(result.code ?? (result.signal ? 1 : 0))
|
|
81
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import process from "node:process"
|
|
2
|
+
import { LOG_PREFIX } from "../shared/branding"
|
|
3
|
+
import { installConsoleTee } from "../shared/log-sink"
|
|
4
|
+
|
|
5
|
+
// Observability (decision 0012): tee console output to VictoriaLogs when
|
|
6
|
+
// VICTORIALOGS_URL is set; no-op otherwise. Installed first so all startup
|
|
7
|
+
// logs are captured.
|
|
8
|
+
installConsoleTee(process.env, "server")
|
|
9
|
+
|
|
10
|
+
// Safety net: prevent stray unhandled rejections from crashing the server.
|
|
11
|
+
// Root causes should still be fixed, but this prevents cascading process death
|
|
12
|
+
// when a child process (e.g., Codex, vendored rg) dies unexpectedly.
|
|
13
|
+
process.on("unhandledRejection", (reason) => {
|
|
14
|
+
const message = reason instanceof Error ? reason.message : String(reason)
|
|
15
|
+
console.warn(LOG_PREFIX, "unhandled rejection (swallowed):", message)
|
|
16
|
+
})
|
|
17
|
+
import {
|
|
18
|
+
fetchLatestPackageVersion,
|
|
19
|
+
installPackageVersion,
|
|
20
|
+
openUrl,
|
|
21
|
+
runCli,
|
|
22
|
+
} from "./cli-runtime"
|
|
23
|
+
import { CLI_STARTUP_UPDATE_RESTART_EXIT_CODE, CLI_UI_UPDATE_RESTART_EXIT_CODE } from "./restart"
|
|
24
|
+
import { startServer } from "./server"
|
|
25
|
+
|
|
26
|
+
// Read version from package.json at the package root
|
|
27
|
+
const pkg = await Bun.file(new URL("../../package.json", import.meta.url)).json()
|
|
28
|
+
const VERSION: string = pkg.version ?? "0.0.0"
|
|
29
|
+
|
|
30
|
+
const argv = process.argv.slice(2)
|
|
31
|
+
let resolveExitAction: ((action: "ui_restart" | "exit") => void) | null = null
|
|
32
|
+
|
|
33
|
+
const result = await runCli(argv, {
|
|
34
|
+
version: VERSION,
|
|
35
|
+
bunVersion: Bun.version,
|
|
36
|
+
startServer: async (options) => {
|
|
37
|
+
const started = await startServer(options)
|
|
38
|
+
if (started.updateManager && options.update) {
|
|
39
|
+
started.updateManager.onChange((snapshot) => {
|
|
40
|
+
if (snapshot.status !== "restart_pending") return
|
|
41
|
+
console.log(`${LOG_PREFIX} update installed, shutting down current process for restart`)
|
|
42
|
+
resolveExitAction?.("ui_restart")
|
|
43
|
+
})
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return started
|
|
47
|
+
},
|
|
48
|
+
fetchLatestVersion: fetchLatestPackageVersion,
|
|
49
|
+
installVersion: installPackageVersion,
|
|
50
|
+
openUrl,
|
|
51
|
+
log: console.log,
|
|
52
|
+
warn: console.warn,
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
if (result.kind === "exited") {
|
|
56
|
+
process.exit(result.code)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (result.kind === "restarting") {
|
|
60
|
+
process.exit(result.reason === "startup_update" ? CLI_STARTUP_UPDATE_RESTART_EXIT_CODE : CLI_UI_UPDATE_RESTART_EXIT_CODE)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const exitAction = await new Promise<"ui_restart" | "exit">((resolve) => {
|
|
64
|
+
resolveExitAction = resolve
|
|
65
|
+
|
|
66
|
+
const shutdown = () => {
|
|
67
|
+
resolve("exit")
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
process.once("SIGINT", shutdown)
|
|
71
|
+
process.once("SIGTERM", shutdown)
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
await result.stop()
|
|
75
|
+
if (exitAction === "ui_restart") {
|
|
76
|
+
console.log(`${LOG_PREFIX} current process stopped, handing restart back to supervisor`)
|
|
77
|
+
}
|
|
78
|
+
process.exit(exitAction === "ui_restart" ? CLI_UI_UPDATE_RESTART_EXIT_CODE : 0)
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { describe, test, expect } from "bun:test"
|
|
2
|
+
import { sanitizeClientLogs, buildNdjson, forwardClientLogs } from "./client-log-forwarder"
|
|
3
|
+
|
|
4
|
+
describe("sanitizeClientLogs", () => {
|
|
5
|
+
test("keeps valid records and tags source/component", () => {
|
|
6
|
+
const out = sanitizeClientLogs({
|
|
7
|
+
logs: [{ level: "warn", msg: "hi", ts: "2026-05-27T10:00:00.000Z", url: "https://a/b" }],
|
|
8
|
+
})
|
|
9
|
+
expect(out).toEqual([
|
|
10
|
+
{ _time: "2026-05-27T10:00:00.000Z", _msg: "hi", level: "warn", source: "frontend", component: "browser", url: "https://a/b" },
|
|
11
|
+
])
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
test("drops entries with no message; defaults bad level to info; backfills bad ts", () => {
|
|
15
|
+
const now = new Date("2026-05-27T12:00:00.000Z")
|
|
16
|
+
const out = sanitizeClientLogs({ logs: [{ msg: "x", level: "nope", ts: "garbage" }, { level: "error" }] }, now)
|
|
17
|
+
expect(out).toHaveLength(1)
|
|
18
|
+
expect(out[0].level).toBe("info")
|
|
19
|
+
expect(out[0]._time).toBe("2026-05-27T12:00:00.000Z")
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
test("non-array / non-object bodies → empty", () => {
|
|
23
|
+
expect(sanitizeClientLogs(null)).toEqual([])
|
|
24
|
+
expect(sanitizeClientLogs({ logs: "x" })).toEqual([])
|
|
25
|
+
expect(sanitizeClientLogs({})).toEqual([])
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
test("caps record count and truncates long messages", () => {
|
|
29
|
+
const logs = Array.from({ length: 1500 }, () => ({ msg: "a".repeat(20000) }))
|
|
30
|
+
const out = sanitizeClientLogs({ logs })
|
|
31
|
+
expect(out.length).toBe(1000)
|
|
32
|
+
expect(out[0]._msg.length).toBe(8192)
|
|
33
|
+
})
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
describe("buildNdjson", () => {
|
|
37
|
+
test("newline-joins one JSON object per record", () => {
|
|
38
|
+
const recs = sanitizeClientLogs({ logs: [{ msg: "a" }, { msg: "b" }] })
|
|
39
|
+
const out = buildNdjson(recs)
|
|
40
|
+
expect(out.split("\n")).toHaveLength(2)
|
|
41
|
+
expect(JSON.parse(out.split("\n")[1])._msg).toBe("b")
|
|
42
|
+
})
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
describe("forwardClientLogs", () => {
|
|
46
|
+
test("ships ndjson to VL when VICTORIALOGS_URL set", async () => {
|
|
47
|
+
const calls: { url: string; body: string }[] = []
|
|
48
|
+
const fetchFn = (async (u: string, init?: RequestInit) => {
|
|
49
|
+
calls.push({ url: u, body: String(init?.body) }); return new Response(null, { status: 204 })
|
|
50
|
+
}) as unknown as typeof fetch
|
|
51
|
+
const n = await forwardClientLogs(
|
|
52
|
+
{ logs: [{ msg: "one", level: "info" }] },
|
|
53
|
+
{ VICTORIALOGS_URL: "http://127.0.0.1:9428" } as unknown as NodeJS.ProcessEnv,
|
|
54
|
+
fetchFn,
|
|
55
|
+
)
|
|
56
|
+
expect(n).toBe(1)
|
|
57
|
+
expect(calls[0].url).toBe("http://127.0.0.1:9428/insert/jsonline?_stream_fields=source,component")
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
test("no VL url → counts but does not ship", async () => {
|
|
61
|
+
let shipped = 0
|
|
62
|
+
const fetchFn = (async () => { shipped++; return new Response(null) }) as unknown as typeof fetch
|
|
63
|
+
const n = await forwardClientLogs({ logs: [{ msg: "x" }] }, {} as NodeJS.ProcessEnv, fetchFn)
|
|
64
|
+
expect(n).toBe(1)
|
|
65
|
+
expect(shipped).toBe(0)
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
test("fetch failure is swallowed", async () => {
|
|
69
|
+
const fetchFn = (async () => { throw new Error("down") }) as unknown as typeof fetch
|
|
70
|
+
await expect(
|
|
71
|
+
forwardClientLogs({ logs: [{ msg: "x" }] }, { VICTORIALOGS_URL: "http://x" } as unknown as NodeJS.ProcessEnv, fetchFn),
|
|
72
|
+
).resolves.toBe(1)
|
|
73
|
+
})
|
|
74
|
+
})
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Forward browser-shipped logs to VictoriaLogs (decision 0012).
|
|
3
|
+
*
|
|
4
|
+
* The frontend POSTs batches to `/api/logs`; this module sanitizes them
|
|
5
|
+
* (caps count + truncates message size, so a misbehaving/malicious client
|
|
6
|
+
* can't flood the store) and ships them to VictoriaLogs as `source=frontend`.
|
|
7
|
+
* VictoriaLogs stays localhost-only — the browser never reaches it directly.
|
|
8
|
+
*
|
|
9
|
+
* Fire-and-forget: VictoriaLogs being down or `VICTORIALOGS_URL` being unset
|
|
10
|
+
* must never make `/api/logs` fail (the client should never error on logging).
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const MAX_RECORDS = 1000
|
|
14
|
+
const MAX_MSG_LEN = 8192
|
|
15
|
+
const LEVELS = new Set(["debug", "info", "warn", "error"])
|
|
16
|
+
|
|
17
|
+
export interface SanitizedClientLog {
|
|
18
|
+
_time: string
|
|
19
|
+
_msg: string
|
|
20
|
+
level: string
|
|
21
|
+
source: "frontend"
|
|
22
|
+
component: "browser"
|
|
23
|
+
url: string
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Validate + clamp a raw `/api/logs` body into VL records. Pure.
|
|
28
|
+
* Drops malformed entries; never throws.
|
|
29
|
+
*/
|
|
30
|
+
export function sanitizeClientLogs(body: unknown, now: Date = new Date()): SanitizedClientLog[] {
|
|
31
|
+
const logs = (body as { logs?: unknown })?.logs
|
|
32
|
+
if (!Array.isArray(logs)) return []
|
|
33
|
+
const out: SanitizedClientLog[] = []
|
|
34
|
+
for (const raw of logs.slice(0, MAX_RECORDS)) {
|
|
35
|
+
if (typeof raw !== "object" || raw === null) continue
|
|
36
|
+
const r = raw as Record<string, unknown>
|
|
37
|
+
const msg = typeof r.msg === "string" ? r.msg.slice(0, MAX_MSG_LEN) : ""
|
|
38
|
+
if (!msg) continue
|
|
39
|
+
const level = typeof r.level === "string" && LEVELS.has(r.level) ? r.level : "info"
|
|
40
|
+
const ts = typeof r.ts === "string" && !Number.isNaN(Date.parse(r.ts)) ? r.ts : now.toISOString()
|
|
41
|
+
const recUrl = typeof r.url === "string" ? r.url.slice(0, 2048) : ""
|
|
42
|
+
out.push({ _time: ts, _msg: msg, level, source: "frontend", component: "browser", url: recUrl })
|
|
43
|
+
}
|
|
44
|
+
return out
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Newline-delimited JSON for VL `/insert/jsonline`. Pure. */
|
|
48
|
+
export function buildNdjson(records: SanitizedClientLog[]): string {
|
|
49
|
+
return records.map((r) => JSON.stringify(r)).join("\n")
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Sanitize and ship a `/api/logs` body to VictoriaLogs. Returns the number of
|
|
54
|
+
* accepted records. Never throws; swallows network errors.
|
|
55
|
+
*/
|
|
56
|
+
export async function forwardClientLogs(
|
|
57
|
+
body: unknown,
|
|
58
|
+
env: NodeJS.ProcessEnv = process.env,
|
|
59
|
+
fetchFn: typeof fetch = fetch,
|
|
60
|
+
): Promise<number> {
|
|
61
|
+
const records = sanitizeClientLogs(body)
|
|
62
|
+
const url = env.VICTORIALOGS_URL?.trim()
|
|
63
|
+
if (records.length === 0 || !url) return records.length
|
|
64
|
+
const endpoint = `${url.replace(/\/$/, "")}/insert/jsonline?_stream_fields=source,component`
|
|
65
|
+
try {
|
|
66
|
+
await fetchFn(endpoint, {
|
|
67
|
+
method: "POST",
|
|
68
|
+
headers: { "Content-Type": "application/x-ndjson" },
|
|
69
|
+
body: buildNdjson(records),
|
|
70
|
+
})
|
|
71
|
+
} catch {
|
|
72
|
+
// observability must never break the ingest endpoint
|
|
73
|
+
}
|
|
74
|
+
return records.length
|
|
75
|
+
}
|