botmux 3.14.0 → 3.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +2 -2
- package/README.md +2 -2
- package/dist/.runtime-build-id +1 -1
- package/dist/adapters/backend/destroy-result.d.ts +159 -0
- package/dist/adapters/backend/destroy-result.d.ts.map +1 -0
- package/dist/adapters/backend/destroy-result.js +223 -0
- package/dist/adapters/backend/destroy-result.js.map +1 -0
- package/dist/adapters/backend/herdr-backend.d.ts +4 -0
- package/dist/adapters/backend/herdr-backend.d.ts.map +1 -1
- package/dist/adapters/backend/herdr-backend.js +23 -1
- package/dist/adapters/backend/herdr-backend.js.map +1 -1
- package/dist/adapters/backend/mojo-backend.d.ts +577 -0
- package/dist/adapters/backend/mojo-backend.d.ts.map +1 -0
- package/dist/adapters/backend/mojo-backend.js +2471 -0
- package/dist/adapters/backend/mojo-backend.js.map +1 -0
- package/dist/adapters/backend/mojo-budgets.d.ts +21 -0
- package/dist/adapters/backend/mojo-budgets.d.ts.map +1 -0
- package/dist/adapters/backend/mojo-budgets.js +22 -0
- package/dist/adapters/backend/mojo-budgets.js.map +1 -0
- package/dist/adapters/backend/mojo-isolated-workspace.d.ts +69 -0
- package/dist/adapters/backend/mojo-isolated-workspace.d.ts.map +1 -0
- package/dist/adapters/backend/mojo-isolated-workspace.js +213 -0
- package/dist/adapters/backend/mojo-isolated-workspace.js.map +1 -0
- package/dist/adapters/backend/mojo-process-tree.d.ts +292 -0
- package/dist/adapters/backend/mojo-process-tree.d.ts.map +1 -0
- package/dist/adapters/backend/mojo-process-tree.js +486 -0
- package/dist/adapters/backend/mojo-process-tree.js.map +1 -0
- package/dist/adapters/backend/mojo-types.d.ts +542 -0
- package/dist/adapters/backend/mojo-types.d.ts.map +1 -0
- package/dist/adapters/backend/mojo-types.js +682 -0
- package/dist/adapters/backend/mojo-types.js.map +1 -0
- package/dist/adapters/backend/reproduce-command.js +3 -3
- package/dist/adapters/backend/reproduce-command.js.map +1 -1
- package/dist/adapters/backend/riff-backend.js +1 -1
- package/dist/adapters/backend/riff-backend.js.map +1 -1
- package/dist/adapters/backend/sandbox.d.ts +44 -7
- package/dist/adapters/backend/sandbox.d.ts.map +1 -1
- package/dist/adapters/backend/sandbox.js +30 -7
- package/dist/adapters/backend/sandbox.js.map +1 -1
- package/dist/adapters/backend/session-backend-selector.d.ts +14 -1
- package/dist/adapters/backend/session-backend-selector.d.ts.map +1 -1
- package/dist/adapters/backend/session-backend-selector.js +82 -10
- package/dist/adapters/backend/session-backend-selector.js.map +1 -1
- package/dist/adapters/backend/tmux-pipe-backend.d.ts +30 -0
- package/dist/adapters/backend/tmux-pipe-backend.d.ts.map +1 -1
- package/dist/adapters/backend/tmux-pipe-backend.js +60 -0
- package/dist/adapters/backend/tmux-pipe-backend.js.map +1 -1
- package/dist/adapters/backend/types.d.ts +108 -6
- package/dist/adapters/backend/types.d.ts.map +1 -1
- package/dist/adapters/backend/types.js.map +1 -1
- package/dist/adapters/backend/zmx-backend.d.ts +11 -0
- package/dist/adapters/backend/zmx-backend.d.ts.map +1 -1
- package/dist/adapters/backend/zmx-backend.js +16 -1
- package/dist/adapters/backend/zmx-backend.js.map +1 -1
- package/dist/adapters/cli/codex-app.d.ts.map +1 -1
- package/dist/adapters/cli/codex-app.js +2 -2
- package/dist/adapters/cli/codex-app.js.map +1 -1
- package/dist/adapters/cli/codex.d.ts.map +1 -1
- package/dist/adapters/cli/codex.js +7 -0
- package/dist/adapters/cli/codex.js.map +1 -1
- package/dist/adapters/cli/copilot.d.ts +3 -1
- package/dist/adapters/cli/copilot.d.ts.map +1 -1
- package/dist/adapters/cli/copilot.js +23 -5
- package/dist/adapters/cli/copilot.js.map +1 -1
- package/dist/adapters/cli/cursor.d.ts.map +1 -1
- package/dist/adapters/cli/cursor.js +26 -4
- package/dist/adapters/cli/cursor.js.map +1 -1
- package/dist/adapters/cli/dsh.d.ts.map +1 -1
- package/dist/adapters/cli/dsh.js +5 -1
- package/dist/adapters/cli/dsh.js.map +1 -1
- package/dist/adapters/cli/grok.d.ts.map +1 -1
- package/dist/adapters/cli/grok.js +24 -5
- package/dist/adapters/cli/grok.js.map +1 -1
- package/dist/adapters/cli/kimi.d.ts.map +1 -1
- package/dist/adapters/cli/kimi.js +19 -1
- package/dist/adapters/cli/kimi.js.map +1 -1
- package/dist/adapters/cli/mojo.d.ts +28 -0
- package/dist/adapters/cli/mojo.d.ts.map +1 -0
- package/dist/adapters/cli/mojo.js +58 -0
- package/dist/adapters/cli/mojo.js.map +1 -0
- package/dist/adapters/cli/pi.d.ts +20 -5
- package/dist/adapters/cli/pi.d.ts.map +1 -1
- package/dist/adapters/cli/pi.js +27 -5
- package/dist/adapters/cli/pi.js.map +1 -1
- package/dist/adapters/cli/read-isolation.d.ts +251 -1
- package/dist/adapters/cli/read-isolation.d.ts.map +1 -1
- package/dist/adapters/cli/read-isolation.js +254 -1
- package/dist/adapters/cli/read-isolation.js.map +1 -1
- package/dist/adapters/cli/registry.d.ts +13 -1
- package/dist/adapters/cli/registry.d.ts.map +1 -1
- package/dist/adapters/cli/registry.js +19 -1
- package/dist/adapters/cli/registry.js.map +1 -1
- package/dist/adapters/cli/runner-input.d.ts +3 -3
- package/dist/adapters/cli/runner-input.d.ts.map +1 -1
- package/dist/adapters/cli/runner-input.js +4 -3
- package/dist/adapters/cli/runner-input.js.map +1 -1
- package/dist/adapters/cli/shared-hints.d.ts +3 -1
- package/dist/adapters/cli/shared-hints.d.ts.map +1 -1
- package/dist/adapters/cli/shared-hints.js +51 -30
- package/dist/adapters/cli/shared-hints.js.map +1 -1
- package/dist/adapters/cli/types.d.ts +33 -4
- package/dist/adapters/cli/types.d.ts.map +1 -1
- package/dist/bot-registry.d.ts +42 -0
- package/dist/bot-registry.d.ts.map +1 -1
- package/dist/bot-registry.js +54 -4
- package/dist/bot-registry.js.map +1 -1
- package/dist/cli/arg-utils.d.ts +5 -0
- package/dist/cli/arg-utils.d.ts.map +1 -1
- package/dist/cli/arg-utils.js +7 -0
- package/dist/cli/arg-utils.js.map +1 -1
- package/dist/cli/bots-list-output.d.ts +130 -2
- package/dist/cli/bots-list-output.d.ts.map +1 -1
- package/dist/cli/bots-list-output.js +173 -27
- package/dist/cli/bots-list-output.js.map +1 -1
- package/dist/cli/daemon-lifecycle-env.d.ts +10 -3
- package/dist/cli/daemon-lifecycle-env.d.ts.map +1 -1
- package/dist/cli/daemon-lifecycle-env.js +38 -11
- package/dist/cli/daemon-lifecycle-env.js.map +1 -1
- package/dist/cli/dashboard-command.d.ts +15 -0
- package/dist/cli/dashboard-command.d.ts.map +1 -1
- package/dist/cli/dashboard-command.js +22 -0
- package/dist/cli/dashboard-command.js.map +1 -1
- package/dist/cli/log-tail.d.ts +54 -0
- package/dist/cli/log-tail.d.ts.map +1 -0
- package/dist/cli/log-tail.js +167 -0
- package/dist/cli/log-tail.js.map +1 -0
- package/dist/cli/pm2-env.d.ts +16 -0
- package/dist/cli/pm2-env.d.ts.map +1 -0
- package/dist/cli/pm2-env.js +63 -0
- package/dist/cli/pm2-env.js.map +1 -0
- package/dist/cli/pm2-fleet-lock.d.ts +26 -0
- package/dist/cli/pm2-fleet-lock.d.ts.map +1 -0
- package/dist/cli/pm2-fleet-lock.js +50 -0
- package/dist/cli/pm2-fleet-lock.js.map +1 -0
- package/dist/cli/pm2-god-retirement.d.ts +59 -0
- package/dist/cli/pm2-god-retirement.d.ts.map +1 -0
- package/dist/cli/pm2-god-retirement.js +95 -0
- package/dist/cli/pm2-god-retirement.js.map +1 -0
- package/dist/cli/session-list-wake.d.ts +41 -0
- package/dist/cli/session-list-wake.d.ts.map +1 -0
- package/dist/cli/session-list-wake.js +102 -0
- package/dist/cli/session-list-wake.js.map +1 -0
- package/dist/cli/workflow.d.ts.map +1 -1
- package/dist/cli/workflow.js +26 -0
- package/dist/cli/workflow.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +941 -118
- package/dist/cli.js.map +1 -1
- package/dist/codex-app-runner.js +261 -49
- package/dist/codex-app-runner.js.map +1 -1
- package/dist/core/cli-runtime-update.d.ts +8 -0
- package/dist/core/cli-runtime-update.d.ts.map +1 -1
- package/dist/core/cli-runtime-update.js +127 -8
- package/dist/core/cli-runtime-update.js.map +1 -1
- package/dist/core/close-residual.d.ts +64 -0
- package/dist/core/close-residual.d.ts.map +1 -0
- package/dist/core/close-residual.js +93 -0
- package/dist/core/close-residual.js.map +1 -0
- package/dist/core/closed-session-card.d.ts.map +1 -1
- package/dist/core/closed-session-card.js +11 -3
- package/dist/core/closed-session-card.js.map +1 -1
- package/dist/core/command-discovery.d.ts.map +1 -1
- package/dist/core/command-discovery.js +4 -1
- package/dist/core/command-discovery.js.map +1 -1
- package/dist/core/command-handler.d.ts.map +1 -1
- package/dist/core/command-handler.js +129 -27
- package/dist/core/command-handler.js.map +1 -1
- package/dist/core/cost-calculator.d.ts +3 -1
- package/dist/core/cost-calculator.d.ts.map +1 -1
- package/dist/core/cost-calculator.js +230 -1
- package/dist/core/cost-calculator.js.map +1 -1
- package/dist/core/daemon-background-close.d.ts +19 -0
- package/dist/core/daemon-background-close.d.ts.map +1 -0
- package/dist/core/daemon-background-close.js +39 -0
- package/dist/core/daemon-background-close.js.map +1 -0
- package/dist/core/dashboard-command/overview.d.ts +4 -0
- package/dist/core/dashboard-command/overview.d.ts.map +1 -1
- package/dist/core/dashboard-command/overview.js +8 -1
- package/dist/core/dashboard-command/overview.js.map +1 -1
- package/dist/core/dashboard-ipc-server.d.ts.map +1 -1
- package/dist/core/dashboard-ipc-server.js +415 -42
- package/dist/core/dashboard-ipc-server.js.map +1 -1
- package/dist/core/dashboard-rows.d.ts +4 -0
- package/dist/core/dashboard-rows.d.ts.map +1 -1
- package/dist/core/dashboard-rows.js +3 -0
- package/dist/core/dashboard-rows.js.map +1 -1
- package/dist/core/dashboard-url.d.ts +37 -0
- package/dist/core/dashboard-url.d.ts.map +1 -1
- package/dist/core/dashboard-url.js +71 -0
- package/dist/core/dashboard-url.js.map +1 -1
- package/dist/core/deferred-schedule-settlement.d.ts +15 -1
- package/dist/core/deferred-schedule-settlement.d.ts.map +1 -1
- package/dist/core/deferred-schedule-settlement.js +4 -1
- package/dist/core/deferred-schedule-settlement.js.map +1 -1
- package/dist/core/device-isolation-daemon.d.ts +91 -1
- package/dist/core/device-isolation-daemon.d.ts.map +1 -1
- package/dist/core/device-isolation-daemon.js +346 -2
- package/dist/core/device-isolation-daemon.js.map +1 -1
- package/dist/core/local-terminal-opener.d.ts.map +1 -1
- package/dist/core/local-terminal-opener.js +5 -1
- package/dist/core/local-terminal-opener.js.map +1 -1
- package/dist/core/loopback-target.d.ts +14 -0
- package/dist/core/loopback-target.d.ts.map +1 -0
- package/dist/core/loopback-target.js +20 -0
- package/dist/core/loopback-target.js.map +1 -0
- package/dist/core/maintenance.d.ts.map +1 -1
- package/dist/core/maintenance.js +16 -2
- package/dist/core/maintenance.js.map +1 -1
- package/dist/core/mojo-containment-command.d.ts +20 -0
- package/dist/core/mojo-containment-command.d.ts.map +1 -0
- package/dist/core/mojo-containment-command.js +266 -0
- package/dist/core/mojo-containment-command.js.map +1 -0
- package/dist/core/mojo-containment.d.ts +575 -0
- package/dist/core/mojo-containment.d.ts.map +1 -0
- package/dist/core/mojo-containment.js +1158 -0
- package/dist/core/mojo-containment.js.map +1 -0
- package/dist/core/mojo-launcher-env-quarantine.d.ts +30 -0
- package/dist/core/mojo-launcher-env-quarantine.d.ts.map +1 -0
- package/dist/core/mojo-launcher-env-quarantine.js +175 -0
- package/dist/core/mojo-launcher-env-quarantine.js.map +1 -0
- package/dist/core/mojo-session-identity.d.ts +3 -0
- package/dist/core/mojo-session-identity.d.ts.map +1 -0
- package/dist/core/mojo-session-identity.js +126 -0
- package/dist/core/mojo-session-identity.js.map +1 -0
- package/dist/core/persistent-backend.d.ts +32 -8
- package/dist/core/persistent-backend.d.ts.map +1 -1
- package/dist/core/persistent-backend.js +55 -17
- package/dist/core/persistent-backend.js.map +1 -1
- package/dist/core/plugins/mcp/gateway.d.ts +14 -1
- package/dist/core/plugins/mcp/gateway.d.ts.map +1 -1
- package/dist/core/plugins/mcp/gateway.js +70 -3
- package/dist/core/plugins/mcp/gateway.js.map +1 -1
- package/dist/core/plugins/mcp/host.d.ts +2 -0
- package/dist/core/plugins/mcp/host.d.ts.map +1 -1
- package/dist/core/plugins/mcp/host.js +2 -0
- package/dist/core/plugins/mcp/host.js.map +1 -1
- package/dist/core/plugins/pm2.d.ts.map +1 -1
- package/dist/core/plugins/pm2.js +16 -3
- package/dist/core/plugins/pm2.js.map +1 -1
- package/dist/core/plugins/service-manager.d.ts +9 -0
- package/dist/core/plugins/service-manager.d.ts.map +1 -1
- package/dist/core/plugins/service-manager.js +33 -16
- package/dist/core/plugins/service-manager.js.map +1 -1
- package/dist/core/preview-port-owner.d.ts +109 -0
- package/dist/core/preview-port-owner.d.ts.map +1 -0
- package/dist/core/preview-port-owner.js +320 -0
- package/dist/core/preview-port-owner.js.map +1 -0
- package/dist/core/remote-cli-ids.d.ts +5 -0
- package/dist/core/remote-cli-ids.d.ts.map +1 -0
- package/dist/core/remote-cli-ids.js +24 -0
- package/dist/core/remote-cli-ids.js.map +1 -0
- package/dist/core/{riff-shutdown-detach.d.ts → remote-shutdown-detach.d.ts} +40 -39
- package/dist/core/remote-shutdown-detach.d.ts.map +1 -0
- package/dist/core/{riff-shutdown-detach.js → remote-shutdown-detach.js} +85 -83
- package/dist/core/remote-shutdown-detach.js.map +1 -0
- package/dist/core/{riff-worker-shutdown-readiness.d.ts → remote-worker-shutdown-readiness.d.ts} +5 -4
- package/dist/core/remote-worker-shutdown-readiness.d.ts.map +1 -0
- package/dist/core/{riff-worker-shutdown-readiness.js → remote-worker-shutdown-readiness.js} +4 -3
- package/dist/core/remote-worker-shutdown-readiness.js.map +1 -0
- package/dist/core/reply-target.d.ts +16 -1
- package/dist/core/reply-target.d.ts.map +1 -1
- package/dist/core/reply-target.js +61 -0
- package/dist/core/reply-target.js.map +1 -1
- package/dist/core/session-group-birth.d.ts +26 -0
- package/dist/core/session-group-birth.d.ts.map +1 -1
- package/dist/core/session-group-birth.js +81 -4
- package/dist/core/session-group-birth.js.map +1 -1
- package/dist/core/session-manager.d.ts +15 -2
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +259 -5
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/session-model.d.ts +46 -0
- package/dist/core/session-model.d.ts.map +1 -0
- package/dist/core/session-model.js +42 -0
- package/dist/core/session-model.js.map +1 -0
- package/dist/core/session-owner-reminder.d.ts +63 -0
- package/dist/core/session-owner-reminder.d.ts.map +1 -0
- package/dist/core/session-owner-reminder.js +196 -0
- package/dist/core/session-owner-reminder.js.map +1 -0
- package/dist/core/session-preview-registry.d.ts +53 -0
- package/dist/core/session-preview-registry.d.ts.map +1 -0
- package/dist/core/session-preview-registry.js +82 -0
- package/dist/core/session-preview-registry.js.map +1 -0
- package/dist/core/session-preview.d.ts +114 -0
- package/dist/core/session-preview.d.ts.map +1 -0
- package/dist/core/session-preview.js +184 -0
- package/dist/core/session-preview.js.map +1 -0
- package/dist/core/session-rename.d.ts.map +1 -1
- package/dist/core/session-rename.js +5 -3
- package/dist/core/session-rename.js.map +1 -1
- package/dist/core/session-wake-deadline.d.ts +5 -0
- package/dist/core/session-wake-deadline.d.ts.map +1 -0
- package/dist/core/session-wake-deadline.js +13 -0
- package/dist/core/session-wake-deadline.js.map +1 -0
- package/dist/core/shutdown-budgets.d.ts +7 -6
- package/dist/core/shutdown-budgets.d.ts.map +1 -1
- package/dist/core/shutdown-budgets.js +11 -10
- package/dist/core/shutdown-budgets.js.map +1 -1
- package/dist/core/skills/frontmatter.d.ts.map +1 -1
- package/dist/core/skills/frontmatter.js +6 -1
- package/dist/core/skills/frontmatter.js.map +1 -1
- package/dist/core/terminal-control-grant.d.ts +74 -0
- package/dist/core/terminal-control-grant.d.ts.map +1 -0
- package/dist/core/terminal-control-grant.js +183 -0
- package/dist/core/terminal-control-grant.js.map +1 -0
- package/dist/core/terminal-proxy.d.ts.map +1 -1
- package/dist/core/terminal-proxy.js +4 -5
- package/dist/core/terminal-proxy.js.map +1 -1
- package/dist/core/terminal-write-auth.d.ts +4 -11
- package/dist/core/terminal-write-auth.d.ts.map +1 -1
- package/dist/core/terminal-write-auth.js +19 -16
- package/dist/core/terminal-write-auth.js.map +1 -1
- package/dist/core/trigger-session.d.ts.map +1 -1
- package/dist/core/trigger-session.js +36 -29
- package/dist/core/trigger-session.js.map +1 -1
- package/dist/core/types.d.ts +88 -10
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js +9 -7
- package/dist/core/types.js.map +1 -1
- package/dist/core/workbench-link.d.ts +81 -0
- package/dist/core/workbench-link.d.ts.map +1 -0
- package/dist/core/workbench-link.js +109 -0
- package/dist/core/workbench-link.js.map +1 -0
- package/dist/core/worker-pool.d.ts +312 -11
- package/dist/core/worker-pool.d.ts.map +1 -1
- package/dist/core/worker-pool.js +2515 -217
- package/dist/core/worker-pool.js.map +1 -1
- package/dist/daemon.d.ts +6 -0
- package/dist/daemon.d.ts.map +1 -1
- package/dist/daemon.js +447 -116
- package/dist/daemon.js.map +1 -1
- package/dist/dashboard/aggregator.d.ts +33 -3
- package/dist/dashboard/aggregator.d.ts.map +1 -1
- package/dist/dashboard/aggregator.js +93 -17
- package/dist/dashboard/aggregator.js.map +1 -1
- package/dist/dashboard/auth-session-connections.d.ts +59 -0
- package/dist/dashboard/auth-session-connections.d.ts.map +1 -0
- package/dist/dashboard/auth-session-connections.js +109 -0
- package/dist/dashboard/auth-session-connections.js.map +1 -0
- package/dist/dashboard/auth.d.ts +99 -1
- package/dist/dashboard/auth.d.ts.map +1 -1
- package/dist/dashboard/auth.js +176 -64
- package/dist/dashboard/auth.js.map +1 -1
- package/dist/dashboard/bot-payload.d.ts +5 -0
- package/dist/dashboard/bot-payload.d.ts.map +1 -1
- package/dist/dashboard/bot-payload.js +4 -0
- package/dist/dashboard/bot-payload.js.map +1 -1
- package/dist/dashboard/control-audit.d.ts +112 -0
- package/dist/dashboard/control-audit.d.ts.map +1 -0
- package/dist/dashboard/control-audit.js +195 -0
- package/dist/dashboard/control-audit.js.map +1 -0
- package/dist/dashboard/control-csrf.d.ts +123 -0
- package/dist/dashboard/control-csrf.d.ts.map +1 -0
- package/dist/dashboard/control-csrf.js +392 -0
- package/dist/dashboard/control-csrf.js.map +1 -0
- package/dist/dashboard/daemon-reconcile.d.ts +26 -0
- package/dist/dashboard/daemon-reconcile.d.ts.map +1 -0
- package/dist/dashboard/daemon-reconcile.js +46 -0
- package/dist/dashboard/daemon-reconcile.js.map +1 -0
- package/dist/dashboard/debug-terminal.d.ts.map +1 -1
- package/dist/dashboard/debug-terminal.js +18 -4
- package/dist/dashboard/debug-terminal.js.map +1 -1
- package/dist/dashboard/events-sse.d.ts +60 -0
- package/dist/dashboard/events-sse.d.ts.map +1 -0
- package/dist/dashboard/events-sse.js +63 -0
- package/dist/dashboard/events-sse.js.map +1 -0
- package/dist/dashboard/h5-auth.d.ts +256 -0
- package/dist/dashboard/h5-auth.d.ts.map +1 -0
- package/dist/dashboard/h5-auth.js +825 -0
- package/dist/dashboard/h5-auth.js.map +1 -0
- package/dist/dashboard/managed-spawn.d.ts +36 -0
- package/dist/dashboard/managed-spawn.d.ts.map +1 -0
- package/dist/dashboard/managed-spawn.js +169 -0
- package/dist/dashboard/managed-spawn.js.map +1 -0
- package/dist/dashboard/preview-content-capability.d.ts +41 -0
- package/dist/dashboard/preview-content-capability.d.ts.map +1 -0
- package/dist/dashboard/preview-content-capability.js +127 -0
- package/dist/dashboard/preview-content-capability.js.map +1 -0
- package/dist/dashboard/preview-contract.d.ts +85 -0
- package/dist/dashboard/preview-contract.d.ts.map +1 -0
- package/dist/dashboard/preview-contract.js +198 -0
- package/dist/dashboard/preview-contract.js.map +1 -0
- package/dist/dashboard/preview-guard-page.d.ts +52 -0
- package/dist/dashboard/preview-guard-page.d.ts.map +1 -0
- package/dist/dashboard/preview-guard-page.js +162 -0
- package/dist/dashboard/preview-guard-page.js.map +1 -0
- package/dist/dashboard/preview-interaction.d.ts +57 -0
- package/dist/dashboard/preview-interaction.d.ts.map +1 -0
- package/dist/dashboard/preview-interaction.js +198 -0
- package/dist/dashboard/preview-interaction.js.map +1 -0
- package/dist/dashboard/preview-proxy.d.ts +113 -0
- package/dist/dashboard/preview-proxy.d.ts.map +1 -0
- package/dist/dashboard/preview-proxy.js +543 -0
- package/dist/dashboard/preview-proxy.js.map +1 -0
- package/dist/dashboard/public-redact.d.ts +23 -0
- package/dist/dashboard/public-redact.d.ts.map +1 -1
- package/dist/dashboard/public-redact.js +90 -21
- package/dist/dashboard/public-redact.js.map +1 -1
- package/dist/dashboard/request-identity.d.ts +77 -0
- package/dist/dashboard/request-identity.d.ts.map +1 -0
- package/dist/dashboard/request-identity.js +72 -0
- package/dist/dashboard/request-identity.js.map +1 -0
- package/dist/dashboard/session-cleanup.d.ts +5 -0
- package/dist/dashboard/session-cleanup.d.ts.map +1 -1
- package/dist/dashboard/session-cleanup.js +4 -0
- package/dist/dashboard/session-cleanup.js.map +1 -1
- package/dist/dashboard/settings-write-applier.d.ts +5 -1
- package/dist/dashboard/settings-write-applier.d.ts.map +1 -1
- package/dist/dashboard/settings-write-applier.js +14 -0
- package/dist/dashboard/settings-write-applier.js.map +1 -1
- package/dist/dashboard/standing-link.d.ts +76 -0
- package/dist/dashboard/standing-link.d.ts.map +1 -0
- package/dist/dashboard/standing-link.js +111 -0
- package/dist/dashboard/standing-link.js.map +1 -0
- package/dist/dashboard/terminal-control.d.ts +105 -0
- package/dist/dashboard/terminal-control.d.ts.map +1 -0
- package/dist/dashboard/terminal-control.js +296 -0
- package/dist/dashboard/terminal-control.js.map +1 -0
- package/dist/dashboard/terminal-front-proxy.d.ts +55 -0
- package/dist/dashboard/terminal-front-proxy.d.ts.map +1 -0
- package/dist/dashboard/terminal-front-proxy.js +397 -0
- package/dist/dashboard/terminal-front-proxy.js.map +1 -0
- package/dist/dashboard/terminal-view-capability.d.ts +97 -0
- package/dist/dashboard/terminal-view-capability.d.ts.map +1 -0
- package/dist/dashboard/terminal-view-capability.js +145 -0
- package/dist/dashboard/terminal-view-capability.js.map +1 -0
- package/dist/dashboard/web/agent-workbench-api.d.ts +59 -0
- package/dist/dashboard/web/agent-workbench-api.d.ts.map +1 -0
- package/dist/dashboard/web/agent-workbench-api.js +227 -0
- package/dist/dashboard/web/agent-workbench-api.js.map +1 -0
- package/dist/dashboard/web/agent-workbench-appearance-menu.d.ts +59 -0
- package/dist/dashboard/web/agent-workbench-appearance-menu.d.ts.map +1 -0
- package/dist/dashboard/web/agent-workbench-appearance-menu.js +195 -0
- package/dist/dashboard/web/agent-workbench-appearance-menu.js.map +1 -0
- package/dist/dashboard/web/agent-workbench-appearance.d.ts +222 -0
- package/dist/dashboard/web/agent-workbench-appearance.d.ts.map +1 -0
- package/dist/dashboard/web/agent-workbench-appearance.js +499 -0
- package/dist/dashboard/web/agent-workbench-appearance.js.map +1 -0
- package/dist/dashboard/web/agent-workbench-capabilities.d.ts +13 -0
- package/dist/dashboard/web/agent-workbench-capabilities.d.ts.map +1 -0
- package/dist/dashboard/web/agent-workbench-capabilities.js +24 -0
- package/dist/dashboard/web/agent-workbench-capabilities.js.map +1 -0
- package/dist/dashboard/web/agent-workbench-chat.d.ts +90 -0
- package/dist/dashboard/web/agent-workbench-chat.d.ts.map +1 -0
- package/dist/dashboard/web/agent-workbench-chat.js +168 -0
- package/dist/dashboard/web/agent-workbench-chat.js.map +1 -0
- package/dist/dashboard/web/agent-workbench-dock-page.d.ts +3 -0
- package/dist/dashboard/web/agent-workbench-dock-page.d.ts.map +1 -0
- package/dist/dashboard/web/agent-workbench-dock-page.js +23 -0
- package/dist/dashboard/web/agent-workbench-dock-page.js.map +1 -0
- package/dist/dashboard/web/agent-workbench-dock-view.d.ts +22 -0
- package/dist/dashboard/web/agent-workbench-dock-view.d.ts.map +1 -0
- package/dist/dashboard/web/agent-workbench-dock-view.js +76 -0
- package/dist/dashboard/web/agent-workbench-dock-view.js.map +1 -0
- package/dist/dashboard/web/agent-workbench-model.d.ts +196 -0
- package/dist/dashboard/web/agent-workbench-model.d.ts.map +1 -0
- package/dist/dashboard/web/agent-workbench-model.js +529 -0
- package/dist/dashboard/web/agent-workbench-model.js.map +1 -0
- package/dist/dashboard/web/agent-workbench-page.d.ts +3 -0
- package/dist/dashboard/web/agent-workbench-page.d.ts.map +1 -0
- package/dist/dashboard/web/agent-workbench-page.js +27 -0
- package/dist/dashboard/web/agent-workbench-page.js.map +1 -0
- package/dist/dashboard/web/agent-workbench-panes.d.ts +49 -0
- package/dist/dashboard/web/agent-workbench-panes.d.ts.map +1 -0
- package/dist/dashboard/web/agent-workbench-panes.js +468 -0
- package/dist/dashboard/web/agent-workbench-panes.js.map +1 -0
- package/dist/dashboard/web/agent-workbench-session-list.d.ts +42 -0
- package/dist/dashboard/web/agent-workbench-session-list.d.ts.map +1 -0
- package/dist/dashboard/web/agent-workbench-session-list.js +353 -0
- package/dist/dashboard/web/agent-workbench-session-list.js.map +1 -0
- package/dist/dashboard/web/agent-workbench-storage.d.ts +49 -0
- package/dist/dashboard/web/agent-workbench-storage.d.ts.map +1 -0
- package/dist/dashboard/web/agent-workbench-storage.js +197 -0
- package/dist/dashboard/web/agent-workbench-storage.js.map +1 -0
- package/dist/dashboard/web/agent-workbench-touch.d.ts +46 -0
- package/dist/dashboard/web/agent-workbench-touch.d.ts.map +1 -0
- package/dist/dashboard/web/agent-workbench-touch.js +96 -0
- package/dist/dashboard/web/agent-workbench-touch.js.map +1 -0
- package/dist/dashboard/web/agent-workbench-view.d.ts +34 -0
- package/dist/dashboard/web/agent-workbench-view.d.ts.map +1 -0
- package/dist/dashboard/web/agent-workbench-view.js +439 -0
- package/dist/dashboard/web/agent-workbench-view.js.map +1 -0
- package/dist/dashboard/web/app.d.ts.map +1 -1
- package/dist/dashboard/web/app.js +63 -2
- package/dist/dashboard/web/app.js.map +1 -1
- package/dist/dashboard/web/bot-defaults-page.d.ts +18 -0
- package/dist/dashboard/web/bot-defaults-page.d.ts.map +1 -1
- package/dist/dashboard/web/bot-defaults-page.js +362 -36
- package/dist/dashboard/web/bot-defaults-page.js.map +1 -1
- package/dist/dashboard/web/bot-defaults.d.ts +8 -0
- package/dist/dashboard/web/bot-defaults.d.ts.map +1 -1
- package/dist/dashboard/web/bot-defaults.js.map +1 -1
- package/dist/dashboard/web/control-csrf.d.ts +4 -0
- package/dist/dashboard/web/control-csrf.d.ts.map +1 -0
- package/dist/dashboard/web/control-csrf.js +26 -0
- package/dist/dashboard/web/control-csrf.js.map +1 -0
- package/dist/dashboard/web/dashboard-routes.d.ts.map +1 -1
- package/dist/dashboard/web/dashboard-routes.js +2 -0
- package/dist/dashboard/web/dashboard-routes.js.map +1 -1
- package/dist/dashboard/web/groups-page.d.ts.map +1 -1
- package/dist/dashboard/web/groups-page.js +11 -2
- package/dist/dashboard/web/groups-page.js.map +1 -1
- package/dist/dashboard/web/i18n.d.ts.map +1 -1
- package/dist/dashboard/web/i18n.js +140 -6
- package/dist/dashboard/web/i18n.js.map +1 -1
- package/dist/dashboard/web/overview-page.d.ts.map +1 -1
- package/dist/dashboard/web/overview-page.js +3 -2
- package/dist/dashboard/web/overview-page.js.map +1 -1
- package/dist/dashboard/web/sessions-page.d.ts.map +1 -1
- package/dist/dashboard/web/sessions-page.js +41 -1
- package/dist/dashboard/web/sessions-page.js.map +1 -1
- package/dist/dashboard/web/sessions.d.ts.map +1 -1
- package/dist/dashboard/web/sessions.js +5 -1
- package/dist/dashboard/web/sessions.js.map +1 -1
- package/dist/dashboard/web/settings-page.d.ts +3 -0
- package/dist/dashboard/web/settings-page.d.ts.map +1 -1
- package/dist/dashboard/web/settings-page.js +56 -10
- package/dist/dashboard/web/settings-page.js.map +1 -1
- package/dist/dashboard/web/skills-page.d.ts.map +1 -1
- package/dist/dashboard/web/skills-page.js +4 -2
- package/dist/dashboard/web/skills-page.js.map +1 -1
- package/dist/dashboard/web/store.d.ts +27 -2
- package/dist/dashboard/web/store.d.ts.map +1 -1
- package/dist/dashboard/web/store.js +43 -4
- package/dist/dashboard/web/store.js.map +1 -1
- package/dist/dashboard/web/ui.d.ts +3 -0
- package/dist/dashboard/web/ui.d.ts.map +1 -1
- package/dist/dashboard/web/ui.js +11 -0
- package/dist/dashboard/web/ui.js.map +1 -1
- package/dist/dashboard/web/update-action.d.ts +12 -0
- package/dist/dashboard/web/update-action.d.ts.map +1 -1
- package/dist/dashboard/web/update-action.js +11 -0
- package/dist/dashboard/web/update-action.js.map +1 -1
- package/dist/dashboard/workbench-doctor.d.ts +114 -0
- package/dist/dashboard/workbench-doctor.d.ts.map +1 -0
- package/dist/dashboard/workbench-doctor.js +620 -0
- package/dist/dashboard/workbench-doctor.js.map +1 -0
- package/dist/dashboard/workbench-ticket.d.ts +165 -0
- package/dist/dashboard/workbench-ticket.d.ts.map +1 -0
- package/dist/dashboard/workbench-ticket.js +495 -0
- package/dist/dashboard/workbench-ticket.js.map +1 -0
- package/dist/dashboard-web/app.js +1 -1
- package/dist/dashboard-web/chunks/agent-workbench-dock-page-IKAC5CFL.js +1 -0
- package/dist/dashboard-web/chunks/agent-workbench-page-M53UN5EB.js +1 -0
- package/dist/dashboard-web/chunks/bot-defaults-page-YKE5SDZF.js +10 -0
- package/dist/dashboard-web/chunks/chunk-22WIADUT.js +2 -0
- package/dist/dashboard-web/chunks/chunk-24ONYQMR.js +1 -0
- package/dist/dashboard-web/chunks/{chunk-6O7KXFD2.js → chunk-2W6SCN3M.js} +1 -1
- package/dist/dashboard-web/chunks/chunk-4XNJX6B3.js +1 -0
- package/dist/dashboard-web/chunks/{chunk-Q2MT4VWH.js → chunk-F6KNPF4R.js} +11 -9
- package/dist/dashboard-web/chunks/chunk-FXW3V4JR.js +1 -0
- package/dist/dashboard-web/chunks/chunk-G5ZS2YUS.js +1 -0
- package/dist/dashboard-web/chunks/chunk-KTQ7B6ZR.js +1 -0
- package/dist/dashboard-web/chunks/{chunk-U3ZFSO7E.js → chunk-NACP7BMR.js} +1 -1
- package/dist/dashboard-web/chunks/{connectors-page-2KZO5KSJ.js → connectors-page-VOISXXII.js} +1 -1
- package/dist/dashboard-web/chunks/{feedback-page-CLVTOLLO.js → feedback-page-RN6HRZMS.js} +1 -1
- package/dist/dashboard-web/chunks/groups-page-UQT6DCB7.js +8 -0
- package/dist/dashboard-web/chunks/{insights-page-4RPU2CRU.js → insights-page-HJFDYR5N.js} +3 -3
- package/dist/dashboard-web/chunks/{monitor-room-OTKQ2CXK.js → monitor-room-V7PCP47V.js} +1 -1
- package/dist/dashboard-web/chunks/{monitoring-page-IJPUB6JT.js → monitoring-page-NKEKVQAP.js} +1 -1
- package/dist/dashboard-web/chunks/{office-page-QSR3J44Y.js → office-page-GVJF73XW.js} +1 -1
- package/dist/dashboard-web/chunks/overview-page-KHLV6VTI.js +1 -0
- package/dist/dashboard-web/chunks/{plugin-page-VEGTCEAS.js → plugin-page-J3OF5IY4.js} +1 -1
- package/dist/dashboard-web/chunks/{roles-page-6NWJMOB5.js → roles-page-WQIRSOLZ.js} +1 -1
- package/dist/dashboard-web/chunks/{schedules-page-6I3LNXD4.js → schedules-page-J2DEY2X6.js} +1 -1
- package/dist/dashboard-web/chunks/sessions-page-7BQFEA5P.js +8 -0
- package/dist/dashboard-web/chunks/settings-page-BJEIOFJH.js +2 -0
- package/dist/dashboard-web/chunks/{skills-page-M2Q6L5CB.js → skills-page-HGIFR3IU.js} +2 -2
- package/dist/dashboard-web/chunks/{team-federation-page-7I7FDE3B.js → team-federation-page-MR5ZPL4B.js} +1 -1
- package/dist/dashboard-web/chunks/{v3-page-HQXRL65L.js → v3-page-GBR5JWPE.js} +2 -2
- package/dist/dashboard-web/chunks/{whiteboards-page-LQKC4TAZ.js → whiteboards-page-QAZM4BN3.js} +1 -1
- package/dist/dashboard-web/index.html +8 -1
- package/dist/dashboard-web/style.css +2006 -10
- package/dist/dashboard.js +1017 -264
- package/dist/dashboard.js.map +1 -1
- package/dist/dsh-runner.js +8 -1
- package/dist/dsh-runner.js.map +1 -1
- package/dist/global-config.d.ts +30 -0
- package/dist/global-config.d.ts.map +1 -1
- package/dist/global-config.js +33 -0
- package/dist/global-config.js.map +1 -1
- package/dist/i18n/en.d.ts.map +1 -1
- package/dist/i18n/en.js +46 -27
- package/dist/i18n/en.js.map +1 -1
- package/dist/i18n/zh.d.ts.map +1 -1
- package/dist/i18n/zh.js +43 -27
- package/dist/i18n/zh.js.map +1 -1
- package/dist/im/lark/card-builder.d.ts +1 -1
- package/dist/im/lark/card-builder.d.ts.map +1 -1
- package/dist/im/lark/card-builder.js +22 -6
- package/dist/im/lark/card-builder.js.map +1 -1
- package/dist/im/lark/card-handler.d.ts.map +1 -1
- package/dist/im/lark/card-handler.js +118 -24
- package/dist/im/lark/card-handler.js.map +1 -1
- package/dist/im/lark/client.d.ts +15 -0
- package/dist/im/lark/client.d.ts.map +1 -1
- package/dist/im/lark/client.js +30 -0
- package/dist/im/lark/client.js.map +1 -1
- package/dist/im/lark/event-dispatcher.d.ts +48 -0
- package/dist/im/lark/event-dispatcher.d.ts.map +1 -1
- package/dist/im/lark/event-dispatcher.js +138 -41
- package/dist/im/lark/event-dispatcher.js.map +1 -1
- package/dist/im/lark/identity-cache.d.ts +14 -6
- package/dist/im/lark/identity-cache.d.ts.map +1 -1
- package/dist/im/lark/identity-cache.js +69 -26
- package/dist/im/lark/identity-cache.js.map +1 -1
- package/dist/im/lark/lark-hosts.d.ts +13 -0
- package/dist/im/lark/lark-hosts.d.ts.map +1 -1
- package/dist/im/lark/lark-hosts.js +16 -0
- package/dist/im/lark/lark-hosts.js.map +1 -1
- package/dist/im/lark/overview-card.d.ts +22 -0
- package/dist/im/lark/overview-card.d.ts.map +1 -1
- package/dist/im/lark/overview-card.js +53 -3
- package/dist/im/lark/overview-card.js.map +1 -1
- package/dist/im/lark/sessions-card.d.ts.map +1 -1
- package/dist/im/lark/sessions-card.js +26 -3
- package/dist/im/lark/sessions-card.js.map +1 -1
- package/dist/im/lark/workflow-slash-command.d.ts +6 -0
- package/dist/im/lark/workflow-slash-command.d.ts.map +1 -1
- package/dist/im/lark/workflow-slash-command.js +7 -0
- package/dist/im/lark/workflow-slash-command.js.map +1 -1
- package/dist/index-daemon.js +27 -4
- package/dist/index-daemon.js.map +1 -1
- package/dist/index-dashboard.d.ts +3 -0
- package/dist/index-dashboard.d.ts.map +1 -0
- package/dist/index-dashboard.js +94 -0
- package/dist/index-dashboard.js.map +1 -0
- package/dist/platform/team-agents-client.d.ts +199 -0
- package/dist/platform/team-agents-client.d.ts.map +1 -0
- package/dist/platform/team-agents-client.js +263 -0
- package/dist/platform/team-agents-client.js.map +1 -0
- package/dist/platform/tunnel-client.d.ts +6 -0
- package/dist/platform/tunnel-client.d.ts.map +1 -1
- package/dist/platform/tunnel-client.js.map +1 -1
- package/dist/services/async-trigger-state.d.ts +7 -3
- package/dist/services/async-trigger-state.d.ts.map +1 -1
- package/dist/services/async-trigger-state.js +28 -0
- package/dist/services/async-trigger-state.js.map +1 -1
- package/dist/services/async-trigger-store.d.ts +13 -4
- package/dist/services/async-trigger-store.d.ts.map +1 -1
- package/dist/services/async-trigger-store.js +55 -8
- package/dist/services/async-trigger-store.js.map +1 -1
- package/dist/services/bot-config-store.d.ts +1 -11
- package/dist/services/bot-config-store.d.ts.map +1 -1
- package/dist/services/bot-config-store.js +27 -3
- package/dist/services/bot-config-store.js.map +1 -1
- package/dist/services/bot-profile-store.d.ts +17 -2
- package/dist/services/bot-profile-store.d.ts.map +1 -1
- package/dist/services/bot-profile-store.js +93 -12
- package/dist/services/bot-profile-store.js.map +1 -1
- package/dist/services/bridge-fallback-gate.d.ts +15 -7
- package/dist/services/bridge-fallback-gate.d.ts.map +1 -1
- package/dist/services/bridge-fallback-gate.js +43 -16
- package/dist/services/bridge-fallback-gate.js.map +1 -1
- package/dist/services/bridge-turn-queue.d.ts +10 -1
- package/dist/services/bridge-turn-queue.d.ts.map +1 -1
- package/dist/services/bridge-turn-queue.js +29 -9
- package/dist/services/bridge-turn-queue.js.map +1 -1
- package/dist/services/claude-transcript.d.ts +19 -0
- package/dist/services/claude-transcript.d.ts.map +1 -1
- package/dist/services/claude-transcript.js +57 -5
- package/dist/services/claude-transcript.js.map +1 -1
- package/dist/services/codex-app-threads.d.ts +2 -0
- package/dist/services/codex-app-threads.d.ts.map +1 -1
- package/dist/services/codex-app-threads.js +1 -1
- package/dist/services/codex-app-threads.js.map +1 -1
- package/dist/services/codex-reasoning-effort.d.ts +8 -0
- package/dist/services/codex-reasoning-effort.d.ts.map +1 -1
- package/dist/services/codex-reasoning-effort.js +24 -0
- package/dist/services/codex-reasoning-effort.js.map +1 -1
- package/dist/services/cursor-resume-policy.d.ts.map +1 -1
- package/dist/services/cursor-resume-policy.js +15 -3
- package/dist/services/cursor-resume-policy.js.map +1 -1
- package/dist/services/open-platform-rename.d.ts.map +1 -1
- package/dist/services/open-platform-rename.js +3 -2
- package/dist/services/open-platform-rename.js.map +1 -1
- package/dist/services/ordinary-turn-recovery.d.ts +76 -0
- package/dist/services/ordinary-turn-recovery.d.ts.map +1 -0
- package/dist/services/ordinary-turn-recovery.js +349 -0
- package/dist/services/ordinary-turn-recovery.js.map +1 -0
- package/dist/services/peer-cross-ref-store.d.ts +8 -0
- package/dist/services/peer-cross-ref-store.d.ts.map +1 -0
- package/dist/services/peer-cross-ref-store.js +95 -0
- package/dist/services/peer-cross-ref-store.js.map +1 -0
- package/dist/services/pi-transcript.d.ts.map +1 -1
- package/dist/services/pi-transcript.js +10 -6
- package/dist/services/pi-transcript.js.map +1 -1
- package/dist/services/resume-fresh-policy.d.ts +17 -0
- package/dist/services/resume-fresh-policy.d.ts.map +1 -0
- package/dist/services/resume-fresh-policy.js +24 -0
- package/dist/services/resume-fresh-policy.js.map +1 -0
- package/dist/services/session-group-title.d.ts +1 -0
- package/dist/services/session-group-title.d.ts.map +1 -1
- package/dist/services/session-group-title.js +16 -1
- package/dist/services/session-group-title.js.map +1 -1
- package/dist/services/session-groups-store.d.ts +30 -0
- package/dist/services/session-groups-store.d.ts.map +1 -1
- package/dist/services/session-groups-store.js +26 -4
- package/dist/services/session-groups-store.js.map +1 -1
- package/dist/services/session-owner-notification.d.ts +9 -0
- package/dist/services/session-owner-notification.d.ts.map +1 -0
- package/dist/services/session-owner-notification.js +10 -0
- package/dist/services/session-owner-notification.js.map +1 -0
- package/dist/services/session-owner-reminder-config-store.d.ts +9 -0
- package/dist/services/session-owner-reminder-config-store.d.ts.map +1 -0
- package/dist/services/session-owner-reminder-config-store.js +24 -0
- package/dist/services/session-owner-reminder-config-store.js.map +1 -0
- package/dist/services/session-owner-reminder-store.d.ts +5 -0
- package/dist/services/session-owner-reminder-store.d.ts.map +1 -0
- package/dist/services/session-owner-reminder-store.js +54 -0
- package/dist/services/session-owner-reminder-store.js.map +1 -0
- package/dist/services/session-store.d.ts +98 -21
- package/dist/services/session-store.d.ts.map +1 -1
- package/dist/services/session-store.js +406 -48
- package/dist/services/session-store.js.map +1 -1
- package/dist/services/structured-bridge-clis.d.ts +15 -2
- package/dist/services/structured-bridge-clis.d.ts.map +1 -1
- package/dist/services/structured-bridge-clis.js +16 -1
- package/dist/services/structured-bridge-clis.js.map +1 -1
- package/dist/services/transcript-resolver.d.ts +1 -1
- package/dist/services/transcript-resolver.d.ts.map +1 -1
- package/dist/services/transcript-resolver.js +6 -1
- package/dist/services/transcript-resolver.js.map +1 -1
- package/dist/setup/bot-config-editor.d.ts.map +1 -1
- package/dist/setup/bot-config-editor.js +7 -2
- package/dist/setup/bot-config-editor.js.map +1 -1
- package/dist/setup/ensure-herdr-integrations.d.ts +13 -0
- package/dist/setup/ensure-herdr-integrations.d.ts.map +1 -1
- package/dist/setup/ensure-herdr-integrations.js +21 -3
- package/dist/setup/ensure-herdr-integrations.js.map +1 -1
- package/dist/setup/open-platform-automation.d.ts +1 -0
- package/dist/setup/open-platform-automation.d.ts.map +1 -1
- package/dist/setup/open-platform-automation.js +107 -7
- package/dist/setup/open-platform-automation.js.map +1 -1
- package/dist/skills/definitions.d.ts +17 -0
- package/dist/skills/definitions.d.ts.map +1 -1
- package/dist/skills/definitions.js +78 -8
- package/dist/skills/definitions.js.map +1 -1
- package/dist/skills/injection-mode.d.ts +24 -3
- package/dist/skills/injection-mode.d.ts.map +1 -1
- package/dist/skills/injection-mode.js +64 -15
- package/dist/skills/injection-mode.js.map +1 -1
- package/dist/skills/installer.d.ts +18 -0
- package/dist/skills/installer.d.ts.map +1 -1
- package/dist/skills/installer.js +45 -1
- package/dist/skills/installer.js.map +1 -1
- package/dist/types.d.ts +222 -10
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/bot-routing.d.ts +1 -1
- package/dist/utils/bot-routing.d.ts.map +1 -1
- package/dist/utils/bot-routing.js.map +1 -1
- package/dist/utils/child-env.d.ts +92 -2
- package/dist/utils/child-env.d.ts.map +1 -1
- package/dist/utils/child-env.js +195 -1
- package/dist/utils/child-env.js.map +1 -1
- package/dist/utils/cli-usage-limit.d.ts +25 -14
- package/dist/utils/cli-usage-limit.d.ts.map +1 -1
- package/dist/utils/cli-usage-limit.js +25 -15
- package/dist/utils/cli-usage-limit.js.map +1 -1
- package/dist/utils/daemon-rejection-guard.d.ts +35 -0
- package/dist/utils/daemon-rejection-guard.d.ts.map +1 -0
- package/dist/utils/daemon-rejection-guard.js +101 -0
- package/dist/utils/daemon-rejection-guard.js.map +1 -0
- package/dist/utils/dashboard-env.d.ts +53 -0
- package/dist/utils/dashboard-env.d.ts.map +1 -0
- package/dist/utils/dashboard-env.js +116 -0
- package/dist/utils/dashboard-env.js.map +1 -0
- package/dist/utils/global-install.d.ts.map +1 -1
- package/dist/utils/global-install.js +61 -5
- package/dist/utils/global-install.js.map +1 -1
- package/dist/utils/install-diagnostics.d.ts +14 -7
- package/dist/utils/install-diagnostics.d.ts.map +1 -1
- package/dist/utils/install-diagnostics.js +20 -11
- package/dist/utils/install-diagnostics.js.map +1 -1
- package/dist/utils/local-dev-update.d.ts +132 -0
- package/dist/utils/local-dev-update.d.ts.map +1 -0
- package/dist/utils/local-dev-update.js +176 -0
- package/dist/utils/local-dev-update.js.map +1 -0
- package/dist/utils/pending-input-queue.d.ts +12 -1
- package/dist/utils/pending-input-queue.d.ts.map +1 -1
- package/dist/utils/pending-input-queue.js.map +1 -1
- package/dist/worker.js +1517 -236
- package/dist/worker.js.map +1 -1
- package/dist/workflows/definition.d.ts +68 -68
- package/dist/workflows/events/payloads.d.ts +14 -14
- package/dist/workflows/events/schema.d.ts +312 -312
- package/dist/workflows/migration/v2-run-archive-schema.d.ts +40 -40
- package/dist/workflows/v3/cli-run.d.ts.map +1 -1
- package/dist/workflows/v3/cli-run.js +7 -0
- package/dist/workflows/v3/cli-run.js.map +1 -1
- package/dist/workflows/v3/goal-cli.d.ts +3 -0
- package/dist/workflows/v3/goal-cli.d.ts.map +1 -1
- package/dist/workflows/v3/goal-cli.js +11 -0
- package/dist/workflows/v3/goal-cli.js.map +1 -1
- package/dist/workflows/v3/run-envelope.d.ts +144 -144
- package/package.json +7 -2
- package/dist/cli/pm2-god-admission.d.ts +0 -8
- package/dist/cli/pm2-god-admission.d.ts.map +0 -1
- package/dist/cli/pm2-god-admission.js +0 -25
- package/dist/cli/pm2-god-admission.js.map +0 -1
- package/dist/core/riff-shutdown-detach.d.ts.map +0 -1
- package/dist/core/riff-shutdown-detach.js.map +0 -1
- package/dist/core/riff-worker-shutdown-readiness.d.ts.map +0 -1
- package/dist/core/riff-worker-shutdown-readiness.js.map +0 -1
- package/dist/dashboard-web/chunks/bot-defaults-page-4RENHE7J.js +0 -10
- package/dist/dashboard-web/chunks/chunk-2WQDS65D.js +0 -1
- package/dist/dashboard-web/chunks/chunk-G5XGBCM6.js +0 -1
- package/dist/dashboard-web/chunks/chunk-WLIVBPJC.js +0 -1
- package/dist/dashboard-web/chunks/groups-page-HLASRTSY.js +0 -8
- package/dist/dashboard-web/chunks/overview-page-6OK63V4V.js +0 -1
- package/dist/dashboard-web/chunks/sessions-page-B2TXGJDO.js +0 -8
- package/dist/dashboard-web/chunks/settings-page-6KJADSC6.js +0 -2
package/dist/cli.js
CHANGED
|
@@ -9,22 +9,24 @@
|
|
|
9
9
|
* botmux start — start daemon and auto plugin services
|
|
10
10
|
* botmux stop [--with-plugin] — stop daemon (optionally stop auto plugin services)
|
|
11
11
|
* botmux restart [--include-pm2] [--with-plugin] — restart daemon, then ensure auto plugin services;
|
|
12
|
-
* --include-pm2
|
|
12
|
+
* --include-pm2 additionally retires the PM2 God after the fleet is verified retired
|
|
13
|
+
* (socket-addressed `pm2 kill`, never a PID signal) so the whole tree restarts from a fresh env
|
|
13
14
|
* botmux restart --bootstrap-shutdown-protocol --yes — operator-approved one-time retirement
|
|
14
15
|
* of a pre-protocol fleet after independently confirming all Session/Riff work is idle
|
|
15
16
|
* botmux logs [--lines] — view daemon logs
|
|
16
17
|
* botmux status — show daemon status
|
|
17
|
-
* botmux upgrade|update — upgrade to latest version
|
|
18
|
+
* botmux upgrade|update — upgrade to latest version (本地 checkout 则 git pull --ff-only + rebuild + restart)
|
|
18
19
|
* botmux device enroll|status|logout — manage the host desktop device credential
|
|
19
20
|
* botmux list — interactive session picker (TUI), attach to managed tmux/ZMX sessions
|
|
20
21
|
* botmux list --plain — plain table output (for piping / scripts)
|
|
22
|
+
* botmux preview <port> — register this session's loopback Web preview
|
|
21
23
|
* botmux delete <id> — close a session by ID prefix
|
|
22
24
|
* botmux delete all — close all active sessions
|
|
23
25
|
* botmux autostart enable|disable|status — manage boot-time autostart (launchd / user systemd / Windows Task Scheduler)
|
|
24
26
|
* botmux whiteboard status|enable|disable|current|list|read|update|write — local project whiteboard
|
|
25
27
|
*/
|
|
26
28
|
import { execSync, execFileSync, spawnSync, spawn } from 'node:child_process';
|
|
27
|
-
import { existsSync, mkdirSync, copyFileSync, readFileSync, writeFileSync, renameSync, readdirSync, readlinkSync, symlinkSync, appendFileSync, statSync, unlinkSync, rmSync, realpathSync } from 'node:fs';
|
|
29
|
+
import { existsSync, mkdirSync, copyFileSync, readFileSync, writeFileSync, renameSync, readdirSync, readlinkSync, symlinkSync, appendFileSync, statSync, unlinkSync, rmSync, realpathSync, chmodSync } from 'node:fs';
|
|
28
30
|
import { underReadIsolation, sendCredFilePath } from './adapters/cli/read-isolation.js';
|
|
29
31
|
import { atomicWriteFileSync } from './utils/atomic-write.js';
|
|
30
32
|
import { join, dirname, basename, resolve } from 'node:path';
|
|
@@ -34,6 +36,7 @@ import { createInterface } from 'node:readline';
|
|
|
34
36
|
import { createRequire } from 'node:module';
|
|
35
37
|
import { randomBytes, randomUUID } from 'node:crypto';
|
|
36
38
|
import { validateWorkingDir } from './core/working-dir.js';
|
|
39
|
+
import { closeResidualClause, parseCloseResidual } from './core/close-residual.js';
|
|
37
40
|
import { findAncestorSessionContext as findLiveAncestorSessionContext, resolveSessionContext, } from './core/session-marker.js';
|
|
38
41
|
import { resolveBotmuxDataDir } from './core/data-dir.js';
|
|
39
42
|
import { dashboardSecretPath } from './core/dashboard-secret.js';
|
|
@@ -55,13 +58,15 @@ import { validateCodexAppManagedSendOrigin, } from './utils/codex-app-dispatch-l
|
|
|
55
58
|
import { hasProtectedSessionMutationOwnership } from './core/session-mutation-guard.js';
|
|
56
59
|
import { logger } from './utils/logger.js';
|
|
57
60
|
import { withFileLock, withFileLockSync } from './utils/file-lock.js';
|
|
58
|
-
import {
|
|
61
|
+
import { pm2CallerEnv } from './cli/pm2-env.js';
|
|
59
62
|
import { scheduleTimeZone } from './utils/timezone.js';
|
|
60
63
|
import { expandHomePath, invalidWorkingDirs } from './utils/working-dir.js';
|
|
61
|
-
import { firstPositional } from './cli/arg-utils.js';
|
|
64
|
+
import { firstPositional, hasFlagOrEq } from './cli/arg-utils.js';
|
|
62
65
|
import { isColdResumeDormant, isRealManagedSession, sessionListDisposition } from './cli/session-list-liveness.js';
|
|
63
66
|
import { computeSessionPickerLayout, } from './cli/session-picker-layout.js';
|
|
64
67
|
import { computeSessionPickerScrollWindow } from './cli/session-picker-viewport.js';
|
|
68
|
+
import { canWakeDormantBackendForAttach, wakeDormantBackendForAttach, } from './cli/session-list-wake.js';
|
|
69
|
+
import { SESSION_WAKE_DEADLINE_HEADER } from './core/session-wake-deadline.js';
|
|
65
70
|
import { terminalCellWidth } from './cli/terminal-width.js';
|
|
66
71
|
import { attachFrozenManagedZmxSession, freezeManagedZmxAttachTarget, } from './cli/zmx-managed-attach.js';
|
|
67
72
|
import { readSupervisorProcessStartIdentity } from './core/process-start-identity.js';
|
|
@@ -81,13 +86,17 @@ import { assertPm2DaemonShutdownCapabilitiesIn } from './cli/pm2-shutdown-capabi
|
|
|
81
86
|
import { evaluateRestartShutdownPreflight } from './cli/restart-shutdown-preflight.js';
|
|
82
87
|
import { recordAndNotifyRestartBootstrapFailure, restartFailurePathIn, } from './cli/restart-failure-notification.js';
|
|
83
88
|
import { resolveRestartFailureOwner } from './cli/restart-failure-owner.js';
|
|
84
|
-
import {
|
|
89
|
+
import { assertNoReplacementPm2God, assertPm2RegistryQuiescentForGodRetirement, retireSoleLivePm2God, } from './cli/pm2-god-retirement.js';
|
|
90
|
+
import { pm2FleetMutationLockTarget, withPm2FleetMutationLock } from './cli/pm2-fleet-lock.js';
|
|
91
|
+
import { LogFileFollower } from './cli/log-tail.js';
|
|
85
92
|
import { requestAttestedDaemonShutdown, requestAttestedDaemonShutdownBatch, } from './cli/supervisor-shutdown-client.js';
|
|
86
93
|
import { assertDaemonPm2GracefulExitPolicy, assertConfiguredPm2FleetReady, assertExactAttestedDaemonSet, classifyStartBotFleetAdmission, normalizeRawPm2StopExitCodes, reconcileLatePm2StartPublication, runBoundedPm2StartTransaction, } from './cli/pm2-start-transaction.js';
|
|
87
94
|
import { callDashboard } from './cli/dashboard-endpoint.js';
|
|
88
|
-
import { DASHBOARD_COMMAND_USAGE, executeDashboardCommand, formatDashboardFallbackFailure, } from './cli/dashboard-command.js';
|
|
89
|
-
import { globalInstallUpdateLockTargetIn, installLatestBotmuxSync } from './core/maintenance.js';
|
|
95
|
+
import { DASHBOARD_COMMAND_USAGE, executeDashboardCommand, formatDashboardFallbackFailure, formatDashboardSuccessLines, } from './cli/dashboard-command.js';
|
|
96
|
+
import { globalInstallUpdateLockTarget, globalInstallUpdateLockTargetIn, installLatestBotmuxSync } from './core/maintenance.js';
|
|
90
97
|
import { formatGlobalInstallCommand, resolveGlobalInstallPlan, UnsupportedGlobalInstallError, } from './utils/global-install.js';
|
|
98
|
+
import { isLocalDevInstall, botmuxCliEntryAt } from './utils/install-info.js';
|
|
99
|
+
import { resolveLocalDevCheckoutDir, isGitWorktree, gitPorcelainStatus, localDevUpdateSteps, describeSpawnFailure, } from './utils/local-dev-update.js';
|
|
91
100
|
import { loadDashboardSecret } from './dashboard/auth.js';
|
|
92
101
|
import { postWorkflowDaemonMutation, } from './workflows/v3/daemon-ipc-client.js';
|
|
93
102
|
import { postWorkflowSessionRunMutation, readWorkflowSessionRelayContext, } from './workflows/v3/session-relay-client.js';
|
|
@@ -98,14 +107,14 @@ import { isRetryableAskHttpStatus } from './core/ask-types.js';
|
|
|
98
107
|
import { hasManagedOriginIsolationMarker, managedOriginDataRootProbeAccess, managedOriginIsolationSentinelAccess, managedOriginLegacyIsolationProbeAccess, readManagedOriginRootLocator, readManagedOriginCapability, } from './core/managed-origin-capability.js';
|
|
99
108
|
import { attestManagedOrigin, } from './core/managed-origin-attestation.js';
|
|
100
109
|
import { rejectLikelyWindowsStdinMojibake, decodeStdinBytes } from './cli/stdin-encoding.js';
|
|
101
|
-
import { formatBotInfoEntriesForCli, formatChatBotsForCli, } from './cli/bots-list-output.js';
|
|
110
|
+
import { collaborationHelp, formatBotInfoEntriesForCli, formatChatBotsForCli, } from './cli/bots-list-output.js';
|
|
102
111
|
import { ensureBotChatGrantMatrix, requestExactChatGrant } from './cli/exact-chat-grant-client.js';
|
|
103
112
|
import { buildFooterAddressing, hasKnownBotMention, knownBotOpenIdsFromCrossRef, orderedFooterRecipients, stripCodeSpans, } from './utils/bot-routing.js';
|
|
104
113
|
import { isLocale, localeForBot, setDefaultLocale, SUPPORTED_LOCALES, t } from './i18n/index.js';
|
|
105
114
|
import { chatAppLink, larkHosts, normalizeBrand } from './im/lark/lark-hosts.js';
|
|
106
115
|
import { mergeDashboardConfig, mergeGlobalConfig, readGlobalConfig, setGlobalLocale, globalConfigPath } from './global-config.js';
|
|
107
116
|
import { createWhiteboard, ensureDefaultWhiteboard, getWhiteboard, listWhiteboards, readWhiteboard, whiteboardEnabled, whiteboardPath, } from './services/whiteboard-store.js';
|
|
108
|
-
import { buildBridgeSendMarkerContent, buildBridgeSendPreviewText, } from './services/bridge-fallback-gate.js';
|
|
117
|
+
import { buildBridgeSendMarkerContent, buildBridgeSendPreviewText, stripTrailingOaiMemoryCitation, } from './services/bridge-fallback-gate.js';
|
|
109
118
|
import { bindRestartLeaseTo, commitRestartIntentAttemptTo, consumeRestartIntentTo, removeRestartIntentAttemptTo, writeRestartAttemptIntentTo, } from './services/restart-intent-store.js';
|
|
110
119
|
import { loadAllSessionsSnapshot, mutateSessionRowOffline } from './services/session-store.js';
|
|
111
120
|
import { evaluateVcMeetingManagedSend, isTrustedVcMeetingHostRelayParent, resolveVcMeetingImTurnOrigin, } from './services/vc-meeting-send-policy.js';
|
|
@@ -151,8 +160,12 @@ const PM2_NAME = 'botmux';
|
|
|
151
160
|
* when those external pm2 installations get moved or removed.
|
|
152
161
|
*/
|
|
153
162
|
const PM2_HOME = join(CONFIG_DIR, 'pm2');
|
|
154
|
-
|
|
163
|
+
// Shared with plugin service-manager (src/cli/pm2-fleet-lock.ts): one lock
|
|
164
|
+
// serializes every botmux-internal mutation of the shared PM2_HOME.
|
|
165
|
+
const PM2_FLEET_MUTATION_LOCK_TARGET = pm2FleetMutationLockTarget();
|
|
155
166
|
const PM2_START_COMMAND_TIMEOUT_MS = 30_000;
|
|
167
|
+
// `pm2 kill` with an already-empty fleet only tears down the God + socket.
|
|
168
|
+
const PM2_GOD_KILL_COMMAND_TIMEOUT_MS = 30_000;
|
|
156
169
|
const PM2_START_VERIFY_MIN_TIMEOUT_MS = 60_000;
|
|
157
170
|
const PM2_START_VERIFY_PER_PROCESS_MS = 2_000;
|
|
158
171
|
const PM2_START_LATE_PUBLICATION_SETTLE_MS = 10_000;
|
|
@@ -190,28 +203,12 @@ function pm2Bin() {
|
|
|
190
203
|
return symlink;
|
|
191
204
|
return 'pm2';
|
|
192
205
|
}
|
|
193
|
-
/** Env for pm2 invocations with an isolated PM2_HOME.
|
|
206
|
+
/** Env for pm2 invocations with an isolated PM2_HOME. The scrub set (session
|
|
207
|
+
* CLI homes, Claude/workflow markers, Dashboard H5 credentials, invoker
|
|
208
|
+
* terminal fingerprints, turn-scoped session identity — plus the TERM
|
|
209
|
+
* re-pin) lives in cli/pm2-env.ts so it stays assertable in a test. */
|
|
194
210
|
function pm2Env(home = PM2_HOME) {
|
|
195
|
-
|
|
196
|
-
// pm2 persists the caller's env into every managed app (and into dump.pm2
|
|
197
|
-
// for resurrect), so a `botmux start/restart` invoked from ANY process that
|
|
198
|
-
// carries a session-level CLI home pointer — most commonly a bot's own
|
|
199
|
-
// session during self-upgrade, whose env holds its injected
|
|
200
|
-
// CLAUDE_CONFIG_DIR — would poison ALL workers, making every non-isolated
|
|
201
|
-
// bot read/write a sibling bot's home. Strip at this boundary so the daemon
|
|
202
|
-
// stays session-agnostic; daemon/worker boot scrub the same keys against
|
|
203
|
-
// stale dumps (see SESSION_CLI_HOME_ENV_KEYS for the full story, including
|
|
204
|
-
// why GROK_HOME is exempt and why deleting beats pinning a default).
|
|
205
|
-
scrubSessionCliHomeEnv(env);
|
|
206
|
-
// Claude session markers ride the same pm2 env-persistence vector; baked in
|
|
207
|
-
// they eventually flip transcript saving off fleet-wide once the tmux server
|
|
208
|
-
// respawns from a poisoned daemon (see CLAUDE_SESSION_MARKER_ENV_KEYS).
|
|
209
|
-
scrubClaudeSessionMarkerEnv(env);
|
|
210
|
-
// Workflow/goal markers identify one short-lived node worker. Persisting
|
|
211
|
-
// them in PM2 would make every daemon — and then every ordinary chat worker
|
|
212
|
-
// it forks — run in workflow mode after a restart initiated from that node.
|
|
213
|
-
scrubWorkflowWorkerEnv(env);
|
|
214
|
-
return env;
|
|
211
|
+
return pm2CallerEnv(process.env, home);
|
|
215
212
|
}
|
|
216
213
|
function listPm2GodDaemonPids(home = PM2_HOME) {
|
|
217
214
|
const marker = `God Daemon (${home})`;
|
|
@@ -716,7 +713,11 @@ function ecosystemConfig(bots = loadBotsJson(), activationAppId) {
|
|
|
716
713
|
});
|
|
717
714
|
apps.push({
|
|
718
715
|
name: 'botmux-dashboard',
|
|
719
|
-
|
|
716
|
+
// index-dashboard.ts, NOT dashboard.js: the entry dotenv-loads
|
|
717
|
+
// ~/.botmux/.env before dynamically importing the dashboard, which is how
|
|
718
|
+
// the Feishu H5 credential family reaches the dashboard WITHOUT being baked
|
|
719
|
+
// into this file's shared env block (see DAEMON_ENV_KEYS).
|
|
720
|
+
script: join(PKG_ROOT, 'dist', 'index-dashboard.js'),
|
|
720
721
|
interpreter,
|
|
721
722
|
cwd: PKG_ROOT,
|
|
722
723
|
autorestart: true,
|
|
@@ -742,7 +743,22 @@ function ecosystemConfig(bots = loadBotsJson(), activationAppId) {
|
|
|
742
743
|
});
|
|
743
744
|
const cfg = { apps };
|
|
744
745
|
const tmpFile = join(CONFIG_DIR, 'ecosystem.config.json');
|
|
745
|
-
|
|
746
|
+
// Owner-only, same as bots.json — kept as defense in depth. The Dashboard
|
|
747
|
+
// Feishu H5 app secret is no longer baked into the env block (the dashboard
|
|
748
|
+
// dotenv-loads it via index-dashboard.ts; DAEMON_ENV_KEYS excludes the
|
|
749
|
+
// family), but this file still bakes operator-configured hosts/ports/paths,
|
|
750
|
+
// an older install's copy may still carry the secret until overwritten, and
|
|
751
|
+
// 0600 keeps any future env-block regression from becoming world-readable.
|
|
752
|
+
// writeFileSync's `mode` applies only when the file is CREATED — chmod as
|
|
753
|
+
// well, or an install that already wrote it 0644 keeps the world-readable
|
|
754
|
+
// mode forever.
|
|
755
|
+
writeFileSync(tmpFile, JSON.stringify(cfg, null, 2), { mode: 0o600 });
|
|
756
|
+
if (process.platform !== 'win32') {
|
|
757
|
+
try {
|
|
758
|
+
chmodSync(tmpFile, 0o600);
|
|
759
|
+
}
|
|
760
|
+
catch { /* best-effort: exotic FS / mounted noperm */ }
|
|
761
|
+
}
|
|
746
762
|
return tmpFile;
|
|
747
763
|
}
|
|
748
764
|
function hasConfig() {
|
|
@@ -2454,7 +2470,7 @@ async function cmdStart() {
|
|
|
2454
2470
|
process.exit(1);
|
|
2455
2471
|
}
|
|
2456
2472
|
}
|
|
2457
|
-
await
|
|
2473
|
+
await withPm2FleetMutationLock(async () => {
|
|
2458
2474
|
await withFileLock(BOTS_JSON_FILE, async () => {
|
|
2459
2475
|
const lockedBots = loadBotsJson();
|
|
2460
2476
|
if (JSON.stringify(lockedBots) !== JSON.stringify(botsForCheck)) {
|
|
@@ -3088,7 +3104,7 @@ async function cmdStop() {
|
|
|
3088
3104
|
throw new Error('[stop] --bootstrap-shutdown-protocol requires --yes after confirming every Session/Riff workload is idle');
|
|
3089
3105
|
}
|
|
3090
3106
|
ensureConfigDir();
|
|
3091
|
-
await
|
|
3107
|
+
await withPm2FleetMutationLock(async () => {
|
|
3092
3108
|
assertNoDuplicatePm2GodDaemons();
|
|
3093
3109
|
cleanupLegacyPm2(bootstrapShutdownProtocol ? 'stop' : undefined);
|
|
3094
3110
|
if (bootstrapShutdownProtocol) {
|
|
@@ -3186,7 +3202,7 @@ async function cmdRestart() {
|
|
|
3186
3202
|
process.exit(1);
|
|
3187
3203
|
}
|
|
3188
3204
|
ensureConfigDir();
|
|
3189
|
-
await
|
|
3205
|
+
await withPm2FleetMutationLock(async () => {
|
|
3190
3206
|
const includePm2 = process.argv.includes('--include-pm2');
|
|
3191
3207
|
const includePluginServices = process.argv.includes('--with-plugin');
|
|
3192
3208
|
const bootstrapShutdownProtocol = process.argv.includes('--bootstrap-shutdown-protocol');
|
|
@@ -3197,9 +3213,6 @@ async function cmdRestart() {
|
|
|
3197
3213
|
if (bootstrapShutdownProtocol && includePm2) {
|
|
3198
3214
|
throw new Error('[restart] --bootstrap-shutdown-protocol cannot be combined with --include-pm2');
|
|
3199
3215
|
}
|
|
3200
|
-
if (includePm2) {
|
|
3201
|
-
assertIncludePm2RestartAdmission(listPm2GodDaemonPids());
|
|
3202
|
-
}
|
|
3203
3216
|
const restartIntentDir = resolveDataDir();
|
|
3204
3217
|
let stagedRestartIntent = null;
|
|
3205
3218
|
try {
|
|
@@ -3210,20 +3223,17 @@ async function cmdRestart() {
|
|
|
3210
3223
|
preflightNodeSanity();
|
|
3211
3224
|
await ensureSystemDependencies();
|
|
3212
3225
|
cleanupLegacyPm2(bootstrapShutdownProtocol ? 'restart' : undefined);
|
|
3213
|
-
if (bootstrapShutdownProtocol
|
|
3214
|
-
|
|
3215
|
-
// a read-only jlist probe would start one and invalidate that admission.
|
|
3216
|
-
// Keep the existing include-pm2 clean-start path unchanged.
|
|
3217
|
-
if (bootstrapShutdownProtocol)
|
|
3218
|
-
bootstrapDeleteAllBotmuxProcesses('restart');
|
|
3219
|
-
else
|
|
3220
|
-
deleteAllBotmuxProcesses();
|
|
3226
|
+
if (bootstrapShutdownProtocol) {
|
|
3227
|
+
bootstrapDeleteAllBotmuxProcesses('restart');
|
|
3221
3228
|
}
|
|
3222
3229
|
else {
|
|
3223
3230
|
// This process is the newly installed code generation even when the
|
|
3224
3231
|
// Dashboard that spawned it is still the old in-memory generation. Do
|
|
3225
3232
|
// the policy probe here, before the generic retirement path throws, so
|
|
3226
3233
|
// the first-upgrade failure becomes durable and reaches the owner.
|
|
3234
|
+
// --include-pm2 takes this path too: the probe's lazy jlist may start a
|
|
3235
|
+
// PM2 God, which is harmless now — God retirement below runs after the
|
|
3236
|
+
// fleet is verified retired, against whichever God owns the home.
|
|
3227
3237
|
const preflight = evaluateRestartShutdownPreflight();
|
|
3228
3238
|
if (preflight.bootstrapRequired) {
|
|
3229
3239
|
const detail = 'current daemon PM2 policy requires the one-time shutdown-protocol bootstrap';
|
|
@@ -3235,8 +3245,24 @@ async function cmdRestart() {
|
|
|
3235
3245
|
}
|
|
3236
3246
|
deleteAllBotmuxProcesses();
|
|
3237
3247
|
}
|
|
3238
|
-
|
|
3248
|
+
// --include-pm2 tears down the whole PM2_HOME, so ALL plugin services get
|
|
3249
|
+
// a graceful stop first instead of dying with the God; auto ones are
|
|
3250
|
+
// re-ensured after the restart, manually-started ones stay down. Stop
|
|
3251
|
+
// errors are collected into reports rather than thrown (service-manager
|
|
3252
|
+
// contract), so this path re-checks them: a service that failed to stop
|
|
3253
|
+
// must block the God retirement below, not die with the God.
|
|
3254
|
+
if (includePm2) {
|
|
3255
|
+
const pluginStopReports = await stopPluginServicesForCli(undefined, {});
|
|
3256
|
+
const failedStops = pluginStopReports.filter(report => report.action === 'failed');
|
|
3257
|
+
if (failedStops.length > 0) {
|
|
3258
|
+
throw new Error(`[restart --include-pm2] plugin service(s) failed to stop gracefully: `
|
|
3259
|
+
+ `${failedStops.map(report => report.pluginId).join(', ')}; `
|
|
3260
|
+
+ 'the PM2 God and every remaining process were left untouched — fix or delete these services, then rerun');
|
|
3261
|
+
}
|
|
3262
|
+
}
|
|
3263
|
+
else if (includePluginServices) {
|
|
3239
3264
|
await stopPluginServicesForCli(undefined, { autoOnly: true });
|
|
3265
|
+
}
|
|
3240
3266
|
cleanupStaleDaemonDescriptors();
|
|
3241
3267
|
const retiredProjection = readVerifiedBotmuxPm2Projection('restart-start');
|
|
3242
3268
|
assertNoUnregisteredLiveDaemonDescriptors('restart-start', retiredProjection);
|
|
@@ -3244,6 +3270,37 @@ async function cmdRestart() {
|
|
|
3244
3270
|
throw new Error(`[restart-start] new PM2 core row(s) appeared after verified retirement: `
|
|
3245
3271
|
+ retiredProjection.map(entry => `${entry.name}:${entry.pid}`).join(', '));
|
|
3246
3272
|
}
|
|
3273
|
+
// Only now — with the core fleet verified retired and plugin services
|
|
3274
|
+
// stopped — is the God a stateless supervisor of nothing, safe to retire
|
|
3275
|
+
// via its own control socket. The fresh `pm2 start` below then births a
|
|
3276
|
+
// new God from this CLI's (pm2Env-scrubbed) environment, which is what
|
|
3277
|
+
// makes --include-pm2 a genuinely complete restart.
|
|
3278
|
+
if (includePm2) {
|
|
3279
|
+
// Independent whole-registry proof, not just the core projection above:
|
|
3280
|
+
// every row still known to the God (a plugin stop that failed, an
|
|
3281
|
+
// orphaned row from an uninstalled plugin) blocks the kill fail-closed.
|
|
3282
|
+
assertPm2RegistryQuiescentForGodRetirement(parsePm2JlistOutputStrict(pm2Capture(['jlist'])).map(row => ({
|
|
3283
|
+
name: typeof row?.name === 'string' && row.name ? row.name : 'unknown',
|
|
3284
|
+
status: typeof row?.pm2_env?.status === 'string' ? row.pm2_env.status : undefined,
|
|
3285
|
+
pid: parsePm2Integer(row?.pid, { nonNegative: true }),
|
|
3286
|
+
})));
|
|
3287
|
+
const retired = await retireSoleLivePm2God({
|
|
3288
|
+
listGodPids: () => listPm2GodDaemonPids(),
|
|
3289
|
+
readStartIdentity: pid => readSupervisorProcessStartIdentity(pid),
|
|
3290
|
+
isAlive: pid => { try {
|
|
3291
|
+
process.kill(pid, 0);
|
|
3292
|
+
return true;
|
|
3293
|
+
}
|
|
3294
|
+
catch {
|
|
3295
|
+
return false;
|
|
3296
|
+
} },
|
|
3297
|
+
pm2Kill: () => runPm2(['kill'], true, PM2_HOME, PM2_GOD_KILL_COMMAND_TIMEOUT_MS),
|
|
3298
|
+
sleep: ms => new Promise(resolve => setTimeout(resolve, ms)),
|
|
3299
|
+
now: () => Date.now(),
|
|
3300
|
+
});
|
|
3301
|
+
if (retired)
|
|
3302
|
+
console.log(`已退役 PM2 God (pid ${retired.pid}),fleet 将以当前干净环境全新启动`);
|
|
3303
|
+
}
|
|
3247
3304
|
await withFileLock(BOTS_JSON_FILE, async () => {
|
|
3248
3305
|
const restartBots = loadBotsJson();
|
|
3249
3306
|
const cfg = ecosystemConfig(restartBots);
|
|
@@ -3262,6 +3319,11 @@ async function cmdRestart() {
|
|
|
3262
3319
|
start: timeoutMs => {
|
|
3263
3320
|
assertBotsConfigSnapshotUnchanged('restart-start', restartBots);
|
|
3264
3321
|
assertNoDuplicatePm2GodDaemons();
|
|
3322
|
+
// After retiring the God this start must be the one to birth
|
|
3323
|
+
// its successor; a God that appeared in between came from some
|
|
3324
|
+
// other client's environment and is refused.
|
|
3325
|
+
if (includePm2)
|
|
3326
|
+
assertNoReplacementPm2God(listPm2GodDaemonPids());
|
|
3265
3327
|
preflightNodeSanity();
|
|
3266
3328
|
runPm2(['start', cfg], true, PM2_HOME, timeoutMs);
|
|
3267
3329
|
},
|
|
@@ -3318,7 +3380,7 @@ function retireExactBotmuxProcess(operation, target, fullProjection) {
|
|
|
3318
3380
|
async function ensureBotDaemonStopped(appId, _opts = {}) {
|
|
3319
3381
|
ensureConfigDir();
|
|
3320
3382
|
try {
|
|
3321
|
-
return await
|
|
3383
|
+
return await withPm2FleetMutationLock(async () => (withFileLock(BOTS_JSON_FILE, async () => {
|
|
3322
3384
|
assertNoDuplicatePm2GodDaemons();
|
|
3323
3385
|
preflightNodeSanity();
|
|
3324
3386
|
cleanupLegacyPm2();
|
|
@@ -3381,7 +3443,7 @@ async function ensureBotDaemonStopped(appId, _opts = {}) {
|
|
|
3381
3443
|
async function ensureBotDaemonStarted(appId, opts = {}) {
|
|
3382
3444
|
ensureConfigDir();
|
|
3383
3445
|
try {
|
|
3384
|
-
return await
|
|
3446
|
+
return await withPm2FleetMutationLock(async () => (withFileLock(BOTS_JSON_FILE, async () => {
|
|
3385
3447
|
assertNoDuplicatePm2GodDaemons();
|
|
3386
3448
|
preflightNodeSanity();
|
|
3387
3449
|
cleanupLegacyPm2();
|
|
@@ -3644,23 +3706,48 @@ function warnIfLegacyBotmuxAlive() {
|
|
|
3644
3706
|
}
|
|
3645
3707
|
if (!legacyPid)
|
|
3646
3708
|
return;
|
|
3709
|
+
// Deliberately NO PM2 client here: this helper runs at the top of read-only
|
|
3710
|
+
// commands (status/logs), and a pm2 jlist against the legacy home would
|
|
3711
|
+
// lazily REVIVE a legacy God the moment the recorded one exits between the
|
|
3712
|
+
// kill(0) probe and the client's connect — a read command must never create
|
|
3713
|
+
// one. The process-table marker scan also closes the stale-pid-file hole:
|
|
3714
|
+
// kill(pid, 0) alone would accept an unrelated process that reused the pid.
|
|
3647
3715
|
try {
|
|
3648
|
-
|
|
3716
|
+
if (!listPm2GodDaemonPids(legacyHome).includes(legacyPid))
|
|
3717
|
+
return;
|
|
3649
3718
|
}
|
|
3650
3719
|
catch {
|
|
3651
3720
|
return;
|
|
3652
3721
|
}
|
|
3722
|
+
// "Still has botmux processes" from PM2's own pid files, not from a client:
|
|
3723
|
+
// the legacy God maintains <name>-<id>.pid under <home>/pids while an app
|
|
3724
|
+
// process runs.
|
|
3653
3725
|
try {
|
|
3654
|
-
const
|
|
3655
|
-
|
|
3656
|
-
|
|
3657
|
-
|
|
3726
|
+
for (const entry of readdirSync(join(legacyHome, 'pids'))) {
|
|
3727
|
+
if (!entry.startsWith(PM2_NAME) || !entry.endsWith('.pid'))
|
|
3728
|
+
continue;
|
|
3729
|
+
let appPid = 0;
|
|
3730
|
+
try {
|
|
3731
|
+
appPid = parseInt(readFileSync(join(legacyHome, 'pids', entry), 'utf-8').trim(), 10);
|
|
3732
|
+
}
|
|
3733
|
+
catch {
|
|
3734
|
+
continue;
|
|
3735
|
+
}
|
|
3736
|
+
if (!appPid)
|
|
3737
|
+
continue;
|
|
3738
|
+
try {
|
|
3739
|
+
process.kill(appPid, 0);
|
|
3740
|
+
}
|
|
3741
|
+
catch {
|
|
3742
|
+
continue;
|
|
3743
|
+
}
|
|
3658
3744
|
console.warn('⚠️ 检测到旧版 PM2_HOME (~/.pm2) 下仍有 botmux 进程,运行 `botmux restart` 完成迁移。\n');
|
|
3745
|
+
return;
|
|
3659
3746
|
}
|
|
3660
3747
|
}
|
|
3661
|
-
catch { /*
|
|
3748
|
+
catch { /* no pids dir */ }
|
|
3662
3749
|
}
|
|
3663
|
-
function cmdLogs() {
|
|
3750
|
+
async function cmdLogs() {
|
|
3664
3751
|
warnIfLegacyBotmuxAlive();
|
|
3665
3752
|
const lines = process.argv.includes('--lines')
|
|
3666
3753
|
? process.argv[process.argv.indexOf('--lines') + 1] || '50'
|
|
@@ -3670,7 +3757,16 @@ function cmdLogs() {
|
|
|
3670
3757
|
const botIdx = process.argv.includes('--bot')
|
|
3671
3758
|
? process.argv[process.argv.indexOf('--bot') + 1]
|
|
3672
3759
|
: undefined;
|
|
3673
|
-
|
|
3760
|
+
// No PM2 client at all: `pm2 logs` lazily births a God when none is alive
|
|
3761
|
+
// (Client.start → pingDaemon false → launchDaemon), which made this read
|
|
3762
|
+
// command a check/use race against `restart --include-pm2` — a God observed
|
|
3763
|
+
// under the fleet lock could be retired before the spawned client connected,
|
|
3764
|
+
// and the connecting client would then create a replacement God inside the
|
|
3765
|
+
// kill→start window. Tailing the log files pm2 itself writes (the exact
|
|
3766
|
+
// out_file/error_file paths ecosystemConfig pins) removes the raced resource
|
|
3767
|
+
// entirely: no interleaving of `botmux logs` can create a God, no fleet lock
|
|
3768
|
+
// is needed, and logs keep working while the fleet is stopped.
|
|
3769
|
+
let sources;
|
|
3674
3770
|
if (botIdx !== undefined) {
|
|
3675
3771
|
const numericIdx = /^\d+$/.test(botIdx) ? Number(botIdx) : undefined;
|
|
3676
3772
|
const selectedIdx = numericIdx === undefined
|
|
@@ -3678,31 +3774,94 @@ function cmdLogs() {
|
|
|
3678
3774
|
: numericIdx >= 0 && numericIdx < bots.length
|
|
3679
3775
|
? numericIdx
|
|
3680
3776
|
: undefined;
|
|
3681
|
-
|
|
3682
|
-
|
|
3683
|
-
|
|
3684
|
-
|
|
3685
|
-
|
|
3777
|
+
if (selectedIdx !== undefined) {
|
|
3778
|
+
sources = coreBotLogSources(bots, selectedIdx);
|
|
3779
|
+
}
|
|
3780
|
+
else {
|
|
3781
|
+
const wanted = numericIdx !== undefined ? `${PM2_NAME}-${botIdx}` : botIdx;
|
|
3782
|
+
sources = allBotmuxLogSources(bots).filter(source => source.label === wanted);
|
|
3783
|
+
if (sources.length === 0) {
|
|
3784
|
+
console.error(`✗ 未找到 ${wanted} 的日志文件(可用:不带 --bot 查看全部,或用 0-based index / pm2 名 / appId)`);
|
|
3785
|
+
process.exitCode = 1;
|
|
3786
|
+
return;
|
|
3787
|
+
}
|
|
3788
|
+
}
|
|
3686
3789
|
}
|
|
3687
3790
|
else {
|
|
3688
|
-
|
|
3689
|
-
|
|
3791
|
+
sources = allBotmuxLogSources(bots);
|
|
3792
|
+
}
|
|
3793
|
+
// Not `|| 50`: an explicit --lines 0 (follow-only) must stay 0.
|
|
3794
|
+
const rawLines = Number.parseInt(lines, 10);
|
|
3795
|
+
const parsedLines = Number.isSafeInteger(rawLines) && rawLines >= 0 ? rawLines : 50;
|
|
3796
|
+
console.log(`跟踪 ${sources.length} 个日志文件(Ctrl+C 退出;fleet 停止时也能查看历史并等待新日志)`);
|
|
3797
|
+
const follower = new LogFileFollower({ sources, writeLine: line => console.log(line) });
|
|
3798
|
+
follower.printInitialTail(parsedLines);
|
|
3799
|
+
follower.start();
|
|
3800
|
+
}
|
|
3801
|
+
/** Log files of one core bot daemon — the exact paths ecosystemConfig pins. */
|
|
3802
|
+
function coreBotLogSources(bots, index) {
|
|
3803
|
+
const label = botProcessName(bots[index], index, PM2_NAME);
|
|
3804
|
+
return [
|
|
3805
|
+
{ label, stream: 'out', file: join(LOG_DIR, `daemon-${index}-out.log`) },
|
|
3806
|
+
{ label, stream: 'err', file: join(LOG_DIR, `daemon-${index}-error.log`) },
|
|
3807
|
+
];
|
|
3808
|
+
}
|
|
3809
|
+
/** Every botmux log file: core daemons + dashboard (ecosystemConfig paths)
|
|
3810
|
+
* plus plugin services (PM2 default logs dir under the shared PM2_HOME). */
|
|
3811
|
+
function allBotmuxLogSources(bots) {
|
|
3812
|
+
const sources = bots.flatMap((_bot, i) => coreBotLogSources(bots, i));
|
|
3813
|
+
sources.push({ label: 'botmux-dashboard', stream: 'out', file: join(LOG_DIR, 'dashboard-out.log') }, { label: 'botmux-dashboard', stream: 'err', file: join(LOG_DIR, 'dashboard-error.log') });
|
|
3814
|
+
const pluginLogsDir = join(PM2_HOME, 'logs');
|
|
3815
|
+
try {
|
|
3816
|
+
for (const entry of readdirSync(pluginLogsDir)) {
|
|
3817
|
+
const match = entry.match(/^(botmux-plugin-.+?)-(out|error)(?:-\d+)?\.log$/);
|
|
3818
|
+
if (!match)
|
|
3819
|
+
continue;
|
|
3820
|
+
sources.push({
|
|
3821
|
+
label: match[1],
|
|
3822
|
+
stream: match[2] === 'error' ? 'err' : 'out',
|
|
3823
|
+
file: join(pluginLogsDir, entry),
|
|
3824
|
+
});
|
|
3825
|
+
}
|
|
3690
3826
|
}
|
|
3691
|
-
|
|
3692
|
-
|
|
3693
|
-
const pm2 = buildPm2SpawnCommand(pm2Bin(), ['logs', target, '--lines', lines]);
|
|
3694
|
-
const child = spawn(pm2.command, pm2.args, {
|
|
3695
|
-
stdio: 'inherit',
|
|
3696
|
-
env: pm2Env(),
|
|
3697
|
-
shell: pm2.shell ?? false,
|
|
3698
|
-
});
|
|
3699
|
-
child.on('exit', code => process.exit(code ?? 0));
|
|
3827
|
+
catch { /* no plugin logs yet */ }
|
|
3828
|
+
return sources;
|
|
3700
3829
|
}
|
|
3701
|
-
function cmdStatus() {
|
|
3830
|
+
async function cmdStatus() {
|
|
3702
3831
|
warnIfLegacyBotmuxAlive();
|
|
3703
|
-
|
|
3832
|
+
// A pm2 client with no live God lazily births one from this process's env.
|
|
3833
|
+
// During an include-pm2 restart's kill→start window that replacement God
|
|
3834
|
+
// would abort the restart with the whole fleet offline, and in a normally
|
|
3835
|
+
// stopped state it would silently resurrect a God nobody asked for — so a
|
|
3836
|
+
// read-only status must check for a God under the fleet lock and never
|
|
3837
|
+
// create one.
|
|
3838
|
+
let entered = false;
|
|
3839
|
+
try {
|
|
3840
|
+
await withPm2FleetMutationLock(async () => {
|
|
3841
|
+
entered = true;
|
|
3842
|
+
if (listPm2GodDaemonPids().length === 0) {
|
|
3843
|
+
console.log('PM2 God 未运行:fleet 已停止(status 不会隐式启动它;需要时运行 botmux start)。');
|
|
3844
|
+
return;
|
|
3845
|
+
}
|
|
3846
|
+
runPm2(['status']);
|
|
3847
|
+
}, { maxWaitMs: 3_000 });
|
|
3848
|
+
}
|
|
3849
|
+
catch (err) {
|
|
3850
|
+
if (entered)
|
|
3851
|
+
throw err;
|
|
3852
|
+
// Non-zero so automation doesn't mistake "couldn't observe" for success.
|
|
3853
|
+
console.log('fleet 停启操作进行中(fleet mutation 锁被占用),请稍后重试 botmux status。');
|
|
3854
|
+
process.exitCode = 1;
|
|
3855
|
+
}
|
|
3704
3856
|
}
|
|
3705
3857
|
function cmdUpgrade() {
|
|
3858
|
+
// 本地 checkout(有 .git/src):走 git pull --ff-only → 重新 build → 从本
|
|
3859
|
+
// checkout 重启,而不是拿全局包管理器去升级(那对 dev 部署无效,见
|
|
3860
|
+
// install-info.ts 的 isLocalDevInstall 说明)。
|
|
3861
|
+
if (isLocalDevInstall()) {
|
|
3862
|
+
cmdUpgradeLocalDev();
|
|
3863
|
+
return;
|
|
3864
|
+
}
|
|
3706
3865
|
try {
|
|
3707
3866
|
const plan = resolveGlobalInstallPlan();
|
|
3708
3867
|
console.log(`🔄 升级中:${formatGlobalInstallCommand(plan)}`);
|
|
@@ -3719,6 +3878,82 @@ function cmdUpgrade() {
|
|
|
3719
3878
|
process.exit(1);
|
|
3720
3879
|
}
|
|
3721
3880
|
}
|
|
3881
|
+
/** 在 checkout 目录里同步跑一条命令,stdio 直通;失败抛错。 */
|
|
3882
|
+
function runInCheckout(cwd, command, args) {
|
|
3883
|
+
const result = spawnSync(command, args, {
|
|
3884
|
+
cwd,
|
|
3885
|
+
stdio: 'inherit',
|
|
3886
|
+
shell: process.platform === 'win32',
|
|
3887
|
+
});
|
|
3888
|
+
if (result.error || result.status !== 0) {
|
|
3889
|
+
throw new Error(describeSpawnFailure(command, args, result));
|
|
3890
|
+
}
|
|
3891
|
+
}
|
|
3892
|
+
/**
|
|
3893
|
+
* 本地 checkout 的更新流程:git 干净检查 → git pull --ff-only → pnpm build →
|
|
3894
|
+
* 从本 checkout 的 dist/cli.js restart。dist/ 被 gitignore,只 pull 不 build
|
|
3895
|
+
* 重启后跑的还是旧代码,故 build 步不可省。定位/干净检查/命令定义与 dashboard
|
|
3896
|
+
* 共用 src/utils/local-dev-update.ts,避免两边逻辑漂移。
|
|
3897
|
+
*/
|
|
3898
|
+
function cmdUpgradeLocalDev() {
|
|
3899
|
+
const dir = resolveLocalDevCheckoutDir();
|
|
3900
|
+
if (!isGitWorktree(dir)) {
|
|
3901
|
+
console.error(`❌ ${dir} 不是 git 工作树,无法用 git pull 更新。请手动更新或改用全局安装。`);
|
|
3902
|
+
process.exit(1);
|
|
3903
|
+
}
|
|
3904
|
+
console.log(`🔄 本地 checkout 更新:${dir}`);
|
|
3905
|
+
// git 干净检查 + pull + build 全程握同一把跨进程 update 锁(与 dashboard 的
|
|
3906
|
+
// /api/update/run 用的是同一个 target),避免 CLI 与 dashboard 同时对同一
|
|
3907
|
+
// checkout 交错跑 pnpm build 而互相清理/覆盖 dist。restart 不在锁内——它有
|
|
3908
|
+
// 自己的 restart lease。
|
|
3909
|
+
try {
|
|
3910
|
+
const lockTarget = globalInstallUpdateLockTarget();
|
|
3911
|
+
// daemon 正常会建好 dataDir;但 CLI 可能在 daemon 尚未起过的机器上先跑
|
|
3912
|
+
// update,锁文件父目录不存在会让 withFileLockSync 报 ENOENT 而盖掉真实错误。
|
|
3913
|
+
mkdirSync(dirname(lockTarget), { recursive: true });
|
|
3914
|
+
withFileLockSync(lockTarget, () => {
|
|
3915
|
+
// 1) fail closed:有未提交改动就中止(不偷偷 stash),让用户自己决定。
|
|
3916
|
+
let status;
|
|
3917
|
+
try {
|
|
3918
|
+
status = gitPorcelainStatus(dir);
|
|
3919
|
+
}
|
|
3920
|
+
catch (error) {
|
|
3921
|
+
throw new Error(`读取 git 状态失败:${error instanceof Error ? error.message : error}`);
|
|
3922
|
+
}
|
|
3923
|
+
if (status) {
|
|
3924
|
+
const err = new Error('dirty');
|
|
3925
|
+
err.dirtyStatus = status;
|
|
3926
|
+
throw err;
|
|
3927
|
+
}
|
|
3928
|
+
// 2~3) git pull --ff-only(分叉/冲突直接报错停下,不自动 merge)+ pnpm build。
|
|
3929
|
+
for (const { command, args } of localDevUpdateSteps()) {
|
|
3930
|
+
console.log(`→ ${command} ${args.join(' ')}`);
|
|
3931
|
+
runInCheckout(dir, command, args);
|
|
3932
|
+
}
|
|
3933
|
+
}, { maxWaitMs: 2_000 });
|
|
3934
|
+
}
|
|
3935
|
+
catch (error) {
|
|
3936
|
+
const dirty = error.dirtyStatus;
|
|
3937
|
+
if (dirty) {
|
|
3938
|
+
console.error('❌ 工作区有未提交改动,已中止更新(不会自动 stash)。请先提交或清理:');
|
|
3939
|
+
console.error(dirty);
|
|
3940
|
+
}
|
|
3941
|
+
else {
|
|
3942
|
+
console.error(`❌ 更新失败:${error instanceof Error ? error.message : error}`);
|
|
3943
|
+
}
|
|
3944
|
+
process.exit(1);
|
|
3945
|
+
}
|
|
3946
|
+
// 4) 用本 checkout 的 cli.js 重启 daemon(不走 PATH,避免被更靠前的全局 botmux 抢先)。
|
|
3947
|
+
try {
|
|
3948
|
+
console.log('→ restart daemon');
|
|
3949
|
+
runInCheckout(dir, process.execPath, [botmuxCliEntryAt(dir), 'restart']);
|
|
3950
|
+
console.log('\n✅ 本地更新完成,daemon 已从最新代码重启。');
|
|
3951
|
+
}
|
|
3952
|
+
catch (error) {
|
|
3953
|
+
console.error(`❌ 重启失败:${error instanceof Error ? error.message : error}`);
|
|
3954
|
+
process.exit(1);
|
|
3955
|
+
}
|
|
3956
|
+
}
|
|
3722
3957
|
/**
|
|
3723
3958
|
* Call one of the dashboard's loopback HMAC `/__cli/*` endpoints. Thin wrapper
|
|
3724
3959
|
* over {@link callDashboard}, which handles 404 disambiguation and self-heals a
|
|
@@ -3792,10 +4027,10 @@ async function cmdDashboard(args) {
|
|
|
3792
4027
|
}
|
|
3793
4028
|
const { action, result: r } = execution;
|
|
3794
4029
|
if (r.ok) {
|
|
3795
|
-
// 首行保持纯 URL
|
|
3796
|
-
|
|
3797
|
-
|
|
3798
|
-
console.log(
|
|
4030
|
+
// 首行保持纯 URL(脚本/复制取第一行即可);随后依次是工作台直达入口、以及走
|
|
4031
|
+
// 中心化平台时的本地直连兜底。行顺序与首行契约见 formatDashboardSuccessLines。
|
|
4032
|
+
for (const line of formatDashboardSuccessLines(r))
|
|
4033
|
+
console.log(line);
|
|
3799
4034
|
return;
|
|
3800
4035
|
}
|
|
3801
4036
|
const portFile = join(CONFIG_DIR, '.dashboard-port');
|
|
@@ -3967,6 +4202,7 @@ async function abandonSessionOffline(session) {
|
|
|
3967
4202
|
delete latest.queuedActivationPending;
|
|
3968
4203
|
delete latest.queuedActivationTail;
|
|
3969
4204
|
delete latest.pendingRepoSetup;
|
|
4205
|
+
delete latest.previewTarget;
|
|
3970
4206
|
applied = true;
|
|
3971
4207
|
return true;
|
|
3972
4208
|
});
|
|
@@ -3984,6 +4220,7 @@ function pruneSessionOfflineIfLedgerEmpty(session) {
|
|
|
3984
4220
|
current.closedAt = new Date().toISOString();
|
|
3985
4221
|
delete current.codexAppDispatchLedger;
|
|
3986
4222
|
delete current.codexAppGenerationCommits;
|
|
4223
|
+
delete current.previewTarget;
|
|
3987
4224
|
pruned = true;
|
|
3988
4225
|
return true;
|
|
3989
4226
|
});
|
|
@@ -4050,8 +4287,15 @@ async function abandonSessionAuthoritatively(session, online = listOnlineDaemons
|
|
|
4050
4287
|
// stays fail-closed at the daemon's sessionCliIpcAuth check.
|
|
4051
4288
|
const res = await postSessionCliIpc(ipcPort, session.sessionId, 'close', {});
|
|
4052
4289
|
const body = await res.json().catch(() => ({}));
|
|
4053
|
-
if (res.ok && body.ok)
|
|
4054
|
-
|
|
4290
|
+
if (res.ok && body.ok) {
|
|
4291
|
+
// Shared parser: a body that DECLARES a residual must warn even when the
|
|
4292
|
+
// `residual` object is missing/malformed, or a bad payload silently
|
|
4293
|
+
// becomes an ordinary success again.
|
|
4294
|
+
const residual = parseCloseResidual(body);
|
|
4295
|
+
return residual
|
|
4296
|
+
? { ok: true, mode: 'daemon', residual }
|
|
4297
|
+
: { ok: true, mode: 'daemon' };
|
|
4298
|
+
}
|
|
4055
4299
|
// Surface the daemon's own rejection reason (e.g. origin_unproven) and
|
|
4056
4300
|
// never fall back to a partial local kill: a fresh descriptor means the
|
|
4057
4301
|
// daemon may still hold authoritative in-memory state.
|
|
@@ -4366,14 +4610,19 @@ function sessionBackingInfo(s, snapshot) {
|
|
|
4366
4610
|
if (s.backendType === 'pty') {
|
|
4367
4611
|
return { backendType: 'pty', probe: 'missing', label: 'pty' };
|
|
4368
4612
|
}
|
|
4369
|
-
if (s.backendType === 'riff') {
|
|
4370
|
-
//
|
|
4371
|
-
// is nothing to probe, attach to, or name as a
|
|
4372
|
-
// (sessionPersistentTarget returns undefined for it, by
|
|
4373
|
-
// stable label and report the nonexistent local backing as
|
|
4374
|
-
// like pty — so it never slips into the legacy tmux branch
|
|
4375
|
-
// undefined target.
|
|
4376
|
-
|
|
4613
|
+
if (s.backendType === 'riff' || s.backendType === 'mojo') {
|
|
4614
|
+
// A remote backend (riff / mojo) runs its agent off-box, not in a local
|
|
4615
|
+
// multiplexer pane: there is nothing to probe, attach to, or name as a
|
|
4616
|
+
// PersistentBackendTarget (sessionPersistentTarget returns undefined for it, by
|
|
4617
|
+
// design). Surface a stable label and report the nonexistent local backing as
|
|
4618
|
+
// missing — exactly like pty — so it never slips into the legacy tmux branch
|
|
4619
|
+
// and dereferences an undefined target.
|
|
4620
|
+
//
|
|
4621
|
+
// Listed literally, not via isRemoteBackendType(): a boolean helper does not
|
|
4622
|
+
// narrow the union, and the exhaustiveness guard at the end of this function
|
|
4623
|
+
// depends on that narrowing. Adding a remote backend therefore fails to
|
|
4624
|
+
// compile here until it is listed — which is how mojo was caught.
|
|
4625
|
+
return { backendType: s.backendType, probe: 'missing', label: s.backendType };
|
|
4377
4626
|
}
|
|
4378
4627
|
if (s.backendType === undefined) {
|
|
4379
4628
|
// Legacy rows predate backend stamping. Only tmux was externally attachable,
|
|
@@ -4417,6 +4666,35 @@ function hasRecoverableBackingSession(s, snapshot) {
|
|
|
4417
4666
|
const target = sessionPersistentTarget(s);
|
|
4418
4667
|
return !!target && backingProbe(snapshot, target) === 'exists';
|
|
4419
4668
|
}
|
|
4669
|
+
async function requestDormantSessionWake(session, context) {
|
|
4670
|
+
const online = listOnlineDaemons();
|
|
4671
|
+
const daemon = session.larkAppId
|
|
4672
|
+
? online.find(d => d.larkAppId === session.larkAppId)
|
|
4673
|
+
: online.length === 1 ? online[0] : undefined;
|
|
4674
|
+
if (!daemon) {
|
|
4675
|
+
return {
|
|
4676
|
+
ok: false,
|
|
4677
|
+
error: session.larkAppId || online.length === 0
|
|
4678
|
+
? '所属 daemon 不在线,请先运行 botmux status'
|
|
4679
|
+
: '历史会话缺少 bot 归属,多 daemon 环境下无法安全唤醒',
|
|
4680
|
+
};
|
|
4681
|
+
}
|
|
4682
|
+
try {
|
|
4683
|
+
const path = `/api/sessions/${encodeURIComponent(session.sessionId)}/wake`;
|
|
4684
|
+
const response = await fetchDaemonIpc(daemon.ipcPort, path, {
|
|
4685
|
+
method: 'POST',
|
|
4686
|
+
signal: context.signal,
|
|
4687
|
+
headers: { [SESSION_WAKE_DEADLINE_HEADER]: String(context.deadlineMs) },
|
|
4688
|
+
});
|
|
4689
|
+
const body = await response.json().catch(() => ({}));
|
|
4690
|
+
if (response.ok && body?.ok)
|
|
4691
|
+
return { ok: true };
|
|
4692
|
+
return { ok: false, error: body?.error ?? `HTTP ${response.status}` };
|
|
4693
|
+
}
|
|
4694
|
+
catch (err) {
|
|
4695
|
+
return { ok: false, error: err?.message ?? String(err) };
|
|
4696
|
+
}
|
|
4697
|
+
}
|
|
4420
4698
|
/** Shorten path for display: replace $HOME with ~. */
|
|
4421
4699
|
function shortenPath(p) {
|
|
4422
4700
|
const home = homedir();
|
|
@@ -4474,6 +4752,13 @@ function interactiveSessionPicker(active, probeSnapshot) {
|
|
|
4474
4752
|
&& backing.probe === 'exists'
|
|
4475
4753
|
&& !!('attachBackend' in backing && backing.attachBackend)
|
|
4476
4754
|
&& !!('target' in backing && backing.target),
|
|
4755
|
+
canWake: canWakeDormantBackendForAttach({
|
|
4756
|
+
isAdopt,
|
|
4757
|
+
probe: backing.probe,
|
|
4758
|
+
realManagedSession: isRealManagedSession(s),
|
|
4759
|
+
attachBackend: 'attachBackend' in backing ? backing.attachBackend : undefined,
|
|
4760
|
+
target: 'target' in backing ? backing.target : undefined,
|
|
4761
|
+
}),
|
|
4477
4762
|
};
|
|
4478
4763
|
});
|
|
4479
4764
|
}
|
|
@@ -4621,6 +4906,9 @@ function interactiveSessionPicker(active, probeSnapshot) {
|
|
|
4621
4906
|
else if (selected.canAttach) {
|
|
4622
4907
|
footerLine = styledFooter('success', `${selected.attachBackend}: ${selected.backendTarget?.sessionName}`, width);
|
|
4623
4908
|
}
|
|
4909
|
+
else if (selected.canWake) {
|
|
4910
|
+
footerLine = styledFooter('warn', `${selected.targetLabel}(Enter 恢复并连接)`, width);
|
|
4911
|
+
}
|
|
4624
4912
|
else {
|
|
4625
4913
|
footerLine = styledFooter('dim', `${selected.targetLabel}(不可连接)`, width);
|
|
4626
4914
|
}
|
|
@@ -4638,7 +4926,8 @@ function interactiveSessionPicker(active, probeSnapshot) {
|
|
|
4638
4926
|
process.stdout.write('\n');
|
|
4639
4927
|
}
|
|
4640
4928
|
// Keybinding hints
|
|
4641
|
-
const
|
|
4929
|
+
const enterAction = selected?.canAttach ? '连接' : selected?.canWake ? '恢复' : '不可连接';
|
|
4930
|
+
const fullHints = `↑/↓ 选择 ⏎ ${enterAction} d 删除 q 退出`;
|
|
4642
4931
|
const compactHints = '↑/↓ 选择 ⏎ d q';
|
|
4643
4932
|
const hints = displayWidth(fullHints) <= width ? fullHints : compactHints;
|
|
4644
4933
|
process.stdout.write(`\n${footerPrefix()}${styledFooter('dim', hints, width)}\n`);
|
|
@@ -4657,14 +4946,23 @@ function interactiveSessionPicker(active, probeSnapshot) {
|
|
|
4657
4946
|
render();
|
|
4658
4947
|
};
|
|
4659
4948
|
process.stdout.on('resize', onResize);
|
|
4949
|
+
let deleteInFlight = false;
|
|
4950
|
+
let wakeInFlight = false;
|
|
4951
|
+
let wakeAbortController = null;
|
|
4952
|
+
let pickerClosed = false;
|
|
4660
4953
|
function cleanup() {
|
|
4954
|
+
if (pickerClosed)
|
|
4955
|
+
return;
|
|
4956
|
+
pickerClosed = true;
|
|
4957
|
+
wakeAbortController?.abort();
|
|
4958
|
+
wakeAbortController = null;
|
|
4959
|
+
process.stdin.off('data', onInput);
|
|
4661
4960
|
process.stdout.off('resize', onResize);
|
|
4662
4961
|
process.stdin.setRawMode(false);
|
|
4663
4962
|
process.stdin.pause();
|
|
4664
4963
|
process.stdout.write('\x1b[?25h'); // show cursor
|
|
4665
4964
|
process.stdout.write('\x1b[?1049l'); // leave alt screen
|
|
4666
4965
|
}
|
|
4667
|
-
let deleteInFlight = false;
|
|
4668
4966
|
async function deleteSession(idx) {
|
|
4669
4967
|
const r = rows[idx];
|
|
4670
4968
|
const s = r.session;
|
|
@@ -4690,11 +4988,27 @@ function interactiveSessionPicker(active, probeSnapshot) {
|
|
|
4690
4988
|
// abandonSessionAuthoritatively is `mode` (see its return type ~3573),
|
|
4691
4989
|
// NOT master's `via` — that belongs to a different helper.
|
|
4692
4990
|
flash = result.mode === 'daemon'
|
|
4693
|
-
?
|
|
4991
|
+
? (result.residual
|
|
4992
|
+
// Local row closed, remote session still running: never the green
|
|
4993
|
+
// "deleted" text, or the operator walks away from a live agent that
|
|
4994
|
+
// still holds the injected credential.
|
|
4995
|
+
? {
|
|
4996
|
+
style: 'warn',
|
|
4997
|
+
text: `⚠ 本地已删除 ${s.sessionId.substring(0, 8)},但${closeResidualClause(result.residual)}`,
|
|
4998
|
+
}
|
|
4999
|
+
: { style: 'success', text: `✓ 已删除 ${s.sessionId.substring(0, 8)}` })
|
|
4694
5000
|
: { style: 'warn', text: `✓ 已离线删除 ${s.sessionId.substring(0, 8)}` };
|
|
4695
5001
|
}
|
|
4696
|
-
|
|
4697
|
-
|
|
5002
|
+
async function onInput(key) {
|
|
5003
|
+
// Exit always wins, including while the wake HTTP or backend probe is
|
|
5004
|
+
// pending. cleanup aborts the shared deadline signal before restoring
|
|
5005
|
+
// the terminal, so Ctrl-C/q/Esc can never be swallowed by in-flight work.
|
|
5006
|
+
if (key === '\x03' || key === 'q' || key === '\x1b') {
|
|
5007
|
+
cleanup();
|
|
5008
|
+
resolve();
|
|
5009
|
+
return;
|
|
5010
|
+
}
|
|
5011
|
+
if (deleteInFlight || wakeInFlight)
|
|
4698
5012
|
return;
|
|
4699
5013
|
// Delete confirmation mode
|
|
4700
5014
|
if (confirmDelete) {
|
|
@@ -4711,16 +5025,11 @@ function interactiveSessionPicker(active, probeSnapshot) {
|
|
|
4711
5025
|
else {
|
|
4712
5026
|
flash = { style: 'dim', text: '取消删除' };
|
|
4713
5027
|
}
|
|
4714
|
-
|
|
5028
|
+
if (!pickerClosed)
|
|
5029
|
+
render();
|
|
4715
5030
|
return;
|
|
4716
5031
|
}
|
|
4717
5032
|
flash = null;
|
|
4718
|
-
// Ctrl-C or q or Esc
|
|
4719
|
-
if (key === '\x03' || key === 'q' || key === '\x1b') {
|
|
4720
|
-
cleanup();
|
|
4721
|
-
resolve();
|
|
4722
|
-
return;
|
|
4723
|
-
}
|
|
4724
5033
|
if (rows.length === 0) {
|
|
4725
5034
|
// No sessions left, only q works
|
|
4726
5035
|
render();
|
|
@@ -4752,11 +5061,40 @@ function interactiveSessionPicker(active, probeSnapshot) {
|
|
|
4752
5061
|
render();
|
|
4753
5062
|
return;
|
|
4754
5063
|
}
|
|
4755
|
-
if (!selected.canAttach) {
|
|
5064
|
+
if (!selected.canAttach && !selected.canWake) {
|
|
4756
5065
|
flash = { style: 'warn', text: '该会话没有可连接的持久后端' };
|
|
4757
5066
|
render();
|
|
4758
5067
|
return;
|
|
4759
5068
|
}
|
|
5069
|
+
if (selected.canWake) {
|
|
5070
|
+
const target = selected.backendTarget;
|
|
5071
|
+
if (!target) {
|
|
5072
|
+
flash = { style: 'error', text: '恢复目标缺失' };
|
|
5073
|
+
render();
|
|
5074
|
+
return;
|
|
5075
|
+
}
|
|
5076
|
+
wakeInFlight = true;
|
|
5077
|
+
const wakeController = new AbortController();
|
|
5078
|
+
wakeAbortController = wakeController;
|
|
5079
|
+
flash = { style: 'warn', text: `正在恢复 ${target.backendType}: ${persistentTargetDisplay(target)}…` };
|
|
5080
|
+
render();
|
|
5081
|
+
const recovered = await wakeDormantBackendForAttach({
|
|
5082
|
+
target,
|
|
5083
|
+
wake: context => requestDormantSessionWake(selected.session, context),
|
|
5084
|
+
probe: probePersistentBackendTarget,
|
|
5085
|
+
signal: wakeController.signal,
|
|
5086
|
+
});
|
|
5087
|
+
if (wakeAbortController === wakeController)
|
|
5088
|
+
wakeAbortController = null;
|
|
5089
|
+
wakeInFlight = false;
|
|
5090
|
+
if (pickerClosed)
|
|
5091
|
+
return;
|
|
5092
|
+
if (!recovered.ok) {
|
|
5093
|
+
flash = { style: 'error', text: `恢复失败: ${recovered.error}` };
|
|
5094
|
+
render();
|
|
5095
|
+
return;
|
|
5096
|
+
}
|
|
5097
|
+
}
|
|
4760
5098
|
if (selected.attachBackend === 'zmx') {
|
|
4761
5099
|
const target = selected.backendTarget;
|
|
4762
5100
|
if (!target || target.backendType !== 'zmx') {
|
|
@@ -4795,7 +5133,8 @@ function interactiveSessionPicker(active, probeSnapshot) {
|
|
|
4795
5133
|
resolve();
|
|
4796
5134
|
return;
|
|
4797
5135
|
}
|
|
4798
|
-
}
|
|
5136
|
+
}
|
|
5137
|
+
process.stdin.on('data', onInput);
|
|
4799
5138
|
});
|
|
4800
5139
|
}
|
|
4801
5140
|
/**
|
|
@@ -4974,6 +5313,7 @@ async function cmdDelete() {
|
|
|
4974
5313
|
let closed = 0;
|
|
4975
5314
|
let offline = 0;
|
|
4976
5315
|
let failed = 0;
|
|
5316
|
+
let residual = 0;
|
|
4977
5317
|
for (const s of toDelete) {
|
|
4978
5318
|
// Explicit abandon boundary: route through the owning daemon so the ledger
|
|
4979
5319
|
// FIFO is cleared atomically with close; offline fallback rereads the row
|
|
@@ -4990,9 +5330,16 @@ async function cmdDelete() {
|
|
|
4990
5330
|
if (result.cleanedBacking)
|
|
4991
5331
|
console.log(` killed ${result.cleanedBacking}`);
|
|
4992
5332
|
}
|
|
5333
|
+
if (result.mode === 'daemon' && result.residual) {
|
|
5334
|
+
// Local row closed, remote session NOT cancelled. Reported per session AND
|
|
5335
|
+
// counted in the summary, so a bulk delete cannot bury it.
|
|
5336
|
+
residual++;
|
|
5337
|
+
console.warn(`⚠ ${s.sessionId.substring(0, 8)} ${s.title}:本地已关闭,但${closeResidualClause(result.residual)}`);
|
|
5338
|
+
continue;
|
|
5339
|
+
}
|
|
4993
5340
|
console.log(`✓ ${s.sessionId.substring(0, 8)} ${s.title}${result.mode === 'offline' ? '(daemon 离线,本地收口)' : ''}`);
|
|
4994
5341
|
}
|
|
4995
|
-
console.log(`\n已关闭 ${closed} 个会话${offline ? `(${offline} 个离线收口)` : ''}${failed ? `,${failed} 个失败` : ''}`);
|
|
5342
|
+
console.log(`\n已关闭 ${closed} 个会话${offline ? `(${offline} 个离线收口)` : ''}${failed ? `,${failed} 个失败` : ''}${residual ? `,${residual} 个有残留需人工清理` : ''}`);
|
|
4996
5343
|
if (failed > 0)
|
|
4997
5344
|
process.exitCode = 1;
|
|
4998
5345
|
}
|
|
@@ -5200,7 +5547,7 @@ async function cmdSuspend() {
|
|
|
5200
5547
|
if (failed > 0)
|
|
5201
5548
|
process.exitCode = 1;
|
|
5202
5549
|
}
|
|
5203
|
-
/** 会话级 CLI IPC(slash/cd/close)的 POST:与 postAsk 同款双路径——能读 host secret
|
|
5550
|
+
/** 会话级 CLI IPC(slash/cd/close/preview)的 POST:与 postAsk 同款双路径——能读 host secret
|
|
5204
5551
|
* (非隔离进程)走 trusted-host HMAC 签名;读不到(沙箱 BOTMUX_SEND_RELAY /
|
|
5205
5552
|
* macOS 读隔离 carve-out)改带本会话当前轮换的 origin capability,由 daemon
|
|
5206
5553
|
* handler 与活跃记录比对。两条路都不读 bots.json。 */
|
|
@@ -5233,6 +5580,82 @@ async function postSessionCliIpc(ipcPort, sessionId, route, payload) {
|
|
|
5233
5580
|
? fetchDaemonIpc(ipcPort, path, init, hostSecret)
|
|
5234
5581
|
: fetch(`http://127.0.0.1:${ipcPort}${path}`, init);
|
|
5235
5582
|
}
|
|
5583
|
+
/** `botmux preview <port>` registers a reachable loopback Web service for the
|
|
5584
|
+
* exact current session. There is intentionally no `--session` escape hatch:
|
|
5585
|
+
* ancestry/env resolution plus the rotating session capability prove which
|
|
5586
|
+
* agent is volunteering the port. The daemon performs the authoritative port
|
|
5587
|
+
* validation/probe and persists the result. */
|
|
5588
|
+
async function cmdPreview(rest) {
|
|
5589
|
+
if (rest.length !== 1 || !/^\d+$/.test(rest[0])) {
|
|
5590
|
+
console.error('用法: botmux preview <port>');
|
|
5591
|
+
process.exit(1);
|
|
5592
|
+
}
|
|
5593
|
+
const port = Number(rest[0]);
|
|
5594
|
+
if (!Number.isSafeInteger(port) || port < 1 || port > 65_535) {
|
|
5595
|
+
console.error('✗ 端口必须是 1-65535 的整数');
|
|
5596
|
+
process.exit(1);
|
|
5597
|
+
}
|
|
5598
|
+
const ctx = findAncestorSessionContext();
|
|
5599
|
+
if (!ctx?.sessionId) {
|
|
5600
|
+
console.error('✗ 无法定位当前会话;preview 只能在 botmux 会话内注册');
|
|
5601
|
+
process.exit(1);
|
|
5602
|
+
}
|
|
5603
|
+
let discoveredPort;
|
|
5604
|
+
try {
|
|
5605
|
+
discoveredPort = findDaemon(process.env.BOTMUX_LARK_APP_ID)?.ipcPort;
|
|
5606
|
+
}
|
|
5607
|
+
catch {
|
|
5608
|
+
// Sandboxed/read-isolated sessions cannot enumerate daemon descriptors.
|
|
5609
|
+
}
|
|
5610
|
+
const ipcPort = resolveDaemonIpcPort(discoveredPort, process.env.BOTMUX_DAEMON_IPC_PORT);
|
|
5611
|
+
if (!ipcPort) {
|
|
5612
|
+
console.error('✗ 无法定位当前会话的 daemon;请确认 daemon 正在运行');
|
|
5613
|
+
process.exit(1);
|
|
5614
|
+
}
|
|
5615
|
+
let response;
|
|
5616
|
+
try {
|
|
5617
|
+
response = await postSessionCliIpc(ipcPort, ctx.sessionId, 'preview', { port });
|
|
5618
|
+
}
|
|
5619
|
+
catch {
|
|
5620
|
+
console.error('✗ 无法连接当前会话的 daemon');
|
|
5621
|
+
process.exit(1);
|
|
5622
|
+
}
|
|
5623
|
+
const body = await response.json().catch(() => ({}));
|
|
5624
|
+
if (response.ok && body.ok && typeof body.preview?.path === 'string') {
|
|
5625
|
+
console.log(`✓ Web 预览已注册: ${body.preview.path}`);
|
|
5626
|
+
return;
|
|
5627
|
+
}
|
|
5628
|
+
const error = body.error ?? `HTTP ${response.status}`;
|
|
5629
|
+
if (error === 'preview_unreachable') {
|
|
5630
|
+
console.error('✗ 端口不可达;请先让 Web 服务监听本机 loopback/0.0.0.0 后再注册');
|
|
5631
|
+
}
|
|
5632
|
+
else if (error === 'preview_owner_unverified') {
|
|
5633
|
+
// P1-12:端口上确实有人在监听,但证明不了那是本会话起的进程。宁可没有预览,
|
|
5634
|
+
// 也不把 Dashboard 用户代理进一个来路不明的本机服务。
|
|
5635
|
+
console.error('✗ 无法确认该端口由本会话的进程持有;请在会话内直接启动 Web 服务后再注册'
|
|
5636
|
+
+ '(不要用 setsid/nohup 脱离进程树,也不要注册别的程序占用的端口)');
|
|
5637
|
+
}
|
|
5638
|
+
else if (error === 'preview_unsupported') {
|
|
5639
|
+
console.error('✗ 当前会话后端(远端 sandbox)的 Web 服务不在本机,Dashboard 预览不支持');
|
|
5640
|
+
}
|
|
5641
|
+
else if (error === 'preview_generation_changed') {
|
|
5642
|
+
console.error('✗ 注册期间会话已换代或已关闭;请在新一轮里重新注册');
|
|
5643
|
+
}
|
|
5644
|
+
else if (error === 'preview_target_changed') {
|
|
5645
|
+
// P1-3:并发注册(例如同时注册两个端口)时,后到的一方不再无声覆盖先落地的那个。
|
|
5646
|
+
console.error('✗ 注册期间本会话的预览目标已被另一次注册改写;同一时刻只能有一个预览目标,请确认后重试');
|
|
5647
|
+
}
|
|
5648
|
+
else if (error === 'remote_host_forbidden') {
|
|
5649
|
+
console.error('✗ 只允许注册本机 loopback 服务');
|
|
5650
|
+
}
|
|
5651
|
+
else if (error === 'origin_unproven' || error === 'managed_action_required') {
|
|
5652
|
+
console.error('✗ 当前会话归属校验失败');
|
|
5653
|
+
}
|
|
5654
|
+
else {
|
|
5655
|
+
console.error(`✗ 预览注册失败: ${error}`);
|
|
5656
|
+
}
|
|
5657
|
+
process.exit(1);
|
|
5658
|
+
}
|
|
5236
5659
|
async function cmdChat(argv) {
|
|
5237
5660
|
const sub = argv[0] ?? '';
|
|
5238
5661
|
if (sub !== 'rename') {
|
|
@@ -6048,7 +6471,7 @@ botmux v${getVersion()} — IM ↔ AI 编程 CLI 桥接
|
|
|
6048
6471
|
start 启动 daemon,并启动 mode=auto 的插件 service
|
|
6049
6472
|
stop 停止 daemon(默认不停止插件 service;--with-plugin 显式停止 mode=auto 的插件 service)
|
|
6050
6473
|
restart 重启 daemon(默认不停止插件 service,core 启动后确保 mode=auto 正在运行;--with-plugin 显式先停再启动 auto service)
|
|
6051
|
-
--include-pm2
|
|
6474
|
+
--include-pm2 在 fleet 安全退役并验证后,经 PM2_HOME socket 退役 PM2 God(绝不按 PID 发信号),再以当前干净环境全新启动——彻底重启整棵进程树;会先优雅停止全部插件 service(auto 的启动后自动恢复)
|
|
6052
6475
|
首次升级若旧 daemon 缺少 shutdown protocol:先独立确认所有 Session/Riff 工作均 idle,再一次性运行
|
|
6053
6476
|
botmux restart --bootstrap-shutdown-protocol --yes;普通 stop/restart 仍保持 fail-closed
|
|
6054
6477
|
logs 查看 daemon 日志(--lines N, --bot <0-based-index|pm2-name|appId>)
|
|
@@ -6060,6 +6483,9 @@ botmux v${getVersion()} — IM ↔ AI 编程 CLI 桥接
|
|
|
6060
6483
|
显式轮换 token,并打印新的登录 URL
|
|
6061
6484
|
device enroll|status|logout
|
|
6062
6485
|
在宿主终端注册、查看或清除 desktop device 凭证(AI CLI 会话内拒绝)
|
|
6486
|
+
mojo-containment list|revoke
|
|
6487
|
+
查看 / 显式撤销无法自证静止的 mojo containment handle(设备隔离
|
|
6488
|
+
blocker 的可审计操作员出口;revoke 需 --yes,存活证据需 --force)
|
|
6063
6489
|
list 列出活跃会话(交互式选择并连接 tmux)
|
|
6064
6490
|
--plain 纯文本表格输出(管道/脚本场景)
|
|
6065
6491
|
delete <id> 关闭指定会话(支持 ID 前缀匹配)
|
|
@@ -6077,6 +6503,10 @@ botmux v${getVersion()} — IM ↔ AI 编程 CLI 桥接
|
|
|
6077
6503
|
term-link [id] 获取活跃会话的「可操作终端」(带写 token)。不回显链接,改由
|
|
6078
6504
|
daemon 把可操作卡片私密发给 owner(群内仅你可见,话题/单聊回退 DM)。
|
|
6079
6505
|
单个活跃会话可省略 id
|
|
6506
|
+
preview <port> (会话内)注册当前会话已启动的本机 Web 服务;Dashboard 登录后通过
|
|
6507
|
+
同源 /preview/<sessionId>/ 访问,不暴露本机地址或任何 token。
|
|
6508
|
+
端口必须由本会话的进程持有(在会话内直接启动,别 setsid/nohup
|
|
6509
|
+
脱离进程树);换代/关闭后需重新注册,远端 sandbox 后端不支持
|
|
6080
6510
|
autostart enable 注册开机自启(macOS launchd / Linux user systemd / Windows Task Scheduler,无需 sudo)
|
|
6081
6511
|
autostart disable 注销开机自启
|
|
6082
6512
|
autostart status 查看自启状态
|
|
@@ -7177,7 +7607,7 @@ import { resolveFeedbackPolicyForDelivery, resolveFeedbackTeamId } from './servi
|
|
|
7177
7607
|
import { normalizeFeedbackPolicy } from './services/feedback-policy.js';
|
|
7178
7608
|
import { applyInlineMentions } from './im/lark/inline-mentions.js';
|
|
7179
7609
|
import { renderBrandTemplate } from './im/lark/brand-template.js';
|
|
7180
|
-
import { resolveBrandLabel, resolveUsageDisplay } from './bot-registry.js';
|
|
7610
|
+
import { effectiveDefaultWorkingDir, loadBotConfigs, resolveBrandLabel, resolveUsageDisplay } from './bot-registry.js';
|
|
7181
7611
|
import { config } from './config.js';
|
|
7182
7612
|
import { getSessionUsageSnapshot } from './core/cost-calculator.js';
|
|
7183
7613
|
import { resolveQuoteTarget, validateMentionDecision, mentionBackAmbiguity, mentionBackAmbiguityError, parseAttentionFlag, attentionUsageError, managedVcQuoteError, managedVcCustomCardError, managedVcSendControlError, managedVcSendPayloadError, containsLarkAtTag, } from './services/send-policy.js';
|
|
@@ -7230,6 +7660,7 @@ async function relaySend(rest, relayDir) {
|
|
|
7230
7660
|
const pos = positionals(rest, ['--card', '--text', '--top-level', '--no-quote', '--mention-back', '--no-mention', '--anyway', '--voice', '--slash']);
|
|
7231
7661
|
content = pos.length > 0 ? pos.join(' ') : await readStdin();
|
|
7232
7662
|
}
|
|
7663
|
+
content = stripTrailingOaiMemoryCitation(content);
|
|
7233
7664
|
const preparedCardContent = cardJsonArg === undefined && cardFile === undefined && !rest.includes('--voice')
|
|
7234
7665
|
? prepareCardMarkdown(extractCardText(content), process.cwd(), 'filesystem')
|
|
7235
7666
|
: undefined;
|
|
@@ -8301,6 +8732,9 @@ async function cmdSend(rest) {
|
|
|
8301
8732
|
content = await readStdin();
|
|
8302
8733
|
}
|
|
8303
8734
|
}
|
|
8735
|
+
// Keep memory attribution in the model's rollout, but never render the
|
|
8736
|
+
// complete internal suffix into Lark or count it in send markers.
|
|
8737
|
+
content = stripTrailingOaiMemoryCitation(content);
|
|
8304
8738
|
if (!contentFile && !customCardRequested)
|
|
8305
8739
|
rejectLikelyWindowsStdinMojibake(content);
|
|
8306
8740
|
const managedPayloadError = managedVcSendPayloadError({
|
|
@@ -8991,8 +9425,8 @@ async function cmdSend(rest) {
|
|
|
8991
9425
|
};
|
|
8992
9426
|
try {
|
|
8993
9427
|
// A file-sandbox relay supplies a host-private copy normalized inside the
|
|
8994
|
-
// sandbox namespace. Voice/doc-comment paths returned above
|
|
8995
|
-
//
|
|
9428
|
+
// sandbox namespace. Voice/doc-comment paths returned above use the same
|
|
9429
|
+
// already-sanitized outbound content, without card-markdown preparation.
|
|
8996
9430
|
let text = extractCardText(content);
|
|
8997
9431
|
const preparedContentFile = process.env.BOTMUX_CARD_PREPARED_CONTENT_FILE;
|
|
8998
9432
|
if (preparedContentFile) {
|
|
@@ -9939,6 +10373,7 @@ async function cmdReport(rest) {
|
|
|
9939
10373
|
const pos = positionals(rest, ['--top-level', '--legacy-dispatch']);
|
|
9940
10374
|
content = pos.length ? pos.join(' ') : await readStdin();
|
|
9941
10375
|
}
|
|
10376
|
+
content = stripTrailingOaiMemoryCitation(content);
|
|
9942
10377
|
if (!contentFile)
|
|
9943
10378
|
rejectLikelyWindowsStdinMojibake(content);
|
|
9944
10379
|
if (!content.trim()) {
|
|
@@ -10307,6 +10742,16 @@ botmux create-group — 用一组机器人新建飞书群
|
|
|
10307
10742
|
--json-status 可选;在 chatId 后追加一行结构化完成状态。默认 stdout 无论完整成功或
|
|
10308
10743
|
部分失败都保持历史兼容,只输出单行 chatId;部分失败仍以非零退出表示。
|
|
10309
10744
|
|
|
10745
|
+
团队模式(跨机建聚焦新群,走中心化平台):
|
|
10746
|
+
botmux create-group --team <teamId> --agent <appId> [--agent ...] [--name "群名"]
|
|
10747
|
+
|
|
10748
|
+
用途:把「同团队、已 opt-in」的**别人机器上的** agent(用 bots list --scope team 发现到的 appId)
|
|
10749
|
+
和它们各自的 owner 一起拉进一个平台代建的聚焦新群,全程 machine-auth。
|
|
10750
|
+
正因为发起人在别人 bot 进群前 @不到它,这条只认 appId、不依赖任何飞书 @,天然绕开视角问题。
|
|
10751
|
+
--agent 至少一个、可多次、按 appId 去重。团队模式忽略 --bot/--kickoff/--working-dir(那些是本机建群用的)。
|
|
10752
|
+
未传 --team:本机唯一团队则自动用它,多个要求显式指定。
|
|
10753
|
+
(往**已存在**的团队群补人是独立命令:botmux bots invite --chat <chatId> --team X --agent ...)
|
|
10754
|
+
|
|
10310
10755
|
行为:
|
|
10311
10756
|
- 第一个解析到的 bot 作为 creator(决定建群身份 + 初始群主 + open_id app scope)。
|
|
10312
10757
|
- 邀请用户 / 转让群主 / @通知 对象都从 creator 的 resolvedAllowedUsers 取首个 open_id(email 自动转换;
|
|
@@ -10325,6 +10770,21 @@ botmux create-group — 用一组机器人新建飞书群
|
|
|
10325
10770
|
`);
|
|
10326
10771
|
return;
|
|
10327
10772
|
}
|
|
10773
|
+
// Deprecation 闸:--chat 曾经是 create-group 的「往已有群补人」模式,现已拆成独立命令
|
|
10774
|
+
// `bots invite`。若仍传 --chat,必须**报错早退**而不是静默忽略——否则用户本意补人、
|
|
10775
|
+
// 却因 --chat 被丢弃照常建了个新群(真实副作用,多一个群)。放在 team 分流之前拦。
|
|
10776
|
+
if (hasFlagOrEq(rest, '--chat')) {
|
|
10777
|
+
console.error('create-group 不再支持 --chat(往已有群补人已拆成独立命令)。请改用:');
|
|
10778
|
+
console.error(' botmux bots invite --chat <chatId> --team <id> --agent <appId>...');
|
|
10779
|
+
process.exit(1);
|
|
10780
|
+
}
|
|
10781
|
+
// 团队模式:走平台端点3 建新群,与本机飞书建群是两条完全不同的路径。必须在 transport
|
|
10782
|
+
// 闸门之前分流——平台代建群,本机 bot 不需要飞书传输身份(沙盒/apiOnly 也能发起)。
|
|
10783
|
+
// 「往已有群补人」是独立的 `bots invite`,不在这里。
|
|
10784
|
+
if (hasFlagOrEq(rest, '--team') || hasFlagOrEq(rest, '--agent')) {
|
|
10785
|
+
await cmdCreateGroupTeam(rest);
|
|
10786
|
+
return;
|
|
10787
|
+
}
|
|
10328
10788
|
// create-group builds a real Feishu group (cross-bot). A no-transport turn
|
|
10329
10789
|
// (apiOnly bot or HTTP virtual session) may not originate one — central gate.
|
|
10330
10790
|
assertTurnTransportOrExit('create-group');
|
|
@@ -10535,6 +10995,110 @@ botmux create-group — 用一组机器人新建飞书群
|
|
|
10535
10995
|
if (!completion.success)
|
|
10536
10996
|
process.exitCode = 1;
|
|
10537
10997
|
}
|
|
10998
|
+
/**
|
|
10999
|
+
* `botmux create-group --team <teamId> --agent <appId>...` — 跨机拉群(走中心化平台端点3)。
|
|
11000
|
+
*
|
|
11001
|
+
* 与本机飞书建群(cmdCreateGroup 主体)是两条不同路径:这里把「同团队、已 opt-in」的、
|
|
11002
|
+
* **别人机器上的** agent(appId 从 bots list --scope team 发现而来)+ 各自 owner + 本机 owner
|
|
11003
|
+
* 一起拉进一个平台代建的聚焦新群。全程 machine-auth、只认 appId、不依赖任何飞书 @。
|
|
11004
|
+
*
|
|
11005
|
+
* 硬约束:CLI 零授权判断(团队成员校验 + opt-in 闸 team.bots 全在平台);平台把未 opt-in /
|
|
11006
|
+
* 拉不动的对象放进 invalidBotIds / invalidOwnerUnionIds 原样带回,这里只如实展示、不解释成
|
|
11007
|
+
* "失败"(未 opt-in 是对方 owner 没加进团队,不是本次调用错)。
|
|
11008
|
+
*
|
|
11009
|
+
* 输出协议对齐主命令:成功拿到 chatId → stdout 写单行 chatId(skill 友好);--json-status
|
|
11010
|
+
* 追加一行结构化 JSON(含 shareLink / invalid* / 部分失败标记)。有 invalid* 时 exit 非零,
|
|
11011
|
+
* 但 stdout 的 chatId 已经可用(群已建,别重建)。
|
|
11012
|
+
*/
|
|
11013
|
+
/**
|
|
11014
|
+
* 解析团队维度命令的目标 teamId:显式 --team 优先;否则打端点1,唯一即用、多个要求指定、
|
|
11015
|
+
* 零个报错。用于「必须落到一个具体 team」的动作(建群 / 补人)——list 侧的空 team 是正常态、
|
|
11016
|
+
* 输出空列表,不走这里。`action` 只用于文案("拉群"/"补人")。
|
|
11017
|
+
*/
|
|
11018
|
+
async function resolveSingleTeamIdOrExit(fetchTeams, describeTeamAgentsFailure, teamArg, action) {
|
|
11019
|
+
if (teamArg)
|
|
11020
|
+
return teamArg;
|
|
11021
|
+
const teamsRes = await fetchTeams();
|
|
11022
|
+
if (!teamsRes.ok) {
|
|
11023
|
+
if (teamsRes.reason === 'unbound')
|
|
11024
|
+
console.error(`本机未绑定平台。${action}需要先 botmux bind。`);
|
|
11025
|
+
else if (teamsRes.reason === 'not_deployed')
|
|
11026
|
+
console.error('平台尚未部署团队端点,等上线后重试。');
|
|
11027
|
+
else
|
|
11028
|
+
console.error(`拉取团队列表失败:${describeTeamAgentsFailure(teamsRes)}`);
|
|
11029
|
+
process.exit(1);
|
|
11030
|
+
}
|
|
11031
|
+
const teams = teamsRes.value;
|
|
11032
|
+
if (teams.length === 0) {
|
|
11033
|
+
console.error(`本机未加入任何平台团队,无法${action}。`);
|
|
11034
|
+
process.exit(1);
|
|
11035
|
+
}
|
|
11036
|
+
if (teams.length > 1) {
|
|
11037
|
+
console.error('本机属于多个平台团队,请用 --team <id> 指定其一:');
|
|
11038
|
+
for (const t of teams)
|
|
11039
|
+
console.error(` ${t.teamId} ${t.teamName}`);
|
|
11040
|
+
process.exit(1);
|
|
11041
|
+
}
|
|
11042
|
+
return teams[0].teamId;
|
|
11043
|
+
}
|
|
11044
|
+
async function cmdCreateGroupTeam(rest) {
|
|
11045
|
+
const { fetchTeams, createTeamGroup, describeTeamAgentsFailure, rateLimitRetryHint } = await import('./platform/team-agents-client.js');
|
|
11046
|
+
const jsonStatus = rest.includes('--json-status');
|
|
11047
|
+
const name = argValue(rest, '--name');
|
|
11048
|
+
const appIds = [...new Set(argValues(rest, '--agent').map(s => s.trim()).filter(Boolean))];
|
|
11049
|
+
if (appIds.length === 0) {
|
|
11050
|
+
console.error('团队模式需要至少一个 --agent <appId>(用 botmux bots list --scope team 发现 appId)。');
|
|
11051
|
+
process.exit(1);
|
|
11052
|
+
}
|
|
11053
|
+
const teamId = await resolveSingleTeamIdOrExit(fetchTeams, describeTeamAgentsFailure, argValue(rest, '--team'), '拉群');
|
|
11054
|
+
const res = await createTeamGroup({ teamId, appIds, ...(name !== undefined ? { name } : {}) });
|
|
11055
|
+
if (!res.ok) {
|
|
11056
|
+
if (res.reason === 'unbound')
|
|
11057
|
+
console.error('本机未绑定平台。拉群需要先 botmux bind。');
|
|
11058
|
+
else if (res.reason === 'not_deployed')
|
|
11059
|
+
console.error('平台尚未部署拉群端点(/v1/machine/groups)。等平台上线后重试。');
|
|
11060
|
+
else if (res.reason === 'rate_limited')
|
|
11061
|
+
console.error(`拉群被限流,${rateLimitRetryHint(res)}。`);
|
|
11062
|
+
else if (res.reason === 'client' && res.status === 403) {
|
|
11063
|
+
// 403 not_in_team_bots:所选 agent(或本机 bot)没 opt-in 进团队。平台可能带回具体 appIds。
|
|
11064
|
+
const which = res.appIds && res.appIds.length ? `:${res.appIds.join('、')}` : '';
|
|
11065
|
+
console.error(`拉群被拒(403 ${res.error}):请确认相关 bot 已由其 owner 在平台「管理机器人」加入该团队${which}。`);
|
|
11066
|
+
}
|
|
11067
|
+
else if (res.reason === 'client' && res.status === 404) {
|
|
11068
|
+
console.error(`团队不存在或本机 owner 不是其成员:teamId=${teamId}`);
|
|
11069
|
+
}
|
|
11070
|
+
else {
|
|
11071
|
+
console.error(`拉群失败:${describeTeamAgentsFailure(res)}`);
|
|
11072
|
+
}
|
|
11073
|
+
process.exit(1);
|
|
11074
|
+
}
|
|
11075
|
+
const { chatId, shareLink, invalidBotIds, invalidOwnerUnionIds } = res.value;
|
|
11076
|
+
if (!chatId) {
|
|
11077
|
+
// 平台回 ok 但没给 chatId:异常,别静默成功。
|
|
11078
|
+
console.error(`平台未返回 chatId(ok=${res.value.ok})。`);
|
|
11079
|
+
if (jsonStatus)
|
|
11080
|
+
console.log(JSON.stringify({ ...res.value, ok: false }));
|
|
11081
|
+
process.exit(1);
|
|
11082
|
+
}
|
|
11083
|
+
// stdout 单行 chatId(skill 友好,主命令同款协议)。
|
|
11084
|
+
console.log(chatId);
|
|
11085
|
+
const hasInvalid = invalidBotIds.length > 0 || invalidOwnerUnionIds.length > 0;
|
|
11086
|
+
if (jsonStatus) {
|
|
11087
|
+
console.log(JSON.stringify({ ok: true, chatId, shareLink, invalidBotIds, invalidOwnerUnionIds, teamId }));
|
|
11088
|
+
}
|
|
11089
|
+
// 未 opt-in / 拉不动的对象走 stderr 提示(不污染 stdout 的 chatId 契约)。
|
|
11090
|
+
if (invalidBotIds.length > 0) {
|
|
11091
|
+
console.error(`⚠️ 以下 agent 未加入团队或拉取失败,未入群:${invalidBotIds.join('、')}`);
|
|
11092
|
+
}
|
|
11093
|
+
if (invalidOwnerUnionIds.length > 0) {
|
|
11094
|
+
console.error(`⚠️ 以下 owner 未能拉入:${invalidOwnerUnionIds.join('、')}`);
|
|
11095
|
+
}
|
|
11096
|
+
if (shareLink)
|
|
11097
|
+
console.error(`群分享链接:${shareLink}`);
|
|
11098
|
+
// 部分失败 → 非零退出(但群已建、chatId 已在 stdout,调用方应复用不重建)。
|
|
11099
|
+
if (hasInvalid)
|
|
11100
|
+
process.exitCode = 1;
|
|
11101
|
+
}
|
|
10538
11102
|
// ─── Bots subcommand ─────────────────────────────────────────────────────────
|
|
10539
11103
|
// ─── botmux ask v0.1.7 ───────────────────────────────────────────────────────
|
|
10540
11104
|
//
|
|
@@ -11179,8 +11743,28 @@ async function cmdUserPromptHook() {
|
|
|
11179
11743
|
}
|
|
11180
11744
|
async function cmdBots(sub, rest) {
|
|
11181
11745
|
process.env.SESSION_DATA_DIR ??= resolveDataDir();
|
|
11746
|
+
// `bots invite`:往**已存在的团队群**补人(同 team、已 opt-in 的 agent + 各自 owner),
|
|
11747
|
+
// 打平台端点4(machine-auth)。独立子命令——与「建新群」的 create-group 语义分开,
|
|
11748
|
+
// 不再靠 create-group --chat 区分(那个入口易被误读成"建群")。在会话/transport 闸门前分流。
|
|
11749
|
+
if (sub === 'invite') {
|
|
11750
|
+
await cmdBotsInvite(rest);
|
|
11751
|
+
return;
|
|
11752
|
+
}
|
|
11182
11753
|
if (sub !== 'list' && sub !== 'ls' && sub !== '') {
|
|
11183
|
-
console.error('用法: botmux bots list [--session-id ID]');
|
|
11754
|
+
console.error('用法: botmux bots list [--scope chat|team] [--team <id>] [--session-id ID]');
|
|
11755
|
+
console.error(' botmux bots invite --chat <chatId> --team <id> --agent <appId>...');
|
|
11756
|
+
process.exit(1);
|
|
11757
|
+
}
|
|
11758
|
+
// `--scope team`:跨机发现同团队、已 opt-in 的 agent(打平台端点2,machine-auth)。
|
|
11759
|
+
// 与默认的 chat scope 完全不同——它不读飞书群花名册、不需要会话/传输身份,
|
|
11760
|
+
// 所以在会话解析 / transport 闸门之前分流。空 team 列表是正常态(没别人 opt-in)。
|
|
11761
|
+
const scope = (argValue(rest, '--scope') ?? 'chat').toLowerCase();
|
|
11762
|
+
if (scope === 'team') {
|
|
11763
|
+
await cmdBotsListTeam(rest);
|
|
11764
|
+
return;
|
|
11765
|
+
}
|
|
11766
|
+
if (scope !== 'chat') {
|
|
11767
|
+
console.error(`未知 --scope "${scope}"(仅支持 chat | team)`);
|
|
11184
11768
|
process.exit(1);
|
|
11185
11769
|
}
|
|
11186
11770
|
// `bots list` reads the Feishu chat roster (listChatBotMembers). A no-transport
|
|
@@ -11208,6 +11792,26 @@ async function cmdBots(sub, rest) {
|
|
|
11208
11792
|
botEntries = JSON.parse(readFileSync(botInfoPath, 'utf-8'));
|
|
11209
11793
|
}
|
|
11210
11794
|
catch { /* */ }
|
|
11795
|
+
const collaborationFactsFor = (appIds) => {
|
|
11796
|
+
const facts = Object.create(null);
|
|
11797
|
+
const wanted = new Set(appIds.filter(Boolean));
|
|
11798
|
+
try {
|
|
11799
|
+
for (const cfg of loadBotConfigs()) {
|
|
11800
|
+
if (!wanted.has(cfg.larkAppId))
|
|
11801
|
+
continue;
|
|
11802
|
+
facts[cfg.larkAppId] = {
|
|
11803
|
+
workspaceSource: cfg.oncallChats?.some(c => c.chatId === s.chatId)
|
|
11804
|
+
? 'oncall'
|
|
11805
|
+
: effectiveDefaultWorkingDir(cfg) ? 'default' : 'unknown',
|
|
11806
|
+
mentionMode: cfg.regularGroupMentionMode ?? 'always',
|
|
11807
|
+
replyMode: cfg.chatReplyModes?.[s.chatId] ?? cfg.regularGroupReplyMode ?? 'chat-topic',
|
|
11808
|
+
transport: cfg.apiOnly !== true,
|
|
11809
|
+
};
|
|
11810
|
+
}
|
|
11811
|
+
}
|
|
11812
|
+
catch { /* config unreadable under isolation: all facts stay unknown */ }
|
|
11813
|
+
return facts;
|
|
11814
|
+
};
|
|
11211
11815
|
try {
|
|
11212
11816
|
const { listChatBotMembers } = await import('./im/lark/client.js');
|
|
11213
11817
|
const chatBots = await listChatBotMembers(appId, s.chatId);
|
|
@@ -11215,15 +11819,220 @@ async function cmdBots(sub, rest) {
|
|
|
11215
11819
|
// botmux daemon on this host). 'introduce' = discovered via /introduce
|
|
11216
11820
|
// collaboration command (external bot, possibly other-tenant). isSelf is
|
|
11217
11821
|
// retained (not filtered) so the model can still identify itself when needed.
|
|
11218
|
-
const result = formatChatBotsForCli(chatBots, appId);
|
|
11219
|
-
console.log(JSON.stringify({ sessionId: sid, chatId: s.chatId, bots: result, total: result.length }, null, 2));
|
|
11822
|
+
const result = formatChatBotsForCli(chatBots, appId, collaborationFactsFor(chatBots.map(bot => bot.larkAppId)));
|
|
11823
|
+
console.log(JSON.stringify({ sessionId: sid, chatId: s.chatId, bots: result, total: result.length, collaborationHelp }, null, 2));
|
|
11220
11824
|
}
|
|
11221
11825
|
catch (err) {
|
|
11222
11826
|
// Fallback to bots-info.json
|
|
11223
|
-
const result = formatBotInfoEntriesForCli(botEntries, appId);
|
|
11224
|
-
console.log(JSON.stringify({ sessionId: sid, bots: result, total: result.length, note: `chat query failed: ${err.message}` }, null, 2));
|
|
11827
|
+
const result = formatBotInfoEntriesForCli(botEntries, appId, collaborationFactsFor(botEntries.map(bot => bot.larkAppId)));
|
|
11828
|
+
console.log(JSON.stringify({ sessionId: sid, bots: result, total: result.length, collaborationHelp, note: `chat query failed: ${err.message}` }, null, 2));
|
|
11225
11829
|
}
|
|
11226
11830
|
}
|
|
11831
|
+
/**
|
|
11832
|
+
* `botmux bots list --scope team [--team <id>]` — 跨机发现同团队、已 opt-in(owner
|
|
11833
|
+
* 加进 team.bots)的 agent,打平台端点2(machine-auth,Bearer=machineToken)。
|
|
11834
|
+
*
|
|
11835
|
+
* 与 chat scope 的关键区别 + 硬约束:
|
|
11836
|
+
* - 走 machine-auth,不读飞书群花名册、不需要传输身份 → 沙盒/apiOnly bot 也能查。
|
|
11837
|
+
* - **CLI 不做任何授权判断**:团队成员校验 + opt-in 闸全在平台,这里只透传、只展示。
|
|
11838
|
+
* - specialties / mentionable 是 agent 自报 → 仅展示,不当可信凭据。
|
|
11839
|
+
* - 未传 --team:先打端点1 拉本机 owner 的团队;唯一就用它,多个要求显式 --team
|
|
11840
|
+
* (不猜),零个则提示本机没加入任何平台团队。
|
|
11841
|
+
*/
|
|
11842
|
+
async function cmdBotsListTeam(rest) {
|
|
11843
|
+
const { fetchTeamAgents, fetchTeams, describeTeamAgentsFailure } = await import('./platform/team-agents-client.js');
|
|
11844
|
+
const jsonOut = argFlag(rest, '--json');
|
|
11845
|
+
let teamId = argValue(rest, '--team');
|
|
11846
|
+
let teamNameHint;
|
|
11847
|
+
if (!teamId) {
|
|
11848
|
+
const teamsRes = await fetchTeams();
|
|
11849
|
+
if (!teamsRes.ok) {
|
|
11850
|
+
if (teamsRes.reason === 'unbound') {
|
|
11851
|
+
console.error('本机未绑定平台(~/.botmux/platform.json 缺失)。团队发现需要先 botmux bind。');
|
|
11852
|
+
}
|
|
11853
|
+
else if (teamsRes.reason === 'not_deployed') {
|
|
11854
|
+
console.error('平台尚未部署团队端点(/v1/machine/teams)。等平台上线后重试。');
|
|
11855
|
+
}
|
|
11856
|
+
else {
|
|
11857
|
+
console.error(`拉取团队列表失败:${describeTeamAgentsFailure(teamsRes)}`);
|
|
11858
|
+
}
|
|
11859
|
+
process.exit(1);
|
|
11860
|
+
}
|
|
11861
|
+
const teams = teamsRes.value;
|
|
11862
|
+
if (teams.length === 0) {
|
|
11863
|
+
// 空不是错误:本机 owner 还没加入任何平台团队。
|
|
11864
|
+
console.log(JSON.stringify({ scope: 'team', teams: [], agents: [], total: 0, note: '本机未加入任何平台团队' }, null, 2));
|
|
11865
|
+
return;
|
|
11866
|
+
}
|
|
11867
|
+
if (teams.length > 1) {
|
|
11868
|
+
console.error('本机属于多个平台团队,请用 --team <id> 指定其一:');
|
|
11869
|
+
for (const t of teams)
|
|
11870
|
+
console.error(` ${t.teamId} ${t.teamName}`);
|
|
11871
|
+
process.exit(1);
|
|
11872
|
+
}
|
|
11873
|
+
teamId = teams[0].teamId;
|
|
11874
|
+
teamNameHint = teams[0].teamName;
|
|
11875
|
+
}
|
|
11876
|
+
const res = await fetchTeamAgents(teamId);
|
|
11877
|
+
if (!res.ok) {
|
|
11878
|
+
if (res.reason === 'unbound') {
|
|
11879
|
+
console.error('本机未绑定平台(~/.botmux/platform.json 缺失)。团队发现需要先 botmux bind。');
|
|
11880
|
+
}
|
|
11881
|
+
else if (res.reason === 'not_deployed') {
|
|
11882
|
+
console.error('平台尚未部署团队 agent 发现端点(/v1/machine/agents)。等平台上线后重试。');
|
|
11883
|
+
}
|
|
11884
|
+
else if (res.reason === 'client' && res.status === 404) {
|
|
11885
|
+
console.error(`团队不存在或本机 owner 不是其成员:teamId=${teamId}`);
|
|
11886
|
+
}
|
|
11887
|
+
else {
|
|
11888
|
+
console.error(`拉取团队 agent 失败:${describeTeamAgentsFailure(res)}`);
|
|
11889
|
+
}
|
|
11890
|
+
process.exit(1);
|
|
11891
|
+
}
|
|
11892
|
+
const { teamName, agents } = res.value;
|
|
11893
|
+
// 输出对齐 chat scope 的 JSON 形状(scope 标注 + total),字段是平台端点2 的原样透传。
|
|
11894
|
+
// 提醒:发现列表只含**已加入 team.bots** 的 agent(owner 在平台「管理机器人」显式加入)。
|
|
11895
|
+
console.log(JSON.stringify({
|
|
11896
|
+
scope: 'team',
|
|
11897
|
+
teamId,
|
|
11898
|
+
teamName: teamName || teamNameHint || teamId,
|
|
11899
|
+
agents,
|
|
11900
|
+
total: agents.length,
|
|
11901
|
+
note: '发现列表只含已加入团队(owner 在平台「管理机器人」opt-in)的 agent;specialties/mentionable 为 agent 自报,仅供参考不可信。',
|
|
11902
|
+
}, null, jsonOut ? 0 : 2));
|
|
11903
|
+
}
|
|
11904
|
+
/**
|
|
11905
|
+
* (a) 自动化辅助:把平台后端 app(platformAppId)拉进目标群 `chatId`。
|
|
11906
|
+
*
|
|
11907
|
+
* 飞书约束:加应用的 app 自己得在群里。所以找一个**已在该群、且本机可用凭据**的 bot 当代理
|
|
11908
|
+
* (addBotToChat proxy)。优先用本会话 bot(它天然在本群且已注册),否则遍历 bots.json 里其它
|
|
11909
|
+
* 非 apiOnly bot、逐个 isInChat 命中即用。全都不在群/加失败(无 scope、群需群主审批)→ 返回失败,
|
|
11910
|
+
* 由调用方回退到手动引导。
|
|
11911
|
+
*/
|
|
11912
|
+
async function tryAutoAddPlatformBot(chatId, platformAppId) {
|
|
11913
|
+
try {
|
|
11914
|
+
const { findLocalBotInChat } = await import('./im/lark/client.js');
|
|
11915
|
+
const { addBotToChat } = await import('./services/groups-store.js');
|
|
11916
|
+
// 用安静探测客户端找一个「已在目标群」的本机 bot 当代理(本会话 bot 优先)。
|
|
11917
|
+
// 不在群的 bot 探测 miss 被静音,不刷 axios 400 噪音(见 client.findLocalBotInChat)。
|
|
11918
|
+
const proxy = await findLocalBotInChat(chatId, process.env.BOTMUX_LARK_APP_ID);
|
|
11919
|
+
if (!proxy)
|
|
11920
|
+
return { ok: false, reason: '本机没有已在该群的 bot 可当代理' };
|
|
11921
|
+
// 代理 bot 需已在注册表(getBotClient 可用)——从 bots.json 注册一次(幂等)。
|
|
11922
|
+
try {
|
|
11923
|
+
const { loadBotConfigs, registerBot } = await import('./bot-registry.js');
|
|
11924
|
+
const cfg = loadBotConfigs().find((c) => c.larkAppId === proxy.larkAppId);
|
|
11925
|
+
if (cfg)
|
|
11926
|
+
registerBot(cfg);
|
|
11927
|
+
}
|
|
11928
|
+
catch { /* 已注册或读配置失败 → addBotToChat 若拿不到 client 会报错并回退 */ }
|
|
11929
|
+
const r = await addBotToChat(proxy.larkAppId, chatId, [platformAppId]);
|
|
11930
|
+
const one = r[0];
|
|
11931
|
+
if (one?.ok)
|
|
11932
|
+
return { ok: true, proxyName: proxy.cliId ? `${proxy.cliId}/${proxy.larkAppId}` : proxy.larkAppId };
|
|
11933
|
+
// 加失败(无 scope / 群需群主审批 / invalid)→ 回退手动引导。
|
|
11934
|
+
return { ok: false, reason: `代理 ${proxy.larkAppId} 添加失败:${one?.error ?? 'unknown'}` };
|
|
11935
|
+
}
|
|
11936
|
+
catch (e) {
|
|
11937
|
+
return { ok: false, reason: String(e?.message ?? e) };
|
|
11938
|
+
}
|
|
11939
|
+
}
|
|
11940
|
+
/**
|
|
11941
|
+
* `botmux bots invite --chat <chatId> --team <id> --agent <appId>...`
|
|
11942
|
+
* —— 往**已存在的团队群**补人(同 team、已 opt-in 的 agent + 各自 owner),打平台端点4。
|
|
11943
|
+
*
|
|
11944
|
+
* 独立于 create-group(建新群):语义是「群已经在了,往里加人」,不该跟建群混在一个命令里。
|
|
11945
|
+
* 全程 machine-auth、只认 appId(发起人在别人 bot 进群前 @不到它,靠 appId 绕开视角问题)。
|
|
11946
|
+
* 授权/opt-in 闸/大厅排除全在平台,CLI 零判断、只如实展示平台判定。
|
|
11947
|
+
* 若平台回 platform_bot_not_in_chat 且带 platformAppId → 自动用群内本机 bot 把平台 app 拉进群、重试。
|
|
11948
|
+
*
|
|
11949
|
+
* 输出:stdout 单行 chatId(skill 友好,与 create-group 一致);--json-status 追加
|
|
11950
|
+
* {ok, chatId, invalidBotIds, invalidOwnerUnionIds, teamId};invalid* 非空 → 非零退出。
|
|
11951
|
+
*/
|
|
11952
|
+
async function cmdBotsInvite(rest) {
|
|
11953
|
+
const { addTeamGroupMembers, fetchTeams, describeTeamAgentsFailure, rateLimitRetryHint, shouldTryAutoAddPlatformBot } = await import('./platform/team-agents-client.js');
|
|
11954
|
+
const jsonStatus = rest.includes('--json-status');
|
|
11955
|
+
const chatArg = (argValue(rest, '--chat') ?? '').trim();
|
|
11956
|
+
const appIds = [...new Set(argValues(rest, '--agent').map(s => s.trim()).filter(Boolean))];
|
|
11957
|
+
if (!chatArg) {
|
|
11958
|
+
console.error('用法: botmux bots invite --chat <chatId> --team <id> --agent <appId>...');
|
|
11959
|
+
console.error('--chat 必填:目标群 chatId(须已有平台机器人 BotmuxPlatform 在场,且你本人已在该群,非机器人大厅)。');
|
|
11960
|
+
console.error('补人恒把 agent + 各自 owner 一起拉进群。');
|
|
11961
|
+
process.exit(1);
|
|
11962
|
+
}
|
|
11963
|
+
if (appIds.length === 0) {
|
|
11964
|
+
console.error('至少一个 --agent <appId>(用 botmux bots list --scope team 发现 appId)。');
|
|
11965
|
+
process.exit(1);
|
|
11966
|
+
}
|
|
11967
|
+
const teamId = await resolveSingleTeamIdOrExit(fetchTeams, describeTeamAgentsFailure, argValue(rest, '--team'), '补人');
|
|
11968
|
+
let res = await addTeamGroupMembers({ chatId: chatArg, teamId, appIds });
|
|
11969
|
+
// (a) 自动化:撞 platform_bot_not_in_chat 且平台回传了 platformAppId → 用群里已有的本机 bot
|
|
11970
|
+
// 当代理,把平台后端 app 拉进目标群,再自动重试一次补人。让「往任意我在的群补人」尽量无感;
|
|
11971
|
+
// 碰到「加 bot 需群主审批 / 代理 bot 无成员管理 scope」时 addBotToChat 会失败,落回下面的手动提示。
|
|
11972
|
+
let autoAddAttempted = false;
|
|
11973
|
+
if (shouldTryAutoAddPlatformBot(res)) {
|
|
11974
|
+
const added = await tryAutoAddPlatformBot(chatArg, res.platformAppId);
|
|
11975
|
+
if (added.ok) {
|
|
11976
|
+
autoAddAttempted = true;
|
|
11977
|
+
console.error(`(已用群内 bot「${added.proxyName}」把平台应用拉进群,重试补人…)`);
|
|
11978
|
+
res = await addTeamGroupMembers({ chatId: chatArg, teamId, appIds });
|
|
11979
|
+
}
|
|
11980
|
+
else {
|
|
11981
|
+
// 自动拉失败:给可操作的手动引导(用平台回传的 app 名/id),不静默。
|
|
11982
|
+
const appLabel = res.platformAppName ? `${res.platformAppName}(${res.platformAppId})` : res.platformAppId;
|
|
11983
|
+
console.error(`补人前置:平台应用不在目标群,且自动添加未成功(${added.reason})。`);
|
|
11984
|
+
console.error(`请在群设置里手动添加应用:${appLabel},然后重试 botmux bots invite。`);
|
|
11985
|
+
process.exit(1);
|
|
11986
|
+
}
|
|
11987
|
+
}
|
|
11988
|
+
if (!res.ok) {
|
|
11989
|
+
if (res.reason === 'unbound')
|
|
11990
|
+
console.error('本机未绑定平台。补人需要先 botmux bind。');
|
|
11991
|
+
else if (res.reason === 'not_deployed')
|
|
11992
|
+
console.error('平台尚未部署补人端点(/v1/machine/groups/:chatId/members)。等平台上线后重试。');
|
|
11993
|
+
else if (res.reason === 'rate_limited')
|
|
11994
|
+
console.error(`补人被限流,${rateLimitRetryHint(res)}。`);
|
|
11995
|
+
else if (res.reason === 'client' && res.status === 403) {
|
|
11996
|
+
// 403 分支(平台按 error code 返回):可行性/归属/opt-in/大厅,各给可操作的提示。
|
|
11997
|
+
const which = res.appIds && res.appIds.length ? `(${res.appIds.join('、')})` : '';
|
|
11998
|
+
const appLabel = res.platformAppName ? `${res.platformAppName}(${res.platformAppId ?? ''})` : (res.platformAppId ?? '平台应用');
|
|
11999
|
+
const hint = res.error === 'platform_bot_not_in_chat'
|
|
12000
|
+
// 已自动拉过还撞它:飞书加 app 通常同步生效,多半是刚加完成员表还没刷新——提示稍等重试,
|
|
12001
|
+
// 别让用户对着刚拉进去的 app 再手动加一遍(pi review nit)。没自动拉过才引导手动添加。
|
|
12002
|
+
? (autoAddAttempted
|
|
12003
|
+
? `平台应用已自动拉进群、但补人仍报它不在——飞书成员同步通常几秒内完成,请稍等片刻重试 botmux bots invite`
|
|
12004
|
+
: `平台应用还不在目标群里——请在群设置添加应用:${appLabel},再补人`)
|
|
12005
|
+
: res.error === 'requester_not_in_chat' ? '你本人还不在目标群里——只能往「你自己已在场」的群补人(先加入该群)'
|
|
12006
|
+
: res.error === 'requester_bot_not_in_chat' ? '你本人还不在目标群里——只能往「你自己已在场」的群补人(先加入该群)'
|
|
12007
|
+
: res.error === 'chat_is_hall' ? '目标群是机器人大厅,不允许往里补人(大厅是 bot-only 身份登记群)'
|
|
12008
|
+
: res.error === 'chat_not_in_team' ? '目标群不是本团队的协作群'
|
|
12009
|
+
: `相关 bot${which} 未由其 owner 在平台「管理机器人」加入该团队`;
|
|
12010
|
+
console.error(`补人被拒(403 ${res.error}):${hint}。`);
|
|
12011
|
+
}
|
|
12012
|
+
else if (res.reason === 'client' && res.status === 404) {
|
|
12013
|
+
console.error(`团队不存在或本机 owner 不是其成员:teamId=${teamId}`);
|
|
12014
|
+
}
|
|
12015
|
+
else {
|
|
12016
|
+
console.error(`补人失败:${describeTeamAgentsFailure(res)}`);
|
|
12017
|
+
}
|
|
12018
|
+
process.exit(1);
|
|
12019
|
+
}
|
|
12020
|
+
const { invalidBotIds, invalidOwnerUnionIds } = res.value;
|
|
12021
|
+
console.log(chatArg); // stdout 单行 chatId(与 create-group 一致)
|
|
12022
|
+
const hasInvalid = invalidBotIds.length > 0 || invalidOwnerUnionIds.length > 0;
|
|
12023
|
+
if (jsonStatus) {
|
|
12024
|
+
console.log(JSON.stringify({ ok: true, chatId: chatArg, invalidBotIds, invalidOwnerUnionIds, teamId }));
|
|
12025
|
+
}
|
|
12026
|
+
// 补人幂等(已在群内视作成功);平台 200 不列「实际加了谁」,成功即 invalid* 为空。
|
|
12027
|
+
if (!hasInvalid)
|
|
12028
|
+
console.error('✅ 补人成功(选中的 agent 及其 owner 均已在群内或已加入)。');
|
|
12029
|
+
if (invalidBotIds.length > 0)
|
|
12030
|
+
console.error(`⚠️ 以下 agent 未加入团队或补入失败:${invalidBotIds.join('、')}`);
|
|
12031
|
+
if (invalidOwnerUnionIds.length > 0)
|
|
12032
|
+
console.error(`⚠️ 以下 owner 未能补入:${invalidOwnerUnionIds.join('、')}`);
|
|
12033
|
+
if (hasInvalid)
|
|
12034
|
+
process.exitCode = 1;
|
|
12035
|
+
}
|
|
11227
12036
|
// ─── botmux lang ─────────────────────────────────────────────────────────────
|
|
11228
12037
|
/** Notify every online daemon to hot-reload its UI locale from disk, so a
|
|
11229
12038
|
* `botmux lang` change takes effect on live cards without a restart. Best
|
|
@@ -11551,6 +12360,9 @@ if (process.env.BOTMUX_WORKFLOW === '1') {
|
|
|
11551
12360
|
'bots',
|
|
11552
12361
|
'skill',
|
|
11553
12362
|
'hook',
|
|
12363
|
+
// Local-only, session-capability-bound preview registration. It has no chat,
|
|
12364
|
+
// workflow, deployment, or external messaging effect.
|
|
12365
|
+
'preview',
|
|
11554
12366
|
'session-ready',
|
|
11555
12367
|
'mcp',
|
|
11556
12368
|
'ask', // dedicated cmdAsk guard emits the humanGate-specific guidance
|
|
@@ -11764,6 +12576,7 @@ async function stopPluginServicesForCli(pluginIds, options = {}) {
|
|
|
11764
12576
|
console.log('\n插件 host service:');
|
|
11765
12577
|
console.log(formatPluginServiceReports(reports));
|
|
11766
12578
|
}
|
|
12579
|
+
return reports;
|
|
11767
12580
|
}
|
|
11768
12581
|
function requirePluginId(raw) {
|
|
11769
12582
|
const id = raw?.trim();
|
|
@@ -12339,10 +13152,10 @@ switch (command) {
|
|
|
12339
13152
|
await cmdRestart();
|
|
12340
13153
|
break;
|
|
12341
13154
|
case 'logs':
|
|
12342
|
-
cmdLogs();
|
|
13155
|
+
await cmdLogs();
|
|
12343
13156
|
break;
|
|
12344
13157
|
case 'status':
|
|
12345
|
-
cmdStatus();
|
|
13158
|
+
await cmdStatus();
|
|
12346
13159
|
break;
|
|
12347
13160
|
case 'upgrade':
|
|
12348
13161
|
case 'update':
|
|
@@ -12362,6 +13175,13 @@ switch (command) {
|
|
|
12362
13175
|
process.exitCode = await runDeviceCommand(process.argv.slice(3));
|
|
12363
13176
|
break;
|
|
12364
13177
|
}
|
|
13178
|
+
case 'mojo-containment': {
|
|
13179
|
+
// The auditable operator exit for fail-closed containment blockers that can
|
|
13180
|
+
// never self-release (weak handles on non-cgroup hosts, unprovable handles).
|
|
13181
|
+
const { runMojoContainmentCommand } = await import('./core/mojo-containment-command.js');
|
|
13182
|
+
process.exitCode = await runMojoContainmentCommand(process.argv.slice(3));
|
|
13183
|
+
break;
|
|
13184
|
+
}
|
|
12365
13185
|
case 'list':
|
|
12366
13186
|
case 'ls':
|
|
12367
13187
|
await cmdList();
|
|
@@ -12409,6 +13229,9 @@ switch (command) {
|
|
|
12409
13229
|
case 'term-link':
|
|
12410
13230
|
await cmdTermLink(process.argv.slice(3));
|
|
12411
13231
|
break;
|
|
13232
|
+
case 'preview':
|
|
13233
|
+
await cmdPreview(process.argv.slice(3));
|
|
13234
|
+
break;
|
|
12412
13235
|
case 'schedule':
|
|
12413
13236
|
await cmdSchedule(process.argv[3] ?? '', process.argv.slice(4));
|
|
12414
13237
|
break;
|