@richhardry/codor 0.10.0
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 +21 -0
- package/README.md +293 -0
- package/bin/codor.mjs +7 -0
- package/node_modules/@codor/adapter-antigravity/dist/adapter.d.ts +41 -0
- package/node_modules/@codor/adapter-antigravity/dist/adapter.d.ts.map +1 -0
- package/node_modules/@codor/adapter-antigravity/dist/adapter.js +275 -0
- package/node_modules/@codor/adapter-antigravity/dist/adapter.js.map +1 -0
- package/node_modules/@codor/adapter-antigravity/dist/index.d.ts +2 -0
- package/node_modules/@codor/adapter-antigravity/dist/index.d.ts.map +1 -0
- package/node_modules/@codor/adapter-antigravity/dist/index.js +2 -0
- package/node_modules/@codor/adapter-antigravity/dist/index.js.map +1 -0
- package/node_modules/@codor/adapter-antigravity/package.json +18 -0
- package/node_modules/@codor/adapter-claude-code/dist/adapter.d.ts +94 -0
- package/node_modules/@codor/adapter-claude-code/dist/adapter.d.ts.map +1 -0
- package/node_modules/@codor/adapter-claude-code/dist/adapter.js +785 -0
- package/node_modules/@codor/adapter-claude-code/dist/adapter.js.map +1 -0
- package/node_modules/@codor/adapter-claude-code/dist/index.d.ts +8 -0
- package/node_modules/@codor/adapter-claude-code/dist/index.d.ts.map +1 -0
- package/node_modules/@codor/adapter-claude-code/dist/index.js +5 -0
- package/node_modules/@codor/adapter-claude-code/dist/index.js.map +1 -0
- package/node_modules/@codor/adapter-claude-code/dist/limits-probe.d.ts +18 -0
- package/node_modules/@codor/adapter-claude-code/dist/limits-probe.d.ts.map +1 -0
- package/node_modules/@codor/adapter-claude-code/dist/limits-probe.js +67 -0
- package/node_modules/@codor/adapter-claude-code/dist/limits-probe.js.map +1 -0
- package/node_modules/@codor/adapter-claude-code/dist/peek.d.ts +3 -0
- package/node_modules/@codor/adapter-claude-code/dist/peek.d.ts.map +1 -0
- package/node_modules/@codor/adapter-claude-code/dist/peek.js +128 -0
- package/node_modules/@codor/adapter-claude-code/dist/peek.js.map +1 -0
- package/node_modules/@codor/adapter-claude-code/dist/query.d.ts +9 -0
- package/node_modules/@codor/adapter-claude-code/dist/query.d.ts.map +1 -0
- package/node_modules/@codor/adapter-claude-code/dist/query.js +7 -0
- package/node_modules/@codor/adapter-claude-code/dist/query.js.map +1 -0
- package/node_modules/@codor/adapter-claude-code/dist/translate.d.ts +40 -0
- package/node_modules/@codor/adapter-claude-code/dist/translate.d.ts.map +1 -0
- package/node_modules/@codor/adapter-claude-code/dist/translate.js +505 -0
- package/node_modules/@codor/adapter-claude-code/dist/translate.js.map +1 -0
- package/node_modules/@codor/adapter-claude-code/package.json +18 -0
- package/node_modules/@codor/adapter-codex/dist/adapter.d.ts +105 -0
- package/node_modules/@codor/adapter-codex/dist/adapter.d.ts.map +1 -0
- package/node_modules/@codor/adapter-codex/dist/adapter.js +625 -0
- package/node_modules/@codor/adapter-codex/dist/adapter.js.map +1 -0
- package/node_modules/@codor/adapter-codex/dist/app-server-transport.d.ts +39 -0
- package/node_modules/@codor/adapter-codex/dist/app-server-transport.d.ts.map +1 -0
- package/node_modules/@codor/adapter-codex/dist/app-server-transport.js +186 -0
- package/node_modules/@codor/adapter-codex/dist/app-server-transport.js.map +1 -0
- package/node_modules/@codor/adapter-codex/dist/index.d.ts +8 -0
- package/node_modules/@codor/adapter-codex/dist/index.d.ts.map +1 -0
- package/node_modules/@codor/adapter-codex/dist/index.js +5 -0
- package/node_modules/@codor/adapter-codex/dist/index.js.map +1 -0
- package/node_modules/@codor/adapter-codex/dist/limits-probe.d.ts +18 -0
- package/node_modules/@codor/adapter-codex/dist/limits-probe.d.ts.map +1 -0
- package/node_modules/@codor/adapter-codex/dist/limits-probe.js +87 -0
- package/node_modules/@codor/adapter-codex/dist/limits-probe.js.map +1 -0
- package/node_modules/@codor/adapter-codex/dist/peek.d.ts +3 -0
- package/node_modules/@codor/adapter-codex/dist/peek.d.ts.map +1 -0
- package/node_modules/@codor/adapter-codex/dist/peek.js +98 -0
- package/node_modules/@codor/adapter-codex/dist/peek.js.map +1 -0
- package/node_modules/@codor/adapter-codex/dist/translate.d.ts +17 -0
- package/node_modules/@codor/adapter-codex/dist/translate.d.ts.map +1 -0
- package/node_modules/@codor/adapter-codex/dist/translate.js +286 -0
- package/node_modules/@codor/adapter-codex/dist/translate.js.map +1 -0
- package/node_modules/@codor/adapter-codex/package.json +17 -0
- package/node_modules/@codor/adapter-copilot/dist/adapter.d.ts +40 -0
- package/node_modules/@codor/adapter-copilot/dist/adapter.d.ts.map +1 -0
- package/node_modules/@codor/adapter-copilot/dist/adapter.js +190 -0
- package/node_modules/@codor/adapter-copilot/dist/adapter.js.map +1 -0
- package/node_modules/@codor/adapter-copilot/dist/index.d.ts +4 -0
- package/node_modules/@codor/adapter-copilot/dist/index.d.ts.map +1 -0
- package/node_modules/@codor/adapter-copilot/dist/index.js +3 -0
- package/node_modules/@codor/adapter-copilot/dist/index.js.map +1 -0
- package/node_modules/@codor/adapter-copilot/dist/translate.d.ts +11 -0
- package/node_modules/@codor/adapter-copilot/dist/translate.d.ts.map +1 -0
- package/node_modules/@codor/adapter-copilot/dist/translate.js +184 -0
- package/node_modules/@codor/adapter-copilot/dist/translate.js.map +1 -0
- package/node_modules/@codor/adapter-copilot/package.json +18 -0
- package/node_modules/@codor/adapter-cursor/dist/adapter.d.ts +32 -0
- package/node_modules/@codor/adapter-cursor/dist/adapter.d.ts.map +1 -0
- package/node_modules/@codor/adapter-cursor/dist/adapter.js +179 -0
- package/node_modules/@codor/adapter-cursor/dist/adapter.js.map +1 -0
- package/node_modules/@codor/adapter-cursor/dist/index.d.ts +9 -0
- package/node_modules/@codor/adapter-cursor/dist/index.d.ts.map +1 -0
- package/node_modules/@codor/adapter-cursor/dist/index.js +8 -0
- package/node_modules/@codor/adapter-cursor/dist/index.js.map +1 -0
- package/node_modules/@codor/adapter-cursor/dist/translate.d.ts +15 -0
- package/node_modules/@codor/adapter-cursor/dist/translate.d.ts.map +1 -0
- package/node_modules/@codor/adapter-cursor/dist/translate.js +164 -0
- package/node_modules/@codor/adapter-cursor/dist/translate.js.map +1 -0
- package/node_modules/@codor/adapter-cursor/package.json +17 -0
- package/node_modules/@codor/adapter-gemini/dist/adapter.d.ts +36 -0
- package/node_modules/@codor/adapter-gemini/dist/adapter.d.ts.map +1 -0
- package/node_modules/@codor/adapter-gemini/dist/adapter.js +245 -0
- package/node_modules/@codor/adapter-gemini/dist/adapter.js.map +1 -0
- package/node_modules/@codor/adapter-gemini/dist/index.d.ts +4 -0
- package/node_modules/@codor/adapter-gemini/dist/index.d.ts.map +1 -0
- package/node_modules/@codor/adapter-gemini/dist/index.js +3 -0
- package/node_modules/@codor/adapter-gemini/dist/index.js.map +1 -0
- package/node_modules/@codor/adapter-gemini/dist/translate.d.ts +12 -0
- package/node_modules/@codor/adapter-gemini/dist/translate.d.ts.map +1 -0
- package/node_modules/@codor/adapter-gemini/dist/translate.js +123 -0
- package/node_modules/@codor/adapter-gemini/dist/translate.js.map +1 -0
- package/node_modules/@codor/adapter-gemini/package.json +18 -0
- package/node_modules/@codor/adapter-opencode/dist/adapter.d.ts +42 -0
- package/node_modules/@codor/adapter-opencode/dist/adapter.d.ts.map +1 -0
- package/node_modules/@codor/adapter-opencode/dist/adapter.js +240 -0
- package/node_modules/@codor/adapter-opencode/dist/adapter.js.map +1 -0
- package/node_modules/@codor/adapter-opencode/dist/index.d.ts +4 -0
- package/node_modules/@codor/adapter-opencode/dist/index.d.ts.map +1 -0
- package/node_modules/@codor/adapter-opencode/dist/index.js +3 -0
- package/node_modules/@codor/adapter-opencode/dist/index.js.map +1 -0
- package/node_modules/@codor/adapter-opencode/dist/translate.d.ts +12 -0
- package/node_modules/@codor/adapter-opencode/dist/translate.d.ts.map +1 -0
- package/node_modules/@codor/adapter-opencode/dist/translate.js +144 -0
- package/node_modules/@codor/adapter-opencode/dist/translate.js.map +1 -0
- package/node_modules/@codor/adapter-opencode/package.json +18 -0
- package/node_modules/@codor/cli/dist/artifact.d.ts +33 -0
- package/node_modules/@codor/cli/dist/artifact.d.ts.map +1 -0
- package/node_modules/@codor/cli/dist/artifact.js +183 -0
- package/node_modules/@codor/cli/dist/artifact.js.map +1 -0
- package/node_modules/@codor/cli/dist/attach.d.ts +25 -0
- package/node_modules/@codor/cli/dist/attach.d.ts.map +1 -0
- package/node_modules/@codor/cli/dist/attach.js +133 -0
- package/node_modules/@codor/cli/dist/attach.js.map +1 -0
- package/node_modules/@codor/cli/dist/connection.d.ts +21 -0
- package/node_modules/@codor/cli/dist/connection.d.ts.map +1 -0
- package/node_modules/@codor/cli/dist/connection.js +130 -0
- package/node_modules/@codor/cli/dist/connection.js.map +1 -0
- package/node_modules/@codor/cli/dist/detect.d.ts +14 -0
- package/node_modules/@codor/cli/dist/detect.d.ts.map +1 -0
- package/node_modules/@codor/cli/dist/detect.js +99 -0
- package/node_modules/@codor/cli/dist/detect.js.map +1 -0
- package/node_modules/@codor/cli/dist/index.d.ts +17 -0
- package/node_modules/@codor/cli/dist/index.d.ts.map +1 -0
- package/node_modules/@codor/cli/dist/index.js +24 -0
- package/node_modules/@codor/cli/dist/index.js.map +1 -0
- package/node_modules/@codor/cli/dist/mirror.d.ts +4 -0
- package/node_modules/@codor/cli/dist/mirror.d.ts.map +1 -0
- package/node_modules/@codor/cli/dist/mirror.js +93 -0
- package/node_modules/@codor/cli/dist/mirror.js.map +1 -0
- package/node_modules/@codor/cli/dist/program.d.ts +17 -0
- package/node_modules/@codor/cli/dist/program.d.ts.map +1 -0
- package/node_modules/@codor/cli/dist/program.js +1068 -0
- package/node_modules/@codor/cli/dist/program.js.map +1 -0
- package/node_modules/@codor/cli/dist/runtime-install.d.ts +61 -0
- package/node_modules/@codor/cli/dist/runtime-install.d.ts.map +1 -0
- package/node_modules/@codor/cli/dist/runtime-install.js +122 -0
- package/node_modules/@codor/cli/dist/runtime-install.js.map +1 -0
- package/node_modules/@codor/cli/dist/runtime-paths.d.ts +24 -0
- package/node_modules/@codor/cli/dist/runtime-paths.d.ts.map +1 -0
- package/node_modules/@codor/cli/dist/runtime-paths.js +69 -0
- package/node_modules/@codor/cli/dist/runtime-paths.js.map +1 -0
- package/node_modules/@codor/cli/dist/setup-flow.d.ts +69 -0
- package/node_modules/@codor/cli/dist/setup-flow.d.ts.map +1 -0
- package/node_modules/@codor/cli/dist/setup-flow.js +110 -0
- package/node_modules/@codor/cli/dist/setup-flow.js.map +1 -0
- package/node_modules/@codor/cli/dist/setup-session.d.ts +105 -0
- package/node_modules/@codor/cli/dist/setup-session.d.ts.map +1 -0
- package/node_modules/@codor/cli/dist/setup-session.js +346 -0
- package/node_modules/@codor/cli/dist/setup-session.js.map +1 -0
- package/node_modules/@codor/cli/dist/setup-ui.d.ts +93 -0
- package/node_modules/@codor/cli/dist/setup-ui.d.ts.map +1 -0
- package/node_modules/@codor/cli/dist/setup-ui.js +249 -0
- package/node_modules/@codor/cli/dist/setup-ui.js.map +1 -0
- package/node_modules/@codor/cli/dist/setup.d.ts +108 -0
- package/node_modules/@codor/cli/dist/setup.d.ts.map +1 -0
- package/node_modules/@codor/cli/dist/setup.js +878 -0
- package/node_modules/@codor/cli/dist/setup.js.map +1 -0
- package/node_modules/@codor/cli/dist/terminal-keys.d.ts +26 -0
- package/node_modules/@codor/cli/dist/terminal-keys.d.ts.map +1 -0
- package/node_modules/@codor/cli/dist/terminal-keys.js +70 -0
- package/node_modules/@codor/cli/dist/terminal-keys.js.map +1 -0
- package/node_modules/@codor/cli/dist/terminal-qr.d.ts +2 -0
- package/node_modules/@codor/cli/dist/terminal-qr.d.ts.map +1 -0
- package/node_modules/@codor/cli/dist/terminal-qr.js +30 -0
- package/node_modules/@codor/cli/dist/terminal-qr.js.map +1 -0
- package/node_modules/@codor/cli/dist/up.d.ts +52 -0
- package/node_modules/@codor/cli/dist/up.d.ts.map +1 -0
- package/node_modules/@codor/cli/dist/up.js +178 -0
- package/node_modules/@codor/cli/dist/up.js.map +1 -0
- package/node_modules/@codor/cli/package.json +24 -0
- package/node_modules/@codor/cli/packaging/systemd/codor.service +21 -0
- package/node_modules/@codor/cli/runtime/web/assets/LandingPage-BOmG5kSw.js +31 -0
- package/node_modules/@codor/cli/runtime/web/assets/LedgerPage-CecuTS8-.js +1 -0
- package/node_modules/@codor/cli/runtime/web/assets/NoChannels-CYiBXGQd.js +11 -0
- package/node_modules/@codor/cli/runtime/web/assets/PairingCodeInput-Cr6XDWx-.js +1 -0
- package/node_modules/@codor/cli/runtime/web/assets/PairingPage-CQBWNA2L.js +6 -0
- package/node_modules/@codor/cli/runtime/web/assets/SettingsPage-DGLbSXE7.js +16 -0
- package/node_modules/@codor/cli/runtime/web/assets/StartupUnavailable-DdQBHip_.js +1 -0
- package/node_modules/@codor/cli/runtime/web/assets/arrow-left-BOpjMuRi.js +6 -0
- package/node_modules/@codor/cli/runtime/web/assets/browser-BWQYNOsD.js +6 -0
- package/node_modules/@codor/cli/runtime/web/assets/graph-toBZHlC0.js +1 -0
- package/node_modules/@codor/cli/runtime/web/assets/index-Cxi54Xim.js +408 -0
- package/node_modules/@codor/cli/runtime/web/assets/index-DjwJakJC.css +1 -0
- package/node_modules/@codor/cli/runtime/web/assets/qr-DpynCwP9.js +8 -0
- package/node_modules/@codor/cli/runtime/web/assets/react-C8w-UNLI.js +32 -0
- package/node_modules/@codor/cli/runtime/web/assets/sodium-DtsvMzX9.js +1 -0
- package/node_modules/@codor/cli/runtime/web/codor-192.png +0 -0
- package/node_modules/@codor/cli/runtime/web/codor-512.png +0 -0
- package/node_modules/@codor/cli/runtime/web/codor-apple-touch-180.png +0 -0
- package/node_modules/@codor/cli/runtime/web/codor-favicon.svg +86 -0
- package/node_modules/@codor/cli/runtime/web/codor-mark.svg +86 -0
- package/node_modules/@codor/cli/runtime/web/codor-maskable-512.png +0 -0
- package/node_modules/@codor/cli/runtime/web/codor-og.png +0 -0
- package/node_modules/@codor/cli/runtime/web/fonts/BitcountPropSingle-latin.woff2 +0 -0
- package/node_modules/@codor/cli/runtime/web/fonts/Geist-Variable.woff2 +0 -0
- package/node_modules/@codor/cli/runtime/web/fonts/GeistMono-Variable.woff2 +0 -0
- package/node_modules/@codor/cli/runtime/web/fonts/OFL.txt +93 -0
- package/node_modules/@codor/cli/runtime/web/index.html +53 -0
- package/node_modules/@codor/cli/runtime/web/manifest.webmanifest +1 -0
- package/node_modules/@codor/cli/runtime/web/sw.js +2 -0
- package/node_modules/@codor/protocol/dist/adapter.d.ts +118 -0
- package/node_modules/@codor/protocol/dist/adapter.d.ts.map +1 -0
- package/node_modules/@codor/protocol/dist/adapter.js +15 -0
- package/node_modules/@codor/protocol/dist/adapter.js.map +1 -0
- package/node_modules/@codor/protocol/dist/body.d.ts +14 -0
- package/node_modules/@codor/protocol/dist/body.d.ts.map +1 -0
- package/node_modules/@codor/protocol/dist/body.js +50 -0
- package/node_modules/@codor/protocol/dist/body.js.map +1 -0
- package/node_modules/@codor/protocol/dist/changelog.d.ts +30 -0
- package/node_modules/@codor/protocol/dist/changelog.d.ts.map +1 -0
- package/node_modules/@codor/protocol/dist/changelog.js +19 -0
- package/node_modules/@codor/protocol/dist/changelog.js.map +1 -0
- package/node_modules/@codor/protocol/dist/default-recipient.d.ts +8 -0
- package/node_modules/@codor/protocol/dist/default-recipient.d.ts.map +1 -0
- package/node_modules/@codor/protocol/dist/default-recipient.js +19 -0
- package/node_modules/@codor/protocol/dist/default-recipient.js.map +1 -0
- package/node_modules/@codor/protocol/dist/delivery.d.ts +42 -0
- package/node_modules/@codor/protocol/dist/delivery.d.ts.map +1 -0
- package/node_modules/@codor/protocol/dist/delivery.js +44 -0
- package/node_modules/@codor/protocol/dist/delivery.js.map +1 -0
- package/node_modules/@codor/protocol/dist/events.d.ts +284 -0
- package/node_modules/@codor/protocol/dist/events.d.ts.map +1 -0
- package/node_modules/@codor/protocol/dist/events.js +150 -0
- package/node_modules/@codor/protocol/dist/events.js.map +1 -0
- package/node_modules/@codor/protocol/dist/ids.d.ts +19 -0
- package/node_modules/@codor/protocol/dist/ids.d.ts.map +1 -0
- package/node_modules/@codor/protocol/dist/ids.js +27 -0
- package/node_modules/@codor/protocol/dist/ids.js.map +1 -0
- package/node_modules/@codor/protocol/dist/index.d.ts +14 -0
- package/node_modules/@codor/protocol/dist/index.d.ts.map +1 -0
- package/node_modules/@codor/protocol/dist/index.js +14 -0
- package/node_modules/@codor/protocol/dist/index.js.map +1 -0
- package/node_modules/@codor/protocol/dist/interaction.d.ts +45 -0
- package/node_modules/@codor/protocol/dist/interaction.d.ts.map +1 -0
- package/node_modules/@codor/protocol/dist/interaction.js +33 -0
- package/node_modules/@codor/protocol/dist/interaction.js.map +1 -0
- package/node_modules/@codor/protocol/dist/member.d.ts +146 -0
- package/node_modules/@codor/protocol/dist/member.d.ts.map +1 -0
- package/node_modules/@codor/protocol/dist/member.js +169 -0
- package/node_modules/@codor/protocol/dist/member.js.map +1 -0
- package/node_modules/@codor/protocol/dist/message.d.ts +166 -0
- package/node_modules/@codor/protocol/dist/message.d.ts.map +1 -0
- package/node_modules/@codor/protocol/dist/message.js +95 -0
- package/node_modules/@codor/protocol/dist/message.js.map +1 -0
- package/node_modules/@codor/protocol/dist/observability.d.ts +80 -0
- package/node_modules/@codor/protocol/dist/observability.d.ts.map +1 -0
- package/node_modules/@codor/protocol/dist/observability.js +41 -0
- package/node_modules/@codor/protocol/dist/observability.js.map +1 -0
- package/node_modules/@codor/protocol/dist/room.d.ts +430 -0
- package/node_modules/@codor/protocol/dist/room.d.ts.map +1 -0
- package/node_modules/@codor/protocol/dist/room.js +142 -0
- package/node_modules/@codor/protocol/dist/room.js.map +1 -0
- package/node_modules/@codor/protocol/dist/ws.d.ts +1348 -0
- package/node_modules/@codor/protocol/dist/ws.d.ts.map +1 -0
- package/node_modules/@codor/protocol/dist/ws.js +282 -0
- package/node_modules/@codor/protocol/dist/ws.js.map +1 -0
- package/node_modules/@codor/protocol/package.json +17 -0
- package/node_modules/@codor/switchboard/dist/adapter-registry.d.ts +14 -0
- package/node_modules/@codor/switchboard/dist/adapter-registry.d.ts.map +1 -0
- package/node_modules/@codor/switchboard/dist/adapter-registry.js +175 -0
- package/node_modules/@codor/switchboard/dist/adapter-registry.js.map +1 -0
- package/node_modules/@codor/switchboard/dist/authorization.d.ts +13 -0
- package/node_modules/@codor/switchboard/dist/authorization.d.ts.map +1 -0
- package/node_modules/@codor/switchboard/dist/authorization.js +96 -0
- package/node_modules/@codor/switchboard/dist/authorization.js.map +1 -0
- package/node_modules/@codor/switchboard/dist/blobs.d.ts +17 -0
- package/node_modules/@codor/switchboard/dist/blobs.d.ts.map +1 -0
- package/node_modules/@codor/switchboard/dist/blobs.js +54 -0
- package/node_modules/@codor/switchboard/dist/blobs.js.map +1 -0
- package/node_modules/@codor/switchboard/dist/collaboration.d.ts +115 -0
- package/node_modules/@codor/switchboard/dist/collaboration.d.ts.map +1 -0
- package/node_modules/@codor/switchboard/dist/collaboration.js +123 -0
- package/node_modules/@codor/switchboard/dist/collaboration.js.map +1 -0
- package/node_modules/@codor/switchboard/dist/continuation.d.ts +33 -0
- package/node_modules/@codor/switchboard/dist/continuation.d.ts.map +1 -0
- package/node_modules/@codor/switchboard/dist/continuation.js +177 -0
- package/node_modules/@codor/switchboard/dist/continuation.js.map +1 -0
- package/node_modules/@codor/switchboard/dist/crypto/browser-sessions.d.ts +22 -0
- package/node_modules/@codor/switchboard/dist/crypto/browser-sessions.d.ts.map +1 -0
- package/node_modules/@codor/switchboard/dist/crypto/browser-sessions.js +92 -0
- package/node_modules/@codor/switchboard/dist/crypto/browser-sessions.js.map +1 -0
- package/node_modules/@codor/switchboard/dist/crypto/challenge.d.ts +37 -0
- package/node_modules/@codor/switchboard/dist/crypto/challenge.d.ts.map +1 -0
- package/node_modules/@codor/switchboard/dist/crypto/challenge.js +132 -0
- package/node_modules/@codor/switchboard/dist/crypto/challenge.js.map +1 -0
- package/node_modules/@codor/switchboard/dist/crypto/keys.d.ts +41 -0
- package/node_modules/@codor/switchboard/dist/crypto/keys.d.ts.map +1 -0
- package/node_modules/@codor/switchboard/dist/crypto/keys.js +145 -0
- package/node_modules/@codor/switchboard/dist/crypto/keys.js.map +1 -0
- package/node_modules/@codor/switchboard/dist/crypto/pairing.d.ts +53 -0
- package/node_modules/@codor/switchboard/dist/crypto/pairing.d.ts.map +1 -0
- package/node_modules/@codor/switchboard/dist/crypto/pairing.js +182 -0
- package/node_modules/@codor/switchboard/dist/crypto/pairing.js.map +1 -0
- package/node_modules/@codor/switchboard/dist/crypto/roomkeys.d.ts +30 -0
- package/node_modules/@codor/switchboard/dist/crypto/roomkeys.d.ts.map +1 -0
- package/node_modules/@codor/switchboard/dist/crypto/roomkeys.js +101 -0
- package/node_modules/@codor/switchboard/dist/crypto/roomkeys.js.map +1 -0
- package/node_modules/@codor/switchboard/dist/daemon.d.ts +545 -0
- package/node_modules/@codor/switchboard/dist/daemon.d.ts.map +1 -0
- package/node_modules/@codor/switchboard/dist/daemon.js +3907 -0
- package/node_modules/@codor/switchboard/dist/daemon.js.map +1 -0
- package/node_modules/@codor/switchboard/dist/fake-adapter.d.ts +103 -0
- package/node_modules/@codor/switchboard/dist/fake-adapter.d.ts.map +1 -0
- package/node_modules/@codor/switchboard/dist/fake-adapter.js +228 -0
- package/node_modules/@codor/switchboard/dist/fake-adapter.js.map +1 -0
- package/node_modules/@codor/switchboard/dist/index.d.ts +44 -0
- package/node_modules/@codor/switchboard/dist/index.d.ts.map +1 -0
- package/node_modules/@codor/switchboard/dist/index.js +24 -0
- package/node_modules/@codor/switchboard/dist/index.js.map +1 -0
- package/node_modules/@codor/switchboard/dist/ledger/resolve.d.ts +12 -0
- package/node_modules/@codor/switchboard/dist/ledger/resolve.d.ts.map +1 -0
- package/node_modules/@codor/switchboard/dist/ledger/resolve.js +17 -0
- package/node_modules/@codor/switchboard/dist/ledger/resolve.js.map +1 -0
- package/node_modules/@codor/switchboard/dist/ledger/vault.d.ts +47 -0
- package/node_modules/@codor/switchboard/dist/ledger/vault.d.ts.map +1 -0
- package/node_modules/@codor/switchboard/dist/ledger/vault.js +175 -0
- package/node_modules/@codor/switchboard/dist/ledger/vault.js.map +1 -0
- package/node_modules/@codor/switchboard/dist/ledger/watch.d.ts +73 -0
- package/node_modules/@codor/switchboard/dist/ledger/watch.d.ts.map +1 -0
- package/node_modules/@codor/switchboard/dist/ledger/watch.js +232 -0
- package/node_modules/@codor/switchboard/dist/ledger/watch.js.map +1 -0
- package/node_modules/@codor/switchboard/dist/local-dirs.d.ts +14 -0
- package/node_modules/@codor/switchboard/dist/local-dirs.d.ts.map +1 -0
- package/node_modules/@codor/switchboard/dist/local-dirs.js +40 -0
- package/node_modules/@codor/switchboard/dist/local-dirs.js.map +1 -0
- package/node_modules/@codor/switchboard/dist/local-socket.d.ts +3 -0
- package/node_modules/@codor/switchboard/dist/local-socket.d.ts.map +1 -0
- package/node_modules/@codor/switchboard/dist/local-socket.js +16 -0
- package/node_modules/@codor/switchboard/dist/local-socket.js.map +1 -0
- package/node_modules/@codor/switchboard/dist/pricing.d.ts +32 -0
- package/node_modules/@codor/switchboard/dist/pricing.d.ts.map +1 -0
- package/node_modules/@codor/switchboard/dist/pricing.js +64 -0
- package/node_modules/@codor/switchboard/dist/pricing.js.map +1 -0
- package/node_modules/@codor/switchboard/dist/process-liveness.d.ts +2 -0
- package/node_modules/@codor/switchboard/dist/process-liveness.d.ts.map +1 -0
- package/node_modules/@codor/switchboard/dist/process-liveness.js +9 -0
- package/node_modules/@codor/switchboard/dist/process-liveness.js.map +1 -0
- package/node_modules/@codor/switchboard/dist/push/producer.d.ts +54 -0
- package/node_modules/@codor/switchboard/dist/push/producer.d.ts.map +1 -0
- package/node_modules/@codor/switchboard/dist/push/producer.js +168 -0
- package/node_modules/@codor/switchboard/dist/push/producer.js.map +1 -0
- package/node_modules/@codor/switchboard/dist/push/subscriptions.d.ts +29 -0
- package/node_modules/@codor/switchboard/dist/push/subscriptions.d.ts.map +1 -0
- package/node_modules/@codor/switchboard/dist/push/subscriptions.js +83 -0
- package/node_modules/@codor/switchboard/dist/push/subscriptions.js.map +1 -0
- package/node_modules/@codor/switchboard/dist/redact.d.ts +11 -0
- package/node_modules/@codor/switchboard/dist/redact.d.ts.map +1 -0
- package/node_modules/@codor/switchboard/dist/redact.js +65 -0
- package/node_modules/@codor/switchboard/dist/redact.js.map +1 -0
- package/node_modules/@codor/switchboard/dist/residency.d.ts +110 -0
- package/node_modules/@codor/switchboard/dist/residency.d.ts.map +1 -0
- package/node_modules/@codor/switchboard/dist/residency.js +587 -0
- package/node_modules/@codor/switchboard/dist/residency.js.map +1 -0
- package/node_modules/@codor/switchboard/dist/router.d.ts +120 -0
- package/node_modules/@codor/switchboard/dist/router.d.ts.map +1 -0
- package/node_modules/@codor/switchboard/dist/router.js +198 -0
- package/node_modules/@codor/switchboard/dist/router.js.map +1 -0
- package/node_modules/@codor/switchboard/dist/server.d.ts +50 -0
- package/node_modules/@codor/switchboard/dist/server.d.ts.map +1 -0
- package/node_modules/@codor/switchboard/dist/server.js +1425 -0
- package/node_modules/@codor/switchboard/dist/server.js.map +1 -0
- package/node_modules/@codor/switchboard/dist/store.d.ts +534 -0
- package/node_modules/@codor/switchboard/dist/store.d.ts.map +1 -0
- package/node_modules/@codor/switchboard/dist/store.js +2625 -0
- package/node_modules/@codor/switchboard/dist/store.js.map +1 -0
- package/node_modules/@codor/switchboard/dist/transport/hyperswarm.d.ts +59 -0
- package/node_modules/@codor/switchboard/dist/transport/hyperswarm.d.ts.map +1 -0
- package/node_modules/@codor/switchboard/dist/transport/hyperswarm.js +183 -0
- package/node_modules/@codor/switchboard/dist/transport/hyperswarm.js.map +1 -0
- package/node_modules/@codor/switchboard/dist/transport/peer.d.ts +87 -0
- package/node_modules/@codor/switchboard/dist/transport/peer.d.ts.map +1 -0
- package/node_modules/@codor/switchboard/dist/transport/peer.js +316 -0
- package/node_modules/@codor/switchboard/dist/transport/peer.js.map +1 -0
- package/node_modules/@codor/switchboard/dist/working-directory.d.ts +2 -0
- package/node_modules/@codor/switchboard/dist/working-directory.d.ts.map +1 -0
- package/node_modules/@codor/switchboard/dist/working-directory.js +26 -0
- package/node_modules/@codor/switchboard/dist/working-directory.js.map +1 -0
- package/node_modules/@codor/switchboard/package.json +33 -0
- package/package.json +58 -0
|
@@ -0,0 +1,1068 @@
|
|
|
1
|
+
import { homedir } from 'node:os';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { MemberStatusResponseSchema, MessageSchema, RunSearchHitSchema, } from '@codor/protocol';
|
|
4
|
+
import { Command, Option } from 'commander';
|
|
5
|
+
import { addRemoteLedgerNote, CryptoVault, HyperswarmTransport, LedgerVault, pairingUrl, } from '@codor/switchboard';
|
|
6
|
+
import { nativeResumeCommand, superviseInteractiveAttach, } from './attach.js';
|
|
7
|
+
import { ProtocolClient } from './connection.js';
|
|
8
|
+
import { detectSession } from './detect.js';
|
|
9
|
+
import { parseMirrorHook } from './mirror.js';
|
|
10
|
+
import { runSetup } from './setup.js';
|
|
11
|
+
import { renderTerminalQr } from './terminal-qr.js';
|
|
12
|
+
import { parseLine, startOutpost, startCodor, waitForShutdown } from './up.js';
|
|
13
|
+
// harn:assume adapter-registry-sole-harness-source ref=registry-cli-composition
|
|
14
|
+
function collectAdapter(value, previous) {
|
|
15
|
+
return [...previous, value];
|
|
16
|
+
}
|
|
17
|
+
export function parseAdapterModules(values) {
|
|
18
|
+
const adapters = [];
|
|
19
|
+
const ids = new Set();
|
|
20
|
+
for (const value of values) {
|
|
21
|
+
const separator = value.indexOf('=');
|
|
22
|
+
if (separator < 1 || separator === value.length - 1) {
|
|
23
|
+
throw new Error('--adapter must be name=module');
|
|
24
|
+
}
|
|
25
|
+
const id = value.slice(0, separator).trim();
|
|
26
|
+
const module = value.slice(separator + 1).trim();
|
|
27
|
+
if (id === '' || module === '')
|
|
28
|
+
throw new Error('--adapter must be name=module');
|
|
29
|
+
if (ids.has(id))
|
|
30
|
+
throw new Error(`duplicate --adapter id '${id}'`);
|
|
31
|
+
ids.add(id);
|
|
32
|
+
adapters.push([id, module]);
|
|
33
|
+
}
|
|
34
|
+
return Object.fromEntries(adapters);
|
|
35
|
+
}
|
|
36
|
+
// harn:assume continuation-writer-follows-journaled-output-ownership ref=continuation-cli-format
|
|
37
|
+
/**
|
|
38
|
+
* A continuation is deliberately kind=run WITHOUT a lifecycle summary — the
|
|
39
|
+
* status, usage and cost belong to its root, not to it. Reading `message.run!`
|
|
40
|
+
* for one would throw and take down every tail subscriber the moment the writer
|
|
41
|
+
* starts emitting them, so identity comes from `run_parent_id` instead.
|
|
42
|
+
*
|
|
43
|
+
* It says only what is true of the row itself: its own permanent id, its
|
|
44
|
+
* author, and which run it continues. No status, no totals, no synthesized id —
|
|
45
|
+
* borrowing the root's would be a claim this row cannot make.
|
|
46
|
+
*/
|
|
47
|
+
const formatRunHeader = (message, author) => {
|
|
48
|
+
if (message.run === undefined && message.run_parent_id !== undefined) {
|
|
49
|
+
return `#${message.id} @${author} run continuation of #${message.run_parent_id}`;
|
|
50
|
+
}
|
|
51
|
+
const run = message.run;
|
|
52
|
+
const usage = run.usage;
|
|
53
|
+
const tokens = usage ? usage.input_tokens + usage.output_tokens : undefined;
|
|
54
|
+
return [
|
|
55
|
+
`#${message.id}`,
|
|
56
|
+
`@${author}`,
|
|
57
|
+
'run',
|
|
58
|
+
run.status,
|
|
59
|
+
tokens === undefined ? undefined : `${tokens}tk`,
|
|
60
|
+
usage?.cost_usd === undefined ? undefined : `$${usage.cost_usd.toFixed(2)}`,
|
|
61
|
+
].filter((part) => part !== undefined).join(' ');
|
|
62
|
+
};
|
|
63
|
+
// harn:end continuation-writer-follows-journaled-output-ownership
|
|
64
|
+
async function readStandardInput() {
|
|
65
|
+
const chunks = [];
|
|
66
|
+
for await (const chunk of process.stdin) {
|
|
67
|
+
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk)));
|
|
68
|
+
}
|
|
69
|
+
return Buffer.concat(chunks).toString('utf8');
|
|
70
|
+
}
|
|
71
|
+
const parsePositiveNumber = (value, label) => {
|
|
72
|
+
const parsed = Number(value);
|
|
73
|
+
if (!Number.isFinite(parsed) || parsed <= 0)
|
|
74
|
+
throw new Error(`${label} must be greater than zero`);
|
|
75
|
+
return parsed;
|
|
76
|
+
};
|
|
77
|
+
const parsePositiveInteger = (value, label) => {
|
|
78
|
+
const parsed = Number(value);
|
|
79
|
+
if (!Number.isSafeInteger(parsed) || parsed < 1)
|
|
80
|
+
throw new Error(`${label} must be a positive integer`);
|
|
81
|
+
return parsed;
|
|
82
|
+
};
|
|
83
|
+
const formatElapsed = (milliseconds) => {
|
|
84
|
+
const seconds = Math.max(0, Math.floor(milliseconds / 1_000));
|
|
85
|
+
const minutes = Math.floor(seconds / 60);
|
|
86
|
+
const remainder = seconds % 60;
|
|
87
|
+
return minutes > 0 ? `${minutes}m${String(remainder).padStart(2, '0')}s` : `${remainder}s`;
|
|
88
|
+
};
|
|
89
|
+
const formatDuration = (milliseconds) => {
|
|
90
|
+
if (milliseconds === undefined)
|
|
91
|
+
return '-';
|
|
92
|
+
return milliseconds >= 1_000 ? `${(milliseconds / 1_000).toFixed(1)}s` : `${Math.round(milliseconds)}ms`;
|
|
93
|
+
};
|
|
94
|
+
// harn:assume cli-waits-consume-only-matching-deliveries ref=collaboration-room-sync
|
|
95
|
+
async function syncRoom(client, room) {
|
|
96
|
+
let self;
|
|
97
|
+
const members = new Map();
|
|
98
|
+
const messages = new Map();
|
|
99
|
+
const deliveries = new Map();
|
|
100
|
+
client.send({ type: 'subscribe', room, since_seq: 0 });
|
|
101
|
+
for (;;) {
|
|
102
|
+
const frame = await client.next();
|
|
103
|
+
if (frame.type === 'error')
|
|
104
|
+
throw new Error(frame.message);
|
|
105
|
+
if (frame.type === 'self')
|
|
106
|
+
self = frame.member_id;
|
|
107
|
+
else if (frame.type === 'member')
|
|
108
|
+
members.set(frame.member.id, frame.member);
|
|
109
|
+
else if (frame.type === 'message')
|
|
110
|
+
messages.set(frame.message.id, frame.message);
|
|
111
|
+
else if (frame.type === 'inbox')
|
|
112
|
+
deliveries.set(frame.delivery.id, frame.delivery);
|
|
113
|
+
else if (frame.type === 'sync_complete') {
|
|
114
|
+
if (!self)
|
|
115
|
+
throw new Error('channel subscription did not identify the caller');
|
|
116
|
+
return { self, members, messages, deliveries };
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
const ownQueuedDeliveries = (snapshot) => [...snapshot.deliveries.values()]
|
|
121
|
+
.filter((delivery) => delivery.recipient === snapshot.self && delivery.state === 'queued')
|
|
122
|
+
.sort((left, right) => left.ts.localeCompare(right.ts));
|
|
123
|
+
async function consumeDelivery(client, room, delivery) {
|
|
124
|
+
client.send({
|
|
125
|
+
type: 'act',
|
|
126
|
+
room,
|
|
127
|
+
act: { act: 'consume_delivery', delivery_id: delivery.id },
|
|
128
|
+
});
|
|
129
|
+
for (;;) {
|
|
130
|
+
const frame = await client.next();
|
|
131
|
+
if (frame.type === 'error')
|
|
132
|
+
throw new Error(frame.message);
|
|
133
|
+
if (frame.type === 'consume_result' && frame.delivery.id === delivery.id)
|
|
134
|
+
return frame.message;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
async function setWait(client, room, self, reason, peers, untilTs) {
|
|
138
|
+
client.send({ type: 'act', room, act: { act: 'wait_begin', reason, peers, until_ts: untilTs } });
|
|
139
|
+
for (;;) {
|
|
140
|
+
const frame = await client.next();
|
|
141
|
+
if (frame.type === 'error')
|
|
142
|
+
throw new Error(frame.message);
|
|
143
|
+
if (frame.type === 'member' && frame.member.id === self && frame.member.waiting)
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
async function clearWait(client, room, self) {
|
|
148
|
+
client.send({ type: 'act', room, act: { act: 'wait_end' } });
|
|
149
|
+
for (;;) {
|
|
150
|
+
const frame = await client.next();
|
|
151
|
+
if (frame.type === 'error')
|
|
152
|
+
throw new Error(frame.message);
|
|
153
|
+
if (frame.type === 'member' && frame.member.id === self && !frame.member.waiting)
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
async function waitForOwnDelivery(client, room, initial, deadline, matches, peerFinishedSelf) {
|
|
158
|
+
let snapshot = initial;
|
|
159
|
+
const find = () => {
|
|
160
|
+
for (const delivery of ownQueuedDeliveries(snapshot)) {
|
|
161
|
+
const message = snapshot.messages.get(delivery.message_id);
|
|
162
|
+
if (message && matches(message))
|
|
163
|
+
return { kind: 'delivery', delivery, message };
|
|
164
|
+
}
|
|
165
|
+
return undefined;
|
|
166
|
+
};
|
|
167
|
+
for (;;) {
|
|
168
|
+
const existing = find();
|
|
169
|
+
if (existing)
|
|
170
|
+
return existing;
|
|
171
|
+
const remaining = deadline - Date.now();
|
|
172
|
+
if (remaining <= 0)
|
|
173
|
+
return undefined;
|
|
174
|
+
let frame;
|
|
175
|
+
try {
|
|
176
|
+
frame = await client.next(remaining);
|
|
177
|
+
}
|
|
178
|
+
catch (error) {
|
|
179
|
+
if (error instanceof Error && error.message.includes('timed out waiting for server frame')) {
|
|
180
|
+
return undefined;
|
|
181
|
+
}
|
|
182
|
+
throw error;
|
|
183
|
+
}
|
|
184
|
+
if (frame.type === 'error')
|
|
185
|
+
throw new Error(frame.message);
|
|
186
|
+
if (frame.type === 'message') {
|
|
187
|
+
snapshot.messages.set(frame.message.id, frame.message);
|
|
188
|
+
if (matches(frame.message))
|
|
189
|
+
snapshot = await syncRoom(client, room);
|
|
190
|
+
}
|
|
191
|
+
else if (frame.type === 'inbox') {
|
|
192
|
+
snapshot.deliveries.set(frame.delivery.id, frame.delivery);
|
|
193
|
+
}
|
|
194
|
+
else if (frame.type === 'member') {
|
|
195
|
+
snapshot.members.set(frame.member.id, frame.member);
|
|
196
|
+
if (frame.member.id === peerFinishedSelf && frame.member.waiting === undefined) {
|
|
197
|
+
return { kind: 'peer_finished' };
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
// harn:end same-round-terminal-peers-end-live-waits
|
|
203
|
+
// harn:end cli-waits-consume-only-matching-deliveries
|
|
204
|
+
// harn:assume cli-hook-inbox-is-silent-when-empty ref=hook-inbox-renderer
|
|
205
|
+
const formatInboxMessage = (message, author) => `#${message.id} from @${author}\n${message.body}`;
|
|
206
|
+
function renderHookInbox(messages) {
|
|
207
|
+
if (messages.length === 0)
|
|
208
|
+
return undefined;
|
|
209
|
+
return JSON.stringify({
|
|
210
|
+
hookSpecificOutput: {
|
|
211
|
+
hookEventName: 'PostToolUse',
|
|
212
|
+
additionalContext: `Codor inbox:\n${messages
|
|
213
|
+
.map(({ message, author }) => formatInboxMessage(message, author))
|
|
214
|
+
.join('\n\n')}`,
|
|
215
|
+
},
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
// harn:end cli-hook-inbox-is-silent-when-empty
|
|
219
|
+
export function createProgram(context = {}) {
|
|
220
|
+
const env = context.env ?? process.env;
|
|
221
|
+
const out = context.stdout ?? ((line) => process.stdout.write(`${line}\n`));
|
|
222
|
+
const err = context.stderr ?? ((line) => process.stderr.write(`${line}\n`));
|
|
223
|
+
const program = new Command();
|
|
224
|
+
// harn:assume human-facing-surfaces-call-rooms-channels ref=cli-channel-terminology
|
|
225
|
+
// harn:assume codor-runtime-identity-is-a-clean-break ref=cli-runtime-identity
|
|
226
|
+
program
|
|
227
|
+
.name('codor')
|
|
228
|
+
.description('Operate local-first multi-agent channels')
|
|
229
|
+
.option('--data-dir <path>', 'switchboard data directory', env.CODOR_DATA_DIR ?? join(homedir(), '.codor'))
|
|
230
|
+
.option('--url <url>', 'remote switchboard URL');
|
|
231
|
+
// harn:assume cli-help-never-renders-selected-bearer ref=redacted-token-option-default
|
|
232
|
+
program.addOption(new Option('--token <token>', 'remote bearer token')
|
|
233
|
+
.default(env.CODOR_MEMBER_TOKEN ?? env.CODOR_TOKEN, '<redacted>'));
|
|
234
|
+
// harn:end cli-help-never-renders-selected-bearer
|
|
235
|
+
// harn:end codor-runtime-identity-is-a-clean-break
|
|
236
|
+
const connectionOptions = () => {
|
|
237
|
+
const options = program.opts();
|
|
238
|
+
// harn:assume member-env-selects-narrow-cli-identity ref=member-connection-options
|
|
239
|
+
return {
|
|
240
|
+
dataDir: options.dataDir,
|
|
241
|
+
remoteUrl: options.url,
|
|
242
|
+
socketPath: options.url === undefined ? env.CODOR_SOCKET : undefined,
|
|
243
|
+
token: options.token,
|
|
244
|
+
};
|
|
245
|
+
// harn:end member-env-selects-narrow-cli-identity
|
|
246
|
+
};
|
|
247
|
+
const withClient = async (fn) => {
|
|
248
|
+
const client = await ProtocolClient.connect(connectionOptions());
|
|
249
|
+
try {
|
|
250
|
+
return await fn(client);
|
|
251
|
+
}
|
|
252
|
+
finally {
|
|
253
|
+
await client.close();
|
|
254
|
+
}
|
|
255
|
+
};
|
|
256
|
+
const channel = (options) => {
|
|
257
|
+
const room = options.channel ?? env.CODOR_CHANNEL;
|
|
258
|
+
if (!room)
|
|
259
|
+
throw new Error('--channel or CODOR_CHANNEL is required');
|
|
260
|
+
return room;
|
|
261
|
+
};
|
|
262
|
+
// harn:assume cli-observability-uses-scoped-rest ref=scoped-rest-client
|
|
263
|
+
const restUrl = (path) => {
|
|
264
|
+
const globals = program.opts();
|
|
265
|
+
const raw = globals.url ?? env.CODOR_URL ?? 'http://127.0.0.1:8137';
|
|
266
|
+
const base = new URL(raw);
|
|
267
|
+
if (base.protocol === 'ws:')
|
|
268
|
+
base.protocol = 'http:';
|
|
269
|
+
else if (base.protocol === 'wss:')
|
|
270
|
+
base.protocol = 'https:';
|
|
271
|
+
if (base.protocol !== 'http:' && base.protocol !== 'https:') {
|
|
272
|
+
throw new Error('--url must use http(s) or ws(s)');
|
|
273
|
+
}
|
|
274
|
+
return new URL(path, `${base.origin}/`);
|
|
275
|
+
};
|
|
276
|
+
const fetchJson = async (url) => {
|
|
277
|
+
const token = program.opts().token;
|
|
278
|
+
if (!token)
|
|
279
|
+
throw new Error('--token, CODOR_TOKEN, or CODOR_MEMBER_TOKEN is required');
|
|
280
|
+
const response = await fetch(url, { headers: { authorization: `Bearer ${token}` } });
|
|
281
|
+
const value = await response.json();
|
|
282
|
+
if (!response.ok) {
|
|
283
|
+
const detail = typeof value === 'object' && value !== null && 'error' in value
|
|
284
|
+
? String(value.error)
|
|
285
|
+
: `${response.status} ${response.statusText}`;
|
|
286
|
+
throw new Error(detail);
|
|
287
|
+
}
|
|
288
|
+
return value;
|
|
289
|
+
};
|
|
290
|
+
// harn:end cli-observability-uses-scoped-rest
|
|
291
|
+
const withCrypto = (fn) => {
|
|
292
|
+
const crypto = new CryptoVault(program.opts().dataDir);
|
|
293
|
+
try {
|
|
294
|
+
return fn(crypto);
|
|
295
|
+
}
|
|
296
|
+
finally {
|
|
297
|
+
crypto.close();
|
|
298
|
+
}
|
|
299
|
+
};
|
|
300
|
+
program
|
|
301
|
+
.command('up')
|
|
302
|
+
.description('start the switchboard in the foreground')
|
|
303
|
+
.option('--host <host>', 'HTTP bind host', '127.0.0.1')
|
|
304
|
+
.option('--port <port>', 'HTTP bind port', (value) => Number(value), 8137)
|
|
305
|
+
.option('--static-root <path>', 'built web client directory')
|
|
306
|
+
.option('--channel <id>', 'initial channel id', 'default')
|
|
307
|
+
.option('--channel-name <name>', 'initial channel name', 'Default')
|
|
308
|
+
.option('--owner <handle>', 'initial owner handle')
|
|
309
|
+
.option('--relay-url <url>', 'optional sealed push relay URL', env.CODOR_RELAY_URL)
|
|
310
|
+
.option('--push-vapid-public-key <key>', 'Web Push VAPID public key', env.CODOR_VAPID_PUBLIC_KEY)
|
|
311
|
+
.option('--join <line>', 'join a private home/outpost line as name:secret')
|
|
312
|
+
// harn:assume tailnet-auto-pairing-explicit-trust ref=trusted-tailnet-up-option
|
|
313
|
+
.option('--trust-tailscale-serve', 'trust Tailscale Serve identity headers for browser enrollment', env.CODOR_TRUST_TAILSCALE_SERVE === '1')
|
|
314
|
+
// harn:end tailnet-auto-pairing-explicit-trust
|
|
315
|
+
.option('--adapter <name=module>', 'trusted adapter module (repeatable)', collectAdapter, [])
|
|
316
|
+
.action(async (options) => {
|
|
317
|
+
const globals = program.opts();
|
|
318
|
+
const running = await startCodor({
|
|
319
|
+
dataDir: globals.dataDir,
|
|
320
|
+
token: globals.token ?? '',
|
|
321
|
+
host: options.host,
|
|
322
|
+
port: options.port,
|
|
323
|
+
staticRoot: options.staticRoot,
|
|
324
|
+
room: options.channel,
|
|
325
|
+
roomName: options.channelName,
|
|
326
|
+
owner: options.owner,
|
|
327
|
+
relayUrl: options.relayUrl,
|
|
328
|
+
pushVapidPublicKey: options.pushVapidPublicKey,
|
|
329
|
+
line: options.join ? parseLine(options.join) : undefined,
|
|
330
|
+
trustTailscaleServe: options.trustTailscaleServe,
|
|
331
|
+
adapters: parseAdapterModules(options.adapter),
|
|
332
|
+
});
|
|
333
|
+
out(`codor http://localhost:${running.server.port}`);
|
|
334
|
+
out(`socket ${running.server.socketPath}`);
|
|
335
|
+
await waitForShutdown(running.close);
|
|
336
|
+
});
|
|
337
|
+
program.command('channels').description('list channels').action(async () => {
|
|
338
|
+
await withClient(async (client) => {
|
|
339
|
+
client.send({ type: 'list_rooms' });
|
|
340
|
+
for (;;) {
|
|
341
|
+
const frame = await client.next();
|
|
342
|
+
if (frame.type === 'error')
|
|
343
|
+
throw new Error(frame.message);
|
|
344
|
+
if (frame.type !== 'rooms')
|
|
345
|
+
continue;
|
|
346
|
+
for (const room of frame.rooms)
|
|
347
|
+
out(`${room.id}\t${room.name}`);
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
350
|
+
});
|
|
351
|
+
});
|
|
352
|
+
program
|
|
353
|
+
.command('serve')
|
|
354
|
+
.description('host resident members for a remote channel home')
|
|
355
|
+
.requiredOption('--join <line>', 'line name and secret as name:secret')
|
|
356
|
+
.option('--adapter <name=module>', 'trusted adapter module (repeatable)', collectAdapter, [])
|
|
357
|
+
.action(async (options) => {
|
|
358
|
+
const running = await startOutpost({
|
|
359
|
+
dataDir: program.opts().dataDir,
|
|
360
|
+
line: parseLine(options.join),
|
|
361
|
+
adapters: parseAdapterModules(options.adapter),
|
|
362
|
+
});
|
|
363
|
+
out(`codor outpost ${running.crypto.keys.identity.device_id}`);
|
|
364
|
+
await waitForShutdown(running.close);
|
|
365
|
+
});
|
|
366
|
+
// harn:end adapter-registry-sole-harness-source
|
|
367
|
+
// harn:assume setup-unattended-mutation-requires-explicit-intent ref=setup-command-surface
|
|
368
|
+
// harn:assume public-install-is-the-primary-command-with-setup-alias ref=install-command-alias
|
|
369
|
+
program
|
|
370
|
+
.command('install')
|
|
371
|
+
.alias('setup')
|
|
372
|
+
.description('install and start the local switchboard service, then pair your first browser')
|
|
373
|
+
.option('--dry-run', 'print every action and generated service content without changing the host')
|
|
374
|
+
.option('--yes', 'approve every setup mutation for unattended use')
|
|
375
|
+
.addOption(new Option('--access <method>', 'browser access method').choices(['localhost', 'tailscale']))
|
|
376
|
+
.action(async (options) => {
|
|
377
|
+
await runSetup({
|
|
378
|
+
access: options.access,
|
|
379
|
+
dryRun: options.dryRun === true,
|
|
380
|
+
env,
|
|
381
|
+
out,
|
|
382
|
+
overrides: {
|
|
383
|
+
...context.setup,
|
|
384
|
+
renderQr: context.renderQr ?? context.setup?.renderQr,
|
|
385
|
+
},
|
|
386
|
+
yes: options.yes === true,
|
|
387
|
+
});
|
|
388
|
+
});
|
|
389
|
+
// harn:end public-install-is-the-primary-command-with-setup-alias
|
|
390
|
+
// harn:end setup-unattended-mutation-requires-explicit-intent
|
|
391
|
+
program
|
|
392
|
+
.command('spawn')
|
|
393
|
+
.requiredOption('-r, --channel <channel>', 'channel id')
|
|
394
|
+
.requiredOption('--harness <harness>', 'registered adapter id')
|
|
395
|
+
.requiredOption('--as <handle>', 'member handle')
|
|
396
|
+
.requiredOption('--cwd <path>', 'working directory')
|
|
397
|
+
.option('--policy <policy>', 'sandbox or permission policy')
|
|
398
|
+
.option('--model <model>', 'model override')
|
|
399
|
+
.action(async (options) => {
|
|
400
|
+
await withClient(async (client) => {
|
|
401
|
+
const existing = new Set();
|
|
402
|
+
client.send({ type: 'subscribe', room: options.channel, since_seq: 0 });
|
|
403
|
+
for (;;) {
|
|
404
|
+
const frame = await client.next();
|
|
405
|
+
if (frame.type === 'member')
|
|
406
|
+
existing.add(frame.member.id);
|
|
407
|
+
if (frame.type === 'error')
|
|
408
|
+
throw new Error(frame.message);
|
|
409
|
+
if (frame.type === 'sync_complete')
|
|
410
|
+
break;
|
|
411
|
+
}
|
|
412
|
+
client.send({
|
|
413
|
+
type: 'act',
|
|
414
|
+
room: options.channel,
|
|
415
|
+
act: {
|
|
416
|
+
act: 'spawn',
|
|
417
|
+
harness: options.harness,
|
|
418
|
+
handle: options.as,
|
|
419
|
+
cwd: options.cwd,
|
|
420
|
+
policy: options.policy,
|
|
421
|
+
model: options.model,
|
|
422
|
+
},
|
|
423
|
+
});
|
|
424
|
+
for (;;) {
|
|
425
|
+
const frame = await client.next();
|
|
426
|
+
if (frame.type === 'error')
|
|
427
|
+
throw new Error(frame.message);
|
|
428
|
+
if (frame.type === 'member' && !existing.has(frame.member.id) && frame.member.handle === options.as) {
|
|
429
|
+
out(`spawned @${frame.member.handle} ${frame.member.id}`);
|
|
430
|
+
return;
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
});
|
|
434
|
+
});
|
|
435
|
+
program
|
|
436
|
+
.command('post')
|
|
437
|
+
.option('-r, --channel <channel>', 'channel id; defaults to CODOR_CHANNEL')
|
|
438
|
+
.option('--wait', 'wait for the first direct reply from an addressed member')
|
|
439
|
+
.option('--timeout <seconds>', 'wait timeout in seconds', (value) => parsePositiveNumber(value, '--timeout'), 300)
|
|
440
|
+
.argument('<message>')
|
|
441
|
+
// harn:assume cli-waits-consume-only-matching-deliveries ref=post-wait-command
|
|
442
|
+
.action(async (message, options) => {
|
|
443
|
+
await withClient(async (client) => {
|
|
444
|
+
const room = channel(options);
|
|
445
|
+
const initial = await syncRoom(client, room);
|
|
446
|
+
const lastMessageId = Math.max(0, ...initial.messages.keys());
|
|
447
|
+
client.send({
|
|
448
|
+
type: 'post',
|
|
449
|
+
room,
|
|
450
|
+
body: message,
|
|
451
|
+
...(options.wait && { awaiting_reply: true }),
|
|
452
|
+
});
|
|
453
|
+
let posted;
|
|
454
|
+
for (;;) {
|
|
455
|
+
const frame = await client.next();
|
|
456
|
+
if (frame.type === 'error')
|
|
457
|
+
throw new Error(frame.message);
|
|
458
|
+
if (frame.type === 'message' &&
|
|
459
|
+
frame.message.id > lastMessageId &&
|
|
460
|
+
frame.message.author === initial.self &&
|
|
461
|
+
frame.message.body === message) {
|
|
462
|
+
posted = frame.message;
|
|
463
|
+
break;
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
out(`posted #${posted.id}`);
|
|
467
|
+
if (!options.wait)
|
|
468
|
+
return;
|
|
469
|
+
if (!env.CODOR_MEMBER_TOKEN)
|
|
470
|
+
throw new Error('post --wait requires CODOR_MEMBER_TOKEN');
|
|
471
|
+
const peers = [...new Set(posted.mentions.map((mention) => mention.member_id))]
|
|
472
|
+
.filter((id) => id !== initial.self && initial.members.get(id)?.removed_ts === undefined);
|
|
473
|
+
if (peers.length === 0)
|
|
474
|
+
throw new Error('post --wait requires at least one addressed member');
|
|
475
|
+
const deadline = Date.now() + options.timeout * 1_000;
|
|
476
|
+
let registered = false;
|
|
477
|
+
try {
|
|
478
|
+
await setWait(client, room, initial.self, 'reply', peers, new Date(deadline).toISOString());
|
|
479
|
+
registered = true;
|
|
480
|
+
const reply = await waitForOwnDelivery(client, room, await syncRoom(client, room), deadline, (candidate) => candidate.id > posted.id &&
|
|
481
|
+
peers.includes(candidate.author) &&
|
|
482
|
+
candidate.mentions.some((mention) => mention.member_id === initial.self), initial.self);
|
|
483
|
+
if (!reply) {
|
|
484
|
+
out(`TIMEOUT after ${String(options.timeout)}s`);
|
|
485
|
+
return;
|
|
486
|
+
}
|
|
487
|
+
if (reply.kind === 'peer_finished') {
|
|
488
|
+
registered = false;
|
|
489
|
+
out('peer finished; no direct reply');
|
|
490
|
+
return;
|
|
491
|
+
}
|
|
492
|
+
const consumed = await consumeDelivery(client, room, reply.delivery);
|
|
493
|
+
out(consumed.body);
|
|
494
|
+
}
|
|
495
|
+
finally {
|
|
496
|
+
if (registered)
|
|
497
|
+
await clearWait(client, room, initial.self);
|
|
498
|
+
}
|
|
499
|
+
});
|
|
500
|
+
});
|
|
501
|
+
// harn:end cli-waits-consume-only-matching-deliveries
|
|
502
|
+
program
|
|
503
|
+
.command('tail')
|
|
504
|
+
.option('-r, --channel <channel>', 'channel id; defaults to CODOR_CHANNEL')
|
|
505
|
+
.option('--once', 'print current history and exit')
|
|
506
|
+
.option('--follow', 'follow new channel messages')
|
|
507
|
+
.option('--until-mention <handle>', 'stop after consuming an own delivery directly mentioning handle')
|
|
508
|
+
.option('--until-any', 'stop after consuming any queued own delivery')
|
|
509
|
+
.option('--timeout <seconds>', 'until timeout in seconds', (value) => parsePositiveNumber(value, '--timeout'), 300)
|
|
510
|
+
// harn:assume cli-waits-consume-only-matching-deliveries ref=tail-wait-command
|
|
511
|
+
.action(async (options) => {
|
|
512
|
+
await withClient(async (client) => {
|
|
513
|
+
const room = channel(options);
|
|
514
|
+
const members = new Map();
|
|
515
|
+
const print = (frame) => {
|
|
516
|
+
if (frame.type === 'member')
|
|
517
|
+
members.set(frame.member.id, frame.member);
|
|
518
|
+
if (frame.type !== 'message')
|
|
519
|
+
return;
|
|
520
|
+
const author = members.get(frame.message.author)?.handle ?? frame.message.author;
|
|
521
|
+
// harn:assume continuation-writer-follows-journaled-output-ownership ref=continuation-cli-tail
|
|
522
|
+
if (frame.message.kind === 'run') {
|
|
523
|
+
// Roots and continuations both print here, each carrying only its
|
|
524
|
+
// own id and body. Nothing is aggregated or hidden: a continuation
|
|
525
|
+
// is a row a reader can scroll to, not a fragment of another one.
|
|
526
|
+
out(formatRunHeader(frame.message, author));
|
|
527
|
+
if (frame.message.body)
|
|
528
|
+
out(frame.message.body);
|
|
529
|
+
// The evidence line below reads `run?.error`, which is undefined for
|
|
530
|
+
// a continuation by construction — so a root's failure can never be
|
|
531
|
+
// reattributed to a row that merely continues it.
|
|
532
|
+
// harn:assume run-failure-evidence-is-surfaced ref=cli-run-error-evidence
|
|
533
|
+
if (frame.message.run?.error)
|
|
534
|
+
out(`error: ${frame.message.run.error}`);
|
|
535
|
+
// harn:end run-failure-evidence-is-surfaced
|
|
536
|
+
// harn:end continuation-writer-follows-journaled-output-ownership
|
|
537
|
+
}
|
|
538
|
+
else {
|
|
539
|
+
out(`#${frame.message.id} @${author} ${frame.message.kind}`);
|
|
540
|
+
if (frame.message.body)
|
|
541
|
+
out(frame.message.body);
|
|
542
|
+
}
|
|
543
|
+
};
|
|
544
|
+
const until = options.untilMention !== undefined || options.untilAny === true;
|
|
545
|
+
if (options.untilMention !== undefined && options.untilAny) {
|
|
546
|
+
throw new Error('--until-mention and --until-any are mutually exclusive');
|
|
547
|
+
}
|
|
548
|
+
if (until && !options.follow)
|
|
549
|
+
throw new Error('--until-* requires --follow');
|
|
550
|
+
if (until && options.once)
|
|
551
|
+
throw new Error('--once cannot be combined with --until-*');
|
|
552
|
+
if (until) {
|
|
553
|
+
if (!env.CODOR_MEMBER_TOKEN)
|
|
554
|
+
throw new Error('tail --until-* requires CODOR_MEMBER_TOKEN');
|
|
555
|
+
const snapshot = await syncRoom(client, room);
|
|
556
|
+
const self = snapshot.members.get(snapshot.self);
|
|
557
|
+
if (!self)
|
|
558
|
+
throw new Error('authenticated member is absent from the channel');
|
|
559
|
+
let mentionedId;
|
|
560
|
+
if (options.untilMention !== undefined) {
|
|
561
|
+
const wanted = options.untilMention.replace(/^@/, '');
|
|
562
|
+
const mentioned = [...snapshot.members.values()].find((member) => member.id === options.untilMention || member.handle === wanted);
|
|
563
|
+
if (!mentioned)
|
|
564
|
+
throw new Error(`no such member ${options.untilMention}`);
|
|
565
|
+
if (mentioned.id !== snapshot.self) {
|
|
566
|
+
throw new Error('--until-mention must name the authenticated member');
|
|
567
|
+
}
|
|
568
|
+
mentionedId = mentioned.id;
|
|
569
|
+
}
|
|
570
|
+
const peers = [...snapshot.members.values()]
|
|
571
|
+
.filter((member) => member.id !== snapshot.self && member.removed_ts === undefined && member.state !== 'dead')
|
|
572
|
+
.map((member) => member.id);
|
|
573
|
+
if (peers.length === 0)
|
|
574
|
+
throw new Error('tail --until-* requires at least one active peer');
|
|
575
|
+
const deadline = Date.now() + options.timeout * 1_000;
|
|
576
|
+
let registered = false;
|
|
577
|
+
try {
|
|
578
|
+
await setWait(client, room, snapshot.self, options.untilAny ? 'any' : 'mention', peers, new Date(deadline).toISOString());
|
|
579
|
+
registered = true;
|
|
580
|
+
const match = await waitForOwnDelivery(client, room, await syncRoom(client, room), deadline, (message) => mentionedId === undefined ||
|
|
581
|
+
message.mentions.some((mention) => mention.member_id === mentionedId));
|
|
582
|
+
if (!match) {
|
|
583
|
+
out(`TIMEOUT after ${String(options.timeout)}s`);
|
|
584
|
+
return;
|
|
585
|
+
}
|
|
586
|
+
const consumed = await consumeDelivery(client, room, match.delivery);
|
|
587
|
+
out(consumed.body);
|
|
588
|
+
return;
|
|
589
|
+
}
|
|
590
|
+
finally {
|
|
591
|
+
if (registered)
|
|
592
|
+
await clearWait(client, room, snapshot.self);
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
client.send({ type: 'subscribe', room, since_seq: 0 });
|
|
596
|
+
for (;;) {
|
|
597
|
+
const frame = await client.next(24 * 60 * 60 * 1_000);
|
|
598
|
+
if (frame.type === 'error')
|
|
599
|
+
throw new Error(frame.message);
|
|
600
|
+
print(frame);
|
|
601
|
+
if (options.once && frame.type === 'sync_complete')
|
|
602
|
+
return;
|
|
603
|
+
}
|
|
604
|
+
});
|
|
605
|
+
});
|
|
606
|
+
// harn:end cli-waits-consume-only-matching-deliveries
|
|
607
|
+
// harn:assume cli-hook-inbox-is-silent-when-empty ref=inbox-command
|
|
608
|
+
program
|
|
609
|
+
.command('inbox')
|
|
610
|
+
.option('-r, --channel <channel>', 'channel id; defaults to CODOR_CHANNEL')
|
|
611
|
+
.option('--new', 'show queued deliveries not yet consumed')
|
|
612
|
+
.option('--consume', 'consume every printed delivery')
|
|
613
|
+
.option('--format <format>', 'text or hook', 'text')
|
|
614
|
+
.action(async (options) => {
|
|
615
|
+
if (!options.new)
|
|
616
|
+
throw new Error('inbox currently requires --new');
|
|
617
|
+
if (!env.CODOR_MEMBER_TOKEN)
|
|
618
|
+
throw new Error('inbox requires CODOR_MEMBER_TOKEN');
|
|
619
|
+
if (options.format !== 'text' && options.format !== 'hook') {
|
|
620
|
+
throw new Error('--format must be text or hook');
|
|
621
|
+
}
|
|
622
|
+
await withClient(async (client) => {
|
|
623
|
+
const room = channel(options);
|
|
624
|
+
const snapshot = await syncRoom(client, room);
|
|
625
|
+
const rendered = [];
|
|
626
|
+
for (const delivery of ownQueuedDeliveries(snapshot)) {
|
|
627
|
+
const message = options.consume
|
|
628
|
+
? await consumeDelivery(client, room, delivery)
|
|
629
|
+
: snapshot.messages.get(delivery.message_id);
|
|
630
|
+
if (!message)
|
|
631
|
+
continue;
|
|
632
|
+
rendered.push({
|
|
633
|
+
message,
|
|
634
|
+
author: snapshot.members.get(message.author)?.handle ?? message.author,
|
|
635
|
+
});
|
|
636
|
+
}
|
|
637
|
+
if (options.format === 'hook') {
|
|
638
|
+
const hook = renderHookInbox(rendered);
|
|
639
|
+
if (hook !== undefined)
|
|
640
|
+
out(hook);
|
|
641
|
+
return;
|
|
642
|
+
}
|
|
643
|
+
for (const item of rendered)
|
|
644
|
+
out(formatInboxMessage(item.message, item.author));
|
|
645
|
+
});
|
|
646
|
+
});
|
|
647
|
+
// harn:end cli-hook-inbox-is-silent-when-empty
|
|
648
|
+
// harn:assume cli-observability-uses-scoped-rest ref=status-command
|
|
649
|
+
program
|
|
650
|
+
.command('status')
|
|
651
|
+
.argument('<member>')
|
|
652
|
+
.option('-r, --channel <channel>', 'channel id; defaults to CODOR_CHANNEL')
|
|
653
|
+
.action(async (memberRef, options) => {
|
|
654
|
+
await withClient(async (client) => {
|
|
655
|
+
const room = channel(options);
|
|
656
|
+
const snapshot = await syncRoom(client, room);
|
|
657
|
+
const wanted = memberRef.replace(/^@/, '');
|
|
658
|
+
const member = [...snapshot.members.values()].find((candidate) => candidate.id === memberRef || candidate.handle === wanted);
|
|
659
|
+
if (!member)
|
|
660
|
+
throw new Error(`no such member ${memberRef}`);
|
|
661
|
+
const url = restUrl(`/api/rooms/${encodeURIComponent(room)}/members/${encodeURIComponent(member.id)}/status`);
|
|
662
|
+
const status = MemberStatusResponseSchema.parse(await fetchJson(url));
|
|
663
|
+
const running = status.current_run ? ` (${formatElapsed(status.current_run.elapsed_ms)})` : '';
|
|
664
|
+
const waiting = status.member.waiting
|
|
665
|
+
? `waiting for ${status.member.waiting.peers.map((peer) => `@${peer}`).join(', ')}`
|
|
666
|
+
: 'not waiting';
|
|
667
|
+
out(`@${status.member.handle} - ${status.member.state}${running}, ${waiting}`);
|
|
668
|
+
status.recent.forEach((item, index) => {
|
|
669
|
+
const clock = new Date(item.ts).toISOString().slice(11, 19);
|
|
670
|
+
out(` ${String(index + 1)}. ${item.kind} ${item.title} ${item.status ?? '-'} ` +
|
|
671
|
+
`${formatDuration(item.duration_ms)} ${clock}`);
|
|
672
|
+
});
|
|
673
|
+
});
|
|
674
|
+
});
|
|
675
|
+
// harn:end cli-observability-uses-scoped-rest
|
|
676
|
+
// harn:assume cli-observability-uses-scoped-rest ref=search-command
|
|
677
|
+
program
|
|
678
|
+
.command('search')
|
|
679
|
+
.argument('<query>')
|
|
680
|
+
.option('-r, --channel <channel>', 'channel id; defaults to CODOR_CHANNEL')
|
|
681
|
+
.option('--runs', 'include bounded projected run evidence')
|
|
682
|
+
.option('--limit <count>', 'result/run scan limit', (value) => parsePositiveInteger(value, '--limit'))
|
|
683
|
+
.action(async (query, options) => {
|
|
684
|
+
await withClient(async (client) => {
|
|
685
|
+
const room = channel(options);
|
|
686
|
+
const snapshot = await syncRoom(client, room);
|
|
687
|
+
const url = restUrl(`/api/rooms/${encodeURIComponent(room)}/search`);
|
|
688
|
+
url.searchParams.set('q', query);
|
|
689
|
+
if (options.runs)
|
|
690
|
+
url.searchParams.set('include', 'runs');
|
|
691
|
+
if (options.limit !== undefined)
|
|
692
|
+
url.searchParams.set('limit', String(options.limit));
|
|
693
|
+
const raw = await fetchJson(url);
|
|
694
|
+
if (typeof raw !== 'object' || raw === null || !('messages' in raw) || !Array.isArray(raw.messages)) {
|
|
695
|
+
throw new Error('invalid search response');
|
|
696
|
+
}
|
|
697
|
+
const messages = raw.messages.map((message) => MessageSchema.parse(message));
|
|
698
|
+
const runs = 'runs' in raw && Array.isArray(raw.runs)
|
|
699
|
+
? raw.runs.map((hit) => RunSearchHitSchema.parse(hit))
|
|
700
|
+
: [];
|
|
701
|
+
for (const message of messages) {
|
|
702
|
+
const author = snapshot.members.get(message.author)?.handle ?? message.author;
|
|
703
|
+
out(`#${message.id} @${author} ${message.kind} ${message.body}`);
|
|
704
|
+
}
|
|
705
|
+
for (const hit of runs) {
|
|
706
|
+
out(`#${hit.message_id}:${hit.item_index} ${hit.kind} ${hit.excerpt}`);
|
|
707
|
+
}
|
|
708
|
+
});
|
|
709
|
+
});
|
|
710
|
+
// harn:end cli-observability-uses-scoped-rest
|
|
711
|
+
program
|
|
712
|
+
.command('members')
|
|
713
|
+
.requiredOption('-r, --channel <channel>', 'channel id')
|
|
714
|
+
.action(async (options) => {
|
|
715
|
+
await withClient(async (client) => {
|
|
716
|
+
const members = [];
|
|
717
|
+
client.send({ type: 'subscribe', room: options.channel, since_seq: 0 });
|
|
718
|
+
for (;;) {
|
|
719
|
+
const frame = await client.next();
|
|
720
|
+
if (frame.type === 'member')
|
|
721
|
+
members.push(frame.member);
|
|
722
|
+
if (frame.type === 'error')
|
|
723
|
+
throw new Error(frame.message);
|
|
724
|
+
if (frame.type !== 'sync_complete')
|
|
725
|
+
continue;
|
|
726
|
+
for (const member of members) {
|
|
727
|
+
out(`@${member.handle}\t${member.state ?? member.kind}\t${member.harness ?? '-'}`);
|
|
728
|
+
}
|
|
729
|
+
return;
|
|
730
|
+
}
|
|
731
|
+
});
|
|
732
|
+
});
|
|
733
|
+
program
|
|
734
|
+
.command('join')
|
|
735
|
+
.argument('<channel>')
|
|
736
|
+
.requiredOption('--as <handle>', 'channel member handle')
|
|
737
|
+
.option('--harness <harness>', 'claude-code or codex')
|
|
738
|
+
.option('--session <id>', 'native session id')
|
|
739
|
+
.option('--cwd <path>', 'session working directory')
|
|
740
|
+
.option('--policy <policy>', 'session policy label')
|
|
741
|
+
.action(async (channel, options) => {
|
|
742
|
+
const detected = detectSession({
|
|
743
|
+
harness: options.harness,
|
|
744
|
+
session: options.session,
|
|
745
|
+
cwd: options.cwd,
|
|
746
|
+
env,
|
|
747
|
+
});
|
|
748
|
+
await withClient(async (client) => {
|
|
749
|
+
const existing = new Set();
|
|
750
|
+
client.send({ type: 'subscribe', room: channel, since_seq: 0 });
|
|
751
|
+
for (;;) {
|
|
752
|
+
const frame = await client.next();
|
|
753
|
+
if (frame.type === 'member')
|
|
754
|
+
existing.add(frame.member.id);
|
|
755
|
+
if (frame.type === 'error')
|
|
756
|
+
throw new Error(frame.message);
|
|
757
|
+
if (frame.type === 'sync_complete')
|
|
758
|
+
break;
|
|
759
|
+
}
|
|
760
|
+
client.send({
|
|
761
|
+
type: 'act',
|
|
762
|
+
room: channel,
|
|
763
|
+
act: {
|
|
764
|
+
act: 'join',
|
|
765
|
+
harness: detected.harness,
|
|
766
|
+
handle: options.as,
|
|
767
|
+
session_ref: detected.session_ref,
|
|
768
|
+
cwd: detected.cwd,
|
|
769
|
+
policy: options.policy,
|
|
770
|
+
},
|
|
771
|
+
});
|
|
772
|
+
for (;;) {
|
|
773
|
+
const frame = await client.next();
|
|
774
|
+
if (frame.type === 'error')
|
|
775
|
+
throw new Error(frame.message);
|
|
776
|
+
if (frame.type === 'member' &&
|
|
777
|
+
!existing.has(frame.member.id) &&
|
|
778
|
+
frame.member.handle === options.as &&
|
|
779
|
+
frame.member.custody === 'mirrored') {
|
|
780
|
+
out(`joined @${frame.member.handle} ${frame.member.id} (${detected.harness})`);
|
|
781
|
+
return;
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
});
|
|
785
|
+
});
|
|
786
|
+
program
|
|
787
|
+
.command('adopt')
|
|
788
|
+
.argument('<member>')
|
|
789
|
+
.requiredOption('-r, --channel <channel>', 'channel id')
|
|
790
|
+
.action(async (memberRef, options) => {
|
|
791
|
+
await withClient(async (client) => {
|
|
792
|
+
let member;
|
|
793
|
+
client.send({ type: 'subscribe', room: options.channel, since_seq: 0 });
|
|
794
|
+
for (;;) {
|
|
795
|
+
const frame = await client.next();
|
|
796
|
+
if (frame.type === 'member' &&
|
|
797
|
+
(frame.member.id === memberRef || frame.member.handle === memberRef.replace(/^@/, ''))) {
|
|
798
|
+
member = frame.member;
|
|
799
|
+
}
|
|
800
|
+
if (frame.type === 'error')
|
|
801
|
+
throw new Error(frame.message);
|
|
802
|
+
if (frame.type === 'sync_complete')
|
|
803
|
+
break;
|
|
804
|
+
}
|
|
805
|
+
if (!member)
|
|
806
|
+
throw new Error(`no such member ${memberRef}`);
|
|
807
|
+
client.send({ type: 'act', room: options.channel, act: { act: 'adopt', member_id: member.id } });
|
|
808
|
+
for (;;) {
|
|
809
|
+
const frame = await client.next();
|
|
810
|
+
if (frame.type === 'error')
|
|
811
|
+
throw new Error(frame.message);
|
|
812
|
+
if (frame.type === 'member' &&
|
|
813
|
+
frame.member.id === member.id &&
|
|
814
|
+
frame.member.custody === 'owned') {
|
|
815
|
+
out(`adopted @${frame.member.handle}`);
|
|
816
|
+
return;
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
});
|
|
820
|
+
});
|
|
821
|
+
program
|
|
822
|
+
.command('mirror-hook', { hidden: true })
|
|
823
|
+
.argument('<source>', 'claude or codex')
|
|
824
|
+
.argument('[payload]', 'hook JSON; Claude hooks default to stdin')
|
|
825
|
+
.action(async (source, payload) => {
|
|
826
|
+
if (source !== 'claude' && source !== 'codex')
|
|
827
|
+
throw new Error(`unsupported mirror source '${source}'`);
|
|
828
|
+
const raw = payload ?? (await readStandardInput());
|
|
829
|
+
const frame = parseMirrorHook(source, raw, env);
|
|
830
|
+
await withClient(async (client) => {
|
|
831
|
+
client.send(frame);
|
|
832
|
+
for (;;) {
|
|
833
|
+
const response = await client.next();
|
|
834
|
+
if (response.type === 'error')
|
|
835
|
+
throw new Error(response.message);
|
|
836
|
+
if (response.type === 'mirror_ack')
|
|
837
|
+
return;
|
|
838
|
+
}
|
|
839
|
+
});
|
|
840
|
+
});
|
|
841
|
+
// harn:assume cli-member-recovery-is-actionable ref=cli-revive-and-attach-surface
|
|
842
|
+
program
|
|
843
|
+
.command('attach')
|
|
844
|
+
.argument('<member>')
|
|
845
|
+
.option('-r, --channel <channel>', 'channel id; omitted searches all channels')
|
|
846
|
+
.action(async (memberRef, options) => {
|
|
847
|
+
await withClient(async (client) => {
|
|
848
|
+
const wanted = memberRef.replace(/^@/, '');
|
|
849
|
+
let rooms = options.channel ? [options.channel] : undefined;
|
|
850
|
+
if (!rooms) {
|
|
851
|
+
client.send({ type: 'list_rooms' });
|
|
852
|
+
for (;;) {
|
|
853
|
+
const frame = await client.next();
|
|
854
|
+
if (frame.type === 'error')
|
|
855
|
+
throw new Error(frame.message);
|
|
856
|
+
if (frame.type === 'rooms') {
|
|
857
|
+
rooms = frame.rooms.map((room) => room.id);
|
|
858
|
+
break;
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
const matches = [];
|
|
863
|
+
for (const room of rooms) {
|
|
864
|
+
client.send({ type: 'subscribe', room, since_seq: 0 });
|
|
865
|
+
for (;;) {
|
|
866
|
+
const frame = await client.next();
|
|
867
|
+
if (frame.type === 'error')
|
|
868
|
+
throw new Error(frame.message);
|
|
869
|
+
if (frame.type === 'member' &&
|
|
870
|
+
frame.member.removed_ts === undefined &&
|
|
871
|
+
(frame.member.id === memberRef || frame.member.handle === wanted)) {
|
|
872
|
+
matches.push({ room, member: frame.member });
|
|
873
|
+
}
|
|
874
|
+
if (frame.type === 'sync_complete')
|
|
875
|
+
break;
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
if (matches.length === 0)
|
|
879
|
+
throw new Error(`no such member ${memberRef}`);
|
|
880
|
+
if (matches.length > 1) {
|
|
881
|
+
const candidates = matches
|
|
882
|
+
.map(({ room, member }) => `${room} (${member.state ?? member.kind})`)
|
|
883
|
+
.sort()
|
|
884
|
+
.join(', ');
|
|
885
|
+
throw new Error(`member ${memberRef} is ambiguous: ${candidates}; pass --channel <channel-id>`);
|
|
886
|
+
}
|
|
887
|
+
const match = matches[0];
|
|
888
|
+
client.send({
|
|
889
|
+
type: 'act',
|
|
890
|
+
room: match.room,
|
|
891
|
+
act: { act: 'attach_acquire', member_id: match.member.id, cli_pid: process.pid },
|
|
892
|
+
});
|
|
893
|
+
let acquired;
|
|
894
|
+
for (;;) {
|
|
895
|
+
const frame = await client.next(24 * 60 * 60 * 1_000);
|
|
896
|
+
if (frame.type === 'error')
|
|
897
|
+
throw new Error(frame.message);
|
|
898
|
+
if (frame.type === 'attach_lease' &&
|
|
899
|
+
frame.status === 'acquired' &&
|
|
900
|
+
frame.member.id === match.member.id &&
|
|
901
|
+
frame.lease) {
|
|
902
|
+
acquired = { member: frame.member, lease: frame.lease };
|
|
903
|
+
break;
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
out(`attaching @${acquired.member.handle} (${acquired.member.harness})`);
|
|
907
|
+
const result = await superviseInteractiveAttach({
|
|
908
|
+
client,
|
|
909
|
+
room: match.room,
|
|
910
|
+
member: acquired.member,
|
|
911
|
+
lease: acquired.lease,
|
|
912
|
+
env,
|
|
913
|
+
commandResolver: context.interactiveCommand ?? nativeResumeCommand,
|
|
914
|
+
spawnChild: context.spawnInteractive,
|
|
915
|
+
heartbeatMs: context.attachHeartbeatMs,
|
|
916
|
+
});
|
|
917
|
+
if (result.status === 'completed')
|
|
918
|
+
out(`re-adopted @${acquired.member.handle}`);
|
|
919
|
+
else
|
|
920
|
+
out(`@${acquired.member.handle} custody remains uncertain until its process group exits`);
|
|
921
|
+
});
|
|
922
|
+
});
|
|
923
|
+
program
|
|
924
|
+
.command('revive')
|
|
925
|
+
.description('revive a dead agent from its persisted native session')
|
|
926
|
+
.argument('<member>')
|
|
927
|
+
.requiredOption('-r, --channel <channel>', 'channel id')
|
|
928
|
+
.action(async (memberRef, options) => {
|
|
929
|
+
await withClient(async (client) => {
|
|
930
|
+
const wanted = memberRef.replace(/^@/, '');
|
|
931
|
+
let member;
|
|
932
|
+
client.send({ type: 'subscribe', room: options.channel, since_seq: 0 });
|
|
933
|
+
for (;;) {
|
|
934
|
+
const frame = await client.next();
|
|
935
|
+
if (frame.type === 'member' &&
|
|
936
|
+
frame.member.removed_ts === undefined &&
|
|
937
|
+
(frame.member.id === memberRef || frame.member.handle === wanted)) {
|
|
938
|
+
member = frame.member;
|
|
939
|
+
}
|
|
940
|
+
if (frame.type === 'error')
|
|
941
|
+
throw new Error(frame.message);
|
|
942
|
+
if (frame.type === 'sync_complete')
|
|
943
|
+
break;
|
|
944
|
+
}
|
|
945
|
+
if (!member)
|
|
946
|
+
throw new Error(`no such member ${memberRef}`);
|
|
947
|
+
client.send({ type: 'act', room: options.channel, act: { act: 'revive', member_id: member.id } });
|
|
948
|
+
for (;;) {
|
|
949
|
+
const frame = await client.next();
|
|
950
|
+
if (frame.type === 'error')
|
|
951
|
+
throw new Error(frame.message);
|
|
952
|
+
if (frame.type === 'member' && frame.member.id === member.id && frame.member.state !== 'dead') {
|
|
953
|
+
out(`revived @${frame.member.handle}`);
|
|
954
|
+
return;
|
|
955
|
+
}
|
|
956
|
+
}
|
|
957
|
+
});
|
|
958
|
+
});
|
|
959
|
+
// harn:end cli-member-recovery-is-actionable
|
|
960
|
+
// harn:assume terminal-pairing-qr-matches-plain-url ref=pair-qr-command
|
|
961
|
+
program
|
|
962
|
+
.command('pair')
|
|
963
|
+
.description('create a ten-minute browser or peer pairing link')
|
|
964
|
+
.option('--endpoint <url>', 'switchboard browser endpoint', 'http://127.0.0.1:8137')
|
|
965
|
+
.option('--no-qr', 'print the plain pairing URL without a terminal QR')
|
|
966
|
+
.action((options) => {
|
|
967
|
+
withCrypto((crypto) => {
|
|
968
|
+
const offer = crypto.pairing.issue(options.endpoint);
|
|
969
|
+
const url = pairingUrl(offer);
|
|
970
|
+
if (options.qr)
|
|
971
|
+
out((context.renderQr ?? renderTerminalQr)(url));
|
|
972
|
+
out(url);
|
|
973
|
+
// harn:assume pairing-code-enrollment-surfaces ref=pair-code-command
|
|
974
|
+
out(`code: ${offer.pairing_code}`);
|
|
975
|
+
// harn:end pairing-code-enrollment-surfaces
|
|
976
|
+
out(`expires ${offer.expires_at}`);
|
|
977
|
+
});
|
|
978
|
+
});
|
|
979
|
+
// harn:end terminal-pairing-qr-matches-plain-url
|
|
980
|
+
program.command('peers').description('list enrolled devices and switchboards').action(() => {
|
|
981
|
+
withCrypto((crypto) => {
|
|
982
|
+
for (const peer of crypto.keys.listPeers()) {
|
|
983
|
+
out(`${peer.device_id}\t${peer.kind}\t${peer.label ?? '-'}`);
|
|
984
|
+
}
|
|
985
|
+
});
|
|
986
|
+
});
|
|
987
|
+
program
|
|
988
|
+
.command('revoke')
|
|
989
|
+
.description('revoke a device or switchboard and rotate channel keys')
|
|
990
|
+
.argument('<peer>', 'device id or label')
|
|
991
|
+
.action((peer) => {
|
|
992
|
+
withCrypto((crypto) => {
|
|
993
|
+
const revoked = crypto.revokePeer(peer);
|
|
994
|
+
out(`revoked ${revoked.device_id}`);
|
|
995
|
+
});
|
|
996
|
+
});
|
|
997
|
+
const ledger = program.command('ledger').description('manage channel shared-memory notes');
|
|
998
|
+
ledger
|
|
999
|
+
.command('init')
|
|
1000
|
+
.requiredOption('-r, --channel <channel>', 'channel id')
|
|
1001
|
+
.action((options) => {
|
|
1002
|
+
const vault = new LedgerVault(program.opts().dataDir, options.channel);
|
|
1003
|
+
vault.bootstrap();
|
|
1004
|
+
out(vault.root);
|
|
1005
|
+
});
|
|
1006
|
+
ledger
|
|
1007
|
+
.command('add')
|
|
1008
|
+
.argument('<name>', 'lowercase note slug')
|
|
1009
|
+
.argument('<body>', 'markdown note body')
|
|
1010
|
+
.requiredOption('-r, --channel <channel>', 'channel id')
|
|
1011
|
+
.requiredOption('--type <type>', 'decision, constraint, or contract')
|
|
1012
|
+
.requiredOption('--as <handle>', 'channel member attribution')
|
|
1013
|
+
.option('--join <line>', 'route the write to the home using name:secret')
|
|
1014
|
+
.option('--home <peer>', 'home switchboard device id')
|
|
1015
|
+
.action(async (name, body, options) => {
|
|
1016
|
+
if (!['decision', 'constraint', 'contract'].includes(options.type)) {
|
|
1017
|
+
throw new Error('--type must be decision, constraint, or contract');
|
|
1018
|
+
}
|
|
1019
|
+
const write = {
|
|
1020
|
+
name,
|
|
1021
|
+
body,
|
|
1022
|
+
type: options.type,
|
|
1023
|
+
author: options.as,
|
|
1024
|
+
};
|
|
1025
|
+
if (options.join || options.home) {
|
|
1026
|
+
if (!options.join || !options.home)
|
|
1027
|
+
throw new Error('--join and --home must be used together');
|
|
1028
|
+
const crypto = new CryptoVault(program.opts().dataDir);
|
|
1029
|
+
const transport = new HyperswarmTransport({ lines: [parseLine(options.join)], crypto });
|
|
1030
|
+
try {
|
|
1031
|
+
await transport.start();
|
|
1032
|
+
await transport.waitForPeer(options.home);
|
|
1033
|
+
const note = await addRemoteLedgerNote(transport, options.home, options.channel, write);
|
|
1034
|
+
out(`${note.relative_path}\t[[${note.name}]]`);
|
|
1035
|
+
}
|
|
1036
|
+
finally {
|
|
1037
|
+
await transport.close();
|
|
1038
|
+
crypto.close();
|
|
1039
|
+
}
|
|
1040
|
+
return;
|
|
1041
|
+
}
|
|
1042
|
+
const note = new LedgerVault(program.opts().dataDir, options.channel).add(write);
|
|
1043
|
+
out(`${note.relative_path}\t[[${note.name}]]`);
|
|
1044
|
+
});
|
|
1045
|
+
ledger
|
|
1046
|
+
.command('show')
|
|
1047
|
+
.argument('<name>', 'note slug')
|
|
1048
|
+
.requiredOption('-r, --channel <channel>', 'channel id')
|
|
1049
|
+
.action((name, options) => {
|
|
1050
|
+
const note = new LedgerVault(program.opts().dataDir, options.channel).note(name);
|
|
1051
|
+
if (!note)
|
|
1052
|
+
throw new Error(`no such ledger note ${name}`);
|
|
1053
|
+
out(note.content.trimEnd());
|
|
1054
|
+
});
|
|
1055
|
+
ledger
|
|
1056
|
+
.command('pull')
|
|
1057
|
+
.requiredOption('-r, --channel <channel>', 'channel id')
|
|
1058
|
+
.option('-d, --destination <path>', 'snapshot parent directory', process.cwd())
|
|
1059
|
+
.action((options) => {
|
|
1060
|
+
out(new LedgerVault(program.opts().dataDir, options.channel).pull(options.destination));
|
|
1061
|
+
});
|
|
1062
|
+
// harn:end human-facing-surfaces-call-rooms-channels
|
|
1063
|
+
return program;
|
|
1064
|
+
}
|
|
1065
|
+
export async function runCli(argv = process.argv, context = {}) {
|
|
1066
|
+
await createProgram(context).parseAsync(argv);
|
|
1067
|
+
}
|
|
1068
|
+
//# sourceMappingURL=program.js.map
|