@phnx-labs/agents-cli 1.22.38 → 1.22.40
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/CHANGELOG.md +302 -0
- package/README.md +111 -70
- package/dist/bin/agents +0 -0
- package/dist/bootstrap.js +46 -27
- package/dist/browser.js +2 -2
- package/dist/cli/command-registry.d.ts +135 -0
- package/dist/cli/command-registry.js +260 -0
- package/dist/commands/accounts.d.ts +20 -0
- package/dist/commands/accounts.js +186 -25
- package/dist/commands/apply.d.ts +3 -3
- package/dist/commands/apply.js +20 -63
- package/dist/commands/browser.js +206 -94
- package/dist/commands/budget.d.ts +7 -6
- package/dist/commands/budget.js +8 -7
- package/dist/commands/cloud.js +17 -20
- package/dist/commands/commands.js +1 -1
- package/dist/commands/computer-actions.d.ts +1 -35
- package/dist/commands/computer-actions.js +13 -181
- package/dist/commands/computer.d.ts +28 -3
- package/dist/commands/computer.js +93 -65
- package/dist/commands/config.js +47 -4
- package/dist/commands/cost.d.ts +2 -1
- package/dist/commands/cost.js +7 -6
- package/dist/commands/daemon.js +151 -4
- package/dist/commands/detach.js +1 -1
- package/dist/commands/doctor.js +19 -11
- package/dist/commands/events.js +1 -1
- package/dist/commands/exec.d.ts +2 -50
- package/dist/commands/exec.js +161 -216
- package/dist/commands/factory.js +1 -1
- package/dist/commands/feed.d.ts +2 -2
- package/dist/commands/feed.js +9 -12
- package/dist/commands/fleet-capture.js +25 -27
- package/dist/commands/focus.d.ts +2 -3
- package/dist/commands/focus.js +34 -9
- package/dist/commands/go.js +15 -5
- package/dist/commands/harness.d.ts +3 -3
- package/dist/commands/harness.js +53 -5
- package/dist/commands/hooks.js +1 -1
- package/dist/commands/import.js +2 -2
- package/dist/commands/insights.d.ts +5 -5
- package/dist/commands/insights.js +15 -9
- package/dist/commands/inspect.d.ts +2 -3
- package/dist/commands/inspect.js +4 -16
- package/dist/commands/logs.d.ts +2 -2
- package/dist/commands/logs.js +9 -9
- package/dist/commands/mcp.js +2 -2
- package/dist/commands/memory.js +1 -1
- package/dist/commands/message.d.ts +1 -1
- package/dist/commands/message.js +7 -7
- package/dist/commands/mine.d.ts +11 -8
- package/dist/commands/mine.js +11 -10
- package/dist/commands/models.js +1 -1
- package/dist/commands/modes.js +1 -1
- package/dist/commands/monitors.js +23 -6
- package/dist/commands/output.d.ts +7 -6
- package/dist/commands/output.js +11 -10
- package/dist/commands/packages.js +4 -4
- package/dist/commands/perf.js +1 -1
- package/dist/commands/permissions.js +49 -32
- package/dist/commands/plugins.d.ts +1 -1
- package/dist/commands/plugins.js +3 -3
- package/dist/commands/profiles.d.ts +6 -9
- package/dist/commands/profiles.js +8 -321
- package/dist/commands/projects.d.ts +9 -0
- package/dist/commands/projects.js +140 -8
- package/dist/commands/prune.js +5 -13
- package/dist/commands/refresh-rules.js +1 -1
- package/dist/commands/repo.d.ts +5 -7
- package/dist/commands/repo.js +34 -18
- package/dist/commands/resource-view.js +4 -0
- package/dist/commands/resume.d.ts +11 -7
- package/dist/commands/resume.js +82 -98
- package/dist/commands/route.d.ts +12 -0
- package/dist/commands/route.js +261 -0
- package/dist/commands/routines.d.ts +2 -3
- package/dist/commands/routines.js +120 -145
- package/dist/commands/rules.js +1 -1
- package/dist/commands/run-account-picker.d.ts +1 -1
- package/dist/commands/run-account-picker.js +2 -2
- package/dist/commands/run-broadcast.d.ts +21 -0
- package/dist/commands/run-broadcast.js +89 -0
- package/dist/commands/run-cloud.js +1 -1
- package/dist/commands/secrets-vault.d.ts +10 -0
- package/dist/commands/{login.js → secrets-vault.js} +34 -14
- package/dist/commands/secrets.d.ts +4 -4
- package/dist/commands/secrets.js +72 -63
- package/dist/commands/sessions-backfill.js +1 -1
- package/dist/commands/sessions-browser.d.ts +1 -1
- package/dist/commands/sessions-browser.js +9 -5
- package/dist/commands/sessions-export.js +2 -2
- package/dist/commands/sessions-inject.js +4 -5
- package/dist/commands/sessions-migrate.js +16 -27
- package/dist/commands/sessions-picker.d.ts +13 -0
- package/dist/commands/sessions-picker.js +182 -8
- package/dist/commands/sessions-resume.d.ts +7 -3
- package/dist/commands/sessions-resume.js +81 -33
- package/dist/commands/sessions-stats.js +2 -2
- package/dist/commands/sessions.d.ts +75 -68
- package/dist/commands/sessions.js +142 -180
- package/dist/commands/setup-computer.js +1 -1
- package/dist/commands/setup-fleet.js +3 -0
- package/dist/commands/setup-mine.d.ts +7 -3
- package/dist/commands/setup-mine.js +13 -8
- package/dist/commands/setup-secrets.js +2 -2
- package/dist/commands/setup-watchdog.js +2 -2
- package/dist/commands/setup.js +7 -2
- package/dist/commands/share.d.ts +65 -0
- package/dist/commands/share.js +253 -20
- package/dist/commands/skills.js +3 -3
- package/dist/commands/snapshot.d.ts +2 -2
- package/dist/commands/snapshot.js +9 -9
- package/dist/commands/ssh.d.ts +1 -1
- package/dist/commands/ssh.js +318 -48
- package/dist/commands/subagents.js +1 -1
- package/dist/commands/sync.js +24 -9
- package/dist/commands/teams.d.ts +2 -2
- package/dist/commands/teams.js +9 -18
- package/dist/commands/tmux.js +5 -1
- package/dist/commands/trash.js +1 -1
- package/dist/commands/usage.d.ts +1 -1
- package/dist/commands/usage.js +2 -2
- package/dist/commands/utils.d.ts +4 -14
- package/dist/commands/utils.js +6 -27
- package/dist/commands/versions.js +2 -2
- package/dist/commands/view.d.ts +9 -77
- package/dist/commands/view.js +23 -8
- package/dist/commands/watchdog.js +1 -1
- package/dist/commands/webhook.js +11 -25
- package/dist/commands/workflows.js +1 -1
- package/dist/index.js +1 -1
- package/dist/lib/account-capabilities.d.ts +39 -0
- package/dist/lib/account-capabilities.js +59 -0
- package/dist/lib/account-catalog.js +1 -1
- package/dist/lib/account-registry.d.ts +66 -0
- package/dist/lib/account-registry.js +151 -5
- package/dist/lib/{rotate.d.ts → accounting/rotate.d.ts} +4 -4
- package/dist/lib/{rotate.js → accounting/rotate.js} +8 -8
- package/dist/lib/{usage.d.ts → accounting/usage.d.ts} +4 -4
- package/dist/lib/{usage.js → accounting/usage.js} +11 -11
- package/dist/lib/agent-spec/provider.js +1 -1
- package/dist/lib/agents.d.ts +72 -4
- package/dist/lib/agents.js +231 -199
- package/dist/lib/answer-router.d.ts +2 -2
- package/dist/lib/ask-classifier.d.ts +1 -1
- package/dist/lib/ask-classifier.js +1 -1
- package/dist/lib/audit/log.js +1 -1
- package/dist/lib/auth-health.d.ts +1 -1
- package/dist/lib/auth-health.js +2 -2
- package/dist/lib/brand.js +1 -1
- package/dist/lib/browser/caller-identity.d.ts +28 -0
- package/dist/lib/browser/caller-identity.js +156 -0
- package/dist/lib/browser/cdp.js +1 -1
- package/dist/lib/browser/drivers/ssh.d.ts +1 -1
- package/dist/lib/browser/drivers/ssh.js +2 -2
- package/dist/lib/browser/hygiene.d.ts +10 -2
- package/dist/lib/browser/hygiene.js +7 -4
- package/dist/lib/browser/ipc.d.ts +22 -2
- package/dist/lib/browser/ipc.js +185 -21
- package/dist/lib/browser/profiles.d.ts +67 -3
- package/dist/lib/browser/profiles.js +129 -7
- package/dist/lib/browser/remote-control.d.ts +2 -2
- package/dist/lib/browser/remote-control.js +3 -3
- package/dist/lib/browser/runtime-state.d.ts +79 -0
- package/dist/lib/browser/runtime-state.js +119 -1
- package/dist/lib/browser/service.d.ts +113 -1
- package/dist/lib/browser/service.js +454 -40
- package/dist/lib/browser/sessions-list.d.ts +2 -1
- package/dist/lib/browser/sessions-list.js +4 -13
- package/dist/lib/browser/types.d.ts +18 -0
- package/dist/lib/budget/config.js +1 -1
- package/dist/lib/budget/ledger.js +1 -1
- package/dist/lib/byok-usage.js +1 -1
- package/dist/lib/capabilities.js +3 -15
- package/dist/lib/catchup.d.ts +1 -1
- package/dist/lib/catchup.js +1 -1
- package/dist/lib/cloud/dispatch.js +1 -1
- package/dist/lib/cloud/host.d.ts +3 -3
- package/dist/lib/cloud/host.js +5 -5
- package/dist/lib/cloud/rush.d.ts +9 -21
- package/dist/lib/cloud/rush.js +33 -120
- package/dist/lib/cloud/types.d.ts +2 -2
- package/dist/lib/commands.js +3 -13
- package/dist/lib/computer/actions.d.ts +36 -0
- package/dist/lib/computer/actions.js +162 -0
- package/dist/lib/computer/dispatch.d.ts +2 -2
- package/dist/lib/computer/dispatch.js +2 -2
- package/dist/lib/computer/sessions-list.d.ts +3 -3
- package/dist/lib/computer/sessions-list.js +2 -2
- package/dist/lib/config-keys.d.ts +12 -3
- package/dist/lib/config-keys.js +29 -6
- package/dist/lib/config-machine-keys.d.ts +4 -2
- package/dist/lib/config-machine-keys.js +6 -2
- package/dist/lib/crabbox/lease.js +9 -1
- package/dist/lib/crabbox/runtimes.d.ts +32 -7
- package/dist/lib/crabbox/runtimes.js +72 -30
- package/dist/lib/{daemon.d.ts → daemon/daemon.d.ts} +59 -1
- package/dist/lib/{daemon.js → daemon/daemon.js} +237 -49
- package/dist/lib/daemon-services.d.ts +1 -1
- package/dist/lib/daemon-services.js +5 -0
- package/dist/lib/daemon-ticks.d.ts +25 -0
- package/dist/lib/daemon-ticks.js +31 -1
- package/dist/lib/daemon-webhooks.d.ts +102 -0
- package/dist/lib/daemon-webhooks.js +210 -0
- package/dist/lib/device-config.d.ts +116 -72
- package/dist/lib/device-config.js +366 -132
- package/dist/lib/devices/config-migration.d.ts +29 -34
- package/dist/lib/devices/config-migration.js +209 -130
- package/dist/lib/devices/connect.d.ts +2 -2
- package/dist/lib/devices/connect.js +2 -2
- package/dist/lib/devices/discovery-policy.d.ts +30 -0
- package/dist/lib/devices/discovery-policy.js +101 -0
- package/dist/lib/devices/fleet-inventory.js +8 -3
- package/dist/lib/devices/harness-inventory.d.ts +1 -1
- package/dist/lib/devices/harness-inventory.js +12 -3
- package/dist/lib/devices/known-hosts.js +1 -1
- package/dist/lib/devices/pool.d.ts +61 -0
- package/dist/lib/devices/pool.js +83 -0
- package/dist/lib/devices/registry.d.ts +1 -1
- package/dist/lib/devices/registry.js +1 -1
- package/dist/lib/devices/resolve-profile.d.ts +2 -2
- package/dist/lib/devices/resolve-profile.js +2 -2
- package/dist/lib/devices/resolve-target.d.ts +1 -1
- package/dist/lib/devices/resolve-target.js +4 -4
- package/dist/lib/devices/self-host.d.ts +1 -1
- package/dist/lib/devices/self-host.js +3 -3
- package/dist/lib/devices/ssh-config.d.ts +1 -1
- package/dist/lib/devices/stats-cache.js +1 -1
- package/dist/lib/devices/sync.d.ts +3 -1
- package/dist/lib/devices/sync.js +3 -3
- package/dist/lib/doctor-diff.js +4 -4
- package/dist/lib/drift-sync.js +2 -2
- package/dist/lib/drift.js +2 -2
- package/dist/lib/event-families.d.ts +1 -1
- package/dist/lib/event-stream.d.ts +1 -1
- package/dist/lib/event-stream.js +2 -2
- package/dist/lib/events-ingest.js +2 -2
- package/dist/lib/exec.d.ts +8 -5
- package/dist/lib/exec.js +19 -13
- package/dist/lib/factory/snapshot.d.ts +1 -1
- package/dist/lib/factory/snapshot.js +1 -1
- package/dist/lib/{activity.d.ts → feed/activity.d.ts} +1 -1
- package/dist/lib/{activity.js → feed/activity.js} +7 -7
- package/dist/lib/{events.d.ts → feed/events.d.ts} +2 -2
- package/dist/lib/{events.js → feed/events.js} +5 -5
- package/dist/lib/{feed.js → feed/feed.js} +4 -4
- package/dist/lib/feed-outcome.d.ts +1 -1
- package/dist/lib/feed-policy.d.ts +1 -1
- package/dist/lib/feed-policy.js +1 -1
- package/dist/lib/feed-post.d.ts +1 -1
- package/dist/lib/feed-post.js +1 -1
- package/dist/lib/feed-ranking.d.ts +1 -1
- package/dist/lib/fleet/apply.d.ts +0 -6
- package/dist/lib/fleet/apply.js +18 -45
- package/dist/lib/fleet/auth-sync.d.ts +16 -35
- package/dist/lib/fleet/auth-sync.js +16 -75
- package/dist/lib/fleet/capture.js +8 -6
- package/dist/lib/fleet/manifest.js +23 -11
- package/dist/lib/fleet/types.d.ts +19 -7
- package/dist/lib/fleet-cache.d.ts +1 -1
- package/dist/lib/fleet-cache.js +1 -1
- package/dist/lib/format.d.ts +19 -0
- package/dist/lib/format.js +44 -1
- package/dist/lib/friction-heuristics.d.ts +1 -1
- package/dist/lib/git.d.ts +24 -1
- package/dist/lib/git.js +82 -38
- package/dist/lib/heal.js +3 -3
- package/dist/lib/hooks.d.ts +18 -0
- package/dist/lib/hooks.js +113 -1
- package/dist/lib/hosts/credentials.d.ts +30 -10
- package/dist/lib/hosts/credentials.js +38 -26
- package/dist/lib/hosts/dispatch.d.ts +3 -3
- package/dist/lib/hosts/dispatch.js +3 -3
- package/dist/lib/hosts/option.d.ts +3 -3
- package/dist/lib/hosts/option.js +7 -8
- package/dist/lib/hosts/passthrough.d.ts +17 -17
- package/dist/lib/hosts/passthrough.js +67 -50
- package/dist/lib/hosts/providers/devices.d.ts +1 -1
- package/dist/lib/hosts/providers/devices.js +1 -1
- package/dist/lib/hosts/reconcile.d.ts +1 -1
- package/dist/lib/hosts/reconcile.js +1 -1
- package/dist/lib/hosts/reconnect.d.ts +1 -1
- package/dist/lib/hosts/reconnect.js +2 -2
- package/dist/lib/hosts/registry.d.ts +5 -5
- package/dist/lib/hosts/registry.js +5 -5
- package/dist/lib/hosts/remote-cmd.d.ts +10 -10
- package/dist/lib/hosts/remote-cmd.js +25 -18
- package/dist/lib/hosts/remote-os.d.ts +4 -4
- package/dist/lib/hosts/remote-os.js +4 -4
- package/dist/lib/hosts/remote-session-id.d.ts +2 -2
- package/dist/lib/hosts/remote-session-id.js +2 -2
- package/dist/lib/hosts/routing-flag.d.ts +3 -3
- package/dist/lib/hosts/routing-flag.js +4 -5
- package/dist/lib/hosts/run-target.d.ts +4 -4
- package/dist/lib/hosts/run-target.js +4 -4
- package/dist/lib/hosts/session-index.d.ts +2 -2
- package/dist/lib/hosts/session-index.js +1 -1
- package/dist/lib/hosts/session-marker.d.ts +1 -1
- package/dist/lib/hosts/session-marker.js +1 -1
- package/dist/lib/hosts/types.d.ts +2 -2
- package/dist/lib/hosts/types.js +2 -2
- package/dist/lib/import.js +1 -1
- package/dist/lib/{migrate.d.ts → installations/migrate.d.ts} +49 -1
- package/dist/lib/{migrate.js → installations/migrate.js} +265 -38
- package/dist/lib/installations/resolve.js +1 -1
- package/dist/lib/installations/strategies.js +1 -1
- package/dist/lib/installations/update.js +2 -2
- package/dist/lib/{versions.d.ts → installations/versions.d.ts} +12 -3
- package/dist/lib/{versions.js → installations/versions.js} +51 -31
- package/dist/lib/isolation-boundary-report.js +1 -1
- package/dist/lib/linear-projects.d.ts +28 -0
- package/dist/lib/linear-projects.js +30 -0
- package/dist/lib/loop.js +1 -1
- package/dist/lib/mailbox-gc.js +1 -1
- package/dist/lib/mailbox.js +1 -1
- package/dist/lib/mcp-registry.d.ts +62 -0
- package/dist/lib/mcp-registry.js +177 -0
- package/dist/lib/mcp.d.ts +5 -0
- package/dist/lib/mcp.js +124 -307
- package/dist/lib/menubar/MenubarHelper.app/Contents/CodeResources +0 -0
- package/dist/lib/menubar/MenubarHelper.app/Contents/MacOS/MenubarHelper +0 -0
- package/dist/lib/menubar/install-menubar.d.ts +7 -0
- package/dist/lib/menubar/install-menubar.js +24 -6
- package/dist/lib/menubar/snapshot.d.ts +7 -0
- package/dist/lib/menubar/snapshot.js +8 -6
- package/dist/lib/models.js +1 -1
- package/dist/lib/monitors/config.d.ts +12 -0
- package/dist/lib/monitors/config.js +3 -0
- package/dist/lib/monitors/dispatch.js +26 -5
- package/dist/lib/monitors/engine.js +10 -0
- package/dist/lib/monitors/sources/webhook.d.ts +1 -1
- package/dist/lib/monitors/state.d.ts +42 -0
- package/dist/lib/monitors/state.js +30 -0
- package/dist/lib/notify.d.ts +1 -1
- package/dist/lib/observe-aliases.d.ts +2 -2
- package/dist/lib/observe-aliases.js +2 -11
- package/dist/lib/output/git-output.js +2 -2
- package/dist/lib/overdue.d.ts +1 -1
- package/dist/lib/overdue.js +1 -1
- package/dist/lib/permissions-registry.d.ts +65 -0
- package/dist/lib/permissions-registry.js +599 -0
- package/dist/lib/permissions.d.ts +24 -13
- package/dist/lib/permissions.js +52 -271
- package/dist/lib/picker.d.ts +16 -0
- package/dist/lib/picker.js +33 -3
- package/dist/lib/placement.d.ts +1 -1
- package/dist/lib/placement.js +4 -4
- package/dist/lib/platform/paths.d.ts +8 -0
- package/dist/lib/platform/paths.js +11 -0
- package/dist/lib/platform/posixpath.js +3 -3
- package/dist/lib/{plugin-marketplace.d.ts → plugins/plugin-marketplace.d.ts} +1 -1
- package/dist/lib/{plugin-marketplace.js → plugins/plugin-marketplace.js} +2 -2
- package/dist/lib/{plugins.d.ts → plugins/plugins.d.ts} +1 -1
- package/dist/lib/{plugins.js → plugins/plugins.js} +7 -7
- package/dist/lib/{skills.d.ts → plugins/skills.d.ts} +1 -1
- package/dist/lib/{skills.js → plugins/skills.js} +7 -7
- package/dist/lib/pricing/cost.d.ts +1 -1
- package/dist/lib/pricing/cost.js +1 -1
- package/dist/lib/profiles-presets.d.ts +1 -1
- package/dist/lib/profiles-presets.js +1 -1
- package/dist/lib/profiles.js +2 -2
- package/dist/lib/project-import.js +4 -1
- package/dist/lib/project-launch.d.ts +1 -1
- package/dist/lib/project-launch.js +3 -3
- package/dist/lib/project-pull.d.ts +128 -0
- package/dist/lib/project-pull.js +384 -0
- package/dist/lib/project-root.d.ts +2 -2
- package/dist/lib/project-root.js +2 -2
- package/dist/lib/project-status.js +1 -1
- package/dist/lib/projects.d.ts +23 -0
- package/dist/lib/projects.js +29 -6
- package/dist/lib/refresh.d.ts +12 -1
- package/dist/lib/refresh.js +16 -9
- package/dist/lib/registry.d.ts +9 -2
- package/dist/lib/registry.js +11 -4
- package/dist/lib/resources.d.ts +2 -2
- package/dist/lib/resources.js +2 -2
- package/dist/lib/routers.d.ts +81 -0
- package/dist/lib/routers.js +153 -0
- package/dist/lib/routine-context.d.ts +1 -0
- package/dist/lib/routine-context.js +7 -2
- package/dist/lib/routine-notify-owner.d.ts +1 -1
- package/dist/lib/routine-notify.d.ts +1 -1
- package/dist/lib/routine-notify.js +1 -1
- package/dist/lib/routine-process-cleanup.d.ts +1 -1
- package/dist/lib/routine-readiness.d.ts +1 -1
- package/dist/lib/routine-readiness.js +11 -4
- package/dist/lib/routines-placement.d.ts +12 -2
- package/dist/lib/routines-placement.js +20 -3
- package/dist/lib/routines-project.d.ts +1 -1
- package/dist/lib/routines-project.js +1 -1
- package/dist/lib/rules/compile.d.ts +1 -0
- package/dist/lib/rules/compile.js +10 -2
- package/dist/lib/rules/rules.js +1 -1
- package/dist/lib/run-notify.js +1 -1
- package/dist/lib/runner.d.ts +80 -2
- package/dist/lib/runner.js +241 -62
- package/dist/lib/sandbox.d.ts +1 -1
- package/dist/lib/scheduler.d.ts +1 -1
- package/dist/lib/scheduler.js +1 -1
- package/dist/lib/{routines.d.ts → scheduling/routines.d.ts} +54 -9
- package/dist/lib/{routines.js → scheduling/routines.js} +173 -14
- package/dist/lib/secrets/Agents CLI.app/Contents/CodeResources +0 -0
- package/dist/lib/secrets/Agents CLI.app/Contents/MacOS/Agents CLI +0 -0
- package/dist/lib/secrets/agent.js +2 -2
- package/dist/lib/secrets/audit.js +1 -1
- package/dist/lib/secrets/bundles.d.ts +3 -3
- package/dist/lib/secrets/bundles.js +7 -7
- package/dist/lib/secrets/index.js +1 -1
- package/dist/lib/secrets/list-filter.d.ts +1 -1
- package/dist/lib/secrets/list-filter.js +2 -2
- package/dist/lib/secrets/push.d.ts +1 -1
- package/dist/lib/secrets/push.js +10 -12
- package/dist/lib/secrets/remote.d.ts +12 -10
- package/dist/lib/secrets/remote.js +21 -14
- package/dist/lib/secrets/vault.js +2 -2
- package/dist/lib/self-heal/checks/shadowing.js +1 -1
- package/dist/lib/self-heal/checks/shims.js +1 -1
- package/dist/lib/self-update.d.ts +39 -4
- package/dist/lib/self-update.js +78 -25
- package/dist/lib/serve/control.d.ts +1 -1
- package/dist/lib/serve/control.js +4 -4
- package/dist/lib/serve/data.d.ts +1 -1
- package/dist/lib/serve/data.js +1 -1
- package/dist/lib/serve/stream.js +1 -1
- package/dist/lib/service-manifest.d.ts +29 -0
- package/dist/lib/service-manifest.js +80 -0
- package/dist/lib/session/active.d.ts +59 -7
- package/dist/lib/session/active.js +158 -17
- package/dist/lib/session/bash-command.js +1 -1
- package/dist/lib/session/claude-accounts.d.ts +1 -1
- package/dist/lib/session/claude-accounts.js +1 -1
- package/dist/lib/session/db.d.ts +10 -2
- package/dist/lib/session/db.js +25 -6
- package/dist/lib/session/discover.d.ts +77 -0
- package/dist/lib/session/discover.js +130 -25
- package/dist/lib/session/fork.js +1 -1
- package/dist/lib/session/inject.d.ts +1 -1
- package/dist/lib/session/insights.d.ts +1 -1
- package/dist/lib/session/insights.js +1 -1
- package/dist/lib/session/live-metadata.d.ts +15 -0
- package/dist/lib/session/live-metadata.js +76 -0
- package/dist/lib/session/migrate-targets.d.ts +1 -1
- package/dist/lib/session/recovery.d.ts +1 -1
- package/dist/lib/session/recovery.js +2 -2
- package/dist/lib/session/remote-active.d.ts +10 -1
- package/dist/lib/session/remote-active.js +8 -3
- package/dist/lib/session/remote-bundle.d.ts +1 -1
- package/dist/lib/session/remote-bundle.js +2 -2
- package/dist/lib/session/remote-list.d.ts +15 -4
- package/dist/lib/session/remote-list.js +40 -3
- package/dist/lib/session/remote.d.ts +5 -5
- package/dist/lib/session/remote.js +13 -17
- package/dist/lib/session/resume-command.d.ts +3 -2
- package/dist/lib/session/resume-command.js +4 -3
- package/dist/lib/session/resume-owner.d.ts +3 -3
- package/dist/lib/session/resume-owner.js +3 -3
- package/dist/lib/session/session-cache.d.ts +12 -0
- package/dist/lib/session/session-cache.js +2 -0
- package/dist/lib/session/shell-programs.js +1 -1
- package/dist/lib/session/state.js +5 -5
- package/dist/lib/session/team-filter.d.ts +1 -1
- package/dist/lib/session/team-filter.js +1 -1
- package/dist/lib/session/types.d.ts +3 -3
- package/dist/lib/session/width.d.ts +2 -2
- package/dist/lib/session/width.js +2 -2
- package/dist/lib/share/delete.d.ts +21 -0
- package/dist/lib/share/delete.js +42 -0
- package/dist/lib/share/publish.d.ts +82 -0
- package/dist/lib/share/publish.js +170 -18
- package/dist/lib/share/worker-template.js +172 -9
- package/dist/lib/shims.js +35 -7
- package/dist/lib/smart-launch.d.ts +36 -6
- package/dist/lib/smart-launch.js +65 -8
- package/dist/lib/snapshot.d.ts +4 -4
- package/dist/lib/snapshot.js +2 -2
- package/dist/lib/ssh-exec.d.ts +1 -1
- package/dist/lib/ssh-exec.js +2 -2
- package/dist/lib/ssh-tunnel.d.ts +10 -10
- package/dist/lib/ssh-tunnel.js +12 -12
- package/dist/lib/staleness/checkers/hooks.d.ts +1 -1
- package/dist/lib/staleness/checkers/hooks.js +1 -1
- package/dist/lib/staleness/detectors/permissions.js +0 -23
- package/dist/lib/staleness/detectors/plugins.js +1 -1
- package/dist/lib/staleness/fingerprint.js +1 -1
- package/dist/lib/staleness/layers.d.ts +1 -1
- package/dist/lib/staleness/layers.js +1 -1
- package/dist/lib/staleness/writers/mcp.js +3 -1
- package/dist/lib/staleness/writers/plugins.js +1 -1
- package/dist/lib/staleness/writers/types.d.ts +8 -0
- package/dist/lib/startup/command-registry.d.ts +5 -147
- package/dist/lib/startup/command-registry.js +38 -303
- package/dist/lib/startup/dev-build.d.ts +0 -21
- package/dist/lib/startup/dev-build.js +29 -8
- package/dist/lib/state.d.ts +19 -1
- package/dist/lib/state.js +120 -51
- package/dist/lib/subagents.js +1 -1
- package/dist/lib/sync-status.js +1 -1
- package/dist/lib/sync-umbrella.d.ts +6 -1
- package/dist/lib/sync-umbrella.js +9 -4
- package/dist/lib/teams/agents.js +2 -2
- package/dist/lib/teams/registry.js +1 -1
- package/dist/lib/terminal/backends/vscodium-agent.js +1 -1
- package/dist/lib/terminal/engine.js +1 -1
- package/dist/lib/terminal/index.d.ts +4 -0
- package/dist/lib/terminal/index.js +3 -0
- package/dist/lib/terminal/inject.d.ts +2 -2
- package/dist/lib/terminal/inject.js +2 -2
- package/dist/lib/terminal/transport.js +1 -1
- package/dist/lib/tmux/index.d.ts +1 -1
- package/dist/lib/tmux/index.js +1 -1
- package/dist/lib/tmux/session.d.ts +21 -5
- package/dist/lib/tmux/session.js +76 -22
- package/dist/lib/triggers/handlers.d.ts +1 -1
- package/dist/lib/triggers/handlers.js +2 -2
- package/dist/lib/triggers/webhook.d.ts +36 -2
- package/dist/lib/triggers/webhook.js +97 -42
- package/dist/lib/types.d.ts +18 -6
- package/dist/lib/usage-fleet.d.ts +1 -1
- package/dist/lib/usage-fleet.js +1 -1
- package/dist/lib/usage-refresh.d.ts +1 -1
- package/dist/lib/usage-refresh.js +3 -3
- package/dist/lib/version-duplicates.js +2 -2
- package/dist/lib/view-types.d.ts +56 -0
- package/dist/lib/view-types.js +1 -0
- package/dist/lib/watchdog/rotate.d.ts +2 -2
- package/dist/lib/watchdog/rotate.js +3 -3
- package/dist/lib/watchdog/runner.d.ts +2 -3
- package/dist/lib/watchdog/runner.js +3 -4
- package/dist/lib/workflows.js +1 -1
- package/package.json +2 -1
- package/scripts/postinstall.js +2 -2
- package/dist/commands/bench.d.ts +0 -2
- package/dist/commands/bench.js +0 -101
- package/dist/commands/cp.d.ts +0 -84
- package/dist/commands/cp.js +0 -231
- package/dist/commands/login.d.ts +0 -2
- package/dist/lib/resources/commands.d.ts +0 -46
- package/dist/lib/resources/commands.js +0 -211
- package/dist/lib/resources/hooks.d.ts +0 -16
- package/dist/lib/resources/hooks.js +0 -153
- package/dist/lib/resources/mcp.d.ts +0 -34
- package/dist/lib/resources/mcp.js +0 -250
- package/dist/lib/resources/memory.d.ts +0 -15
- package/dist/lib/resources/memory.js +0 -46
- package/dist/lib/resources/permissions.d.ts +0 -13
- package/dist/lib/resources/permissions.js +0 -202
- package/dist/lib/resources/rules.d.ts +0 -46
- package/dist/lib/resources/rules.js +0 -179
- package/dist/lib/resources/skills.d.ts +0 -37
- package/dist/lib/resources/skills.js +0 -247
- package/dist/lib/resources/subagents.d.ts +0 -46
- package/dist/lib/resources/subagents.js +0 -202
- /package/dist/lib/{feed.d.ts → feed/feed.d.ts} +0 -0
package/dist/commands/exec.js
CHANGED
|
@@ -18,8 +18,6 @@ import { recordDispatchedRun } from '../lib/audit/log.js';
|
|
|
18
18
|
import { maybeShowStarNudge } from '../lib/star-nudge.js';
|
|
19
19
|
import { warnUnpushedWork, shouldWarnUnpushed } from '../lib/warn-unpushed.js';
|
|
20
20
|
import { warnOrphanedOpenPr } from '../lib/pr-land-detach.js';
|
|
21
|
-
import { isHostPinned, pinHostKey, managedKnownHostsPath } from '../lib/devices/known-hosts.js';
|
|
22
|
-
import { sshResolve } from '../lib/hosts/ssh-config.js';
|
|
23
21
|
import * as fs from 'fs';
|
|
24
22
|
import * as path from 'path';
|
|
25
23
|
import * as os from 'os';
|
|
@@ -27,6 +25,7 @@ import { spawnSync } from 'child_process';
|
|
|
27
25
|
import { randomUUID } from 'crypto';
|
|
28
26
|
import { isSessionTrackedAgent } from '../lib/session/types.js';
|
|
29
27
|
import { applyActiveRulesPresetAtRun } from '../lib/rules/run-sync.js';
|
|
28
|
+
import { handleBroadcast } from './run-broadcast.js';
|
|
30
29
|
/** Distinguish a terminal account-picker marker from an explicit @version pin. */
|
|
31
30
|
export function parseRunAccountPickerRequest(agentSpec) {
|
|
32
31
|
const requested = agentSpec.endsWith('@');
|
|
@@ -39,8 +38,8 @@ export function parseRunAccountPickerRequest(agentSpec) {
|
|
|
39
38
|
}
|
|
40
39
|
/** Return every option whose routing semantics conflict with a local account choice. */
|
|
41
40
|
/**
|
|
42
|
-
* The `--
|
|
43
|
-
* machine over SSH". `--
|
|
41
|
+
* The `--device` alias family — the flags that mean "dispatch this run to another
|
|
42
|
+
* machine over SSH". `--device` is canonical; `--on`/`--computer` are hidden
|
|
44
43
|
* aliases. Returns the values actually given (so callers can both test presence
|
|
45
44
|
* and read the target). Kept in ONE place because a guard that listed only a
|
|
46
45
|
* subset silently let `--terminal --device` open a local tab and drop the remote
|
|
@@ -62,7 +61,7 @@ export function runAccountPickerConflicts(options) {
|
|
|
62
61
|
if (options.box)
|
|
63
62
|
conflicts.push('--box');
|
|
64
63
|
if (hostTargetGiven(options).length)
|
|
65
|
-
conflicts.push('--
|
|
64
|
+
conflicts.push('--device');
|
|
66
65
|
return conflicts;
|
|
67
66
|
}
|
|
68
67
|
/** Type guard that narrows a string to a known AgentId. */
|
|
@@ -186,46 +185,6 @@ export function writeAlwaysFreshRepos(repos) {
|
|
|
186
185
|
/* best-effort — losing the preference just means the picker shows next time */
|
|
187
186
|
}
|
|
188
187
|
}
|
|
189
|
-
/**
|
|
190
|
-
* Decide whether `--copy-creds` may ship credentials (and the Claude OAuth
|
|
191
|
-
* token) to `host` (RUSH-1767). Credentials ship only to a host whose SSH host
|
|
192
|
-
* key is pinned in the managed known_hosts store, so the offload never rides an
|
|
193
|
-
* accept-new (TOFU) connection a machine-in-the-middle could intercept.
|
|
194
|
-
*
|
|
195
|
-
* Resolution:
|
|
196
|
-
* - The checked target is the host's concrete `address`, else its ssh-config
|
|
197
|
-
* HostName (`ssh -G`), else its name — so an alias is pinned/verified against
|
|
198
|
-
* the SAME real host the strict dispatch later connects to.
|
|
199
|
-
* - An unpinned NON-device (a bare `~/.ssh/config` `Host` alias or literal,
|
|
200
|
-
* which `agents ssh <name>` can't reach — "Unknown device") is self-pinned in
|
|
201
|
-
* place via ssh-keyscan. A registered device is left unpinned here — it earns
|
|
202
|
-
* its pin through the normal `agents ssh <name>` accept-new connect, so the
|
|
203
|
-
* caller steers there instead.
|
|
204
|
-
*
|
|
205
|
-
* The network seams (`resolve`, `selfPin`) default to the real ssh -G /
|
|
206
|
-
* ssh-keyscan implementations and are injectable so the ship/refuse decision is
|
|
207
|
-
* unit-testable against real known_hosts fixtures with no network.
|
|
208
|
-
*/
|
|
209
|
-
export function decideCopyCredsGate(host, deps = {}) {
|
|
210
|
-
const file = deps.file ?? managedKnownHostsPath();
|
|
211
|
-
const resolve = deps.resolve ?? sshResolve;
|
|
212
|
-
const selfPin = deps.selfPin ?? ((target, port, f) => pinHostKey(target, { file: f, ...(port ? { port } : {}) }).pinned);
|
|
213
|
-
// For an ssh-config host `host.address` is unset; ssh -G gives the real
|
|
214
|
-
// HostName (and Port) the strict dispatch will verify against.
|
|
215
|
-
const cfg = host.address ? undefined : resolve(host.name);
|
|
216
|
-
const pinTarget = host.address ?? cfg?.hostname ?? host.name;
|
|
217
|
-
// Not pinned yet? A registered device earns its pin through the normal
|
|
218
|
-
// accept-new connect (`agents ssh <name>` / the fleet sweep), so leave it and
|
|
219
|
-
// let the caller steer there. A bare ssh-config alias / literal is NOT a
|
|
220
|
-
// registered device — that flow can never pin it — so pin it right here.
|
|
221
|
-
let selfPinned = false;
|
|
222
|
-
if (!isHostPinned(pinTarget, file) && host.provider !== 'devices') {
|
|
223
|
-
const cfgPort = cfg?.port;
|
|
224
|
-
const port = cfgPort && cfgPort !== '22' ? Number(cfgPort) : undefined;
|
|
225
|
-
selfPinned = selfPin(pinTarget, port, file);
|
|
226
|
-
}
|
|
227
|
-
return { allowed: isHostPinned(pinTarget, file), pinTarget, selfPinned };
|
|
228
|
-
}
|
|
229
188
|
/**
|
|
230
189
|
* Build the LoopConfig the driver consumes from CLI flags and/or a workflow's
|
|
231
190
|
* `loop:` frontmatter block (issue #332). Returns undefined when neither source
|
|
@@ -445,8 +404,7 @@ async function resolveRunCwd(options, opts) {
|
|
|
445
404
|
* a session started when none did.
|
|
446
405
|
*/
|
|
447
406
|
async function handleTerminalHandoff(agentSpec, options, prompt) {
|
|
448
|
-
const { parseTerminalFlag, openRunInTerminal, toHostSamples } = await import('../lib/terminal/
|
|
449
|
-
const { currentContext } = await import('../lib/terminal/index.js');
|
|
407
|
+
const { parseTerminalFlag, openRunInTerminal, toHostSamples, currentContext } = await import('../lib/terminal/index.js');
|
|
450
408
|
const parsed = parseTerminalFlag(options.terminal);
|
|
451
409
|
if (parsed.error) {
|
|
452
410
|
console.error(chalk.red(parsed.error));
|
|
@@ -478,10 +436,10 @@ async function handleTerminalHandoff(agentSpec, options, prompt) {
|
|
|
478
436
|
process.exit(1);
|
|
479
437
|
}
|
|
480
438
|
}
|
|
481
|
-
// --
|
|
439
|
+
// --device and its aliases (--on/--computer) all mean "dispatch this
|
|
482
440
|
// run to another machine over SSH", which is incompatible with opening a
|
|
483
441
|
// terminal tab on THIS machine — so reject the whole alias family, not just
|
|
484
|
-
// the canonical flag. The rule and its wording live once, in the --
|
|
442
|
+
// the canonical flag. The rule and its wording live once, in the --device
|
|
485
443
|
// forwarding table, so the classification a reviewer reads and the error a
|
|
486
444
|
// user sees can't drift.
|
|
487
445
|
if (hostTargetGiven(options).length) {
|
|
@@ -490,7 +448,7 @@ async function handleTerminalHandoff(agentSpec, options, prompt) {
|
|
|
490
448
|
process.exit(1);
|
|
491
449
|
}
|
|
492
450
|
// Machine-readable output would land in the tab, where the caller that asked
|
|
493
|
-
// for it can never read it. Same class of failure as --
|
|
451
|
+
// for it can never read it. Same class of failure as --device: refuse, don't
|
|
494
452
|
// hand back a stream that goes nowhere.
|
|
495
453
|
const streamFlag = options.json ? '--json' : options.emitSessionId ? '--emit-session-id' : undefined;
|
|
496
454
|
if (streamFlag) {
|
|
@@ -500,7 +458,7 @@ async function handleTerminalHandoff(agentSpec, options, prompt) {
|
|
|
500
458
|
// `--project` owns the working directory, but the main action resolves it far
|
|
501
459
|
// below this handoff — so without this the tab would open in THIS process's
|
|
502
460
|
// cwd (launchd's `/` for a menu-bar click) while the run inside it moved to
|
|
503
|
-
// the project. `forRemote: false` because --terminal is always local (--
|
|
461
|
+
// the project. `forRemote: false` because --terminal is always local (--device
|
|
504
462
|
// is rejected above).
|
|
505
463
|
const cwd = await resolveRunCwd(options, { forRemote: false });
|
|
506
464
|
const { getActiveSessions } = await import('../lib/session/active.js');
|
|
@@ -532,8 +490,8 @@ async function handleTerminalHandoff(agentSpec, options, prompt) {
|
|
|
532
490
|
/** Register the `agents run <agent> [prompt]` command. */
|
|
533
491
|
export function registerRunCommand(program) {
|
|
534
492
|
const runCmd = program
|
|
535
|
-
.command('run
|
|
536
|
-
.description('Execute an agent. Pass a prompt for headless runs; omit it to launch the agent interactively.')
|
|
493
|
+
.command('run [agent] [prompt]')
|
|
494
|
+
.description('Execute an agent. Pass a prompt for headless runs; omit it to launch the agent interactively. With --broadcast, run the same prompt/task across an agent × model matrix.')
|
|
537
495
|
.option('-m, --mode <mode>', 'How much the agent can do: plan (read-only), edit (can write files), auto (smart classifier auto-approves safe ops, prompts for risky), skip (bypass all permission prompts). Omitted Codex mode defaults to safe writable edit; other harnesses default to plan. \'full\' accepted as alias for skip.', 'plan')
|
|
538
496
|
.option('-e, --effort <effort>', 'Reasoning effort: low | medium | high | xhigh | max | auto (claude and codex only)', 'auto')
|
|
539
497
|
.option('--model <model>', 'Cost tier (cheap|default|best|ultra) or a concrete model id; tiers resolve per harness+version to a supported model')
|
|
@@ -542,22 +500,27 @@ export function registerRunCommand(program) {
|
|
|
542
500
|
.option('--no-auto-secrets', 'Skip auto-injection of secrets declared by a workflow\'s frontmatter `secrets:` field. Has no effect on bare-agent runs.')
|
|
543
501
|
.option('--secrets-keys <keys>', 'Inject only this comma-separated subset of keys from --secrets bundles (e.g. KEY1,KEY2). Missing keys are an error. Applies to all --secrets bundles on this run.')
|
|
544
502
|
.option('--allow-expired', 'Inject secrets even if their expiry date has passed (overrides the pre-run expiry abort).')
|
|
545
|
-
.option('--cwd <dir>', 'Working directory for the agent (defaults to current directory). With --
|
|
546
|
-
.option('-P, --project <ref>', 'Project shorthand <slug>[@worktree], resolved against your projects root (auto-inferred, cached). Sets the cwd locally or on --
|
|
503
|
+
.option('--cwd <dir>', 'Working directory for the agent (defaults to current directory). With --device, the directory ON the device.')
|
|
504
|
+
.option('-P, --project <ref>', 'Project shorthand <slug>[@worktree], resolved against your projects root (auto-inferred, cached). Sets the cwd locally or on --device.')
|
|
547
505
|
.option('--add-dir <dir>', 'Grant access to an additional directory outside the project (Claude, Codex, Cursor, Kimi, Grok; repeatable)', (val, prev) => [...prev, val], [])
|
|
548
506
|
.option('--json', 'Stream events as JSON lines (for parsing by other tools)')
|
|
549
507
|
.option('--quiet', 'Suppress preamble (rotation banner, "Running:" line). Useful when piping JSON events to a parser.', false)
|
|
550
508
|
.option('--headless', 'Force headless mode. Auto-enabled when a prompt is provided; pass explicitly to stay headless with no prompt (reads the prompt from stdin).', false)
|
|
551
509
|
.option('--no-auth-check', 'Skip the pre-launch "looks logged out" warning on an interactive run (advisory; never blocks anyway). Also silenced by AGENTS_NO_AUTH_CHECK=1.')
|
|
552
510
|
.option('-i, --interactive', 'Force interactive mode even when a prompt is provided. Mutually exclusive with --headless.')
|
|
511
|
+
.option('--broadcast', 'Run the same prompt or --task across multiple agents (comma-separated [agent]) × --model cells. Replaces the former `agents bench` group.')
|
|
512
|
+
.option('--task <id>', 'With --broadcast: house benchmark task id under apps/cli/bench/tasks/')
|
|
513
|
+
.option('--list-tasks', 'With --broadcast: list available broadcast task ids')
|
|
514
|
+
.option('--results [run-id]', 'With --broadcast: show one saved matrix run, or list saved runs newest first')
|
|
515
|
+
.option('--concurrency <n>', 'With --broadcast: maximum cells running at once', '3')
|
|
553
516
|
.option('--resume [id]', 'Recover a previous conversation on its origin device. The exact healthy origin uses native resume; otherwise a healthy version of the same harness replays via /continue. Pair with a prompt to continue headlessly.')
|
|
554
517
|
.option('--session-id <id>', 'Force a NEW conversation to use this exact session UUID (Claude only). This CREATES a session — to resume an existing one, use --resume.')
|
|
555
|
-
.option('--name <slug>', 'Name the run — seeds the session label so it shows up as `<name>` in `agents sessions` and resolves by it (and `agents logs <name>` for --
|
|
518
|
+
.option('--name <slug>', 'Name the run — seeds the session label so it shows up as `<name>` in `agents sessions` and resolves by it (and `agents hosts logs <name>` for --device runs) instead of an opaque id. An agent-generated title later refines the label; your name shows until then. Optional.')
|
|
556
519
|
.option('--notify', 'Post a desktop notification when a headless run finishes. Fired by this process on exit, so it survives whatever launched the run (the menu bar dispatching it, a terminal you closed).')
|
|
557
520
|
.option('--terminal [backend]', "Open this run in a real terminal tab instead of here. Without a value the terminal is detected from your live sessions (`agents sessions --active` host), so it lands where you already work — Ghostty for a Ghostty user, iTerm for an iTerm user. Name one to force it: iterm | ghostty | terminal | tmux | vscodium-agent. This is how the menu bar's New Session opens.")
|
|
558
521
|
.option('--verbose', 'Show detailed execution logs')
|
|
559
522
|
.option('--raw', 'Interactive runs on macOS/Linux launch inside a shared tmux session (for %pane addressing + re-attach). Pass --raw to spawn the agent directly instead. Also disabled by AGENTS_NO_TMUX=1.')
|
|
560
|
-
.option('--no-tmux', 'Spawn the agent directly instead of wrapping it in the shared tmux session. Same effect as --raw / AGENTS_NO_TMUX=1. Use this to see the agent\'s full startup output when a launch is failing
|
|
523
|
+
.option('--no-tmux', 'Spawn the agent directly instead of wrapping it in the shared tmux session. Same effect as --raw / AGENTS_NO_TMUX=1. Use this to see the agent\'s full startup output when a launch is failing; to turn the wrap off for every run on this machine, set `agents config set devices.<name>.tmux off`.')
|
|
561
524
|
.option('--disable-tmux', 'Alias for --no-tmux.')
|
|
562
525
|
.option('--timeout <duration>', 'Kill the agent after this duration (e.g., 30m, 1h, 2h30m)')
|
|
563
526
|
.option('--fallback <agents>', 'Comma-separated agents to try on rate-limit failure. Each entry accepts an optional @version pin (e.g., codex@0.116.0,antigravity). The primary runs first; if it exits with a rate-limit error, the next agent picks up via /continue handoff.')
|
|
@@ -572,14 +535,13 @@ export function registerRunCommand(program) {
|
|
|
572
535
|
.option('--budget <tokens>', 'Loop token hard-cap: stop once cumulative tokens reach this (stoppedBy: budget), enforced outside the agent. Loop only.')
|
|
573
536
|
.option('--until <signal>', 'Loop stop condition. `signal` reads <runDir>/loop-signal.json {continue,reason} each iteration; absent or continue:false stops (fail-closed). Loop only.')
|
|
574
537
|
.option('--interval <dur>', 'Loop delay between iterations ("0" back-to-back, "30m" paces). Loop only.')
|
|
575
|
-
.option('--where <spec>', 'Where this run\'s body executes (one placement door): local | device:<name> | auto | lease[:backend] | cloud[:provider]. Expands to --
|
|
576
|
-
.option('--
|
|
577
|
-
.option('--
|
|
578
|
-
.option('--
|
|
579
|
-
.option('--
|
|
580
|
-
.option('--
|
|
581
|
-
.option('--
|
|
582
|
-
.option('--lease [backend]', "Run on a cloud box (via crabbox) and tear it down after — reuses a warm box from the repo's profile pool when one is ready (--fresh forces a new box). Optional backend selects the cloud (hetzner/aws/do). Same as --where lease[:backend]. Unlike --host, no machine is registered.")
|
|
538
|
+
.option('--where <spec>', 'Where this run\'s body executes (one placement door): local | device:<name> | auto | lease[:backend] | cloud[:provider]. Expands to --device/--lease/--cloud. Do not combine with those flags. See docs/00-concepts.md#placement.')
|
|
539
|
+
.option('-D, --device <name>', 'Offload this run onto another machine over SSH — a registered device, or user@host. Pass "auto" to pick the least-loaded reachable device where the requested agent is installed and signed in, keeping the run local when no remote is better. Same as --where device:<name>. See `agents devices`.')
|
|
540
|
+
.option('--remote-cwd <dir>', "Explicit device working directory for --device runs, used VERBATIM (overrides --cwd; usually --cwd suffices — it re-roots a local-home path onto the remote home). Pass a single-quoted '$HOME/…' or a valid remote absolute path; a local ~ expands here and won't exist there (/Users/you vs /home/you).")
|
|
541
|
+
.option('--no-follow', 'With --device, dispatch detached and return immediately (track via `agents hosts ps/logs`).')
|
|
542
|
+
.option('--any', 'With --device <cap> (a capability tag), pick any matching device instead of erroring when several match.')
|
|
543
|
+
.option('--copy-creds', 'Deprecated refusal: native OAuth/session credentials cannot be copied between devices. Use `agents accounts sync <account> --device <device>` for a portable provider credential.')
|
|
544
|
+
.option('--lease [backend]', "Run on a cloud box (via crabbox) and tear it down after — reuses a warm box from the repo's profile pool when one is ready (--fresh forces a new box). Optional backend selects the cloud (hetzner/aws/do). Same as --where lease[:backend]. Unlike --device, no machine is registered.")
|
|
583
545
|
.option('--box <slug>', 'Reuse an existing warm crabbox box for this run instead of provisioning a disposable --lease box.')
|
|
584
546
|
.option('--keep-box', 'With --lease, keep the box after the run instead of stopping it.')
|
|
585
547
|
.option('--fresh', "With --lease, always provision a brand-new box (skip the warm profile-pool reuse) and tear it down after the run.")
|
|
@@ -587,21 +549,21 @@ export function registerRunCommand(program) {
|
|
|
587
549
|
.option('--bare', 'With --lease, skip copying your local ~/.agents setup (skills/hooks/commands/MCP) onto the box.')
|
|
588
550
|
.option('--tailscale', 'Lease the box onto your tailnet (reachable only over Tailscale) rather than a public IP.')
|
|
589
551
|
.option('--no-tailscale', 'Force a public-IP lease even when a reuse context would default to Tailscale.')
|
|
590
|
-
.option('--cloud', 'Vendor cloud placement: dispatch to the agent\'s native cloud (claude→rush, codex→codex, cursor→cursor, droid→factory, antigravity→antigravity) and stream the result. Same dispatch as `agents cloud run --agent <agent>`; tracked by `agents cloud list/status/logs`. Same as --where cloud. Mutually exclusive with --
|
|
552
|
+
.option('--cloud', 'Vendor cloud placement: dispatch to the agent\'s native cloud (claude→rush, codex→codex, cursor→cursor, droid→factory, antigravity→antigravity) and stream the result. Same dispatch as `agents cloud run --agent <agent>`; tracked by `agents cloud list/status/logs`. Same as --where cloud. Mutually exclusive with --device/--lease and local-run flags.')
|
|
591
553
|
.option('--provider <id>', 'With --cloud: override the agent\'s native cloud provider (rush | codex | cursor | factory | antigravity | host).')
|
|
592
554
|
.option('--repo <owner/repo>', 'With --cloud: GitHub repository. Repeatable for multi-repo dispatch (Rush Cloud only).', (val, prev) => [...prev, val], [])
|
|
593
555
|
.option('--branch <name>', 'With --cloud: target git branch.')
|
|
594
556
|
.option('--cloud-env <id>', 'With --cloud: Codex Cloud environment ID (run\'s --env is the KEY=VAL passthrough, so the cloud env id gets its own flag).');
|
|
595
|
-
// `--on` and `--computer` are hidden aliases of `--
|
|
596
|
-
runCmd.addOption(new Option('--on <name>', 'Alias of --
|
|
597
|
-
runCmd.addOption(new Option('--computer <name>', 'Alias of --
|
|
557
|
+
// `--on` and `--computer` are hidden aliases of `--device` — same behavior.
|
|
558
|
+
runCmd.addOption(new Option('--on <name>', 'Alias of --device.').hideHelp());
|
|
559
|
+
runCmd.addOption(new Option('--computer <name>', 'Alias of --device.').hideHelp());
|
|
598
560
|
// Deprecated one-release alias: `agents run … --smart` → treat as `--device auto`.
|
|
599
561
|
runCmd.addOption(new Option('--smart', 'Deprecated: use --device auto (affinity host pick).').hideHelp());
|
|
600
|
-
// Internal: the `--
|
|
562
|
+
// Internal: the `--device` dispatch forwards this so the REMOTE run prints its
|
|
601
563
|
// resolved session id as a one-line stdout sentinel (hosts/session-marker.ts),
|
|
602
564
|
// letting the launcher relate the remote-created session back to itself for
|
|
603
565
|
// every agent — not just Claude, whose id it forces up front.
|
|
604
|
-
runCmd.addOption(new Option('--emit-session-id', 'internal: print the resolved session id for a --
|
|
566
|
+
runCmd.addOption(new Option('--emit-session-id', 'internal: print the resolved session id for a --device launcher to capture').hideHelp());
|
|
605
567
|
// Required for the documented `agents run <agent> [prompt] -- <native flags>`
|
|
606
568
|
// passthrough: commander >=13 rejects excess operands by default, so any
|
|
607
569
|
// post-`--` token died with "too many arguments" before the action ran. The
|
|
@@ -625,11 +587,11 @@ export function registerRunCommand(program) {
|
|
|
625
587
|
# Full-auto: affinity-pick the host, then the harness with the most
|
|
626
588
|
# account headroom, then a balanced account on it
|
|
627
589
|
agents run auto "fix the flaky test" --mode edit
|
|
628
|
-
|
|
590
|
+
agents run auto --device yosemite-s0 "fix the flaky test" # pin the device
|
|
629
591
|
agents run auto --interactive --device auto --strategy balanced --mode auto
|
|
630
592
|
|
|
631
593
|
# Placement (one door — where the body runs). Old flags still work.
|
|
632
|
-
agents run claude "…" --where device:yosemite-s0 # = --
|
|
594
|
+
agents run claude "…" --where device:yosemite-s0 # = --device yosemite-s0
|
|
633
595
|
agents run claude "…" --where auto # = --device auto
|
|
634
596
|
agents run claude "fix CI" --where lease --mode edit
|
|
635
597
|
|
|
@@ -662,6 +624,12 @@ export function registerRunCommand(program) {
|
|
|
662
624
|
agents run claude "fix the failing tests" --lease --fresh
|
|
663
625
|
agents run claude "fix the failing tests" --box warm-one
|
|
664
626
|
|
|
627
|
+
# Broadcast one prompt (or --task) across agents × models
|
|
628
|
+
agents run --broadcast claude,codex "say hello" --model cheap,default
|
|
629
|
+
agents run --broadcast --task hello-repo --model cheap
|
|
630
|
+
agents run --broadcast --list-tasks
|
|
631
|
+
agents run --broadcast --results --json
|
|
632
|
+
|
|
665
633
|
# Pass arbitrary native flags to the underlying CLI via -- separator
|
|
666
634
|
agents run kimi -- --plan --some-kimi-option value
|
|
667
635
|
agents run claude "fix the bug" -- --custom-flag
|
|
@@ -692,7 +660,7 @@ export function registerRunCommand(program) {
|
|
|
692
660
|
excluded account and the earliest window reset — use --strategy pinned to force.
|
|
693
661
|
|
|
694
662
|
'auto' harness (agents run auto): picks the host (14d usage affinity,
|
|
695
|
-
unless --
|
|
663
|
+
unless --device is given), the harness (installed CLIs weighted by
|
|
696
664
|
best-account headroom), and the account (the strategy above). Zero
|
|
697
665
|
healthy accounts on any harness exits nonzero with the earliest reset.
|
|
698
666
|
|
|
@@ -708,10 +676,10 @@ export function registerRunCommand(program) {
|
|
|
708
676
|
cloud list/status/logs/cancel/message. --provider overrides the routing;
|
|
709
677
|
--repo/--branch/--cloud-env refine the task. Agents without a native
|
|
710
678
|
cloud (kimi, grok, cursor, opencode, …) fail loud unless --provider is
|
|
711
|
-
given. --cloud is mutually exclusive with --
|
|
679
|
+
given. --cloud is mutually exclusive with --device/--lease and with
|
|
712
680
|
local-run flags (--loop, --resume, --secrets, --terminal, …).
|
|
713
681
|
|
|
714
|
-
Resume: --resume <id> resolves full IDs locally first, then fleet-wide, and recovers on the source device with its cwd/mode. The exact healthy origin version uses native resume; otherwise a healthy version of the same harness replays via /continue. agents resume <id> infers the harness too.
|
|
682
|
+
Resume: --resume <id> resolves full IDs locally first, then fleet-wide, and recovers on the source device with its cwd/mode. The exact healthy origin version uses native resume; otherwise a healthy version of the same harness replays via /continue. agents sessions resume <id> infers the harness too.
|
|
715
683
|
|
|
716
684
|
Passthrough: everything after -- is forwarded verbatim to the underlying agent CLI.
|
|
717
685
|
agents run kimi -- --plan --some-native-flag value
|
|
@@ -737,6 +705,37 @@ export function registerRunCommand(program) {
|
|
|
737
705
|
// a native flag, not a prompt. Run interactively.
|
|
738
706
|
prompt = undefined;
|
|
739
707
|
}
|
|
708
|
+
// Broadcast matrix (formerly `agents bench`): list/results/run cells, then exit.
|
|
709
|
+
// --broadcast is local-only fan-out — it is mutually exclusive with --host/--device.
|
|
710
|
+
if (options.broadcast || options.listTasks || options.results !== undefined) {
|
|
711
|
+
const whereIsRemote = typeof options.where === 'string' && options.where.trim().toLowerCase() !== 'local';
|
|
712
|
+
if (hostTargetGiven(options).length > 0 || whereIsRemote) {
|
|
713
|
+
console.error(chalk.red('--broadcast is local-only and cannot be combined with --host/--device/--where.'));
|
|
714
|
+
process.exit(1);
|
|
715
|
+
}
|
|
716
|
+
try {
|
|
717
|
+
await handleBroadcast({
|
|
718
|
+
listTasks: options.listTasks === true,
|
|
719
|
+
results: options.results,
|
|
720
|
+
task: options.task,
|
|
721
|
+
model: options.model,
|
|
722
|
+
concurrency: options.concurrency,
|
|
723
|
+
json: options.json,
|
|
724
|
+
agentsCsv: agentSpec,
|
|
725
|
+
prompt,
|
|
726
|
+
requireRun: options.broadcast === true && !options.listTasks && options.results === undefined,
|
|
727
|
+
});
|
|
728
|
+
}
|
|
729
|
+
catch (err) {
|
|
730
|
+
console.error(chalk.red(err.message));
|
|
731
|
+
process.exit(1);
|
|
732
|
+
}
|
|
733
|
+
return;
|
|
734
|
+
}
|
|
735
|
+
if (!agentSpec) {
|
|
736
|
+
console.error(chalk.red("Missing required argument 'agent'. Example: agents run claude \"hello\"."));
|
|
737
|
+
process.exit(1);
|
|
738
|
+
}
|
|
740
739
|
// --cloud: vendor cloud placement. Validate BEFORE the terminal handoff
|
|
741
740
|
// and every local dispatch path — flags like --terminal/--loop/--resume
|
|
742
741
|
// belong to the local runner and must error, never ride along silently.
|
|
@@ -757,7 +756,7 @@ export function registerRunCommand(program) {
|
|
|
757
756
|
await handleTerminalHandoff(agentSpec, options, prompt);
|
|
758
757
|
return;
|
|
759
758
|
}
|
|
760
|
-
// Placement: --where expands into --
|
|
759
|
+
// Placement: --where expands into --device / --lease / --cloud before any
|
|
761
760
|
// dispatch. One door for "where does the body run?" — old flags remain
|
|
762
761
|
// aliases. See lib/placement.ts and docs/concepts.md#placement.
|
|
763
762
|
{
|
|
@@ -808,7 +807,7 @@ export function registerRunCommand(program) {
|
|
|
808
807
|
return;
|
|
809
808
|
}
|
|
810
809
|
// --notify: post a desktop notification when this run finishes. Armed on
|
|
811
|
-
// process exit so it covers EVERY dispatch path below (local, --
|
|
810
|
+
// process exit so it covers EVERY dispatch path below (local, --device,
|
|
812
811
|
// --lease, the error path) instead of one branch. Only for headless runs
|
|
813
812
|
// — an interactive run ends in front of the person who started it.
|
|
814
813
|
if (options.notify && prompt !== undefined) {
|
|
@@ -902,7 +901,7 @@ export function registerRunCommand(program) {
|
|
|
902
901
|
const [requestedAgent, requestedVersion] = normalizedAgentSpec.split('@');
|
|
903
902
|
if (!autoHarnessRequested && requestedAgent !== resolvedResumeSource.agent) {
|
|
904
903
|
console.error(chalk.red(`Session ${resolvedResumeSource.shortId} belongs to ${resolvedResumeSource.agent}, not ${requestedAgent}. ` +
|
|
905
|
-
`Use: agents resume ${resolvedResumeSource.id}`));
|
|
904
|
+
`Use: agents sessions resume ${resolvedResumeSource.id}`));
|
|
906
905
|
process.exit(1);
|
|
907
906
|
}
|
|
908
907
|
if (!autoHarnessRequested && requestedVersion && requestedVersion !== resolvedResumeSource.version) {
|
|
@@ -970,13 +969,13 @@ export function registerRunCommand(program) {
|
|
|
970
969
|
console.error(chalk.red(`agents run auto picks the harness and account itself — the trailing-@ account picker needs a concrete harness (agents run <harness>@).`));
|
|
971
970
|
process.exit(1);
|
|
972
971
|
}
|
|
973
|
-
// Host layer: with no explicit --
|
|
974
|
-
// affinity pick. Skipped on a
|
|
972
|
+
// Host layer: with no explicit --device, default to the
|
|
973
|
+
// affinity pick. Skipped on a device-dispatched run — its dispatcher
|
|
975
974
|
// already resolved this layer (see runAutoDefaultsToAffinity).
|
|
976
975
|
if (!resolvedResumeSource && runAutoDefaultsToAffinity(options))
|
|
977
976
|
options.device = 'auto';
|
|
978
977
|
}
|
|
979
|
-
// --device auto
|
|
978
|
+
// --device auto (and deprecated --smart): live fleet pick.
|
|
980
979
|
// Harness is always the agent the user typed — never auto-picked.
|
|
981
980
|
// Placement failure propagates; an automatic request never becomes local.
|
|
982
981
|
{
|
|
@@ -1134,10 +1133,10 @@ export function registerRunCommand(program) {
|
|
|
1134
1133
|
netMode = 'public';
|
|
1135
1134
|
}
|
|
1136
1135
|
}
|
|
1137
|
-
const {
|
|
1136
|
+
const { assertNoNativeOAuthTransfer, detectSignedInRuntimes, inferLeaseRuntime, profileNeedsBaseRuntimeCredentials } = await import('../lib/crabbox/runtimes.js');
|
|
1138
1137
|
const { leaseAndRun, leaseWorkspaceId } = await import('../lib/crabbox/lease.js');
|
|
1139
1138
|
const { boxAddress } = await import('./lease.js');
|
|
1140
|
-
const { getConfiguredRunStrategy, resolveRunVersion } = await import('../lib/rotate.js');
|
|
1139
|
+
const { getConfiguredRunStrategy, resolveRunVersion } = await import('../lib/accounting/rotate.js');
|
|
1141
1140
|
const { profileExists, readProfile, resolveProfileEnv } = await import('../lib/profiles.js');
|
|
1142
1141
|
const detected = await detectSignedInRuntimes();
|
|
1143
1142
|
const [agentName, rawLeaseVersion] = normalizedAgentSpec.split('@');
|
|
@@ -1185,40 +1184,17 @@ export function registerRunCommand(program) {
|
|
|
1185
1184
|
console.error(chalk.yellow(`Profile '${agentName}' needs ${runtime} credentials, but ${runtime} is not signed in locally. Sign in locally, then retry.`));
|
|
1186
1185
|
process.exit(1);
|
|
1187
1186
|
}
|
|
1188
|
-
//
|
|
1189
|
-
//
|
|
1190
|
-
//
|
|
1191
|
-
|
|
1192
|
-
//
|
|
1193
|
-
// Only claude's token is account-switched: resolveClaudeCredentialsBlob
|
|
1194
|
-
// honors preferEmail across version-homes. The other runtimes'
|
|
1195
|
-
// buildCredentialScript copies the single currently-active credential file
|
|
1196
|
-
// with no switching, so run the balanced picker only for claude — otherwise
|
|
1197
|
-
// the notice below would name a balanced-picked account that is NOT the one
|
|
1198
|
-
// actually shipped. Best-effort: fall back to the default account on error.
|
|
1199
|
-
let leaseEmail = detected.find((d) => d.id === runtime)?.email ?? null;
|
|
1200
|
-
if (credentialRuntimes.includes('claude')) {
|
|
1201
|
-
try {
|
|
1202
|
-
const strategy = getConfiguredRunStrategy(runtime, leaseCwd);
|
|
1203
|
-
const { rotation } = await resolveRunVersion(runtime, strategy, leaseCwd);
|
|
1204
|
-
if (rotation?.picked.email)
|
|
1205
|
-
leaseEmail = rotation.picked.email;
|
|
1206
|
-
}
|
|
1207
|
-
catch {
|
|
1208
|
-
/* strategy resolution is best-effort; keep the default signed-in account */
|
|
1209
|
-
}
|
|
1210
|
-
}
|
|
1187
|
+
// Refuse before account selection, OAuth reads, box lookup, or lease
|
|
1188
|
+
// provisioning. A provider-backed profile has no credentialRuntimes and
|
|
1189
|
+
// proceeds with its portable provider environment instead.
|
|
1190
|
+
assertNoNativeOAuthTransfer(credentialRuntimes, detected);
|
|
1211
1191
|
// Headless-by-contract: don't prompt, but print exactly what ships and
|
|
1212
1192
|
// where — copying an auth token to a cloud box is a credential transfer.
|
|
1213
1193
|
// The box is destroyed after the run, so the credential's lifetime is
|
|
1214
1194
|
// bounded by the run.
|
|
1215
|
-
const whatShips =
|
|
1216
|
-
?
|
|
1217
|
-
:
|
|
1218
|
-
? `${dispatchProfile ? `profile '${dispatchProfile.name}', ` : ''}${runtime} credentials`
|
|
1219
|
-
: dispatchProfile
|
|
1220
|
-
? `profile '${dispatchProfile.name}'`
|
|
1221
|
-
: `${runtime} credentials`;
|
|
1195
|
+
const whatShips = dispatchProfile
|
|
1196
|
+
? `profile '${dispatchProfile.name}'`
|
|
1197
|
+
: `${runtime} runtime setup`;
|
|
1222
1198
|
const boxLifecycle = reuseSlug
|
|
1223
1199
|
? `Reusing crabbox box ${reuseSlug}`
|
|
1224
1200
|
: options.fresh
|
|
@@ -1231,18 +1207,8 @@ export function registerRunCommand(program) {
|
|
|
1231
1207
|
: options.fresh
|
|
1232
1208
|
? 'the box is destroyed after the run'
|
|
1233
1209
|
: 'the shared-pool box is kept after the run';
|
|
1234
|
-
console.error(chalk.gray(`${boxLifecycle} · shipping ${whatShips}
|
|
1235
|
-
|
|
1236
|
-
// written to ~/.claude/.credentials.json on the box — otherwise Claude boots
|
|
1237
|
-
// "Not logged in". `preferEmail` targets the strategy-picked account so the
|
|
1238
|
-
// token matches the account this run resolved to.
|
|
1239
|
-
let claudeCredentialsJson = null;
|
|
1240
|
-
if (credentialRuntimes.includes('claude')) {
|
|
1241
|
-
claudeCredentialsJson = await resolveClaudeCredentialsBlob({ preferEmail: leaseEmail });
|
|
1242
|
-
if (!claudeCredentialsJson) {
|
|
1243
|
-
console.error(chalk.yellow('Warning: could not read the local Claude OAuth token — the box may come up "Not logged in".'));
|
|
1244
|
-
}
|
|
1245
|
-
}
|
|
1210
|
+
console.error(chalk.gray(`${boxLifecycle} · shipping ${whatShips}; ${boxAfterRun}.`));
|
|
1211
|
+
const claudeCredentialsJson = null;
|
|
1246
1212
|
// Progress UI (F2, RUSH-1921). A self-throttled spinner (NOT ora — see
|
|
1247
1213
|
// progress.ts) covers provisioning; the box-side bootstrap then streams a
|
|
1248
1214
|
// structured step stream (sync → install → runtime → creds → …) via the
|
|
@@ -1368,7 +1334,7 @@ export function registerRunCommand(program) {
|
|
|
1368
1334
|
process.exit(1);
|
|
1369
1335
|
}
|
|
1370
1336
|
}
|
|
1371
|
-
// --
|
|
1337
|
+
// --device/--on/--computer: offload this run onto a registered agent host
|
|
1372
1338
|
// over SSH instead of running locally. The three flags are aliases.
|
|
1373
1339
|
const hostGiven = hostTargetGiven(options);
|
|
1374
1340
|
// --project <slug>[@worktree]: resolve the projects-root shorthand into a
|
|
@@ -1380,7 +1346,7 @@ export function registerRunCommand(program) {
|
|
|
1380
1346
|
}
|
|
1381
1347
|
if (hostGiven.length > 0) {
|
|
1382
1348
|
if (new Set(hostGiven).size > 1) {
|
|
1383
|
-
console.error(chalk.red('Conflicting --
|
|
1349
|
+
console.error(chalk.red('Conflicting --device values values — pass just one.'));
|
|
1384
1350
|
process.exit(1);
|
|
1385
1351
|
}
|
|
1386
1352
|
const hostName = hostGiven[0];
|
|
@@ -1392,7 +1358,7 @@ export function registerRunCommand(program) {
|
|
|
1392
1358
|
const { runInteractiveOnHost } = await import('../lib/hosts/dispatch.js');
|
|
1393
1359
|
const { registerInteractiveHostSession } = await import('../lib/hosts/session-index.js');
|
|
1394
1360
|
const { RUN_OPTION_REJECT_MESSAGES } = await import('../lib/hosts/remote-cmd.js');
|
|
1395
|
-
const { normalizeRunStrategy, RUN_STRATEGIES } = await import('../lib/rotate.js');
|
|
1361
|
+
const { normalizeRunStrategy, RUN_STRATEGIES } = await import('../lib/accounting/rotate.js');
|
|
1396
1362
|
// The forwarding contract (RUN_OPTION_FORWARDING): options that cannot
|
|
1397
1363
|
// cross the SSH boundary fail loud BEFORE dispatch — never a silent
|
|
1398
1364
|
// drop. Value-aware: only reject what was actually passed.
|
|
@@ -1412,6 +1378,12 @@ export function registerRunCommand(program) {
|
|
|
1412
1378
|
console.error(chalk.red(msg));
|
|
1413
1379
|
process.exit(1);
|
|
1414
1380
|
}
|
|
1381
|
+
if (options.copyCreds) {
|
|
1382
|
+
console.error(chalk.red('Refusing --copy-creds: native OAuth/session credentials are device-local and cannot be copied. ' +
|
|
1383
|
+
'Create a portable provider account and run `agents accounts sync <account> --device <device>` instead.'));
|
|
1384
|
+
process.exit(1);
|
|
1385
|
+
return;
|
|
1386
|
+
}
|
|
1415
1387
|
// Shared resolution (name → capability tag → error). A password-auth
|
|
1416
1388
|
// device throws DeviceOffloadUnsupportedError inside the helper and
|
|
1417
1389
|
// propagates untouched — it's printed cleanly by the top-level catch in
|
|
@@ -1468,78 +1440,7 @@ export function registerRunCommand(program) {
|
|
|
1468
1440
|
// which can't run over a detached remote dispatch — only forward a
|
|
1469
1441
|
// concrete id.
|
|
1470
1442
|
const resumeId = typeof options.resume === 'string' ? options.resume : undefined;
|
|
1471
|
-
// --copy-creds: provision runtime credentials (and the Claude OAuth token)
|
|
1472
|
-
// on the remote host before the run, then shred them after. Unlike
|
|
1473
|
-
// --lease, a host is persistent, so this is strictly opt-in per run.
|
|
1474
1443
|
let hostCopyCreds;
|
|
1475
|
-
if (options.copyCreds) {
|
|
1476
|
-
// Refuse to copy credentials (and the Claude OAuth token) to a host
|
|
1477
|
-
// whose SSH host key isn't pinned in the managed known_hosts store:
|
|
1478
|
-
// the offload transport would otherwise ride an accept-new (TOFU)
|
|
1479
|
-
// connection, so a machine-in-the-middle on an unverified first
|
|
1480
|
-
// connect could capture the tokens. The dispatch itself then verifies
|
|
1481
|
-
// strictly against that pin (see lib/hosts/dispatch.ts) (RUSH-1767).
|
|
1482
|
-
// The ship/refuse decision — pinTarget resolution, the non-device
|
|
1483
|
-
// self-pin, and the final pinned check — lives in decideCopyCredsGate
|
|
1484
|
-
// (unit-tested against real known_hosts fixtures).
|
|
1485
|
-
const gate = decideCopyCredsGate(host);
|
|
1486
|
-
if (gate.selfPinned) {
|
|
1487
|
-
console.error(chalk.gray(`Pinned "${host.name}" (${gate.pinTarget}) into the managed known_hosts store.`));
|
|
1488
|
-
}
|
|
1489
|
-
if (!gate.allowed) {
|
|
1490
|
-
console.error(chalk.red(`Refusing --copy-creds to "${host.name}": its SSH host key isn't pinned, so the ` +
|
|
1491
|
-
`credentials could be exposed to a machine-in-the-middle on an unverified first connect.`));
|
|
1492
|
-
console.error(chalk.gray(host.provider === 'devices'
|
|
1493
|
-
? `Pin the key first by connecting once so agents records and verifies it: agents ssh ${host.name}\n` +
|
|
1494
|
-
`Then re-run with --copy-creds.`
|
|
1495
|
-
: `Couldn't reach "${gate.pinTarget}" to pin its key (ssh-keyscan returned nothing). ` +
|
|
1496
|
-
`Make sure the host is reachable over SSH, then re-run with --copy-creds.`));
|
|
1497
|
-
process.exit(1);
|
|
1498
|
-
}
|
|
1499
|
-
// --copy-creds ships live credentials to a host, so it deliberately
|
|
1500
|
-
// requires an interactive terminal to pick which signed-in runtimes to
|
|
1501
|
-
// send and to confirm the transfer. Fail clean in a non-TTY/headless
|
|
1502
|
-
// shell instead of hanging on the picker/confirm below — auto-shipping
|
|
1503
|
-
// every signed-in token without consent would defeat the gate's purpose.
|
|
1504
|
-
if (!isInteractiveTerminal()) {
|
|
1505
|
-
// No non-interactive form: --copy-creds must pick which signed-in
|
|
1506
|
-
// runtimes to ship and confirm the transfer, so pass no alternatives
|
|
1507
|
-
// (an empty list prints just the "needs an interactive terminal" line).
|
|
1508
|
-
requireInteractiveSelection('--copy-creds (it selects which credentials to ship and confirms the transfer)', []);
|
|
1509
|
-
}
|
|
1510
|
-
const { detectSignedInRuntimes, pickRuntimes, resolveClaudeCredentialsBlob } = await import('../lib/crabbox/runtimes.js');
|
|
1511
|
-
const { confirm } = await import('@inquirer/prompts');
|
|
1512
|
-
const detected = await detectSignedInRuntimes();
|
|
1513
|
-
const runtimes = await pickRuntimes(detected);
|
|
1514
|
-
if (runtimes.length === 0) {
|
|
1515
|
-
console.error(chalk.yellow('No runtimes selected. Sign into one locally then retry, or omit --copy-creds.'));
|
|
1516
|
-
process.exit(1);
|
|
1517
|
-
}
|
|
1518
|
-
const names = runtimes
|
|
1519
|
-
.map((id) => {
|
|
1520
|
-
const d = detected.find((x) => x.id === id);
|
|
1521
|
-
return `${d?.label ?? id}${d?.email ? ` (${d.email})` : ''}`;
|
|
1522
|
-
})
|
|
1523
|
-
.join(', ');
|
|
1524
|
-
const whatShips = runtimes.includes('claude') ? 'credentials + Claude OAuth token' : 'credentials';
|
|
1525
|
-
const ok = await confirm({
|
|
1526
|
-
message: `Copy ${whatShips} for ${names} to host "${host.name}", run there, then shred them after?`,
|
|
1527
|
-
default: false,
|
|
1528
|
-
});
|
|
1529
|
-
if (!ok) {
|
|
1530
|
-
console.error(chalk.yellow('Aborted — no credentials pushed, no run dispatched.'));
|
|
1531
|
-
process.exit(1);
|
|
1532
|
-
}
|
|
1533
|
-
let claudeCredentialsJson = null;
|
|
1534
|
-
if (runtimes.includes('claude')) {
|
|
1535
|
-
const claudeEmail = detected.find((d) => d.id === 'claude')?.email ?? null;
|
|
1536
|
-
claudeCredentialsJson = await resolveClaudeCredentialsBlob({ preferEmail: claudeEmail });
|
|
1537
|
-
if (!claudeCredentialsJson) {
|
|
1538
|
-
console.error(chalk.yellow('Warning: could not read the local Claude OAuth token — the host may boot Claude "Not logged in".'));
|
|
1539
|
-
}
|
|
1540
|
-
}
|
|
1541
|
-
hostCopyCreds = { runtimes, detected, claudeCredentialsJson };
|
|
1542
|
-
}
|
|
1543
1444
|
// Decide whether this host run is interactive. No prompt always means
|
|
1544
1445
|
// interactive (matching local resolveInteractive); --interactive forces
|
|
1545
1446
|
// interactive even when a prompt is provided; --headless forces headless
|
|
@@ -1673,7 +1574,7 @@ export function registerRunCommand(program) {
|
|
|
1673
1574
|
// Headless host run: launch detached, tail the remote log, and follow
|
|
1674
1575
|
// until the remote process exits.
|
|
1675
1576
|
if (prompt === undefined) {
|
|
1676
|
-
console.error(chalk.red('A prompt is required for headless host runs: agents run <agent> "<task>" --
|
|
1577
|
+
console.error(chalk.red('A prompt is required for headless host runs: agents run <agent> "<task>" --device <name>'));
|
|
1677
1578
|
process.exit(1);
|
|
1678
1579
|
}
|
|
1679
1580
|
// Session-id mint, detached dispatch, and local session-index
|
|
@@ -1839,9 +1740,9 @@ export function registerRunCommand(program) {
|
|
|
1839
1740
|
import('../lib/profiles.js'),
|
|
1840
1741
|
import('../lib/secrets/bundles.js'),
|
|
1841
1742
|
import('../lib/secrets/remote.js'),
|
|
1842
|
-
import('../lib/rotate.js'),
|
|
1843
|
-
import('../lib/versions.js'),
|
|
1844
|
-
import('../lib/plugins.js'),
|
|
1743
|
+
import('../lib/accounting/rotate.js'),
|
|
1744
|
+
import('../lib/installations/versions.js'),
|
|
1745
|
+
import('../lib/plugins/plugins.js'),
|
|
1845
1746
|
import('../lib/workflows.js'),
|
|
1846
1747
|
import('../lib/run-defaults.js'),
|
|
1847
1748
|
import('../lib/mcp.js'),
|
|
@@ -2189,20 +2090,64 @@ export function registerRunCommand(program) {
|
|
|
2189
2090
|
}
|
|
2190
2091
|
}
|
|
2191
2092
|
version = resolveVersionAlias(agent, version);
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2093
|
+
// Account selection follows the binding order: explicit --account → exact
|
|
2094
|
+
// `agent@version` binding → device-scoped `agent` binding → per-harness
|
|
2095
|
+
// default. The exact-installation binding needs the concrete launch
|
|
2096
|
+
// version, so resolve it (default when the run named no version).
|
|
2097
|
+
const { resolveSpawnAccount } = await import('../lib/account-registry.js');
|
|
2098
|
+
// Binding key: a custom harness keys on its own profile name; a native /
|
|
2099
|
+
// global run keys on the exact `agent@version` (default when unpinned).
|
|
2100
|
+
const bindingTarget = fromProfile ? rawAgent : (version ? `${agent}@${version}` : `${agent}@${getGlobalDefault(agent) ?? ''}`);
|
|
2101
|
+
let spawnAccount = null;
|
|
2102
|
+
try {
|
|
2103
|
+
spawnAccount = resolveSpawnAccount(options.account, agent, version, readMeta(), { useDefault: !fromProfile, provider: profileProvider, target: bindingTarget });
|
|
2104
|
+
}
|
|
2105
|
+
catch (err) {
|
|
2106
|
+
console.error(chalk.red(err.message));
|
|
2107
|
+
process.exit(1);
|
|
2108
|
+
}
|
|
2109
|
+
// Downstream rotation gating asks only "was an account selected?".
|
|
2110
|
+
const configuredAccount = spawnAccount?.name;
|
|
2111
|
+
if (spawnAccount) {
|
|
2195
2112
|
if (options.cloud || options.provider || options.lease) {
|
|
2196
2113
|
console.error(chalk.red('--account selects a device-local credential and cannot be combined with cloud or lease placement.'));
|
|
2197
2114
|
process.exit(1);
|
|
2198
2115
|
}
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2116
|
+
if (spawnAccount.kind === 'native') {
|
|
2117
|
+
// A native login is owned by the harness and read from its own home —
|
|
2118
|
+
// it cannot be forwarded to another machine, and no secret/env is
|
|
2119
|
+
// injected. Fail closed for a remote target.
|
|
2120
|
+
const remoteTarget = options.host || options.device;
|
|
2121
|
+
if (remoteTarget) {
|
|
2122
|
+
console.error(chalk.red(`Account '${spawnAccount.name}' is a device-local ${spawnAccount.agent} login and cannot be forwarded to '${remoteTarget}'. Sign in on that device and name the login there.`));
|
|
2123
|
+
process.exit(1);
|
|
2124
|
+
}
|
|
2125
|
+
if (version) {
|
|
2126
|
+
// A pinned version must actually be signed in as the named identity.
|
|
2127
|
+
const { getAccountInfo } = await import('../lib/agents.js');
|
|
2128
|
+
const info = await getAccountInfo(agent, getVersionHomePath(agent, version));
|
|
2129
|
+
const liveKey = info.accountKey ?? info.email?.toLowerCase() ?? null;
|
|
2130
|
+
if (!info.signedIn || liveKey !== spawnAccount.identityKey) {
|
|
2131
|
+
console.error(chalk.red(`Account '${spawnAccount.name}' names a specific ${spawnAccount.agent} identity, but ${agent}@${version} ${info.signedIn ? 'is signed in as a different identity' : 'is not signed in'}. Sign in as that identity, or re-name the account.`));
|
|
2132
|
+
process.exit(1);
|
|
2133
|
+
}
|
|
2134
|
+
}
|
|
2135
|
+
else {
|
|
2136
|
+
// No version pinned: locate the installed version currently signed in
|
|
2137
|
+
// as this identity and pin the run to it — do NOT assume the global
|
|
2138
|
+
// default holds it (the account may live on another installed copy).
|
|
2139
|
+
const { resolveAccountVersion } = await import('../lib/accounting/rotate.js');
|
|
2140
|
+
const matched = await resolveAccountVersion(agent, spawnAccount.identityKey);
|
|
2141
|
+
if (!matched) {
|
|
2142
|
+
console.error(chalk.red(`No installed ${spawnAccount.agent} version is signed in as the identity named by account '${spawnAccount.name}'. Sign in as that identity, or attach a different account.`));
|
|
2143
|
+
process.exit(1);
|
|
2144
|
+
}
|
|
2145
|
+
version = matched;
|
|
2146
|
+
}
|
|
2147
|
+
// Native identity confirmed live; the harness reads it from its home.
|
|
2202
2148
|
}
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
process.exit(1);
|
|
2149
|
+
else {
|
|
2150
|
+
accountEnv = spawnAccount.env;
|
|
2206
2151
|
}
|
|
2207
2152
|
}
|
|
2208
2153
|
// --resume: resolve a prior conversation and rewrite the run target to
|
|
@@ -2470,7 +2415,7 @@ export function registerRunCommand(program) {
|
|
|
2470
2415
|
// An isolated copy is never repaired by adopting another version, so
|
|
2471
2416
|
// `add <agent>@latest` would build an unrelated NORMAL install rather
|
|
2472
2417
|
// than fix what the user asked to run. Point at the isolated re-add.
|
|
2473
|
-
const { isVersionIsolated } = await import('../lib/versions.js');
|
|
2418
|
+
const { isVersionIsolated } = await import('../lib/installations/versions.js');
|
|
2474
2419
|
const hint = isVersionIsolated(agent, launchTarget)
|
|
2475
2420
|
? `agents add ${agent}@${launchTarget} --isolated`
|
|
2476
2421
|
: `agents add ${agent}@latest`;
|
|
@@ -2537,7 +2482,7 @@ export function registerRunCommand(program) {
|
|
|
2537
2482
|
// home would false-negative. It can still false-negative for opaque
|
|
2538
2483
|
// credentials, so this NEVER blocks — it warns and launches anyway. Skipped
|
|
2539
2484
|
// for --json/--quiet, when a rotation already picked a signed-in account,
|
|
2540
|
-
// and via --no-auth-check / AGENTS_NO_AUTH_CHECK=1. (--
|
|
2485
|
+
// and via --no-auth-check / AGENTS_NO_AUTH_CHECK=1. (--device/--lease return
|
|
2541
2486
|
// earlier.)
|
|
2542
2487
|
{
|
|
2543
2488
|
const { shouldCheckLoginBeforeLaunch, loginHint } = await import('../lib/signin-badge.js');
|
|
@@ -2745,7 +2690,7 @@ export function registerRunCommand(program) {
|
|
|
2745
2690
|
toolsRestrict: workflowToolsRestrict,
|
|
2746
2691
|
mcpConfigPath: workflowMcpConfigPath,
|
|
2747
2692
|
passthroughArgs,
|
|
2748
|
-
// Set only on the REMOTE side of a `--
|
|
2693
|
+
// Set only on the REMOTE side of a `--device` dispatch (the launcher
|
|
2749
2694
|
// forwards `--emit-session-id`): print the resolved session id as a
|
|
2750
2695
|
// stdout sentinel so the launcher captures the id this run coined.
|
|
2751
2696
|
emitSessionId: options.emitSessionId === true,
|