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,288 @@
|
|
|
1
|
+
import type { PresetDefinition } from "../shared/preset-types"
|
|
2
|
+
import type { TranscriptEntry } from "../shared/types"
|
|
3
|
+
import type { QuickResponseAdapter } from "./quick-response"
|
|
4
|
+
import { normalizeGeneratedPrompt, normalizeIntent, toTranscriptLine, truncateLine } from "./transcript-utils"
|
|
5
|
+
import { MAX_MERGE_SESSIONS } from "../shared/merge-presets"
|
|
6
|
+
|
|
7
|
+
const ANALYSIS_SCHEMA = {
|
|
8
|
+
type: "object",
|
|
9
|
+
properties: {
|
|
10
|
+
compactInstruction: { type: "string" },
|
|
11
|
+
nextInstruction: { type: "string" },
|
|
12
|
+
},
|
|
13
|
+
required: ["compactInstruction", "nextInstruction"],
|
|
14
|
+
additionalProperties: false,
|
|
15
|
+
} as const
|
|
16
|
+
|
|
17
|
+
const SUMMARY_SCHEMA = {
|
|
18
|
+
type: "object",
|
|
19
|
+
properties: {
|
|
20
|
+
summary: { type: "string" },
|
|
21
|
+
},
|
|
22
|
+
required: ["summary"],
|
|
23
|
+
additionalProperties: false,
|
|
24
|
+
} as const
|
|
25
|
+
|
|
26
|
+
const MAX_ANALYSIS_CHARS = 1_200
|
|
27
|
+
const MAX_SOURCE_SUMMARY_CHARS = 2_400
|
|
28
|
+
const MAX_FORK_TRANSCRIPT_LINES = 32
|
|
29
|
+
const MAX_FORK_TRANSCRIPT_CHARS = 14_000
|
|
30
|
+
const MAX_FORK_LINE_CHARS = 700
|
|
31
|
+
const MAX_MERGE_TOTAL_CHARS = 14_000
|
|
32
|
+
const MERGE_FLOOR_PER_SESSION_CHARS = 500
|
|
33
|
+
const MAX_MERGE_LINE_CHARS = 700
|
|
34
|
+
|
|
35
|
+
export interface SessionSeedSource {
|
|
36
|
+
chatId: string
|
|
37
|
+
entries: TranscriptEntry[]
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
interface SessionSeedIntentPlan {
|
|
41
|
+
compactInstruction: string
|
|
42
|
+
nextInstruction: string
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function buildForkTranscriptExcerpt(entries: TranscriptEntry[]): string {
|
|
46
|
+
const lines = entries
|
|
47
|
+
.map((entry) => toTranscriptLine(entry, MAX_FORK_LINE_CHARS))
|
|
48
|
+
.filter((line): line is string => Boolean(line))
|
|
49
|
+
|
|
50
|
+
if (lines.length === 0) return "No prior transcript context was available."
|
|
51
|
+
|
|
52
|
+
const selected = lines.slice(-MAX_FORK_TRANSCRIPT_LINES)
|
|
53
|
+
const omittedCount = lines.length - selected.length
|
|
54
|
+
const header = omittedCount > 0
|
|
55
|
+
? [`Recent source transcript excerpt. Older relevant lines omitted: ${omittedCount}.`]
|
|
56
|
+
: ["Recent source transcript excerpt:"]
|
|
57
|
+
|
|
58
|
+
const combined = [...header, ...selected]
|
|
59
|
+
let serialized = combined.join("\n")
|
|
60
|
+
if (serialized.length <= MAX_FORK_TRANSCRIPT_CHARS) return serialized
|
|
61
|
+
|
|
62
|
+
const trimmed: string[] = []
|
|
63
|
+
let remaining = MAX_FORK_TRANSCRIPT_CHARS - header[0]!.length - 1
|
|
64
|
+
for (let index = selected.length - 1; index >= 0; index -= 1) {
|
|
65
|
+
const line = selected[index]!
|
|
66
|
+
const cost = line.length + 1
|
|
67
|
+
if (remaining - cost < 0) break
|
|
68
|
+
trimmed.unshift(line)
|
|
69
|
+
remaining -= cost
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return [...header, ...trimmed].join("\n")
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function sliceAfterLastContextCleared(entries: TranscriptEntry[]): TranscriptEntry[] {
|
|
76
|
+
let lastClearedIndex = -1
|
|
77
|
+
for (let i = entries.length - 1; i >= 0; i -= 1) {
|
|
78
|
+
if (entries[i]!.kind === "context_cleared") {
|
|
79
|
+
lastClearedIndex = i
|
|
80
|
+
break
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return lastClearedIndex >= 0 ? entries.slice(lastClearedIndex + 1) : entries
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function buildBudgetedTranscriptExcerpt(entries: TranscriptEntry[], charBudget: number): string {
|
|
87
|
+
const sliced = sliceAfterLastContextCleared(entries)
|
|
88
|
+
const lines = sliced
|
|
89
|
+
.map((entry) => toTranscriptLine(entry, MAX_MERGE_LINE_CHARS))
|
|
90
|
+
.filter((line): line is string => Boolean(line))
|
|
91
|
+
|
|
92
|
+
if (lines.length === 0) return "No prior transcript context was available."
|
|
93
|
+
|
|
94
|
+
const header = "Recent source transcript excerpt:"
|
|
95
|
+
const headerCost = header.length + 1
|
|
96
|
+
const selected: string[] = []
|
|
97
|
+
let remaining = charBudget - headerCost
|
|
98
|
+
|
|
99
|
+
for (let i = lines.length - 1; i >= 0; i -= 1) {
|
|
100
|
+
const line = lines[i]!
|
|
101
|
+
const cost = line.length + 1
|
|
102
|
+
if (remaining - cost < 0) break
|
|
103
|
+
selected.unshift(line)
|
|
104
|
+
remaining -= cost
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (selected.length === 0) {
|
|
108
|
+
const lastLine = lines[lines.length - 1]!
|
|
109
|
+
const maxLen = Math.max(0, charBudget - headerCost - 1)
|
|
110
|
+
if (maxLen > 0) {
|
|
111
|
+
selected.push(truncateLine(lastLine, maxLen))
|
|
112
|
+
} else {
|
|
113
|
+
return "No prior transcript context was available."
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const omittedCount = lines.length - selected.length
|
|
118
|
+
const finalHeader = omittedCount > 0
|
|
119
|
+
? `Recent source transcript excerpt. Older relevant lines omitted: ${omittedCount}.`
|
|
120
|
+
: header
|
|
121
|
+
|
|
122
|
+
return [finalHeader, ...selected].join("\n")
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function computeUsableLength(entries: TranscriptEntry[]): number {
|
|
126
|
+
const sliced = sliceAfterLastContextCleared(entries)
|
|
127
|
+
return sliced
|
|
128
|
+
.map((entry) => toTranscriptLine(entry, MAX_MERGE_LINE_CHARS))
|
|
129
|
+
.filter((line): line is string => Boolean(line))
|
|
130
|
+
.reduce((sum, line) => sum + line.length, 0)
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function allocateSessionBudgets(sources: SessionSeedSource[]): number[] {
|
|
134
|
+
if (sources.length > MAX_MERGE_SESSIONS) {
|
|
135
|
+
throw new Error(`Cannot compact more than ${MAX_MERGE_SESSIONS} sessions (got ${sources.length})`)
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const usableLengths = sources.map((source) => computeUsableLength(source.entries))
|
|
139
|
+
const nonEmptyCount = usableLengths.filter((length) => length > 0).length
|
|
140
|
+
if (nonEmptyCount === 0) return usableLengths.map(() => 0)
|
|
141
|
+
|
|
142
|
+
const totalFloor = nonEmptyCount * MERGE_FLOOR_PER_SESSION_CHARS
|
|
143
|
+
const remainder = Math.max(0, MAX_MERGE_TOTAL_CHARS - totalFloor)
|
|
144
|
+
const totalUsable = usableLengths.reduce((sum, length) => sum + length, 0)
|
|
145
|
+
|
|
146
|
+
return usableLengths.map((length) => {
|
|
147
|
+
if (length === 0) return 0
|
|
148
|
+
const proportional = totalUsable > 0
|
|
149
|
+
? Math.floor(remainder * (length / totalUsable))
|
|
150
|
+
: Math.floor(remainder / nonEmptyCount)
|
|
151
|
+
return MERGE_FLOOR_PER_SESSION_CHARS + proportional
|
|
152
|
+
})
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function getFallbackInstruction(intent: string, preset?: PresetDefinition | null): string {
|
|
156
|
+
return normalizeIntent(intent) || preset?.defaultIntent || "Continue with the most useful verified next step."
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
async function analyzeSessionSeedIntent(args: {
|
|
160
|
+
mode: "fork" | "merge"
|
|
161
|
+
intent: string
|
|
162
|
+
preset?: PresetDefinition | null
|
|
163
|
+
cwd: string
|
|
164
|
+
adapter: QuickResponseAdapter
|
|
165
|
+
}): Promise<SessionSeedIntentPlan> {
|
|
166
|
+
const fallbackInstruction = getFallbackInstruction(args.intent, args.preset)
|
|
167
|
+
const result = await args.adapter.generateStructured<SessionSeedIntentPlan>({
|
|
168
|
+
cwd: args.cwd,
|
|
169
|
+
task: `${args.mode} session seed analysis`,
|
|
170
|
+
prompt: [
|
|
171
|
+
"Analyze the user's instruction for preparing a new coding session.",
|
|
172
|
+
"Return two fields only:",
|
|
173
|
+
"compactInstruction: how to compact the selected source context.",
|
|
174
|
+
"nextInstruction: what the new session should do after compaction.",
|
|
175
|
+
"Keep both concise, concrete, and execution-oriented.",
|
|
176
|
+
"Do not mention orchestration, parent or child sessions, or the compaction step itself.",
|
|
177
|
+
args.preset ? `Selected preset: ${args.preset.label}. ${args.preset.generatorHint}` : "No explicit preset was selected.",
|
|
178
|
+
"",
|
|
179
|
+
`User instruction:\n${fallbackInstruction}`,
|
|
180
|
+
].join("\n"),
|
|
181
|
+
schema: ANALYSIS_SCHEMA,
|
|
182
|
+
parse: (value) => {
|
|
183
|
+
const output = value && typeof value === "object"
|
|
184
|
+
? value as { compactInstruction?: unknown; nextInstruction?: unknown }
|
|
185
|
+
: {}
|
|
186
|
+
const compactInstruction = normalizeGeneratedPrompt(output.compactInstruction, MAX_ANALYSIS_CHARS)
|
|
187
|
+
const nextInstruction = normalizeGeneratedPrompt(output.nextInstruction, MAX_ANALYSIS_CHARS)
|
|
188
|
+
if (!compactInstruction || !nextInstruction) return null
|
|
189
|
+
return { compactInstruction, nextInstruction }
|
|
190
|
+
},
|
|
191
|
+
})
|
|
192
|
+
|
|
193
|
+
return result ?? {
|
|
194
|
+
compactInstruction: fallbackInstruction,
|
|
195
|
+
nextInstruction: fallbackInstruction,
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function buildSourceExcerpt(source: SessionSeedSource, sources: SessionSeedSource[]): string {
|
|
200
|
+
if (sources.length <= 1) return buildForkTranscriptExcerpt(source.entries)
|
|
201
|
+
const budgets = allocateSessionBudgets(sources)
|
|
202
|
+
const sourceIndex = sources.findIndex((candidate) => candidate.chatId === source.chatId)
|
|
203
|
+
return buildBudgetedTranscriptExcerpt(source.entries, budgets[sourceIndex] ?? 0)
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
async function compactSourceContext(args: {
|
|
207
|
+
source: SessionSeedSource
|
|
208
|
+
sources: SessionSeedSource[]
|
|
209
|
+
compactInstruction: string
|
|
210
|
+
cwd: string
|
|
211
|
+
adapter: QuickResponseAdapter
|
|
212
|
+
}): Promise<{ label: string; summary: string }> {
|
|
213
|
+
const excerpt = buildSourceExcerpt(args.source, args.sources)
|
|
214
|
+
const fallbackSummary = excerpt.slice(0, MAX_SOURCE_SUMMARY_CHARS).trim()
|
|
215
|
+
|
|
216
|
+
const result = await args.adapter.generateStructured<string>({
|
|
217
|
+
cwd: args.cwd,
|
|
218
|
+
task: "session context compaction",
|
|
219
|
+
prompt: [
|
|
220
|
+
"Compact this transcript for reuse in a new coding session.",
|
|
221
|
+
`Optimize the compaction for this goal: ${args.compactInstruction}`,
|
|
222
|
+
"Preserve exact file names, symbols, commands, constraints, and open questions when they are present and proven.",
|
|
223
|
+
"Drop filler, repetition, and orchestration wording.",
|
|
224
|
+
"Prefer concise markdown with sections like Relevant Context, Constraints, Evidence, and Open Questions when useful.",
|
|
225
|
+
"",
|
|
226
|
+
excerpt,
|
|
227
|
+
].join("\n"),
|
|
228
|
+
schema: SUMMARY_SCHEMA,
|
|
229
|
+
parse: (value) => {
|
|
230
|
+
const output = value && typeof value === "object" ? value as { summary?: unknown } : {}
|
|
231
|
+
return normalizeGeneratedPrompt(output.summary, MAX_SOURCE_SUMMARY_CHARS)
|
|
232
|
+
},
|
|
233
|
+
})
|
|
234
|
+
|
|
235
|
+
return {
|
|
236
|
+
label: args.source.chatId,
|
|
237
|
+
summary: result ?? (fallbackSummary || "No prior transcript context was available."),
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function buildFinalSeedPrompt(nextInstruction: string, summaries: Array<{ label: string; summary: string }>): string {
|
|
242
|
+
const sections = [`## Objective\n${nextInstruction}`]
|
|
243
|
+
|
|
244
|
+
if (summaries.length === 1) {
|
|
245
|
+
sections.push(`## Relevant Context\n${summaries[0]!.summary}`)
|
|
246
|
+
} else {
|
|
247
|
+
sections.push([
|
|
248
|
+
"## Compacted Contexts",
|
|
249
|
+
...summaries.map((summary) => `### ${summary.label}\n${summary.summary}`),
|
|
250
|
+
].join("\n\n"))
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
sections.push(
|
|
254
|
+
"## Constraints\nPreserve proven constraints from the context above. Call out contradictions or missing evidence before making risky changes.",
|
|
255
|
+
)
|
|
256
|
+
sections.push("## Next Step\nStart directly on the objective using the compacted context above.")
|
|
257
|
+
|
|
258
|
+
return sections.join("\n\n")
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
export async function buildSessionSeedPrompt(args: {
|
|
262
|
+
mode: "fork" | "merge"
|
|
263
|
+
intent: string
|
|
264
|
+
preset?: PresetDefinition | null
|
|
265
|
+
sources: SessionSeedSource[]
|
|
266
|
+
cwd: string
|
|
267
|
+
adapter: QuickResponseAdapter
|
|
268
|
+
}): Promise<string> {
|
|
269
|
+
const plan = await analyzeSessionSeedIntent({
|
|
270
|
+
mode: args.mode,
|
|
271
|
+
intent: args.intent,
|
|
272
|
+
preset: args.preset,
|
|
273
|
+
cwd: args.cwd,
|
|
274
|
+
adapter: args.adapter,
|
|
275
|
+
})
|
|
276
|
+
|
|
277
|
+
const summaries = await Promise.all(
|
|
278
|
+
args.sources.map((source) => compactSourceContext({
|
|
279
|
+
source,
|
|
280
|
+
sources: args.sources,
|
|
281
|
+
compactInstruction: plan.compactInstruction,
|
|
282
|
+
cwd: args.cwd,
|
|
283
|
+
adapter: args.adapter,
|
|
284
|
+
})),
|
|
285
|
+
)
|
|
286
|
+
|
|
287
|
+
return buildFinalSeedPrompt(plan.nextInstruction, summaries)
|
|
288
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test"
|
|
2
|
+
import { ensureCloudflaredInstalled, logShareDetails, startShareTunnel } from "./share"
|
|
3
|
+
|
|
4
|
+
describe("ensureCloudflaredInstalled", () => {
|
|
5
|
+
test("returns immediately when the binary already exists", async () => {
|
|
6
|
+
const installCalls: string[] = []
|
|
7
|
+
|
|
8
|
+
const result = await ensureCloudflaredInstalled({
|
|
9
|
+
cloudflaredBin: "/tmp/cloudflared",
|
|
10
|
+
existsSync: () => true,
|
|
11
|
+
installCloudflared: async (to) => {
|
|
12
|
+
installCalls.push(to)
|
|
13
|
+
return to
|
|
14
|
+
},
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
expect(result).toBe("/tmp/cloudflared")
|
|
18
|
+
expect(installCalls).toEqual([])
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
test("installs the binary on demand when it is missing", async () => {
|
|
22
|
+
const installCalls: string[] = []
|
|
23
|
+
const logLines: string[] = []
|
|
24
|
+
|
|
25
|
+
const result = await ensureCloudflaredInstalled({
|
|
26
|
+
cloudflaredBin: "/tmp/cloudflared",
|
|
27
|
+
existsSync: () => false,
|
|
28
|
+
installCloudflared: async (to) => {
|
|
29
|
+
installCalls.push(to)
|
|
30
|
+
return to
|
|
31
|
+
},
|
|
32
|
+
log: (message) => {
|
|
33
|
+
logLines.push(message)
|
|
34
|
+
},
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
expect(result).toBe("/tmp/cloudflared")
|
|
38
|
+
expect(installCalls).toEqual(["/tmp/cloudflared"])
|
|
39
|
+
expect(logLines).toEqual(["installing cloudflared binary"])
|
|
40
|
+
})
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
describe("startShareTunnel", () => {
|
|
44
|
+
test("starts a quick tunnel after ensuring the binary exists", async () => {
|
|
45
|
+
const installCalls: string[] = []
|
|
46
|
+
const quickTunnelUrls: string[] = []
|
|
47
|
+
let stopCalls = 0
|
|
48
|
+
|
|
49
|
+
const shareTunnel = await startShareTunnel("http://localhost:3333", {
|
|
50
|
+
cloudflaredBin: "/tmp/cloudflared",
|
|
51
|
+
existsSync: () => false,
|
|
52
|
+
installCloudflared: async (to) => {
|
|
53
|
+
installCalls.push(to)
|
|
54
|
+
return to
|
|
55
|
+
},
|
|
56
|
+
createQuickTunnel: (localUrl) => {
|
|
57
|
+
quickTunnelUrls.push(localUrl)
|
|
58
|
+
return {
|
|
59
|
+
once(event, listener) {
|
|
60
|
+
if (event === "url") {
|
|
61
|
+
queueMicrotask(() => (listener as (url: string) => void)("https://kanna.trycloudflare.com"))
|
|
62
|
+
}
|
|
63
|
+
return this
|
|
64
|
+
},
|
|
65
|
+
off(_event, _listener) {
|
|
66
|
+
return this
|
|
67
|
+
},
|
|
68
|
+
stop() {
|
|
69
|
+
stopCalls += 1
|
|
70
|
+
return true
|
|
71
|
+
},
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
expect(installCalls).toEqual(["/tmp/cloudflared"])
|
|
77
|
+
expect(quickTunnelUrls).toEqual(["http://localhost:3333"])
|
|
78
|
+
expect(shareTunnel.publicUrl).toBe("https://kanna.trycloudflare.com")
|
|
79
|
+
shareTunnel.stop()
|
|
80
|
+
expect(stopCalls).toBe(1)
|
|
81
|
+
})
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
describe("logShareDetails", () => {
|
|
85
|
+
test("prints qr, public url, and local url in the expected order", async () => {
|
|
86
|
+
const logLines: string[] = []
|
|
87
|
+
|
|
88
|
+
await logShareDetails(
|
|
89
|
+
(message) => {
|
|
90
|
+
logLines.push(message)
|
|
91
|
+
},
|
|
92
|
+
"https://kanna.trycloudflare.com",
|
|
93
|
+
"http://localhost:3333",
|
|
94
|
+
async (url) => `[qr:${url}]\n`,
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
expect(logLines).toEqual([
|
|
98
|
+
"QR Code:",
|
|
99
|
+
"[qr:https://kanna.trycloudflare.com]",
|
|
100
|
+
"",
|
|
101
|
+
"Public URL:",
|
|
102
|
+
"https://kanna.trycloudflare.com",
|
|
103
|
+
"",
|
|
104
|
+
"Local URL:",
|
|
105
|
+
"http://localhost:3333",
|
|
106
|
+
])
|
|
107
|
+
})
|
|
108
|
+
})
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { existsSync } from "node:fs"
|
|
2
|
+
import QRCode from "qrcode"
|
|
3
|
+
import { Tunnel, bin as cloudflaredBin, install as installCloudflared } from "cloudflared"
|
|
4
|
+
|
|
5
|
+
export interface StartedShareTunnel {
|
|
6
|
+
publicUrl: string
|
|
7
|
+
stop: () => void
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface ShareTunnelProcess {
|
|
11
|
+
once(event: "url", listener: (url: string) => void): ShareTunnelProcess
|
|
12
|
+
once(event: "error", listener: (error: Error) => void): ShareTunnelProcess
|
|
13
|
+
once(event: "exit", listener: (code: number | null, signal: NodeJS.Signals | null) => void): ShareTunnelProcess
|
|
14
|
+
off(event: "url", listener: (url: string) => void): ShareTunnelProcess
|
|
15
|
+
off(event: "error", listener: (error: Error) => void): ShareTunnelProcess
|
|
16
|
+
off(event: "exit", listener: (code: number | null, signal: NodeJS.Signals | null) => void): ShareTunnelProcess
|
|
17
|
+
stop(): boolean
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface ShareTunnelDeps {
|
|
21
|
+
cloudflaredBin?: string
|
|
22
|
+
existsSync?: (path: string) => boolean
|
|
23
|
+
installCloudflared?: (to: string) => Promise<string>
|
|
24
|
+
createQuickTunnel?: (localUrl: string) => ShareTunnelProcess
|
|
25
|
+
log?: (message: string) => void
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export async function renderTerminalQr(url: string) {
|
|
29
|
+
return QRCode.toString(url, {
|
|
30
|
+
type: "terminal",
|
|
31
|
+
small: true,
|
|
32
|
+
errorCorrectionLevel: "M",
|
|
33
|
+
})
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export async function ensureCloudflaredInstalled(
|
|
37
|
+
deps: ShareTunnelDeps = {},
|
|
38
|
+
) {
|
|
39
|
+
const resolvedBin = deps.cloudflaredBin ?? cloudflaredBin
|
|
40
|
+
const fileExists = deps.existsSync ?? existsSync
|
|
41
|
+
const installBinary = deps.installCloudflared ?? installCloudflared
|
|
42
|
+
|
|
43
|
+
if (fileExists(resolvedBin)) {
|
|
44
|
+
return resolvedBin
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
deps.log?.("installing cloudflared binary")
|
|
48
|
+
await installBinary(resolvedBin)
|
|
49
|
+
return resolvedBin
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export async function startShareTunnel(localUrl: string, deps: ShareTunnelDeps = {}): Promise<StartedShareTunnel> {
|
|
53
|
+
await ensureCloudflaredInstalled(deps)
|
|
54
|
+
const tunnel = (deps.createQuickTunnel ?? ((url) => Tunnel.quick(url)))(localUrl)
|
|
55
|
+
|
|
56
|
+
const publicUrl = await new Promise<string>((resolve, reject) => {
|
|
57
|
+
let settled = false
|
|
58
|
+
|
|
59
|
+
const cleanup = () => {
|
|
60
|
+
tunnel.off("url", handleUrl)
|
|
61
|
+
tunnel.off("error", handleError)
|
|
62
|
+
tunnel.off("exit", handleExit)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const settle = (callback: () => void) => {
|
|
66
|
+
if (settled) return
|
|
67
|
+
settled = true
|
|
68
|
+
cleanup()
|
|
69
|
+
callback()
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const handleUrl = (url: string) => {
|
|
73
|
+
settle(() => resolve(url))
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const handleError = (error: Error) => {
|
|
77
|
+
settle(() => reject(error))
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const handleExit = (code: number | null, signal: NodeJS.Signals | null) => {
|
|
81
|
+
settle(() => reject(new Error(`Cloudflare tunnel exited before a public URL was ready (code: ${String(code)}, signal: ${String(signal)})`)))
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
tunnel.once("url", handleUrl)
|
|
85
|
+
tunnel.once("error", handleError)
|
|
86
|
+
tunnel.once("exit", handleExit)
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
return {
|
|
90
|
+
publicUrl,
|
|
91
|
+
stop: () => {
|
|
92
|
+
tunnel.stop()
|
|
93
|
+
},
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export async function logShareDetails(
|
|
98
|
+
log: (message: string) => void,
|
|
99
|
+
publicUrl: string,
|
|
100
|
+
localUrl: string,
|
|
101
|
+
renderShareQrImpl: (url: string) => Promise<string> = renderTerminalQr,
|
|
102
|
+
) {
|
|
103
|
+
const qrCode = await renderShareQrImpl(publicUrl)
|
|
104
|
+
|
|
105
|
+
log("QR Code:")
|
|
106
|
+
log(qrCode.trimEnd())
|
|
107
|
+
log("")
|
|
108
|
+
log("Public URL:")
|
|
109
|
+
log(publicUrl)
|
|
110
|
+
log("")
|
|
111
|
+
log("Local URL:")
|
|
112
|
+
log(localUrl)
|
|
113
|
+
}
|