borgmcp 1.1.14 → 2.0.1
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/CONTRIBUTING.md +15 -0
- package/LICENSE +201 -33
- package/NOTICE +2 -0
- package/README.md +86 -37
- package/SECURITY.md +7 -0
- package/dist/agent-runtime.d.ts.map +1 -0
- package/dist/agent-runtime.js +63 -1
- package/dist/agent-runtime.js.map +1 -0
- package/dist/assimilate-cmd.d.ts +96 -17
- package/dist/assimilate-cmd.d.ts.map +1 -0
- package/dist/assimilate-cmd.js +1396 -48
- package/dist/assimilate-cmd.js.map +1 -0
- package/dist/assimilate-deps.d.ts.map +1 -0
- package/dist/assimilate-deps.js +384 -3
- package/dist/assimilate-deps.js.map +1 -0
- package/dist/assimilate-guard.d.ts +5 -7
- package/dist/assimilate-guard.d.ts.map +1 -0
- package/dist/assimilate-guard.js +60 -2
- package/dist/assimilate-guard.js.map +1 -0
- package/dist/assimilate-welcome.d.ts +1 -1
- package/dist/assimilate-welcome.d.ts.map +1 -0
- package/dist/assimilate-welcome.js +32 -2
- package/dist/assimilate-welcome.js.map +1 -0
- package/dist/auth-env.d.ts +7 -50
- package/dist/auth-env.d.ts.map +1 -0
- package/dist/auth-env.js +22 -1
- package/dist/auth-env.js.map +1 -0
- package/dist/backends/launch-all-pastelist.d.ts.map +1 -0
- package/dist/backends/launch-all-pastelist.js +11 -4
- package/dist/backends/launch-all-pastelist.js.map +1 -0
- package/dist/backends/launch-all-tmux.d.ts.map +1 -0
- package/dist/backends/launch-all-tmux.js +49 -1
- package/dist/backends/launch-all-tmux.js.map +1 -0
- package/dist/backends/launch-all-windows.d.ts.map +1 -0
- package/dist/backends/launch-all-windows.js +86 -12
- package/dist/backends/launch-all-windows.js.map +1 -0
- package/dist/bare-launch-menu.d.ts.map +1 -0
- package/dist/bare-launch-menu.js +83 -6
- package/dist/bare-launch-menu.js.map +1 -0
- package/dist/claude-launch-args.d.ts.map +1 -0
- package/dist/claude-launch-args.js +34 -1
- package/dist/claude-launch-args.js.map +1 -0
- package/dist/claude.d.ts.map +1 -0
- package/dist/claude.js +486 -14
- package/dist/claude.js.map +1 -0
- package/dist/cleanup-cmd.d.ts +5 -5
- package/dist/cleanup-cmd.d.ts.map +1 -0
- package/dist/cleanup-cmd.js +453 -14
- package/dist/cleanup-cmd.js.map +1 -0
- package/dist/clear-rewake-core.d.ts.map +1 -0
- package/dist/clear-rewake-core.js +23 -2
- package/dist/clear-rewake-core.js.map +1 -0
- package/dist/clear-rewake.d.ts.map +1 -0
- package/dist/clear-rewake.js +19 -1
- package/dist/clear-rewake.js.map +1 -0
- package/dist/cli-help.d.ts +5 -0
- package/dist/cli-help.d.ts.map +1 -0
- package/dist/cli-help.js +104 -56
- package/dist/cli-help.js.map +1 -0
- package/dist/cli-platform.d.ts.map +1 -0
- package/dist/cli-platform.js +106 -4
- package/dist/cli-platform.js.map +1 -0
- package/dist/cli-tool-approval.d.ts +84 -0
- package/dist/cli-tool-approval.d.ts.map +1 -0
- package/dist/cli-tool-approval.js +583 -0
- package/dist/cli-tool-approval.js.map +1 -0
- package/dist/codex-app-server.d.ts.map +1 -0
- package/dist/codex-app-server.js +228 -4
- package/dist/codex-app-server.js.map +1 -0
- package/dist/codex-app-wake.d.ts.map +1 -0
- package/dist/codex-app-wake.js +432 -2
- package/dist/codex-app-wake.js.map +1 -0
- package/dist/codex-launch.d.ts.map +1 -0
- package/dist/codex-launch.js +102 -2
- package/dist/codex-launch.js.map +1 -0
- package/dist/codex-remote.d.ts +6 -1
- package/dist/codex-remote.d.ts.map +1 -0
- package/dist/codex-remote.js +394 -1
- package/dist/codex-remote.js.map +1 -0
- package/dist/codex-wake-resolve.d.ts.map +1 -0
- package/dist/codex-wake-resolve.js +127 -1
- package/dist/codex-wake-resolve.js.map +1 -0
- package/dist/config-utils.d.ts +2 -2
- package/dist/config-utils.d.ts.map +1 -0
- package/dist/config-utils.js +766 -3
- package/dist/config-utils.js.map +1 -0
- package/dist/config.d.ts +98 -61
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +426 -1
- package/dist/config.js.map +1 -0
- package/dist/console-prefix.d.ts.map +1 -0
- package/dist/console-prefix.js +96 -1
- package/dist/console-prefix.js.map +1 -0
- package/dist/cube-name.d.ts.map +1 -0
- package/dist/cube-name.js +67 -1
- package/dist/cube-name.js.map +1 -0
- package/dist/cubes.d.ts +126 -18
- package/dist/cubes.d.ts.map +1 -0
- package/dist/cubes.js +412 -4
- package/dist/cubes.js.map +1 -0
- package/dist/debug.d.ts.map +1 -0
- package/dist/debug.js +71 -1
- package/dist/debug.js.map +1 -0
- package/dist/direct-log.d.ts.map +1 -0
- package/dist/direct-log.js +11 -1
- package/dist/direct-log.js.map +1 -0
- package/dist/docs-sections.d.ts +11 -10
- package/dist/docs-sections.d.ts.map +1 -0
- package/dist/docs-sections.js +113 -3
- package/dist/docs-sections.js.map +1 -0
- package/dist/drone-lifecycle.d.ts +5 -21
- package/dist/drone-lifecycle.d.ts.map +1 -0
- package/dist/drone-lifecycle.js +57 -8
- package/dist/drone-lifecycle.js.map +1 -0
- package/dist/drone-management.d.ts +29 -0
- package/dist/drone-management.d.ts.map +1 -0
- package/dist/drone-management.js +109 -0
- package/dist/drone-management.js.map +1 -0
- package/dist/ensure-mcp-config.d.ts.map +1 -0
- package/dist/ensure-mcp-config.js +36 -1
- package/dist/ensure-mcp-config.js.map +1 -0
- package/dist/evict-drone.d.ts +10 -2
- package/dist/evict-drone.d.ts.map +1 -0
- package/dist/evict-drone.js +60 -1
- package/dist/evict-drone.js.map +1 -0
- package/dist/gc-orphan-inboxes.d.ts.map +1 -0
- package/dist/gc-orphan-inboxes.js +177 -1
- package/dist/gc-orphan-inboxes.js.map +1 -0
- package/dist/inbox-monitor.d.ts +1 -1
- package/dist/inbox-monitor.d.ts.map +1 -0
- package/dist/inbox-monitor.js +856 -3
- package/dist/inbox-monitor.js.map +1 -0
- package/dist/index.d.ts +6 -4
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1034 -37
- package/dist/index.js.map +1 -0
- package/dist/launch-all-cmd.d.ts.map +1 -0
- package/dist/launch-all-cmd.js +377 -37
- package/dist/launch-all-cmd.js.map +1 -0
- package/dist/launch-all-command.d.ts.map +1 -0
- package/dist/launch-all-command.js +24 -1
- package/dist/launch-all-command.js.map +1 -0
- package/dist/launch-all-deps.d.ts +3 -8
- package/dist/launch-all-deps.d.ts.map +1 -0
- package/dist/launch-all-deps.js +99 -1
- package/dist/launch-all-deps.js.map +1 -0
- package/dist/launch-all-discovery.d.ts +1 -0
- package/dist/launch-all-discovery.d.ts.map +1 -0
- package/dist/launch-all-discovery.js +90 -4
- package/dist/launch-all-discovery.js.map +1 -0
- package/dist/launch-all-locks.d.ts.map +1 -0
- package/dist/launch-all-locks.js +54 -1
- package/dist/launch-all-locks.js.map +1 -0
- package/dist/launch-gate.d.ts +2 -2
- package/dist/launch-gate.d.ts.map +1 -0
- package/dist/launch-gate.js +73 -1
- package/dist/launch-gate.js.map +1 -0
- package/dist/lifecycle-log-guard.d.ts.map +1 -0
- package/dist/lifecycle-log-guard.js +93 -2
- package/dist/lifecycle-log-guard.js.map +1 -0
- package/dist/list-roles-render.d.ts.map +1 -0
- package/dist/list-roles-render.js +40 -6
- package/dist/list-roles-render.js.map +1 -0
- package/dist/local-log-routing.d.ts +19 -0
- package/dist/local-log-routing.d.ts.map +1 -0
- package/dist/local-log-routing.js +67 -0
- package/dist/local-log-routing.js.map +1 -0
- package/dist/local-manage-tool-result.d.ts +8 -0
- package/dist/local-manage-tool-result.d.ts.map +1 -0
- package/dist/local-manage-tool-result.js +10 -0
- package/dist/local-manage-tool-result.js.map +1 -0
- package/dist/local-server-cursor.d.ts +34 -0
- package/dist/local-server-cursor.d.ts.map +1 -0
- package/dist/local-server-cursor.js +151 -0
- package/dist/local-server-cursor.js.map +1 -0
- package/dist/log-audit.d.ts.map +1 -0
- package/dist/log-audit.js +194 -3
- package/dist/log-audit.js.map +1 -0
- package/dist/log-stream.d.ts +31 -18
- package/dist/log-stream.d.ts.map +1 -0
- package/dist/log-stream.js +1214 -12
- package/dist/log-stream.js.map +1 -0
- package/dist/model-presets.d.ts.map +1 -0
- package/dist/model-presets.js +19 -1
- package/dist/model-presets.js.map +1 -0
- package/dist/name-validator.d.ts.map +1 -0
- package/dist/name-validator.js +22 -1
- package/dist/name-validator.js.map +1 -0
- package/dist/opencode-drone.d.ts.map +1 -0
- package/dist/opencode-drone.js +438 -4
- package/dist/opencode-drone.js.map +1 -0
- package/dist/opencode-plugin.d.ts +1 -1
- package/dist/opencode-plugin.d.ts.map +1 -0
- package/dist/opencode-plugin.js +29 -4
- package/dist/opencode-plugin.js.map +1 -0
- package/dist/parse-assimilate-args.d.ts +1 -1
- package/dist/parse-assimilate-args.d.ts.map +1 -0
- package/dist/parse-assimilate-args.js +125 -1
- package/dist/parse-assimilate-args.js.map +1 -0
- package/dist/parse-launch-all-args.d.ts.map +1 -0
- package/dist/parse-launch-all-args.js +70 -1
- package/dist/parse-launch-all-args.js.map +1 -0
- package/dist/postinstall-banner.d.ts.map +1 -0
- package/dist/postinstall-banner.js +27 -2
- package/dist/postinstall-banner.js.map +1 -0
- package/dist/postinstall.d.ts.map +1 -0
- package/dist/postinstall.js +32 -5
- package/dist/postinstall.js.map +1 -0
- package/dist/readiness-probe.d.ts.map +1 -0
- package/dist/readiness-probe.js +9 -1
- package/dist/readiness-probe.js.map +1 -0
- package/dist/regen-format.d.ts.map +1 -0
- package/dist/regen-format.js +545 -13
- package/dist/regen-format.js.map +1 -0
- package/dist/regen.d.ts.map +1 -0
- package/dist/regen.js +141 -5
- package/dist/regen.js.map +1 -0
- package/dist/remote-client.d.ts +65 -175
- package/dist/remote-client.d.ts.map +1 -0
- package/dist/remote-client.js +942 -1
- package/dist/remote-client.js.map +1 -0
- package/dist/reset-local-seat-cmd.d.ts +50 -0
- package/dist/reset-local-seat-cmd.d.ts.map +1 -0
- package/dist/reset-local-seat-cmd.js +169 -0
- package/dist/reset-local-seat-cmd.js.map +1 -0
- package/dist/role-match.d.ts.map +1 -0
- package/dist/role-match.js +23 -1
- package/dist/role-match.js.map +1 -0
- package/dist/role-resolver.d.ts.map +1 -0
- package/dist/role-resolver.js +60 -1
- package/dist/role-resolver.js.map +1 -0
- package/dist/roster-render.d.ts.map +1 -0
- package/dist/roster-render.js +121 -3
- package/dist/roster-render.js.map +1 -0
- package/dist/seat-probe.d.ts +32 -13
- package/dist/seat-probe.d.ts.map +1 -0
- package/dist/seat-probe.js +78 -1
- package/dist/seat-probe.js.map +1 -0
- package/dist/seat-store.d.ts +76 -0
- package/dist/seat-store.d.ts.map +1 -0
- package/dist/seat-store.js +309 -0
- package/dist/seat-store.js.map +1 -0
- package/dist/seats.d.ts +303 -0
- package/dist/seats.d.ts.map +1 -0
- package/dist/seats.js +551 -0
- package/dist/seats.js.map +1 -0
- package/dist/self-path.d.ts +27 -0
- package/dist/self-path.d.ts.map +1 -0
- package/dist/self-path.js +43 -0
- package/dist/self-path.js.map +1 -0
- package/dist/server-endpoint.d.ts +9 -0
- package/dist/server-endpoint.d.ts.map +1 -0
- package/dist/server-endpoint.js +38 -0
- package/dist/server-endpoint.js.map +1 -0
- package/dist/server-errors.d.ts +50 -0
- package/dist/server-errors.d.ts.map +1 -0
- package/dist/server-errors.js +79 -0
- package/dist/server-errors.js.map +1 -0
- package/dist/server-handshake.d.ts +197 -0
- package/dist/server-handshake.d.ts.map +1 -0
- package/dist/server-handshake.js +505 -0
- package/dist/server-handshake.js.map +1 -0
- package/dist/server-response.d.ts +3 -0
- package/dist/server-response.d.ts.map +1 -0
- package/dist/server-response.js +67 -0
- package/dist/server-response.js.map +1 -0
- package/dist/server-trust.d.ts +14 -0
- package/dist/server-trust.d.ts.map +1 -0
- package/dist/server-trust.js +225 -0
- package/dist/server-trust.js.map +1 -0
- package/dist/setup-confirm.d.ts +3 -3
- package/dist/setup-confirm.d.ts.map +1 -0
- package/dist/setup-confirm.js +126 -2
- package/dist/setup-confirm.js.map +1 -0
- package/dist/setup.d.ts +5 -3
- package/dist/setup.d.ts.map +1 -0
- package/dist/setup.js +169 -46
- package/dist/setup.js.map +1 -0
- package/dist/shell-escape.d.ts.map +1 -0
- package/dist/shell-escape.js +22 -1
- package/dist/shell-escape.js.map +1 -0
- package/dist/spawn.d.ts +2 -2
- package/dist/spawn.d.ts.map +1 -0
- package/dist/spawn.js +29 -10
- package/dist/spawn.js.map +1 -0
- package/dist/startup-services.d.ts +0 -1
- package/dist/startup-services.d.ts.map +1 -0
- package/dist/startup-services.js +26 -1
- package/dist/startup-services.js.map +1 -0
- package/dist/stream-owner.d.ts +6 -2
- package/dist/stream-owner.d.ts.map +1 -0
- package/dist/stream-owner.js +547 -2
- package/dist/stream-owner.js.map +1 -0
- package/dist/stream-status.d.ts +1 -1
- package/dist/stream-status.d.ts.map +1 -0
- package/dist/stream-status.js +268 -3
- package/dist/stream-status.js.map +1 -0
- package/dist/sync-roles-render.d.ts.map +1 -0
- package/dist/sync-roles-render.js +118 -3
- package/dist/sync-roles-render.js.map +1 -0
- package/dist/sync.d.ts +1 -1
- package/dist/sync.d.ts.map +1 -0
- package/dist/sync.js +286 -22
- package/dist/sync.js.map +1 -0
- package/dist/terminal-title.d.ts.map +1 -0
- package/dist/terminal-title.js +68 -1
- package/dist/terminal-title.js.map +1 -0
- package/dist/token-store.d.ts +17 -111
- package/dist/token-store.d.ts.map +1 -0
- package/dist/token-store.js +81 -1
- package/dist/token-store.js.map +1 -0
- package/dist/tool-manifest.d.ts +4 -8
- package/dist/tool-manifest.d.ts.map +1 -0
- package/dist/tool-manifest.js +554 -1
- package/dist/tool-manifest.js.map +1 -0
- package/dist/tool-scope.d.ts +8 -12
- package/dist/tool-scope.d.ts.map +1 -0
- package/dist/tool-scope.js +97 -1
- package/dist/tool-scope.js.map +1 -0
- package/dist/types.d.ts +1 -5
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +5 -0
- package/dist/types.js.map +1 -0
- package/dist/unknown-subcommand.d.ts +1 -1
- package/dist/unknown-subcommand.d.ts.map +1 -0
- package/dist/unknown-subcommand.js +37 -1
- package/dist/unknown-subcommand.js.map +1 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +78 -2
- package/dist/version.js.map +1 -0
- package/dist/working-repo.d.ts.map +1 -0
- package/dist/working-repo.js +81 -1
- package/dist/working-repo.js.map +1 -0
- package/dist/worktree-lifecycle.d.ts.map +1 -0
- package/dist/worktree-lifecycle.js +205 -2
- package/dist/worktree-lifecycle.js.map +1 -0
- package/docs/EXTRACTION_PROVENANCE.md +40 -0
- package/docs/LOCAL_SERVER.md +103 -0
- package/docs/RELEASING.md +120 -0
- package/package.json +52 -34
- package/src/agent-runtime.ts +69 -0
- package/src/assimilate-cmd.ts +1864 -0
- package/src/assimilate-deps.ts +444 -0
- package/src/assimilate-guard.ts +84 -0
- package/src/assimilate-welcome.ts +39 -0
- package/src/auth-env.ts +21 -0
- package/src/backends/launch-all-pastelist.ts +18 -0
- package/src/backends/launch-all-tmux.ts +75 -0
- package/src/backends/launch-all-windows.ts +110 -0
- package/src/bare-launch-menu.ts +148 -0
- package/src/claude-launch-args.ts +35 -0
- package/src/claude.ts +561 -0
- package/src/cleanup-cmd.ts +581 -0
- package/src/clear-rewake-core.ts +34 -0
- package/src/clear-rewake.ts +18 -0
- package/src/cli-help.ts +116 -0
- package/src/cli-platform.ts +127 -0
- package/src/cli-tool-approval.ts +716 -0
- package/src/codex-app-server.ts +238 -0
- package/src/codex-app-wake.ts +512 -0
- package/src/codex-launch.ts +140 -0
- package/src/codex-remote.ts +500 -0
- package/src/codex-wake-resolve.ts +155 -0
- package/src/config-utils.ts +811 -0
- package/src/config.ts +595 -0
- package/src/console-prefix.ts +99 -0
- package/src/cube-name.ts +69 -0
- package/src/cubes.ts +583 -0
- package/src/debug.ts +73 -0
- package/src/direct-log.ts +9 -0
- package/src/docs-sections.ts +129 -0
- package/src/drone-lifecycle.ts +62 -0
- package/src/drone-management.ts +162 -0
- package/src/ensure-mcp-config.ts +54 -0
- package/src/evict-drone.ts +82 -0
- package/src/gc-orphan-inboxes.ts +229 -0
- package/src/inbox-monitor.ts +1005 -0
- package/src/index.ts +1201 -0
- package/src/launch-all-cmd.ts +450 -0
- package/src/launch-all-command.ts +30 -0
- package/src/launch-all-deps.ts +188 -0
- package/src/launch-all-discovery.ts +118 -0
- package/src/launch-all-locks.ts +74 -0
- package/src/launch-gate.ts +80 -0
- package/src/lifecycle-log-guard.ts +144 -0
- package/src/list-roles-render.ts +52 -0
- package/src/local-log-routing.ts +106 -0
- package/src/local-manage-tool-result.ts +10 -0
- package/src/local-server-cursor.ts +188 -0
- package/src/log-audit.ts +207 -0
- package/src/log-stream.ts +1505 -0
- package/src/model-presets.ts +22 -0
- package/src/name-validator.ts +23 -0
- package/src/opencode-drone.ts +528 -0
- package/src/opencode-plugin.ts +38 -0
- package/src/parse-assimilate-args.ts +125 -0
- package/src/parse-launch-all-args.ts +100 -0
- package/src/postinstall-banner.ts +37 -0
- package/src/postinstall.ts +36 -0
- package/src/readiness-probe.ts +12 -0
- package/src/regen-format.ts +651 -0
- package/src/regen.ts +150 -0
- package/src/remote-client.ts +1379 -0
- package/src/reset-local-seat-cmd.ts +227 -0
- package/src/role-match.ts +29 -0
- package/src/role-resolver.ts +93 -0
- package/src/roster-render.ts +196 -0
- package/src/seat-probe.ts +115 -0
- package/src/seat-store.ts +333 -0
- package/src/seats.ts +741 -0
- package/src/self-path.ts +51 -0
- package/src/server-endpoint.ts +38 -0
- package/src/server-errors.ts +93 -0
- package/src/server-handshake.ts +783 -0
- package/src/server-response.ts +76 -0
- package/src/server-trust.ts +245 -0
- package/src/setup-confirm.ts +171 -0
- package/src/setup.ts +186 -0
- package/src/shell-escape.ts +22 -0
- package/src/spawn.ts +31 -0
- package/src/startup-services.ts +36 -0
- package/src/stream-owner.ts +686 -0
- package/src/stream-status.ts +337 -0
- package/src/sync-roles-render.ts +163 -0
- package/src/sync.ts +363 -0
- package/src/terminal-title.ts +76 -0
- package/src/token-store.ts +102 -0
- package/src/tool-manifest.ts +610 -0
- package/src/tool-scope.ts +110 -0
- package/src/types.ts +5 -0
- package/src/unknown-subcommand.ts +35 -0
- package/src/version.ts +82 -0
- package/src/working-repo.ts +99 -0
- package/src/worktree-lifecycle.ts +254 -0
- package/dist/auth-recovery.d.ts +0 -33
- package/dist/auth-recovery.js +0 -1
- package/dist/auth.d.ts +0 -103
- package/dist/auth.js +0 -39
- package/dist/device-auth.d.ts +0 -75
- package/dist/device-auth.js +0 -1
- package/dist/drone-address.d.ts +0 -26
- package/dist/drone-address.js +0 -1
- package/dist/get-started.d.ts +0 -20
- package/dist/get-started.js +0 -2
- package/dist/health-beat.d.ts +0 -134
- package/dist/health-beat.js +0 -1
- package/dist/role-section.d.ts +0 -110
- package/dist/role-section.js +0 -8
- package/dist/setup-action.d.ts +0 -20
- package/dist/setup-action.js +0 -1
- package/dist/stale-version-check.d.ts +0 -40
- package/dist/stale-version-check.js +0 -1
- package/dist/subscription-retry.d.ts +0 -40
- package/dist/subscription-retry.js +0 -1
- package/dist/templates.d.ts +0 -107
- package/dist/templates.js +0 -359
- package/dist/token-crypto.d.ts +0 -50
- package/dist/token-crypto.js +0 -1
package/src/debug.ts
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Opt-in debug logging for the borg CLI (Queen observability ask — surface
|
|
3
|
+
* exactly what failed on errors like the cross-account assimilate 404).
|
|
4
|
+
*
|
|
5
|
+
* Enabled by `--debug` on the command line OR a truthy `BORG_DEBUG` env var,
|
|
6
|
+
* wired at the CLI entry points (top-level `borg` dispatcher + `borg setup`
|
|
7
|
+
* + `borg assimilate`). When on, `authedFetch` (remote-client.ts) emits one
|
|
8
|
+
* line per HTTP request + the server error body on failure.
|
|
9
|
+
*
|
|
10
|
+
* Output goes to STDERR with a `[borg:debug]` prefix so it never contaminates
|
|
11
|
+
* STDOUT (the MCP / tool output stream a drone session proxies).
|
|
12
|
+
*
|
|
13
|
+
* HARD INVARIANT: debug output must NEVER contain token material — no
|
|
14
|
+
* Authorization header, id_token, or refresh_token. Call sites log only
|
|
15
|
+
* method / path / status / server-error-body; the Bearer token is omitted.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
let debugEnabled = false;
|
|
19
|
+
|
|
20
|
+
/** Enable or disable debug logging for the lifetime of the process. */
|
|
21
|
+
export function setDebug(on: boolean): void {
|
|
22
|
+
debugEnabled = on;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** Whether debug logging is currently enabled. */
|
|
26
|
+
export function isDebug(): boolean {
|
|
27
|
+
return debugEnabled;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Emit a debug line to STDERR with the `[borg:debug]` prefix. No-op when
|
|
32
|
+
* debug is disabled, so call sites need not guard. STDOUT is left clean for
|
|
33
|
+
* MCP / tool output.
|
|
34
|
+
*/
|
|
35
|
+
export function debugLog(...args: unknown[]): void {
|
|
36
|
+
if (!debugEnabled) return;
|
|
37
|
+
if (args.length > 0 && typeof args[0] === 'string') {
|
|
38
|
+
console.error(`[borg:debug] ${args[0]}`, ...args.slice(1));
|
|
39
|
+
} else {
|
|
40
|
+
console.error('[borg:debug]', ...args);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Resolve debug state from a process argv array + `BORG_DEBUG` at a CLI entry
|
|
46
|
+
* point. Enables debug when `--debug` is present OR `BORG_DEBUG` is truthy,
|
|
47
|
+
* then STRIPS every `--debug` token from the array IN PLACE so downstream
|
|
48
|
+
* subcommand parsers (which reject unknown flags) never see it. Idempotent —
|
|
49
|
+
* safe to call from more than one entry point in the same process.
|
|
50
|
+
*/
|
|
51
|
+
export function initDebugFromArgv(argv: string[]): void {
|
|
52
|
+
const hasFlag = argv.includes('--debug');
|
|
53
|
+
if (hasFlag || isTruthyEnv(process.env.BORG_DEBUG)) {
|
|
54
|
+
setDebug(true);
|
|
55
|
+
}
|
|
56
|
+
if (hasFlag) {
|
|
57
|
+
for (let i = argv.length - 1; i >= 0; i -= 1) {
|
|
58
|
+
if (argv[i] === '--debug') argv.splice(i, 1);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** A non-empty env value other than the common falsy spellings is truthy. */
|
|
64
|
+
function isTruthyEnv(value: string | undefined): boolean {
|
|
65
|
+
if (!value) return false;
|
|
66
|
+
const v = value.trim().toLowerCase();
|
|
67
|
+
return v !== '' && v !== '0' && v !== 'false' && v !== 'no' && v !== 'off';
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** Test hook: reset module-level debug state between tests. */
|
|
71
|
+
export function _resetDebugForTests(): void {
|
|
72
|
+
debugEnabled = false;
|
|
73
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export function normalizeDirectLogRecipients(value: unknown): string[] {
|
|
2
|
+
if (value == null) return [];
|
|
3
|
+
const raw = Array.isArray(value) ? value : [value];
|
|
4
|
+
const recipients = raw
|
|
5
|
+
.filter((item): item is string => typeof item === 'string')
|
|
6
|
+
.map((item) => item.trim())
|
|
7
|
+
.filter((item) => item.length > 0);
|
|
8
|
+
return [...new Set(recipients)];
|
|
9
|
+
}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The single source for the in-product docs index.
|
|
3
|
+
*
|
|
4
|
+
* `borg_docs` (index.ts) returns these sections so an agent can route a "how
|
|
5
|
+
* does borgmcp work / setup / concept / tool" question to the right
|
|
6
|
+
* repository-local document, then WebFetch the URL for the content. Pure data +
|
|
7
|
+
* a lazy keyword match — NO server-side fetch, NO RAG/embeddings.
|
|
8
|
+
*
|
|
9
|
+
* This is a local-only client: every URL points at the public source repository
|
|
10
|
+
* (its README + `docs/`), never a hosted product site.
|
|
11
|
+
*
|
|
12
|
+
* `page` is the repository-local file each section maps to (anti-drift anchor).
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
export const DOCS_BASE_URL = "https://github.com/Byte-Ventures/borg-mcp-client";
|
|
16
|
+
|
|
17
|
+
const README_URL = `${DOCS_BASE_URL}#readme`;
|
|
18
|
+
const LOCAL_SERVER_URL = `${DOCS_BASE_URL}/blob/main/docs/LOCAL_SERVER.md`;
|
|
19
|
+
|
|
20
|
+
export interface DocsSection {
|
|
21
|
+
/** logical topic key */
|
|
22
|
+
slug: string;
|
|
23
|
+
title: string;
|
|
24
|
+
/** repository-local document URL the agent should WebFetch */
|
|
25
|
+
url: string;
|
|
26
|
+
/** the repository file this section maps to (anti-drift anchor) */
|
|
27
|
+
page: string;
|
|
28
|
+
summary: string;
|
|
29
|
+
/** extra match terms for the topic lookup */
|
|
30
|
+
keywords: string[];
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export const DOCS_SECTIONS: DocsSection[] = [
|
|
34
|
+
{
|
|
35
|
+
slug: "overview",
|
|
36
|
+
title: "Overview",
|
|
37
|
+
url: README_URL,
|
|
38
|
+
page: "README.md",
|
|
39
|
+
summary: "What Borg MCP is + the cube / drone / role / log mental model.",
|
|
40
|
+
keywords: ["overview", "what is", "intro", "mental model", "how it works", "start"],
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
slug: "concepts",
|
|
44
|
+
title: "Core concepts",
|
|
45
|
+
url: README_URL,
|
|
46
|
+
page: "README.md",
|
|
47
|
+
summary: "Cubes, drones, roles, the activity log + signals, claims, decisions.",
|
|
48
|
+
keywords: ["cube", "drone", "role", "log", "signal", "claim", "decision", "coordinate", "coordination"],
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
slug: "install",
|
|
52
|
+
title: "Install client",
|
|
53
|
+
url: README_URL,
|
|
54
|
+
page: "README.md",
|
|
55
|
+
summary: "Install the published Borg MCP client and verify the borg CLI.",
|
|
56
|
+
keywords: ["install", "installation", "npm", "client", "borgmcp", "borg help", "claude code", "codex", "opencode"],
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
slug: "run-server",
|
|
60
|
+
title: "Run server",
|
|
61
|
+
url: LOCAL_SERVER_URL,
|
|
62
|
+
page: "docs/LOCAL_SERVER.md",
|
|
63
|
+
summary: "Run a self-hosted borgmcp-server: setup, start, endpoint, network configuration.",
|
|
64
|
+
keywords: ["server", "self-hosted", "borgmcp-server", "borg-mcp-server", "setup", "start", "listen port", "7091", "local server", "--lan", "tls"],
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
slug: "enroll",
|
|
68
|
+
title: "Enroll",
|
|
69
|
+
url: LOCAL_SERVER_URL,
|
|
70
|
+
page: "docs/LOCAL_SERVER.md",
|
|
71
|
+
summary: "Connect a client to a self-hosted server: invitations, assimilate --host --enroll, credentials.",
|
|
72
|
+
keywords: ["enroll", "enrollment", "invitation", "invite", "assimilate", "--host", "credential", "credentials", "owner", "join server"],
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
slug: "self-hosting",
|
|
76
|
+
title: "Self-hosting operations",
|
|
77
|
+
url: LOCAL_SERVER_URL,
|
|
78
|
+
page: "docs/LOCAL_SERVER.md",
|
|
79
|
+
summary: "Operate a self-hosted server: data directory, credential rotation and grants, capacity, backup, upgrades.",
|
|
80
|
+
keywords: ["self-hosting", "operations", "operate", "backup", "restore", "upgrade", "rotate", "revoke", "grant", "capacity", "data directory", "license"],
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
slug: "cli",
|
|
84
|
+
title: "CLI commands",
|
|
85
|
+
url: README_URL,
|
|
86
|
+
page: "README.md",
|
|
87
|
+
summary: "Client launch, sync, cleanup, worktree maintenance, and launch-all reference.",
|
|
88
|
+
keywords: ["cli", "command", "sync", "cleanup", "worktree", "launch", "launch-all", "terminal", "maintenance", "prune"],
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
slug: "tools",
|
|
92
|
+
title: "Tool reference",
|
|
93
|
+
url: README_URL,
|
|
94
|
+
page: "README.md",
|
|
95
|
+
summary: "Every borg_* tool — name, description, params.",
|
|
96
|
+
keywords: ["tool", "tools", "api", "reference", "param", "borg_"],
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
slug: "faq",
|
|
100
|
+
title: "FAQ",
|
|
101
|
+
url: README_URL,
|
|
102
|
+
page: "README.md",
|
|
103
|
+
summary: "Common questions — agents, coordination, worktrees, docs maturity, security.",
|
|
104
|
+
keywords: ["faq", "question", "agent", "claude", "codex", "opencode", "coordination", "worktree", "security", "reporting", "second agent"],
|
|
105
|
+
},
|
|
106
|
+
];
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Lazy topic match: a section matches when the topic shares a whitespace token
|
|
110
|
+
* with the section's slug / title / summary / keywords (case-insensitive,
|
|
111
|
+
* substring both ways so "price"↔"pricing"). Returns matches ranked by hit
|
|
112
|
+
* count; empty when nothing matches (the caller then shows the full index).
|
|
113
|
+
*/
|
|
114
|
+
export function matchDocsSections(topic: string): DocsSection[] {
|
|
115
|
+
const tokens = topic.toLowerCase().split(/[^a-z0-9_]+/).filter((t) => t.length >= 2);
|
|
116
|
+
if (tokens.length === 0) return [];
|
|
117
|
+
const scored = DOCS_SECTIONS.map((s) => {
|
|
118
|
+
const haystack = [s.slug, s.title, s.summary, ...s.keywords].join(" ").toLowerCase();
|
|
119
|
+
const hits = tokens.filter((t) => haystack.includes(t) || s.keywords.some((k) => t.includes(k))).length;
|
|
120
|
+
return { s, hits };
|
|
121
|
+
}).filter((x) => x.hits > 0);
|
|
122
|
+
scored.sort((a, b) => b.hits - a.hits);
|
|
123
|
+
return scored.map((x) => x.s);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/** Render sections as a plain-text index (title — summary — URL per line). */
|
|
127
|
+
export function formatDocsIndex(sections: DocsSection[]): string {
|
|
128
|
+
return sections.map((s) => `- ${s.title} — ${s.summary}\n ${s.url}`).join("\n");
|
|
129
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* gh#877: drone-lifecycle signals shared across the client wire layers
|
|
3
|
+
* (log-stream SSE + remote-client authedFetch + index tool funnel).
|
|
4
|
+
*
|
|
5
|
+
* Two distinct, NON-conflated outcomes when a drone's session stops resolving:
|
|
6
|
+
*
|
|
7
|
+
* - DroneEvictedError (server 410 / code DRONE_EVICTED) — TERMINAL. The seat is
|
|
8
|
+
* gone. The client emits one harness-neutral stop-session recovery path. This
|
|
9
|
+
* is the SOLE authoritative
|
|
10
|
+
* teardown trigger (SEC R2): an SSE eviction frame or inbox sentinel is only
|
|
11
|
+
* a WAKE HINT — the agent confirms via an authed call returning this code.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
export const DRONE_EVICTED_CODE = 'DRONE_EVICTED';
|
|
15
|
+
|
|
16
|
+
export class DroneEvictedError extends Error {
|
|
17
|
+
constructor(
|
|
18
|
+
message = 'This seat was removed from the cube.'
|
|
19
|
+
) {
|
|
20
|
+
super(message);
|
|
21
|
+
this.name = 'DroneEvictedError';
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Marker the agent's /loop + role playbook branch on. Single-sourced so the
|
|
27
|
+
* SSE wake sentinel (log-stream) and the tool-result funnel (index) agree.
|
|
28
|
+
*/
|
|
29
|
+
export const EVICTED_RESULT_MARKER = '[CUBE-EVICTED]';
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* The recognizable tool RESULT the agent sees when an authed call returns the
|
|
33
|
+
* AUTHORITATIVE 410 DRONE_EVICTED. Spells out the sanctioned graceful-shutdown
|
|
34
|
+
* sequence so the agent acts on it deterministically.
|
|
35
|
+
*/
|
|
36
|
+
export function formatEvictedToolResult(cubeName?: string): string {
|
|
37
|
+
const cube = cubeName ?? 'the selected cube';
|
|
38
|
+
return (
|
|
39
|
+
`${EVICTED_RESULT_MARKER} This seat was removed from cube ${cube}.\n\n` +
|
|
40
|
+
'Borg has stopped listening for activity for this seat. Do not retry this request or restart the loop.\n\n' +
|
|
41
|
+
'Your worktree and project files are unchanged. Finish any local file safety checks, then end this agent session.\n\n' +
|
|
42
|
+
'To rejoin later, start a new session and use a new invitation from the server operator. Do not re-assimilate from this evicted session.'
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Extract the structured error code from a worker error body. The worker error
|
|
48
|
+
* funnel (sanitizeError → createHttpError) emits `{ code, message }`; some
|
|
49
|
+
* legacy/nested shapes use `{ error: { code } }`. Returns null when absent or
|
|
50
|
+
* unparseable — callers must NOT treat a bare status (410) as authoritative
|
|
51
|
+
* without the matching code (SEC R2/R4: spoof + ambiguity resistance).
|
|
52
|
+
*/
|
|
53
|
+
export function errorCodeFromBody(body: string): string | null {
|
|
54
|
+
try {
|
|
55
|
+
const parsed = JSON.parse(body) as any;
|
|
56
|
+
if (typeof parsed?.code === 'string') return parsed.code;
|
|
57
|
+
if (typeof parsed?.error?.code === 'string') return parsed.error.code;
|
|
58
|
+
} catch {
|
|
59
|
+
// not JSON
|
|
60
|
+
}
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import type { EvictDroneResult, ReassignDroneResult } from 'borgmcp-shared/protocol';
|
|
2
|
+
import {
|
|
3
|
+
activeCubeWithFreshRegenIdentity,
|
|
4
|
+
getActiveCube,
|
|
5
|
+
refreshActiveCubeMetadata,
|
|
6
|
+
type ActiveCube,
|
|
7
|
+
} from './cubes.js';
|
|
8
|
+
import {
|
|
9
|
+
assertUuidShape,
|
|
10
|
+
formatEvictDroneSuccess,
|
|
11
|
+
formatReassignDroneSuccess,
|
|
12
|
+
resolveDroneIdByLabel,
|
|
13
|
+
} from './evict-drone.js';
|
|
14
|
+
import {
|
|
15
|
+
evictDrone,
|
|
16
|
+
getCubeForManagement,
|
|
17
|
+
reassignDrone,
|
|
18
|
+
type LocalManageOperation,
|
|
19
|
+
} from './remote-client.js';
|
|
20
|
+
|
|
21
|
+
interface ManagedCube {
|
|
22
|
+
id: string;
|
|
23
|
+
name: string;
|
|
24
|
+
roles: any[];
|
|
25
|
+
drones: any[];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface DroneManagementDeps {
|
|
29
|
+
getActiveCube: () => Promise<ActiveCube | null>;
|
|
30
|
+
getCubeForManagement: (
|
|
31
|
+
cubeId: string,
|
|
32
|
+
operation: LocalManageOperation,
|
|
33
|
+
active: ActiveCube,
|
|
34
|
+
) => Promise<ManagedCube>;
|
|
35
|
+
reassignDrone: (droneId: string, roleId: string, active: ActiveCube) => Promise<ReassignDroneResult>;
|
|
36
|
+
evictDrone: (
|
|
37
|
+
droneId: string,
|
|
38
|
+
options: { cubeId: string; cubeName: string; targetReference: string; active: ActiveCube },
|
|
39
|
+
) => Promise<EvictDroneResult>;
|
|
40
|
+
refreshActiveCubeMetadata: (active: ActiveCube) => Promise<boolean>;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const defaultDeps: DroneManagementDeps = {
|
|
44
|
+
getActiveCube,
|
|
45
|
+
getCubeForManagement,
|
|
46
|
+
reassignDrone,
|
|
47
|
+
evictDrone,
|
|
48
|
+
refreshActiveCubeMetadata,
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export const STALE_ROLE_DISPLAY_WARNING =
|
|
52
|
+
'Local display warning: The server committed this change, but Borg could not refresh this worktree\'s saved seat metadata. Local role details may be stale. Do not retry the reassignment.\n\n' +
|
|
53
|
+
'Run `borg_regen` to refresh the server-authoritative role. If this session still shows the previous role, restart this agent session once. Do not re-assimilate or repeat the management request.';
|
|
54
|
+
|
|
55
|
+
function opaqueNotFound(): never {
|
|
56
|
+
throw new Error('Borg server request failed (HTTP 404)');
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
async function requireActiveCube(deps: DroneManagementDeps): Promise<ActiveCube> {
|
|
60
|
+
const active = await deps.getActiveCube();
|
|
61
|
+
if (!active) throw new Error('Not assimilated to a cube. Use borg_assimilate <cube-name> first.');
|
|
62
|
+
return active;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export async function runReassignDroneTool(
|
|
66
|
+
input: { droneId: unknown; roleId: unknown },
|
|
67
|
+
deps: DroneManagementDeps = defaultDeps,
|
|
68
|
+
): Promise<string> {
|
|
69
|
+
if (typeof input.droneId !== 'string' || input.droneId.length === 0) {
|
|
70
|
+
throw new Error('drone_id is required');
|
|
71
|
+
}
|
|
72
|
+
if (typeof input.roleId !== 'string' || input.roleId.length === 0) {
|
|
73
|
+
throw new Error('role_id is required');
|
|
74
|
+
}
|
|
75
|
+
assertUuidShape(input.droneId, 'drone_id');
|
|
76
|
+
assertUuidShape(input.roleId, 'role_id');
|
|
77
|
+
|
|
78
|
+
const active = await requireActiveCube(deps);
|
|
79
|
+
const operation = {
|
|
80
|
+
operation: `reassign drone ${JSON.stringify(input.droneId)} to role ${JSON.stringify(input.roleId)} in cube ${JSON.stringify(active.name)}`,
|
|
81
|
+
cubeName: active.name,
|
|
82
|
+
noMutation: 'No drone was reassigned.',
|
|
83
|
+
};
|
|
84
|
+
const cube = await deps.getCubeForManagement(active.cubeId, operation, active);
|
|
85
|
+
const priorDrone = cube.drones.find((candidate) => candidate.id === input.droneId);
|
|
86
|
+
const role = cube.roles.find((candidate) => candidate.id === input.roleId);
|
|
87
|
+
if (!priorDrone || !role) opaqueNotFound();
|
|
88
|
+
|
|
89
|
+
const { drone } = await deps.reassignDrone(input.droneId, input.roleId, active);
|
|
90
|
+
if (drone.id !== input.droneId || drone.cube_id !== cube.id || drone.role_id !== input.roleId) {
|
|
91
|
+
throw new Error(
|
|
92
|
+
'[LOCAL-MANAGE-COMMIT-UNCONFIRMED] The server returned a successful reassignment response for unexpected identifiers. The reassignment may already be committed. Do not retry blindly; inspect the cube roster first.',
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
let warning = '';
|
|
97
|
+
if (drone.id === active.droneId) {
|
|
98
|
+
try {
|
|
99
|
+
const refreshed = await deps.refreshActiveCubeMetadata(activeCubeWithFreshRegenIdentity(active, {
|
|
100
|
+
cube,
|
|
101
|
+
drone,
|
|
102
|
+
role,
|
|
103
|
+
}));
|
|
104
|
+
if (!refreshed) warning = `\n\n${STALE_ROLE_DISPLAY_WARNING}`;
|
|
105
|
+
} catch {
|
|
106
|
+
warning = `\n\n${STALE_ROLE_DISPLAY_WARNING}`;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return formatReassignDroneSuccess({
|
|
111
|
+
droneLabel: drone.label,
|
|
112
|
+
cubeName: cube.name,
|
|
113
|
+
roleName: role.name,
|
|
114
|
+
droneId: drone.id,
|
|
115
|
+
roleId: drone.role_id,
|
|
116
|
+
}) + warning;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export async function runEvictDroneTool(
|
|
120
|
+
args: Record<string, unknown> | undefined,
|
|
121
|
+
deps: DroneManagementDeps = defaultDeps,
|
|
122
|
+
): Promise<string> {
|
|
123
|
+
const droneIdInput = typeof args?.drone_id === 'string' ? args.drone_id : undefined;
|
|
124
|
+
const labelInput = typeof args?.label === 'string' ? args.label.trim() : undefined;
|
|
125
|
+
const cubeIdInput = typeof args?.cube_id === 'string' ? args.cube_id : undefined;
|
|
126
|
+
if (droneIdInput !== undefined && (labelInput !== undefined || cubeIdInput !== undefined)) {
|
|
127
|
+
throw new Error('Provide drone_id OR label with cube_id, not both.');
|
|
128
|
+
}
|
|
129
|
+
if (droneIdInput === undefined && (!labelInput || !cubeIdInput)) {
|
|
130
|
+
throw new Error('Provide drone_id OR both label and cube_id.');
|
|
131
|
+
}
|
|
132
|
+
if (droneIdInput !== undefined) assertUuidShape(droneIdInput, 'drone_id');
|
|
133
|
+
if (cubeIdInput !== undefined) assertUuidShape(cubeIdInput, 'cube_id');
|
|
134
|
+
|
|
135
|
+
const active = await requireActiveCube(deps);
|
|
136
|
+
const cubeId = droneIdInput === undefined ? cubeIdInput! : active.cubeId;
|
|
137
|
+
const targetReference = labelInput ?? droneIdInput!;
|
|
138
|
+
const fallbackCubeName = cubeId === active.cubeId ? active.name : cubeId;
|
|
139
|
+
const operation = {
|
|
140
|
+
operation: `remove ${JSON.stringify(targetReference)} from cube ${JSON.stringify(fallbackCubeName)}`,
|
|
141
|
+
cubeName: fallbackCubeName,
|
|
142
|
+
noMutation: 'No drone was removed.',
|
|
143
|
+
};
|
|
144
|
+
const cube = await deps.getCubeForManagement(cubeId, operation, active);
|
|
145
|
+
const priorDrone = labelInput === undefined
|
|
146
|
+
? cube.drones.find((candidate) => candidate.id === droneIdInput)
|
|
147
|
+
: resolveDroneIdByLabel(cube.drones, labelInput);
|
|
148
|
+
if (!priorDrone) opaqueNotFound();
|
|
149
|
+
|
|
150
|
+
const result = await deps.evictDrone(priorDrone.id, {
|
|
151
|
+
cubeId,
|
|
152
|
+
cubeName: cube.name,
|
|
153
|
+
targetReference,
|
|
154
|
+
active,
|
|
155
|
+
});
|
|
156
|
+
if (result.drone_id !== priorDrone.id || result.evicted !== true) {
|
|
157
|
+
throw new Error(
|
|
158
|
+
'[LOCAL-MANAGE-COMMIT-UNCONFIRMED] The server returned a successful eviction response for an unexpected drone. The eviction may already be committed. Do not retry blindly; inspect the cube roster first.',
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
return formatEvictDroneSuccess(priorDrone.label, cube.name);
|
|
162
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { BorgCli } from './cubes.js';
|
|
2
|
+
import {
|
|
3
|
+
addCodexMcpServer,
|
|
4
|
+
addMcpServer,
|
|
5
|
+
addOpenCodeMcpServer,
|
|
6
|
+
isCodexMcpServerConfigured,
|
|
7
|
+
isMcpServerConfigured,
|
|
8
|
+
isOpenCodeMcpServerConfigured,
|
|
9
|
+
} from './config-utils.js';
|
|
10
|
+
|
|
11
|
+
export interface EnsureMcpConfigDeps {
|
|
12
|
+
isClaudeConfigured: () => boolean;
|
|
13
|
+
addClaude: () => void;
|
|
14
|
+
isCodexConfigured: () => boolean;
|
|
15
|
+
addCodex: () => void;
|
|
16
|
+
isOpenCodeConfigured: () => boolean;
|
|
17
|
+
addOpenCode: () => void;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const defaultDeps: EnsureMcpConfigDeps = {
|
|
21
|
+
isClaudeConfigured: isMcpServerConfigured,
|
|
22
|
+
addClaude: addMcpServer,
|
|
23
|
+
isCodexConfigured: isCodexMcpServerConfigured,
|
|
24
|
+
addCodex: addCodexMcpServer,
|
|
25
|
+
isOpenCodeConfigured: isOpenCodeMcpServerConfigured,
|
|
26
|
+
addOpenCode: addOpenCodeMcpServer,
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Ensure borg is registered as an MCP server for one selected agent CLI.
|
|
31
|
+
*
|
|
32
|
+
* Returns true when this call added the registration and false when it was
|
|
33
|
+
* already present. Errors from an adder intentionally propagate so callers
|
|
34
|
+
* can fail the launch with the CLI-specific remediation message.
|
|
35
|
+
*/
|
|
36
|
+
export function ensureCliMcpConfigured(
|
|
37
|
+
cli: BorgCli,
|
|
38
|
+
deps: EnsureMcpConfigDeps = defaultDeps,
|
|
39
|
+
): boolean {
|
|
40
|
+
switch (cli) {
|
|
41
|
+
case 'claude':
|
|
42
|
+
if (deps.isClaudeConfigured()) return false;
|
|
43
|
+
deps.addClaude();
|
|
44
|
+
return true;
|
|
45
|
+
case 'codex':
|
|
46
|
+
if (deps.isCodexConfigured()) return false;
|
|
47
|
+
deps.addCodex();
|
|
48
|
+
return true;
|
|
49
|
+
case 'opencode':
|
|
50
|
+
if (deps.isOpenCodeConfigured()) return false;
|
|
51
|
+
deps.addOpenCode();
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure label→id resolution for the `borg_evict-drone` tool (gh#718).
|
|
3
|
+
*
|
|
4
|
+
* the local drone-management route
|
|
5
|
+
* both take a drone UUID. Coordinators, however, see drone LABELS everywhere
|
|
6
|
+
* (roster, regen, cube log) and rarely the UUIDs. This helper lets the tool
|
|
7
|
+
* accept a label and resolve it to the drone id client-side, against the
|
|
8
|
+
* cube-grant-scoped detail returned by `getCube` (the same id+label pairs
|
|
9
|
+
* `borg_list-drones` renders). No I/O here — a pure function so it can be
|
|
10
|
+
* unit-tested in isolation; the handler in index.ts owns the network calls.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
export interface EvictableDrone {
|
|
14
|
+
id: string;
|
|
15
|
+
label: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Strict whole-string UUID shape check for the `drone_id` input (gh#782).
|
|
20
|
+
* The handler rejects non-UUID values before building the DELETE URL: a
|
|
21
|
+
* label passed as drone_id gets a clear "use label + cube_id" hint instead
|
|
22
|
+
* of a confusing 404, and a path-shaped value ("../cubes/<uuid>") is never
|
|
23
|
+
* interpolated into a request path. Case-insensitive; anchored so embedded
|
|
24
|
+
* or suffixed UUIDs do not pass.
|
|
25
|
+
*/
|
|
26
|
+
export function isUuidShape(value: string): boolean {
|
|
27
|
+
return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(value);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Assert-style wrapper around isUuidShape (gh#782, reassign half). Called
|
|
32
|
+
* by the remote-client functions that interpolate a drone id into the
|
|
33
|
+
* request path (reassignDrone PATCH, evictDrone DELETE) — FIRST, before
|
|
34
|
+
* any token fetch or network I/O, so a path-shaped value like
|
|
35
|
+
* "../cubes/<uuid>" can never reach URL construction. The `label` names
|
|
36
|
+
* the offending input in the caller-facing error.
|
|
37
|
+
*/
|
|
38
|
+
export function assertUuidShape(value: string, label: string): void {
|
|
39
|
+
if (!isUuidShape(value)) {
|
|
40
|
+
throw new Error(`${label} "${value}" is not a UUID`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Resolve an exact drone label to its `{ id, label }` within a single cube's
|
|
46
|
+
* drone list. Labels are unique per cube, so an exact match is unambiguous.
|
|
47
|
+
* Returns null when no drone carries the label (the handler turns that into a
|
|
48
|
+
* caller-facing error). Matching is exact (not substring) and label-only — a
|
|
49
|
+
* UUID passed here never resolves, because the handler routes UUIDs through the
|
|
50
|
+
* explicit `drone_id` input instead.
|
|
51
|
+
*/
|
|
52
|
+
export function resolveDroneIdByLabel(
|
|
53
|
+
drones: ReadonlyArray<EvictableDrone>,
|
|
54
|
+
label: string
|
|
55
|
+
): EvictableDrone | null {
|
|
56
|
+
const target = label.trim();
|
|
57
|
+
const match = drones.find((d) => d.label === target);
|
|
58
|
+
return match ? { id: match.id, label: match.label } : null;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function formatReassignDroneSuccess(input: {
|
|
62
|
+
droneLabel: string;
|
|
63
|
+
cubeName: string;
|
|
64
|
+
roleName: string;
|
|
65
|
+
droneId: string;
|
|
66
|
+
roleId: string;
|
|
67
|
+
}): string {
|
|
68
|
+
return (
|
|
69
|
+
`Reassigned ${input.droneLabel} in cube ${input.cubeName} to role ${input.roleName}.\n` +
|
|
70
|
+
`Drone id: ${input.droneId}\n` +
|
|
71
|
+
`Role id: ${input.roleId}`
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function formatEvictDroneSuccess(droneLabel: string, cubeName: string): string {
|
|
76
|
+
return (
|
|
77
|
+
`Removed ${droneLabel} from cube ${cubeName}.\n` +
|
|
78
|
+
'The seat credential is revoked. The session will stop after its next Borg request.\n' +
|
|
79
|
+
'The worktree and project files were not deleted. Activity history remains attributed to the removed seat.\n' +
|
|
80
|
+
'After its work is merged, run `borg cleanup` to review whether the worktree can be pruned.'
|
|
81
|
+
);
|
|
82
|
+
}
|