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
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { roleSlug } from './role-resolver.js';
|
|
2
|
+
/**
|
|
3
|
+
* Resolve the public `borg_log to:` addressing forms against the local
|
|
4
|
+
* server's authoritative cube roster. This intentionally mirrors the hosted
|
|
5
|
+
* route resolver: exact drone id/label first, then displayed short UUID, then
|
|
6
|
+
* role name/slug expansion. Every failure is closed before the log POST so a
|
|
7
|
+
* miss can never degrade into a broadcast.
|
|
8
|
+
*/
|
|
9
|
+
export function resolveLocalLogRecipients(recipients, drones, roles) {
|
|
10
|
+
const ids = new Set();
|
|
11
|
+
for (const recipient of recipients) {
|
|
12
|
+
if (recipient === '@human-seat') {
|
|
13
|
+
const roleIds = new Set(roles.filter((role) => role.is_human_seat === true).map((role) => role.id));
|
|
14
|
+
const matches = drones.filter((drone) => drone.role_id !== null && drone.role_id !== undefined && roleIds.has(drone.role_id));
|
|
15
|
+
if (roleIds.size === 0 || matches.length === 0) {
|
|
16
|
+
throw new Error('Direct-message role recipient has no active drones: @human-seat. ' +
|
|
17
|
+
'Use borg_roster to find active recipients, or omit to broadcast if your role can broadcast.');
|
|
18
|
+
}
|
|
19
|
+
for (const drone of matches)
|
|
20
|
+
ids.add(drone.id);
|
|
21
|
+
continue;
|
|
22
|
+
}
|
|
23
|
+
const droneMatches = drones.filter((drone) => drone.id === recipient || drone.label === recipient);
|
|
24
|
+
const uniqueDroneIds = [...new Set(droneMatches.map((drone) => drone.id))];
|
|
25
|
+
if (uniqueDroneIds.length > 1) {
|
|
26
|
+
throw new Error(`Ambiguous direct-message recipient: ${recipient} matches multiple drones`);
|
|
27
|
+
}
|
|
28
|
+
if (uniqueDroneIds.length === 1) {
|
|
29
|
+
ids.add(uniqueDroneIds[0]);
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
const shortUuid = recipient.replace(/`/g, '').replace(/^id:/i, '').toLowerCase();
|
|
33
|
+
if (/^[0-9a-f]{8,}$/.test(shortUuid)) {
|
|
34
|
+
const prefixMatches = drones.filter((drone) => drone.id.toLowerCase().startsWith(shortUuid));
|
|
35
|
+
const uniquePrefixIds = [...new Set(prefixMatches.map((drone) => drone.id))];
|
|
36
|
+
if (uniquePrefixIds.length > 1) {
|
|
37
|
+
const listed = prefixMatches
|
|
38
|
+
.map((drone) => `${drone.id} (${drone.label ?? 'unlabeled'})`)
|
|
39
|
+
.join(', ');
|
|
40
|
+
throw new Error(`Ambiguous short-uuid recipient: ${recipient} matches multiple drones — ${listed}. ` +
|
|
41
|
+
'Address by the full drone id.');
|
|
42
|
+
}
|
|
43
|
+
if (uniquePrefixIds.length === 1) {
|
|
44
|
+
ids.add(uniquePrefixIds[0]);
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
const roleMatches = roles.filter((role) => roleSlug(role.name) === roleSlug(recipient));
|
|
49
|
+
const uniqueRoleIds = [...new Set(roleMatches.map((role) => role.id))];
|
|
50
|
+
if (uniqueRoleIds.length > 1) {
|
|
51
|
+
throw new Error(`Ambiguous direct-message recipient: ${recipient} matches multiple roles`);
|
|
52
|
+
}
|
|
53
|
+
if (uniqueRoleIds.length === 0) {
|
|
54
|
+
throw new Error(`Unknown direct-message recipient: ${recipient}. ` +
|
|
55
|
+
'Use an exact drone label, drone id, role name, or role slug.');
|
|
56
|
+
}
|
|
57
|
+
const roleDrones = drones.filter((drone) => drone.role_id === uniqueRoleIds[0]);
|
|
58
|
+
if (roleDrones.length === 0) {
|
|
59
|
+
throw new Error(`Direct-message role recipient has no active drones: ${recipient}. ` +
|
|
60
|
+
'Use borg_roster to find active recipients, or omit to broadcast if your role can broadcast.');
|
|
61
|
+
}
|
|
62
|
+
for (const drone of roleDrones)
|
|
63
|
+
ids.add(drone.id);
|
|
64
|
+
}
|
|
65
|
+
return [...ids];
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=local-log-routing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"local-log-routing.js","sourceRoot":"","sources":["../src/local-log-routing.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"local-manage-tool-result.d.ts","sourceRoot":"","sources":["../src/local-manage-tool-result.ts"],"names":[],"mappings":"AAEA,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,OAAO;;;;;;SAOzD"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { LocalManageRequiredError } from './server-errors.js';
|
|
2
|
+
export function formatLocalManageToolResult(error) {
|
|
3
|
+
if (!(error instanceof LocalManageRequiredError))
|
|
4
|
+
return null;
|
|
5
|
+
return {
|
|
6
|
+
content: [{ type: 'text', text: error.message }],
|
|
7
|
+
isError: true,
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=local-manage-tool-result.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"local-manage-tool-result.js","sourceRoot":"","sources":["../src/local-manage-tool-result.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,EAAE,MAAM,oBAAoB,CAAC;AAE9D,MAAM,UAAU,2BAA2B,CAAC,KAAc;IACxD,IAAI,CAAC,CAAC,KAAK,YAAY,wBAAwB,CAAC;QAAE,OAAO,IAAI,CAAC;IAE9D,OAAO;QACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC;QACzD,OAAO,EAAE,IAAa;KACvB,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export interface LocalServerCursor {
|
|
2
|
+
id: string;
|
|
3
|
+
created_at: string;
|
|
4
|
+
}
|
|
5
|
+
export interface LocalServerCursorBinding {
|
|
6
|
+
origin: string;
|
|
7
|
+
trustIdentity: string;
|
|
8
|
+
cubeId: string;
|
|
9
|
+
droneId: string;
|
|
10
|
+
/**
|
|
11
|
+
* client#41: cursor-purpose namespace. Absent (the default) is the UNREAD
|
|
12
|
+
* WATERMARK — the point `read-log unread_only` reads from and advances only
|
|
13
|
+
* on an explicit successful drain. `'stream'` is the SSE DELIVERY/RESUME
|
|
14
|
+
* cursor the live tail advances as it delivers events. Keeping these under
|
|
15
|
+
* separate keys stops SSE delivery from consuming the unread watermark (a
|
|
16
|
+
* wake-triggering entry would otherwise disappear from `unread_only` before
|
|
17
|
+
* the agent drained it — a silent missed wake).
|
|
18
|
+
*/
|
|
19
|
+
purpose?: 'stream';
|
|
20
|
+
}
|
|
21
|
+
export declare function getLocalServerCursor(binding: LocalServerCursorBinding): Promise<LocalServerCursor | null>;
|
|
22
|
+
export declare function advanceLocalServerCursor(binding: LocalServerCursorBinding, cursor: LocalServerCursor): Promise<void>;
|
|
23
|
+
/**
|
|
24
|
+
* client#42: reset (delete) a persisted cursor for `binding`. Used by the SSE
|
|
25
|
+
* recovery path when the server returns 410 CURSOR_EXPIRED for the stream's
|
|
26
|
+
* resume cursor — the pointed-at entry has been pruned server-side, so the
|
|
27
|
+
* stale cursor can never be resumed and must be cleared, letting the next
|
|
28
|
+
* stream connect re-establish from a fresh valid point (the current tail)
|
|
29
|
+
* instead of looping forever on the dead cursor. No-op when no cursor is
|
|
30
|
+
* stored for the binding.
|
|
31
|
+
*/
|
|
32
|
+
export declare function clearLocalServerCursor(binding: LocalServerCursorBinding): Promise<void>;
|
|
33
|
+
export declare function encodeLocalServerCursor(cursor: LocalServerCursor): string;
|
|
34
|
+
//# sourceMappingURL=local-server-cursor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"local-server-cursor.d.ts","sourceRoot":"","sources":["../src/local-server-cursor.ts"],"names":[],"mappings":"AASA,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,wBAAwB;IACvC,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB;;;;;;;;OAQG;IACH,OAAO,CAAC,EAAE,QAAQ,CAAC;CACpB;AAyGD,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,wBAAwB,GAChC,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAInC;AAED,wBAAsB,wBAAwB,CAC5C,OAAO,EAAE,wBAAwB,EACjC,MAAM,EAAE,iBAAiB,GACxB,OAAO,CAAC,IAAI,CAAC,CAgBf;AAED;;;;;;;;GAQG;AACH,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,wBAAwB,GAChC,OAAO,CAAC,IAAI,CAAC,CAQf;AAED,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,iBAAiB,GAAG,MAAM,CAGzE"}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { mkdir, open, readFile, rename, stat, unlink, writeFile } from 'node:fs/promises';
|
|
3
|
+
import { homedir } from 'node:os';
|
|
4
|
+
import { dirname, join } from 'node:path';
|
|
5
|
+
const CURSOR_FILE = join(homedir(), '.config', 'borgmcp', 'local-server-cursors.json');
|
|
6
|
+
const CURSOR_LOCK = `${CURSOR_FILE}.lock`;
|
|
7
|
+
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
8
|
+
function cursorKey(binding) {
|
|
9
|
+
if (new URL(binding.origin).origin !== binding.origin ||
|
|
10
|
+
!UUID_RE.test(binding.cubeId) ||
|
|
11
|
+
!UUID_RE.test(binding.droneId)) {
|
|
12
|
+
throw new Error('invalid local Borg server cursor binding');
|
|
13
|
+
}
|
|
14
|
+
const hash = createHash('sha256')
|
|
15
|
+
.update(binding.origin)
|
|
16
|
+
.update('\0')
|
|
17
|
+
.update(binding.trustIdentity)
|
|
18
|
+
.update('\0')
|
|
19
|
+
.update(binding.cubeId)
|
|
20
|
+
.update('\0')
|
|
21
|
+
.update(binding.droneId);
|
|
22
|
+
// The purpose component is appended ONLY when present, so the unread-watermark
|
|
23
|
+
// key (purpose absent) stays byte-identical to the pre-client#41 key — already
|
|
24
|
+
// persisted watermarks are not orphaned by the upgrade. The 'stream' delivery
|
|
25
|
+
// cursor gets a distinct key.
|
|
26
|
+
if (binding.purpose) {
|
|
27
|
+
hash.update('\0').update(binding.purpose);
|
|
28
|
+
}
|
|
29
|
+
return hash.digest('hex');
|
|
30
|
+
}
|
|
31
|
+
function validCursor(value) {
|
|
32
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value))
|
|
33
|
+
return false;
|
|
34
|
+
const cursor = value;
|
|
35
|
+
return UUID_RE.test(String(cursor.id ?? '')) &&
|
|
36
|
+
typeof cursor.created_at === 'string' &&
|
|
37
|
+
Number.isFinite(Date.parse(cursor.created_at)) &&
|
|
38
|
+
new Date(cursor.created_at).toISOString() === cursor.created_at;
|
|
39
|
+
}
|
|
40
|
+
async function readState() {
|
|
41
|
+
try {
|
|
42
|
+
const parsed = JSON.parse(await readFile(CURSOR_FILE, 'utf8'));
|
|
43
|
+
if (parsed.version !== 1 ||
|
|
44
|
+
typeof parsed.cursors !== 'object' ||
|
|
45
|
+
parsed.cursors === null ||
|
|
46
|
+
Array.isArray(parsed.cursors) ||
|
|
47
|
+
Object.values(parsed.cursors).some((cursor) => !validCursor(cursor))) {
|
|
48
|
+
throw new Error('invalid');
|
|
49
|
+
}
|
|
50
|
+
return parsed;
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
if (error.code === 'ENOENT') {
|
|
54
|
+
return { version: 1, cursors: {} };
|
|
55
|
+
}
|
|
56
|
+
throw new Error('local Borg server cursor state is corrupt');
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
async function writeState(state) {
|
|
60
|
+
await mkdir(dirname(CURSOR_FILE), { recursive: true });
|
|
61
|
+
const temporary = `${CURSOR_FILE}.${process.pid}.${Date.now()}.tmp`;
|
|
62
|
+
await writeFile(temporary, JSON.stringify(state, null, 2) + '\n', { mode: 0o600 });
|
|
63
|
+
await rename(temporary, CURSOR_FILE);
|
|
64
|
+
}
|
|
65
|
+
async function withLock(operation) {
|
|
66
|
+
await mkdir(dirname(CURSOR_LOCK), { recursive: true });
|
|
67
|
+
for (let attempt = 0; attempt < 200; attempt += 1) {
|
|
68
|
+
let handle;
|
|
69
|
+
try {
|
|
70
|
+
handle = await open(CURSOR_LOCK, 'wx', 0o600);
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
if (error.code !== 'EEXIST')
|
|
74
|
+
throw error;
|
|
75
|
+
try {
|
|
76
|
+
const metadata = await stat(CURSOR_LOCK);
|
|
77
|
+
if (Date.now() - metadata.mtimeMs > 30_000) {
|
|
78
|
+
await unlink(CURSOR_LOCK);
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
catch (inspectionError) {
|
|
83
|
+
if (inspectionError.code === 'ENOENT')
|
|
84
|
+
continue;
|
|
85
|
+
throw inspectionError;
|
|
86
|
+
}
|
|
87
|
+
await new Promise((resolvePromise) => setTimeout(resolvePromise, 10));
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
try {
|
|
91
|
+
return await operation();
|
|
92
|
+
}
|
|
93
|
+
finally {
|
|
94
|
+
await handle.close();
|
|
95
|
+
try {
|
|
96
|
+
await unlink(CURSOR_LOCK);
|
|
97
|
+
}
|
|
98
|
+
catch (error) {
|
|
99
|
+
if (error.code !== 'ENOENT')
|
|
100
|
+
throw error;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
throw new Error('local Borg server cursor state is busy');
|
|
105
|
+
}
|
|
106
|
+
export async function getLocalServerCursor(binding) {
|
|
107
|
+
const key = cursorKey(binding);
|
|
108
|
+
const state = await readState();
|
|
109
|
+
return state.cursors[key] ?? null;
|
|
110
|
+
}
|
|
111
|
+
export async function advanceLocalServerCursor(binding, cursor) {
|
|
112
|
+
if (!validCursor(cursor))
|
|
113
|
+
throw new Error('invalid local Borg server cursor');
|
|
114
|
+
const key = cursorKey(binding);
|
|
115
|
+
await withLock(async () => {
|
|
116
|
+
const state = await readState();
|
|
117
|
+
const prior = state.cursors[key];
|
|
118
|
+
if (prior &&
|
|
119
|
+
(prior.created_at > cursor.created_at ||
|
|
120
|
+
(prior.created_at === cursor.created_at && prior.id >= cursor.id))) {
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
state.cursors[key] = cursor;
|
|
124
|
+
await writeState(state);
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* client#42: reset (delete) a persisted cursor for `binding`. Used by the SSE
|
|
129
|
+
* recovery path when the server returns 410 CURSOR_EXPIRED for the stream's
|
|
130
|
+
* resume cursor — the pointed-at entry has been pruned server-side, so the
|
|
131
|
+
* stale cursor can never be resumed and must be cleared, letting the next
|
|
132
|
+
* stream connect re-establish from a fresh valid point (the current tail)
|
|
133
|
+
* instead of looping forever on the dead cursor. No-op when no cursor is
|
|
134
|
+
* stored for the binding.
|
|
135
|
+
*/
|
|
136
|
+
export async function clearLocalServerCursor(binding) {
|
|
137
|
+
const key = cursorKey(binding);
|
|
138
|
+
await withLock(async () => {
|
|
139
|
+
const state = await readState();
|
|
140
|
+
if (!(key in state.cursors))
|
|
141
|
+
return;
|
|
142
|
+
delete state.cursors[key];
|
|
143
|
+
await writeState(state);
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
export function encodeLocalServerCursor(cursor) {
|
|
147
|
+
if (!validCursor(cursor))
|
|
148
|
+
throw new Error('invalid local Borg server cursor');
|
|
149
|
+
return Buffer.from(JSON.stringify(cursor), 'utf8').toString('base64url');
|
|
150
|
+
}
|
|
151
|
+
//# sourceMappingURL=local-server-cursor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"local-server-cursor.js","sourceRoot":"","sources":["../src/local-server-cursor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"log-audit.d.ts","sourceRoot":"","sources":["../src/log-audit.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;;;;;;GAuBG"}
|
package/dist/log-audit.js
CHANGED
|
@@ -1,4 +1,195 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
/**
|
|
3
|
+
* borg-log-audit
|
|
4
|
+
*
|
|
5
|
+
* Domain-agnostic nudge: scans the Claude Code transcript and emits a
|
|
6
|
+
* one-line warning to stdout if the drone has accumulated MATERIAL_THRESHOLD
|
|
7
|
+
* or more state-changing tool calls (Edit / Write / Bash / etc.) since the
|
|
8
|
+
* last borg_log post. Wired in as a UserPromptSubmit hook so the warning
|
|
9
|
+
* becomes additional context for the next turn.
|
|
10
|
+
*
|
|
11
|
+
* Two refinements vs the v1 1-tool threshold (per drone-6's review):
|
|
12
|
+
* 1. Counts material tools across all assistant turns until either the
|
|
13
|
+
* threshold is hit OR a borg_log call is found (cooldown). One
|
|
14
|
+
* diagnostic Bash no longer triggers; substantive work always does.
|
|
15
|
+
* 2. Any borg_log in the scanback suppresses the nudge — so the drone
|
|
16
|
+
* gets a turn of breathing room after each post.
|
|
17
|
+
*
|
|
18
|
+
* Stays generic — knows nothing about git, branches, or any project's
|
|
19
|
+
* conventions. Only the Anthropic tool name `mcp__borg__borg_log` and a
|
|
20
|
+
* small set of canonical mutating tool names. If no cube is active in
|
|
21
|
+
* this project, silently exits.
|
|
22
|
+
*
|
|
23
|
+
* Hook input arrives as JSON on stdin (Claude Code's standard hook
|
|
24
|
+
* contract). The relevant field is `transcript_path`.
|
|
25
|
+
*/
|
|
26
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
27
|
+
import { getActiveCube } from './cubes.js';
|
|
28
|
+
import { handleVersionFlag } from './version.js';
|
|
29
|
+
import { gateAllowsActivation } from './launch-gate.js';
|
|
30
|
+
const MATERIAL_TOOLS = new Set([
|
|
31
|
+
'Edit',
|
|
32
|
+
'Write',
|
|
33
|
+
'MultiEdit',
|
|
34
|
+
'NotebookEdit',
|
|
35
|
+
'Bash',
|
|
36
|
+
'apply_patch',
|
|
37
|
+
'exec_command',
|
|
38
|
+
'functions.exec_command',
|
|
39
|
+
'functions.apply_patch',
|
|
40
|
+
]);
|
|
41
|
+
const LOG_TOOL = 'mcp__borg__borg_log';
|
|
42
|
+
// Number of state-changing tool calls since the last borg_log that the
|
|
43
|
+
// drone is allowed before the audit nudges. 1 false-positives on
|
|
44
|
+
// diagnostic Bash (git status, ls, etc.); 3 has been comfortable in
|
|
45
|
+
// dogfooding — any substantive work crosses it within a turn or two.
|
|
46
|
+
const MATERIAL_THRESHOLD = 3;
|
|
47
|
+
// Cap on how many transcript entries we scan backwards before giving up.
|
|
48
|
+
// Sessions with thousands of turns still resolve in milliseconds at this
|
|
49
|
+
// bound, and anything truly old is no longer "the last span the drone
|
|
50
|
+
// failed to log."
|
|
51
|
+
const MAX_SCAN = 400;
|
|
52
|
+
function isUserPrompt(entry) {
|
|
53
|
+
const type = entry?.type ?? entry?.role;
|
|
54
|
+
if (type !== 'user')
|
|
55
|
+
return false;
|
|
56
|
+
const content = entry?.message?.content ?? entry?.content;
|
|
57
|
+
if (typeof content === 'string')
|
|
58
|
+
return content.trim().length > 0;
|
|
59
|
+
if (!Array.isArray(content))
|
|
60
|
+
return false;
|
|
61
|
+
// A "real" user prompt has at least one text block. A tool_result-only
|
|
62
|
+
// user message is a continuation of an assistant span, not a prompt.
|
|
63
|
+
return content.some((b) => b?.type === 'text');
|
|
64
|
+
}
|
|
65
|
+
function isAssistant(entry) {
|
|
66
|
+
const type = entry?.type ?? entry?.role;
|
|
67
|
+
return type === 'assistant' || type === 'response_item';
|
|
68
|
+
}
|
|
69
|
+
function scanAssistant(entry, state) {
|
|
70
|
+
const payload = entry?.payload;
|
|
71
|
+
const payloadToolName = payload?.type === 'function_call' || payload?.type === 'custom_tool_call'
|
|
72
|
+
? payload.name
|
|
73
|
+
: null;
|
|
74
|
+
if (typeof payloadToolName === 'string') {
|
|
75
|
+
if (payloadToolName === LOG_TOOL) {
|
|
76
|
+
state.loggedRecently = true;
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
if (MATERIAL_TOOLS.has(payloadToolName))
|
|
80
|
+
state.material += 1;
|
|
81
|
+
}
|
|
82
|
+
const content = entry?.message?.content ?? entry?.content ?? [];
|
|
83
|
+
if (!Array.isArray(content))
|
|
84
|
+
return;
|
|
85
|
+
// Walk the blocks newest-first WITHIN the entry. The caller already
|
|
86
|
+
// visits entries newest-first. Counting forward within an entry would
|
|
87
|
+
// either miss post-log material work (if log is in the same entry as
|
|
88
|
+
// later material blocks) or inflate the count with pre-log work that
|
|
89
|
+
// the log already covered. Reversing here keeps "material since the
|
|
90
|
+
// last log" honest at block granularity.
|
|
91
|
+
for (let i = content.length - 1; i >= 0; i--) {
|
|
92
|
+
const block = content[i];
|
|
93
|
+
if (block?.type !== 'tool_use')
|
|
94
|
+
continue;
|
|
95
|
+
if (block.name === LOG_TOOL) {
|
|
96
|
+
state.loggedRecently = true;
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
if (MATERIAL_TOOLS.has(block.name))
|
|
100
|
+
state.material += 1;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
async function readStdin() {
|
|
104
|
+
if (process.stdin.isTTY)
|
|
105
|
+
return '';
|
|
106
|
+
const chunks = [];
|
|
107
|
+
for await (const chunk of process.stdin)
|
|
108
|
+
chunks.push(chunk);
|
|
109
|
+
return Buffer.concat(chunks).toString('utf-8');
|
|
110
|
+
}
|
|
111
|
+
async function main() {
|
|
112
|
+
handleVersionFlag();
|
|
113
|
+
// gh#673 P1 (WI-4): the log-audit nudge only activates in
|
|
114
|
+
// borg-launched sessions — vanilla `claude` gets no borg hook output.
|
|
115
|
+
// Exit-0 no-op (a UserPromptSubmit hook must never block the prompt).
|
|
116
|
+
// ACTIVATION-only, never a security gate.
|
|
117
|
+
if (!gateAllowsActivation('borg-log-audit UserPromptSubmit hook')) {
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
const raw = await readStdin();
|
|
121
|
+
let input = {};
|
|
122
|
+
if (raw.trim()) {
|
|
123
|
+
try {
|
|
124
|
+
input = JSON.parse(raw);
|
|
125
|
+
}
|
|
126
|
+
catch {
|
|
127
|
+
// No usable input — silent exit.
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
if (!input.transcript_path || !existsSync(input.transcript_path))
|
|
132
|
+
return;
|
|
133
|
+
if (input.cwd && existsSync(input.cwd)) {
|
|
134
|
+
try {
|
|
135
|
+
process.chdir(input.cwd);
|
|
136
|
+
}
|
|
137
|
+
catch {
|
|
138
|
+
// Best-effort only; fall back to the hook process cwd.
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
// Only nudge if there's an active cube in this project. Otherwise the
|
|
142
|
+
// hook is fully inert.
|
|
143
|
+
const active = await getActiveCube();
|
|
144
|
+
if (!active)
|
|
145
|
+
return;
|
|
146
|
+
const lines = readFileSync(input.transcript_path, 'utf-8').split('\n').filter(Boolean);
|
|
147
|
+
if (lines.length === 0)
|
|
148
|
+
return;
|
|
149
|
+
// Walk the transcript backwards from the end. The trailing entry MAY
|
|
150
|
+
// be the user prompt that triggered this hook; skip it if so. From
|
|
151
|
+
// there, accumulate material tool calls until either we hit a borg_log
|
|
152
|
+
// (cooldown — suppress) or we cross MATERIAL_THRESHOLD (nudge). The
|
|
153
|
+
// scan stops after MAX_SCAN entries to bound work on huge sessions.
|
|
154
|
+
let i = lines.length - 1;
|
|
155
|
+
const tail = safeParse(lines[i]);
|
|
156
|
+
if (tail && isUserPrompt(tail))
|
|
157
|
+
i--;
|
|
158
|
+
const state = { material: 0, loggedRecently: false };
|
|
159
|
+
let scanned = 0;
|
|
160
|
+
for (; i >= 0 && scanned < MAX_SCAN; i--, scanned++) {
|
|
161
|
+
const entry = safeParse(lines[i]);
|
|
162
|
+
if (!entry)
|
|
163
|
+
continue;
|
|
164
|
+
if (isAssistant(entry)) {
|
|
165
|
+
scanAssistant(entry, state);
|
|
166
|
+
// Threshold has primacy over cooldown: when both could fire on the
|
|
167
|
+
// same entry (e.g. an entry containing [log, Bash, Bash, Bash]
|
|
168
|
+
// where the reversed scan first counts 3 material blocks before
|
|
169
|
+
// hitting the log), we want the nudge — the post-log material
|
|
170
|
+
// work hasn't been logged yet.
|
|
171
|
+
if (state.material >= MATERIAL_THRESHOLD) {
|
|
172
|
+
process.stdout.write(`Heads up: ${state.material}+ state-changing tool calls since the last \`borg_log\` post. ` +
|
|
173
|
+
'If that work was a substantive unit (a change that ships, a blocker hit, a finding ' +
|
|
174
|
+
"worth sharing), post to the cube log per your role's conventions before continuing.\n");
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
if (state.loggedRecently)
|
|
178
|
+
return; // cooldown
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
// Reached MAX_SCAN or start of transcript without finding either a
|
|
182
|
+
// log call or enough material work. Silent.
|
|
183
|
+
}
|
|
184
|
+
function safeParse(line) {
|
|
185
|
+
try {
|
|
186
|
+
return JSON.parse(line);
|
|
187
|
+
}
|
|
188
|
+
catch {
|
|
189
|
+
return null;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
main().catch(() => {
|
|
193
|
+
// Never fail a hook — silent on error.
|
|
194
|
+
});
|
|
195
|
+
//# sourceMappingURL=log-audit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"log-audit.js","sourceRoot":"","sources":["../src/log-audit.ts"],"names":[],"mappings":"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"}
|
package/dist/log-stream.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Real-time drone wakeup via Server-Sent Events.
|
|
3
3
|
*
|
|
4
|
-
* Replaces the long-poll + inbox-file-shim path
|
|
4
|
+
* Replaces the former long-poll + inbox-file-shim wire path
|
|
5
5
|
* for the wire layer; the local inbox file is preserved as the
|
|
6
6
|
* Claude-side wake primitive (Monitor on tail -F) because Claude Code
|
|
7
7
|
* does not currently wake idle agent loops on MCP-protocol
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* docs/superpowers/specs/2026-05-11-server-push-log-subscription.md
|
|
10
10
|
*
|
|
11
11
|
* Lifetimes:
|
|
12
|
-
* - One persistent fetch-streaming connection to
|
|
12
|
+
* - One persistent fetch-streaming connection to the cube-scoped stream
|
|
13
13
|
* per active cube.
|
|
14
14
|
* - On every received `event: log`, a single line is appended to the
|
|
15
15
|
* per-drone inbox file (same format the old poller wrote).
|
|
@@ -23,8 +23,29 @@
|
|
|
23
23
|
* MCP tool can probe without perturbing the stream (no second
|
|
24
24
|
* connection, no second auth — just an in-process state snapshot).
|
|
25
25
|
*/
|
|
26
|
+
import { type BroadcastHwm } from 'borgmcp-shared/log-stream-hwm';
|
|
26
27
|
import { getActiveCube } from './cubes.js';
|
|
28
|
+
import { loadBorgServerTrust } from './server-trust.js';
|
|
29
|
+
import { getLocalServerCursor, type LocalServerCursor } from './local-server-cursor.js';
|
|
27
30
|
import { acquireStreamLease, type StreamOwnershipSnapshot } from './stream-owner.js';
|
|
31
|
+
export declare const LOCAL_SERVER_SSE_FRAME_LIMIT_BYTES: number;
|
|
32
|
+
/**
|
|
33
|
+
* client#42: the server's structured code for an expired stream resume cursor
|
|
34
|
+
* (server 410 CURSOR_EXPIRED — the pointed-at entry was pruned). Recoverable,
|
|
35
|
+
* NOT terminal: distinct from DRONE_EVICTED.
|
|
36
|
+
*/
|
|
37
|
+
export declare const CURSOR_EXPIRED_CODE = "CURSOR_EXPIRED";
|
|
38
|
+
/**
|
|
39
|
+
* client#42: recoverable stream error thrown when the server rejects the resume
|
|
40
|
+
* cursor as expired. The resume cursor is reset BEFORE this is thrown, so the
|
|
41
|
+
* reconnect loop's next connect re-establishes from a fresh valid point instead
|
|
42
|
+
* of looping forever on the dead cursor. Deliberately NOT a DroneEvictedError —
|
|
43
|
+
* the reconnect loop treats only DroneEvictedError as terminal, so this falls
|
|
44
|
+
* through to the ordinary backoff-reconnect (which now recovers).
|
|
45
|
+
*/
|
|
46
|
+
export declare class StreamCursorExpiredError extends Error {
|
|
47
|
+
constructor(message?: string);
|
|
48
|
+
}
|
|
28
49
|
export declare function setModuleInjectOpenCode(fn: (text: string) => Promise<boolean>): void;
|
|
29
50
|
export declare const INBOX_TAIL_LINES_CAP = 512;
|
|
30
51
|
export declare const INBOX_TAIL_TRIM_THRESHOLD_LINES: number;
|
|
@@ -39,10 +60,6 @@ export interface StreamStatus {
|
|
|
39
60
|
runLoopRestartCount: number;
|
|
40
61
|
ownership?: StreamOwnershipSnapshot;
|
|
41
62
|
}
|
|
42
|
-
export interface BroadcastHwm {
|
|
43
|
-
id: string;
|
|
44
|
-
created_at: string;
|
|
45
|
-
}
|
|
46
63
|
/**
|
|
47
64
|
* Snapshot of the current stream status. Safe to call at any time
|
|
48
65
|
* (returns a copy, does not interact with the running connection).
|
|
@@ -94,12 +111,14 @@ export declare function startLogStream(opts?: {
|
|
|
94
111
|
export interface StreamDeps {
|
|
95
112
|
/** Override the global fetch (tests inject a controlled Response). */
|
|
96
113
|
fetchImpl?: typeof fetch;
|
|
114
|
+
/** Override persisted trust loading to verify pre-network confinement. */
|
|
115
|
+
loadTrust?: typeof loadBorgServerTrust;
|
|
116
|
+
/** Override stream cursor loading to verify pre-storage confinement. */
|
|
117
|
+
getCursor?: typeof getLocalServerCursor;
|
|
97
118
|
/** Override the inbox-line append (tests assert against the calls). */
|
|
98
119
|
appendLine?: (cubeId: string, droneId: string, line: string) => Promise<void>;
|
|
99
120
|
/** Override durable inbox-entry dedup (tests avoid real filesystem). */
|
|
100
121
|
hasInboxEntryId?: (cubeId: string, droneId: string, entryId: string, renderedLine: string) => Promise<boolean>;
|
|
101
|
-
/** Override the auth-token fetch (tests skip the OS keychain). */
|
|
102
|
-
getToken?: () => Promise<string>;
|
|
103
122
|
/** Optional Codex app-server wake sink; tests inject a spy. */
|
|
104
123
|
wakeCodex?: (reason: string) => void;
|
|
105
124
|
/** Override the heartbeat watchdog timeout. */
|
|
@@ -112,14 +131,6 @@ export interface StreamDeps {
|
|
|
112
131
|
ownerDeps?: import('./stream-owner.js').StreamOwnerDeps;
|
|
113
132
|
/** Override owner stale threshold in focused duplicate-process tests. */
|
|
114
133
|
ownerStaleMs?: number;
|
|
115
|
-
/**
|
|
116
|
-
* gh#541 WU-2: invoked after a delivered inbound entry is freshly written to
|
|
117
|
-
* the inbox file (the wake-path RECEIPT — the event that should wake the
|
|
118
|
-
* agent). Default records the receipt watermark + emits a best-effort health
|
|
119
|
-
* beat (child-process HTTP via the real global fetch — its own wire, NOT the
|
|
120
|
-
* SSE stream's fetchImpl — below the agent classifier). Tests inject a spy.
|
|
121
|
-
*/
|
|
122
|
-
onInboxReceipt?: (active: ActiveCube, token: string) => void;
|
|
123
134
|
/**
|
|
124
135
|
* Optional opencode entry injector for autonomous drone processing.
|
|
125
136
|
* When provided AND the injection succeeds, the inbox file write is skipped
|
|
@@ -140,6 +151,7 @@ export interface StreamDeps {
|
|
|
140
151
|
export interface RunLoopTestDeps {
|
|
141
152
|
getActiveCube?: typeof getActiveCube;
|
|
142
153
|
acquireStreamLease?: typeof acquireStreamLease;
|
|
154
|
+
streamOnce?: typeof streamOnce;
|
|
143
155
|
sleep?: (ms: number) => Promise<void>;
|
|
144
156
|
maxIterations?: number;
|
|
145
157
|
}
|
|
@@ -159,6 +171,7 @@ export interface ActiveCube {
|
|
|
159
171
|
droneId: string;
|
|
160
172
|
sessionToken: string;
|
|
161
173
|
apiUrl: string;
|
|
174
|
+
serverTrustIdentity?: string;
|
|
162
175
|
}
|
|
163
176
|
export declare function streamOnce(active: ActiveCube, lastEventId: string | null, onEventId: (id: string) => void, deps?: StreamDeps): Promise<void>;
|
|
164
177
|
export declare function streamOnceIfOwner(active: ActiveCube, lastEventId: string | null, onEventId: (id: string) => void, deps?: StreamDeps): Promise<'streamed' | 'skipped'>;
|
|
@@ -166,6 +179,7 @@ export type ParsedEvent = {
|
|
|
166
179
|
type: 'log';
|
|
167
180
|
id: string;
|
|
168
181
|
data: any;
|
|
182
|
+
cursor?: LocalServerCursor;
|
|
169
183
|
} | {
|
|
170
184
|
type: 'heartbeat';
|
|
171
185
|
ts: string | null;
|
|
@@ -187,7 +201,7 @@ export type ParsedEvent = {
|
|
|
187
201
|
*
|
|
188
202
|
* Exported so tests can pump a synthetic ReadableStream through it.
|
|
189
203
|
*/
|
|
190
|
-
export declare function parseSSE(body: ReadableStream<Uint8Array
|
|
204
|
+
export declare function parseSSE(body: ReadableStream<Uint8Array>, maxFrameBytes?: number): AsyncGenerator<ParsedEvent>;
|
|
191
205
|
export interface EnrichedEntry {
|
|
192
206
|
id?: string;
|
|
193
207
|
entry_id?: string;
|
|
@@ -196,7 +210,6 @@ export interface EnrichedEntry {
|
|
|
196
210
|
role_name?: string | null;
|
|
197
211
|
message?: string;
|
|
198
212
|
}
|
|
199
|
-
export declare function compareBroadcastHwm(a: BroadcastHwm, b: BroadcastHwm): number;
|
|
200
213
|
/**
|
|
201
214
|
* Format one inbox-file line. Preserves the long-poll inbox.ts prefix
|
|
202
215
|
* shape (`<iso-ts> <drone-label> (<role-name>): ...`) so existing
|