@phnx-labs/agents-cli 1.22.35 → 1.22.38
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 +708 -2
- package/README.md +83 -43
- package/dist/bin/agents +0 -0
- package/dist/bootstrap.d.ts +15 -0
- package/dist/bootstrap.js +1212 -0
- package/dist/commands/accounts.d.ts +4 -11
- package/dist/commands/accounts.js +159 -27
- package/dist/commands/artifacts-setup.d.ts +53 -0
- package/dist/commands/artifacts-setup.js +161 -0
- package/dist/commands/artifacts.d.ts +18 -0
- package/dist/commands/artifacts.js +58 -0
- package/dist/commands/attach.js +17 -4
- package/dist/commands/audit.d.ts +8 -8
- package/dist/commands/audit.js +57 -47
- package/dist/commands/browser.js +17 -11
- package/dist/commands/cloud.js +1 -1
- package/dist/commands/commands.js +0 -11
- package/dist/commands/computer-actions.js +33 -0
- package/dist/commands/computer-sessions-picker.d.ts +21 -0
- package/dist/commands/computer-sessions-picker.js +141 -0
- package/dist/commands/computer.d.ts +16 -0
- package/dist/commands/computer.js +37 -0
- package/dist/commands/config.d.ts +17 -0
- package/dist/commands/config.js +401 -0
- package/dist/commands/cp.d.ts +84 -0
- package/dist/commands/cp.js +231 -0
- package/dist/commands/daemon.js +243 -14
- package/dist/commands/detach-core.d.ts +1 -1
- package/dist/commands/detach-core.js +12 -2
- package/dist/commands/detach.js +1 -1
- package/dist/commands/doctor.js +64 -5
- package/dist/commands/events.d.ts +24 -12
- package/dist/commands/events.js +144 -28
- package/dist/commands/exec.js +86 -42
- package/dist/commands/feed.d.ts +1 -1
- package/dist/commands/feed.js +16 -8
- package/dist/commands/focus.d.ts +33 -1
- package/dist/commands/focus.js +177 -17
- package/dist/commands/funnel.d.ts +1 -1
- package/dist/commands/go.js +9 -8
- package/dist/commands/harness-wizard.d.ts +1 -0
- package/dist/commands/harness-wizard.js +18 -20
- package/dist/commands/harness.d.ts +2 -0
- package/dist/commands/harness.js +59 -26
- package/dist/commands/hooks.js +0 -11
- package/dist/commands/inspect.d.ts +94 -1
- package/dist/commands/inspect.js +866 -40
- package/dist/commands/lease.d.ts +4 -4
- package/dist/commands/lease.js +6 -6
- package/dist/commands/login.js +0 -13
- package/dist/commands/logs.d.ts +2 -2
- package/dist/commands/logs.js +2 -2
- package/dist/commands/mcp.js +7 -2
- package/dist/commands/memory.js +7 -2
- package/dist/commands/message.d.ts +1 -1
- package/dist/commands/message.js +37 -6
- package/dist/commands/models.js +71 -0
- package/dist/commands/monitors.d.ts +0 -1
- package/dist/commands/monitors.js +199 -16
- package/dist/commands/open.d.ts +18 -0
- package/dist/commands/open.js +57 -0
- package/dist/commands/packages.js +2 -107
- package/dist/commands/plugins.js +6 -3
- package/dist/commands/profiles.d.ts +1 -0
- package/dist/commands/profiles.js +16 -160
- package/dist/commands/projects.d.ts +28 -0
- package/dist/commands/projects.js +125 -5
- package/dist/commands/reconnect.js +9 -3
- package/dist/commands/repo.js +42 -1
- package/dist/commands/resource-view.d.ts +17 -0
- package/dist/commands/resource-view.js +36 -14
- package/dist/commands/resume.js +9 -2
- package/dist/commands/routines.d.ts +0 -2
- package/dist/commands/routines.js +473 -289
- package/dist/commands/secrets.js +52 -9
- package/dist/commands/sessions-export.d.ts +47 -0
- package/dist/commands/sessions-export.js +116 -6
- package/dist/commands/sessions-import.d.ts +17 -0
- package/dist/commands/sessions-import.js +114 -6
- package/dist/commands/sessions-migrate.d.ts +27 -0
- package/dist/commands/sessions-migrate.js +50 -9
- package/dist/commands/sessions-picker.js +23 -4
- package/dist/commands/sessions-resume.d.ts +5 -3
- package/dist/commands/sessions-resume.js +30 -8
- package/dist/commands/sessions-watch.d.ts +2 -0
- package/dist/commands/sessions-watch.js +53 -0
- package/dist/commands/sessions.d.ts +66 -3
- package/dist/commands/sessions.js +298 -47
- package/dist/commands/setup-browser.js +2 -2
- package/dist/commands/setup-mine.js +0 -1
- package/dist/commands/setup-preferences.js +1 -1
- package/dist/commands/setup.d.ts +5 -0
- package/dist/commands/setup.js +31 -5
- package/dist/commands/share.d.ts +87 -7
- package/dist/commands/share.js +320 -58
- package/dist/commands/skills.js +0 -11
- package/dist/commands/snapshot.js +1 -1
- package/dist/commands/ssh.d.ts +18 -0
- package/dist/commands/ssh.js +549 -210
- package/dist/commands/sync.d.ts +5 -0
- package/dist/commands/sync.js +212 -32
- package/dist/commands/teams.d.ts +13 -0
- package/dist/commands/teams.js +187 -30
- package/dist/commands/tickets.d.ts +2 -0
- package/dist/commands/tickets.js +43 -0
- package/dist/commands/update.js +2 -4
- package/dist/commands/usage.js +5 -3
- package/dist/commands/versions.js +11 -5
- package/dist/commands/view.js +15 -7
- package/dist/commands/webhook.d.ts +2 -2
- package/dist/commands/webhook.js +7 -7
- package/dist/index.d.ts +18 -2
- package/dist/index.js +53 -1264
- package/dist/lib/account-catalog.d.ts +18 -0
- package/dist/lib/account-catalog.js +38 -0
- package/dist/lib/account-provider-registry.d.ts +18 -0
- package/dist/lib/account-provider-registry.js +59 -0
- package/dist/lib/account-registry.d.ts +40 -0
- package/dist/lib/account-registry.js +239 -0
- package/dist/lib/account-schema.d.ts +44 -0
- package/dist/lib/account-schema.js +91 -0
- package/dist/lib/account-state-service.d.ts +21 -0
- package/dist/lib/account-state-service.js +60 -0
- package/dist/lib/activity.d.ts +1 -1
- package/dist/lib/activity.js +8 -1
- package/dist/lib/add-dir.d.ts +80 -0
- package/dist/lib/add-dir.js +241 -0
- package/dist/lib/agent-cli-commands.d.ts +11 -0
- package/dist/lib/agent-cli-commands.js +30 -0
- package/dist/lib/agents.js +44 -36
- package/dist/lib/answer-router.js +1 -1
- package/dist/lib/audit/log.d.ts +10 -27
- package/dist/lib/audit/log.js +20 -33
- package/dist/lib/auth-health.d.ts +9 -2
- package/dist/lib/auth-health.js +42 -9
- package/dist/lib/auto-pull-worker.js +7 -3
- package/dist/lib/auto-pull.d.ts +44 -3
- package/dist/lib/auto-pull.js +88 -5
- package/dist/lib/binary-shadow.d.ts +28 -0
- package/dist/lib/binary-shadow.js +121 -0
- package/dist/lib/brand.js +9 -3
- package/dist/lib/browser/hygiene.d.ts +90 -0
- package/dist/lib/browser/hygiene.js +146 -0
- package/dist/lib/browser/ipc.d.ts +30 -0
- package/dist/lib/browser/ipc.js +93 -4
- package/dist/lib/browser/profiles.d.ts +3 -2
- package/dist/lib/browser/profiles.js +65 -22
- package/dist/lib/browser/service.d.ts +79 -1
- package/dist/lib/browser/service.js +231 -14
- package/dist/lib/browser/sessions-list.d.ts +22 -2
- package/dist/lib/browser/sessions-list.js +54 -6
- package/dist/lib/browser/stream.d.ts +2 -0
- package/dist/lib/browser/stream.js +1 -0
- package/dist/lib/browser/types.d.ts +63 -1
- package/dist/lib/byok-usage.d.ts +9 -2
- package/dist/lib/byok-usage.js +133 -31
- package/dist/lib/cli-entry.d.ts +11 -0
- package/dist/lib/cli-entry.js +46 -0
- package/dist/lib/cloud/host.d.ts +1 -1
- package/dist/lib/cloud/host.js +2 -2
- package/dist/lib/cloud/registry.js +1 -1
- package/dist/lib/cloud/types.d.ts +1 -1
- package/dist/lib/commands.d.ts +2 -0
- package/dist/lib/commands.js +2 -0
- package/dist/lib/computer/sessions-list.d.ts +150 -0
- package/dist/lib/computer/sessions-list.js +363 -0
- package/dist/lib/config-keys.d.ts +86 -0
- package/dist/lib/config-keys.js +193 -0
- package/dist/lib/config-machine-keys.d.ts +12 -0
- package/dist/lib/config-machine-keys.js +17 -0
- package/dist/lib/config-transfer.js +3 -4
- package/dist/lib/crabbox/cli.d.ts +2 -2
- package/dist/lib/crabbox/cli.js +3 -3
- package/dist/lib/daemon-health.d.ts +22 -0
- package/dist/lib/daemon-health.js +33 -3
- package/dist/lib/daemon-services.d.ts +50 -0
- package/dist/lib/daemon-services.js +147 -0
- package/dist/lib/daemon-ticks.d.ts +35 -66
- package/dist/lib/daemon-ticks.js +77 -159
- package/dist/lib/daemon.d.ts +72 -4
- package/dist/lib/daemon.js +792 -202
- package/dist/lib/deeplink/register.d.ts +49 -0
- package/dist/lib/deeplink/register.js +253 -0
- package/dist/lib/deeplink/url.d.ts +34 -0
- package/dist/lib/deeplink/url.js +78 -0
- package/dist/lib/device-config.d.ts +124 -29
- package/dist/lib/device-config.js +346 -141
- package/dist/lib/devices/config-migration.d.ts +42 -0
- package/dist/lib/devices/config-migration.js +203 -0
- package/dist/lib/devices/connect.js +14 -7
- package/dist/lib/devices/doctor-findings.d.ts +4 -1
- package/dist/lib/devices/doctor-findings.js +20 -1
- package/dist/lib/devices/harness-inventory.d.ts +11 -2
- package/dist/lib/devices/harness-inventory.js +0 -0
- package/dist/lib/devices/health.d.ts +1 -1
- package/dist/lib/devices/health.js +1 -1
- package/dist/lib/devices/pending.d.ts +22 -1
- package/dist/lib/devices/pending.js +67 -2
- package/dist/lib/devices/registry.d.ts +0 -25
- package/dist/lib/devices/registry.js +2 -83
- package/dist/lib/devices/resolve-profile.d.ts +20 -0
- package/dist/lib/devices/resolve-profile.js +44 -0
- package/dist/lib/devices/rollout-verify.d.ts +101 -0
- package/dist/lib/devices/rollout-verify.js +193 -0
- package/dist/lib/devices/ssh-config.js +10 -6
- package/dist/lib/event-families.d.ts +28 -0
- package/dist/lib/event-families.js +159 -0
- package/dist/lib/event-stream.d.ts +9 -1
- package/dist/lib/event-stream.js +26 -9
- package/dist/lib/events.d.ts +9 -1
- package/dist/lib/events.js +21 -6
- package/dist/lib/exec.d.ts +30 -0
- package/dist/lib/exec.js +219 -121
- package/dist/lib/feed-broadcast.d.ts +25 -0
- package/dist/lib/feed-broadcast.js +33 -0
- package/dist/lib/feed.d.ts +1 -1
- package/dist/lib/feed.js +12 -2
- package/dist/lib/fleet/capture.js +15 -0
- package/dist/lib/fleet/manifest.js +9 -0
- package/dist/lib/fleet/types.d.ts +9 -0
- package/dist/lib/fleet-status.js +10 -8
- package/dist/lib/git.d.ts +21 -7
- package/dist/lib/git.js +122 -18
- package/dist/lib/hosts/dispatch.d.ts +8 -3
- package/dist/lib/hosts/dispatch.js +29 -3
- package/dist/lib/hosts/logs.d.ts +2 -2
- package/dist/lib/hosts/logs.js +2 -2
- package/dist/lib/hosts/option.js +1 -1
- package/dist/lib/hosts/passthrough.d.ts +2 -3
- package/dist/lib/hosts/passthrough.js +10 -24
- package/dist/lib/hosts/progress.js +2 -2
- package/dist/lib/hosts/providers/devices.d.ts +3 -3
- package/dist/lib/hosts/providers/devices.js +18 -12
- package/dist/lib/hosts/ready.d.ts +5 -1
- package/dist/lib/hosts/ready.js +9 -3
- package/dist/lib/hosts/reconcile.d.ts +2 -2
- package/dist/lib/hosts/reconcile.js +2 -2
- package/dist/lib/hosts/registry.js +14 -9
- package/dist/lib/hosts/remote-cmd.js +1 -1
- package/dist/lib/hosts/remote-os.d.ts +9 -6
- package/dist/lib/hosts/remote-os.js +13 -6
- package/dist/lib/hosts/routing-flag.d.ts +23 -0
- package/dist/lib/hosts/routing-flag.js +41 -0
- package/dist/lib/hosts/run-target.js +1 -1
- package/dist/lib/hosts/session-index.js +1 -1
- package/dist/lib/hosts/tasks.d.ts +6 -6
- package/dist/lib/hosts/tasks.js +4 -4
- package/dist/lib/hosts/types.js +1 -2
- package/dist/lib/installations/resolve.js +2 -8
- package/dist/lib/isolation-boundary-report.js +0 -1
- package/dist/lib/linear-cache.d.ts +7 -0
- package/dist/lib/linear-cache.js +31 -4
- package/dist/lib/linear-project-counts.d.ts +1 -1
- package/dist/lib/linear-project-counts.js +2 -3
- package/dist/lib/linear-projects.js +1 -1
- package/dist/lib/loop.d.ts +2 -2
- package/dist/lib/loop.js +1 -1
- package/dist/lib/mailbox-target.d.ts +1 -1
- package/dist/lib/mailbox-target.js +1 -1
- package/dist/lib/manifest.js +7 -4
- 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/snapshot.d.ts +3 -2
- package/dist/lib/menubar/snapshot.js +6 -2
- package/dist/lib/migrate-fold.d.ts +33 -0
- package/dist/lib/migrate-fold.js +130 -0
- package/dist/lib/migrate.d.ts +34 -34
- package/dist/lib/migrate.js +106 -128
- package/dist/lib/model-tier-overrides.d.ts +1 -1
- package/dist/lib/model-tier-overrides.js +15 -2
- package/dist/lib/monitors/config.d.ts +17 -3
- package/dist/lib/monitors/config.js +52 -18
- package/dist/lib/monitors/engine.d.ts +32 -1
- package/dist/lib/monitors/engine.js +94 -11
- package/dist/lib/monitors/fingerprint.d.ts +46 -0
- package/dist/lib/monitors/fingerprint.js +100 -0
- package/dist/lib/monitors/remote.d.ts +47 -0
- package/dist/lib/monitors/remote.js +81 -0
- package/dist/lib/monitors/state.d.ts +39 -0
- package/dist/lib/monitors/state.js +56 -0
- package/dist/lib/onepassword.d.ts +17 -0
- package/dist/lib/onepassword.js +52 -22
- package/dist/lib/open-url.js +1 -1
- package/dist/lib/picker.d.ts +14 -2
- package/dist/lib/picker.js +35 -13
- package/dist/lib/placement.d.ts +1 -1
- package/dist/lib/placement.js +1 -1
- package/dist/lib/pr-land-detach.d.ts +32 -0
- package/dist/lib/pr-land-detach.js +79 -0
- package/dist/lib/profiles-presets.js +13 -0
- package/dist/lib/profiles.d.ts +2 -0
- package/dist/lib/profiles.js +44 -1
- package/dist/lib/project-focus.d.ts +1 -1
- package/dist/lib/project-focus.js +1 -1
- package/dist/lib/project-probe.d.ts +4 -1
- package/dist/lib/project-probe.js +5 -4
- package/dist/lib/project-resources.js +2 -18
- package/dist/lib/project-root.d.ts +16 -0
- package/dist/lib/project-root.js +20 -2
- package/dist/lib/projects.d.ts +20 -1
- package/dist/lib/projects.js +78 -1
- package/dist/lib/refresh-coordinator.d.ts +24 -0
- package/dist/lib/refresh-coordinator.js +52 -0
- package/dist/lib/registry.d.ts +2 -2
- package/dist/lib/registry.js +1 -1
- package/dist/lib/resource-aliases.d.ts +13 -0
- package/dist/lib/resource-aliases.js +26 -0
- package/dist/lib/resources/types.d.ts +1 -1
- package/dist/lib/resources.d.ts +16 -0
- package/dist/lib/resources.js +66 -10
- package/dist/lib/rotate.d.ts +3 -4
- package/dist/lib/rotate.js +4 -5
- package/dist/lib/routine-activation.d.ts +22 -2
- package/dist/lib/routine-activation.js +63 -16
- package/dist/lib/routine-context.d.ts +9 -2
- package/dist/lib/routine-context.js +21 -6
- package/dist/lib/routines-project.d.ts +58 -48
- package/dist/lib/routines-project.js +190 -159
- package/dist/lib/routines.d.ts +44 -7
- package/dist/lib/routines.js +92 -34
- package/dist/lib/run-defaults.d.ts +5 -0
- package/dist/lib/run-defaults.js +11 -1
- package/dist/lib/runner.d.ts +10 -8
- package/dist/lib/runner.js +122 -39
- 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.d.ts +10 -3
- package/dist/lib/secrets/agent.js +43 -8
- package/dist/lib/secrets/bundles.d.ts +1 -0
- package/dist/lib/secrets/bundles.js +92 -80
- package/dist/lib/secrets/index.js +32 -9
- package/dist/lib/secrets/install-helper.d.ts +4 -4
- package/dist/lib/secrets/install-helper.js +4 -4
- package/dist/lib/secrets/push.d.ts +40 -6
- package/dist/lib/secrets/push.js +58 -11
- package/dist/lib/secrets/read-backoff.d.ts +1 -1
- package/dist/lib/secrets/read-backoff.js +1 -1
- package/dist/lib/secrets/remote.d.ts +24 -0
- package/dist/lib/secrets/remote.js +54 -4
- package/dist/lib/self-update.d.ts +108 -0
- package/dist/lib/self-update.js +276 -1
- package/dist/lib/session/active.d.ts +89 -5
- package/dist/lib/session/active.js +165 -10
- package/dist/lib/session/actor-sidecar.d.ts +1 -1
- package/dist/lib/session/actor-sidecar.js +2 -1
- package/dist/lib/session/db.d.ts +176 -2
- package/dist/lib/session/db.js +484 -17
- package/dist/lib/session/discover.js +27 -4
- package/dist/lib/session/hook-sessions.js +2 -2
- package/dist/lib/session/host-link.d.ts +2 -2
- package/dist/lib/session/host-link.js +2 -2
- package/dist/lib/session/pid-registry.d.ts +21 -0
- package/dist/lib/session/pid-registry.js +57 -0
- package/dist/lib/session/remote-active.js +15 -1
- package/dist/lib/session/remote.js +2 -2
- package/dist/lib/session/session-cache.d.ts +14 -7
- package/dist/lib/session/session-cache.js +51 -9
- package/dist/lib/session/state.d.ts +2 -2
- package/dist/lib/session/state.js +1 -1
- package/dist/lib/session/sync/config.d.ts +10 -10
- package/dist/lib/session/sync/config.js +10 -10
- package/dist/lib/session/sync/r2.d.ts +34 -0
- package/dist/lib/session/sync/r2.js +123 -0
- package/dist/lib/session/throughput.d.ts +1 -1
- package/dist/lib/session/throughput.js +1 -1
- package/dist/lib/session/types.d.ts +23 -1
- package/dist/lib/session/types.js +15 -0
- package/dist/lib/session/viewing-in.d.ts +1 -1
- package/dist/lib/session/viewing-in.js +1 -1
- package/dist/lib/session/watch.d.ts +101 -0
- package/dist/lib/session/watch.js +242 -0
- package/dist/lib/share/analytics.js +1 -1
- package/dist/lib/share/capture.d.ts +1 -1
- package/dist/lib/share/capture.js +3 -3
- package/dist/lib/share/config.d.ts +6 -2
- package/dist/lib/share/config.js +6 -5
- package/dist/lib/share/delete.js +3 -3
- package/dist/lib/share/provision.d.ts +38 -0
- package/dist/lib/share/provision.js +51 -1
- package/dist/lib/share/publish.d.ts +40 -1
- package/dist/lib/share/publish.js +119 -6
- package/dist/lib/share/worker-template.d.ts +12 -1
- package/dist/lib/share/worker-template.js +83 -7
- package/dist/lib/shims.d.ts +17 -3
- package/dist/lib/shims.js +98 -26
- package/dist/lib/signin-badge.js +3 -2
- package/dist/lib/skills.js +2 -0
- package/dist/lib/smart-launch.d.ts +8 -8
- package/dist/lib/smart-launch.js +80 -59
- package/dist/lib/ssh-exec.d.ts +1 -1
- package/dist/lib/ssh-exec.js +1 -1
- package/dist/lib/staleness/prune.d.ts +88 -0
- package/dist/lib/staleness/prune.js +69 -0
- package/dist/lib/staleness/registry.d.ts +1 -1
- package/dist/lib/staleness/writers/commands.js +34 -2
- package/dist/lib/staleness/writers/hooks.js +16 -0
- package/dist/lib/staleness/writers/kinds.d.ts +6 -0
- package/dist/lib/staleness/writers/skills.js +19 -0
- package/dist/lib/staleness/writers/subagents.d.ts +2 -3
- package/dist/lib/staleness/writers/subagents.js +0 -4
- package/dist/lib/staleness/writers/types.d.ts +32 -0
- package/dist/lib/startup/command-registry.d.ts +16 -14
- package/dist/lib/startup/command-registry.js +26 -37
- package/dist/lib/startup/dev-build.d.ts +10 -0
- package/dist/lib/startup/dev-build.js +13 -1
- package/dist/lib/startup/spellcheck.d.ts +18 -0
- package/dist/lib/startup/spellcheck.js +45 -0
- package/dist/lib/state.d.ts +36 -9
- package/dist/lib/state.js +96 -46
- package/dist/lib/subagents-registry.d.ts +0 -7
- package/dist/lib/subagents-registry.js +38 -65
- package/dist/lib/subagents.d.ts +0 -33
- package/dist/lib/subagents.js +0 -75
- package/dist/lib/sync-umbrella.js +1 -1
- package/dist/lib/teams/agents.d.ts +41 -1
- package/dist/lib/teams/agents.js +187 -9
- package/dist/lib/teams/api.d.ts +11 -1
- package/dist/lib/teams/api.js +11 -2
- package/dist/lib/teams/delivery.d.ts +41 -0
- package/dist/lib/teams/delivery.js +60 -0
- package/dist/lib/teams/registry.d.ts +14 -0
- package/dist/lib/teams/registry.js +51 -1
- package/dist/lib/teams/remoteWorktree.d.ts +19 -0
- package/dist/lib/teams/remoteWorktree.js +29 -0
- package/dist/lib/teams/scheduler.d.ts +1 -1
- package/dist/lib/teams/scheduler.js +1 -1
- package/dist/lib/teams/worktree.d.ts +24 -0
- package/dist/lib/teams/worktree.js +42 -0
- package/dist/lib/terminal/backends/vscodium-agent.d.ts +10 -1
- package/dist/lib/terminal/backends/vscodium-agent.js +16 -5
- package/dist/lib/terminal/engine.d.ts +4 -0
- package/dist/lib/terminal/engine.js +8 -2
- package/dist/lib/terminal/types.d.ts +21 -2
- package/dist/lib/testdata/refresh-coordinator-worker.d.ts +1 -0
- package/dist/lib/testdata/refresh-coordinator-worker.js +23 -0
- package/dist/lib/tickets/list.d.ts +53 -0
- package/dist/lib/tickets/list.js +153 -0
- package/dist/lib/tmux/orphan-reap.d.ts +316 -0
- package/dist/lib/tmux/orphan-reap.js +638 -0
- package/dist/lib/tmux/session.d.ts +68 -3
- package/dist/lib/tmux/session.js +96 -5
- package/dist/lib/triggers/handlers.d.ts +20 -0
- package/dist/lib/triggers/handlers.js +16 -1
- package/dist/lib/triggers/webhook.js +10 -0
- package/dist/lib/types.d.ts +47 -35
- package/dist/lib/usage-fleet.d.ts +32 -0
- package/dist/lib/usage-fleet.js +125 -0
- package/dist/lib/usage-refresh.d.ts +3 -3
- package/dist/lib/usage-refresh.js +19 -16
- package/dist/lib/usage.d.ts +51 -47
- package/dist/lib/usage.js +145 -153
- package/dist/lib/version-duplicates.js +2 -2
- package/dist/lib/versions.d.ts +56 -13
- package/dist/lib/versions.js +195 -45
- package/dist/lib/watchdog/log.d.ts +1 -1
- package/dist/lib/watchdog/log.js +3 -3
- package/dist/lib/watchdog/rotate.d.ts +6 -6
- package/dist/lib/watchdog/rotate.js +6 -6
- package/dist/lib/workflows.d.ts +1 -1
- package/dist/lib/yaml-io.d.ts +47 -0
- package/dist/lib/yaml-io.js +55 -0
- package/package.json +2 -1
- package/scripts/install-helper.js +2 -2
- package/scripts/postinstall.js +23 -20
- package/dist/commands/defaults.d.ts +0 -7
- package/dist/commands/defaults.js +0 -107
- package/dist/commands/export.d.ts +0 -11
- package/dist/commands/export.js +0 -215
- package/dist/commands/helper.d.ts +0 -12
- package/dist/commands/helper.js +0 -87
- package/dist/commands/hosts.d.ts +0 -11
- package/dist/commands/hosts.js +0 -330
- package/dist/commands/lock.d.ts +0 -12
- package/dist/commands/lock.js +0 -70
- package/dist/commands/pull.d.ts +0 -17
- package/dist/commands/pull.js +0 -39
- package/dist/commands/push.d.ts +0 -14
- package/dist/commands/push.js +0 -30
- package/dist/commands/set.d.ts +0 -15
- package/dist/commands/set.js +0 -79
- package/dist/commands/setup-share.d.ts +0 -17
- package/dist/commands/setup-share.js +0 -85
- package/dist/commands/wallet.d.ts +0 -20
- package/dist/commands/wallet.js +0 -216
- package/dist/commands/worktree.d.ts +0 -19
- package/dist/commands/worktree.js +0 -272
- package/dist/lib/account-labels.d.ts +0 -24
- package/dist/lib/account-labels.js +0 -72
- package/dist/lib/auto-dispatch-linear.d.ts +0 -18
- package/dist/lib/auto-dispatch-linear.js +0 -107
- package/dist/lib/auto-dispatch-provider.d.ts +0 -10
- package/dist/lib/auto-dispatch-provider.js +0 -30
- package/dist/lib/auto-dispatch.d.ts +0 -93
- package/dist/lib/auto-dispatch.js +0 -128
- package/dist/lib/export.d.ts +0 -72
- package/dist/lib/export.js +0 -269
- package/dist/lib/lock.d.ts +0 -93
- package/dist/lib/lock.js +0 -207
- package/dist/lib/wallet/index.d.ts +0 -78
- package/dist/lib/wallet/index.js +0 -253
|
@@ -0,0 +1,1212 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Full CLI bootstrap — loaded only after index.ts's argv fast paths miss.
|
|
3
|
+
*
|
|
4
|
+
* RUSH-2335: `src/index.ts` is a slim shell that statically imports only the
|
|
5
|
+
* leaf `lib/secrets/sync-commands.js` so `__secrets-*` / `__vault-age-helper` /
|
|
6
|
+
* `__shim` / `__daemon-run` can exit without evaluating the
|
|
7
|
+
* commander + self-update + command-registry graph (~140ms saved per
|
|
8
|
+
* synchronous broker read). Everything below that shell lands here via
|
|
9
|
+
* `await import('./bootstrap.js')`.
|
|
10
|
+
*
|
|
11
|
+
* This module is the previous body of `index.ts` (command registration, update
|
|
12
|
+
* checks, first-run setup, migrations, parse). Side-effecting top-level code
|
|
13
|
+
* runs on import — that is intentional.
|
|
14
|
+
*/
|
|
15
|
+
import { Command } from 'commander';
|
|
16
|
+
import chalk from 'chalk';
|
|
17
|
+
import * as fs from 'fs';
|
|
18
|
+
import * as path from 'path';
|
|
19
|
+
import { fileURLToPath } from 'url';
|
|
20
|
+
import { detectDevBuild } from './lib/startup/dev-build.js';
|
|
21
|
+
import { configureRootCommand } from './lib/startup/root-command.js';
|
|
22
|
+
// `ora`, `@inquirer/prompts`, `./commands/utils.js`, and the agents/versions/shims
|
|
23
|
+
// modules are imported dynamically at their use sites: they are needed only on
|
|
24
|
+
// interactive / update / shim-repair paths, never for fast commands like
|
|
25
|
+
// `--version`, `--help`, or `view`. Keeping them off the module-eval path is
|
|
26
|
+
// what gets cold starts under the target.
|
|
27
|
+
// Get version from package.json
|
|
28
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
29
|
+
const packageJsonPath = path.join(__dirname, '..', 'package.json');
|
|
30
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
|
|
31
|
+
const VERSION = packageJson.version;
|
|
32
|
+
import { NPM_PACKAGE_NAME, deriveGlobalPrefix, detectPackageManager, installPackageIntoPrefix, installPackageWithBun, verifyInstalledVersion, refreshAliasShims, downloadVerifiedTarball, } from './lib/self-update.js';
|
|
33
|
+
import { registerUpgradeCommand } from './commands/upgrade.js';
|
|
34
|
+
// Detect dev/working-tree builds and default the noisy startup steps off.
|
|
35
|
+
// Three cases trip this:
|
|
36
|
+
// 1. Dev install (scripts/install.sh) — package.json version stamped 0.0.0-dev.<sha>
|
|
37
|
+
// 2. Running `node dist/index.js` from a working tree — repo root has .git/
|
|
38
|
+
// 3. Running tsx/ts-node from src/ — also has .git/ at the repo root
|
|
39
|
+
// For all three: skip auto-pull (no network noise + no surprise FF on the
|
|
40
|
+
// system repo while iterating), skip migration (a buggy in-progress migration
|
|
41
|
+
// must not scribble on the user's real ~/.agents/), and skip the update prompt
|
|
42
|
+
// (the "0.0.0-dev -> 1.x.y" message is misleading). Each individual env var
|
|
43
|
+
// can still be set explicitly to override (set to '0' to re-enable).
|
|
44
|
+
const IS_DEV_BUILD = detectDevBuild(process.argv[1] || '', VERSION);
|
|
45
|
+
if (IS_DEV_BUILD) {
|
|
46
|
+
if (process.env.AGENTS_NO_AUTOPULL === undefined)
|
|
47
|
+
process.env.AGENTS_NO_AUTOPULL = '1';
|
|
48
|
+
if (process.env.AGENTS_SKIP_MIGRATION === undefined)
|
|
49
|
+
process.env.AGENTS_SKIP_MIGRATION = '1';
|
|
50
|
+
if (process.env.AGENTS_CLI_DISABLE_AUTO_UPDATE === undefined)
|
|
51
|
+
process.env.AGENTS_CLI_DISABLE_AUTO_UPDATE = '1';
|
|
52
|
+
}
|
|
53
|
+
// Command registration is lazy: instead of statically importing every command
|
|
54
|
+
// module on each invocation (which loaded the whole ~50-module tree before the
|
|
55
|
+
// first byte of output), the registry maps a command name to a thunk that
|
|
56
|
+
// imports only what that command needs. See src/lib/startup/command-registry.ts.
|
|
57
|
+
// Individual load* registrars are not imported here — registerEagerForRequest
|
|
58
|
+
// and the lazy path pull them via COMMAND_LOADERS. The full-tree
|
|
59
|
+
// registerAllEagerCommands path was removed (RUSH-2329): unknown/typo commands
|
|
60
|
+
// spellcheck against KNOWN_TOP_LEVEL_COMMANDS and register only the corrected
|
|
61
|
+
// name.
|
|
62
|
+
import { COMMAND_LOADERS, LAZY_COMMAND_NAMES, KNOWN_TOP_LEVEL_COMMANDS, RETIRED_TOP_LEVEL_COMMANDS, } from './lib/startup/command-registry.js';
|
|
63
|
+
import { closestTopLevelCommand } from './lib/startup/spellcheck.js';
|
|
64
|
+
import { applyGlobalHelpConventions } from './lib/help.js';
|
|
65
|
+
import { renderWhatsNew } from './lib/whats-new.js';
|
|
66
|
+
import { getCliLaunch } from './lib/cli-entry.js';
|
|
67
|
+
import { emit, emitFriction, redactArgs } from './lib/events.js';
|
|
68
|
+
import { stampProvenance } from './lib/event-provenance.js';
|
|
69
|
+
import { die } from './lib/format.js';
|
|
70
|
+
// Leaf (zero imports). Gates the dynamic passthrough import so the ~187ms
|
|
71
|
+
// hosts graph is never loaded when no routing flag is present (RUSH-2374).
|
|
72
|
+
import { hasHostRoutingFlag } from './lib/hosts/routing-flag.js';
|
|
73
|
+
// White-label: the shim for a brand (e.g. `jack`) exports AGENTS_BRAND, so the
|
|
74
|
+
// CLI presents its own name/help/errors as the brand. Unbranded (AGENTS_BRAND
|
|
75
|
+
// unset) resolves to 'agents' and everything below is byte-identical to before.
|
|
76
|
+
const BRAND = resolveBrandName();
|
|
77
|
+
const program = configureRootCommand(new Command(), BRAND, VERSION);
|
|
78
|
+
// ─── Audit backbone ────────────────────────────────────────────────────────────
|
|
79
|
+
// One choke point logs every `agents <module> <cmd>` invocation to the structured
|
|
80
|
+
// event log — so team create/disband, agent run, secrets access, and everything
|
|
81
|
+
// else is captured generically (with SSH/remote-user attribution added in emit()),
|
|
82
|
+
// no per-command wiring. `agents events` reads it back. Attached to the root
|
|
83
|
+
// program, so it's inherited by every subcommand regardless of lazy registration.
|
|
84
|
+
/** Command path from the acting command up to (but excluding) the `agents` root. */
|
|
85
|
+
function auditCommandPath(cmd) {
|
|
86
|
+
const parts = [];
|
|
87
|
+
let c = cmd;
|
|
88
|
+
while (c && c.name() && c.name() !== BRAND) {
|
|
89
|
+
parts.unshift(c.name());
|
|
90
|
+
c = c.parent;
|
|
91
|
+
}
|
|
92
|
+
return parts;
|
|
93
|
+
}
|
|
94
|
+
const auditStarts = new WeakMap();
|
|
95
|
+
/**
|
|
96
|
+
* Commands that WRITE the event stream, so recording their own invocation would
|
|
97
|
+
* add records to the log they are writing into. `events emit` is batched — one
|
|
98
|
+
* flush every few seconds per open editor window — so auditing it would bury the
|
|
99
|
+
* real events under two `command.*` records per flush. `_internal friction`
|
|
100
|
+
* exists for the same reason (shell guards fire before any `agents` process
|
|
101
|
+
* exists, so they cannot emit in-process) and had the same self-logging bug.
|
|
102
|
+
*/
|
|
103
|
+
const AUDIT_EXEMPT_COMMANDS = new Set([
|
|
104
|
+
'events emit',
|
|
105
|
+
'_internal friction',
|
|
106
|
+
]);
|
|
107
|
+
program.hook('preAction', (_thisCommand, actionCommand) => {
|
|
108
|
+
try {
|
|
109
|
+
const parts = auditCommandPath(actionCommand);
|
|
110
|
+
if (parts.length === 0)
|
|
111
|
+
return;
|
|
112
|
+
if (AUDIT_EXEMPT_COMMANDS.has(parts.join(' ')))
|
|
113
|
+
return;
|
|
114
|
+
auditStarts.set(actionCommand, Date.now());
|
|
115
|
+
emit('command.start', {
|
|
116
|
+
module: parts[0],
|
|
117
|
+
command: parts.join(' '),
|
|
118
|
+
// Commander exposes positional operands in actionCommand.args but omits
|
|
119
|
+
// parsed option values. Audit the real argv so sensitive flags are seen
|
|
120
|
+
// and redacted instead of silently bypassing the policy.
|
|
121
|
+
args: redactArgs(process.argv.slice(2, 22)),
|
|
122
|
+
cwd: process.cwd(),
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
catch {
|
|
126
|
+
// Audit logging must never break command dispatch.
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
program.hook('postAction', (_thisCommand, actionCommand) => {
|
|
130
|
+
try {
|
|
131
|
+
const parts = auditCommandPath(actionCommand);
|
|
132
|
+
if (parts.length === 0)
|
|
133
|
+
return;
|
|
134
|
+
if (AUDIT_EXEMPT_COMMANDS.has(parts.join(' ')))
|
|
135
|
+
return;
|
|
136
|
+
const started = auditStarts.get(actionCommand);
|
|
137
|
+
const durationMs = started !== undefined ? Date.now() - started : undefined;
|
|
138
|
+
const command = parts.join(' ');
|
|
139
|
+
emit('command.end', {
|
|
140
|
+
module: parts[0],
|
|
141
|
+
command,
|
|
142
|
+
...(durationMs !== undefined ? { durationMs } : {}),
|
|
143
|
+
});
|
|
144
|
+
if (parts[0] === 'run') {
|
|
145
|
+
const agentName = actionCommand.args?.[0] ? String(actionCommand.args[0]).split('@')[0] : 'run';
|
|
146
|
+
void import('./lib/analytics/usage-db.js').then(({ recordUsage }) => {
|
|
147
|
+
recordUsage({
|
|
148
|
+
kind: 'agent',
|
|
149
|
+
name: agentName || 'run',
|
|
150
|
+
event: 'invoke',
|
|
151
|
+
source: 'cli',
|
|
152
|
+
meta: durationMs !== undefined ? { durationMs } : undefined,
|
|
153
|
+
});
|
|
154
|
+
}).catch(() => { });
|
|
155
|
+
}
|
|
156
|
+
// Disposable perf warehouse — fail-soft spool append (no SQLite on this path).
|
|
157
|
+
if (durationMs !== undefined && parts[0] !== 'perf') {
|
|
158
|
+
// sessionId/agent are resolvable here the same way emit() resolves them
|
|
159
|
+
// for command.start/command.end above (the shared provenance floor,
|
|
160
|
+
// event-provenance.ts) — without this, every command.end perf sample
|
|
161
|
+
// was anonymous, unlike the audit log record right next to it.
|
|
162
|
+
const { sessionId, agent } = stampProvenance();
|
|
163
|
+
void import('./lib/perf/spool.js').then(({ recordSample }) => {
|
|
164
|
+
recordSample({
|
|
165
|
+
kind: 'command.end',
|
|
166
|
+
label: command,
|
|
167
|
+
durationMs,
|
|
168
|
+
cwd: process.cwd(),
|
|
169
|
+
sessionId,
|
|
170
|
+
agent,
|
|
171
|
+
});
|
|
172
|
+
}).catch(() => { });
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
catch {
|
|
176
|
+
// Best-effort completion record; the start line is the durable audit fact.
|
|
177
|
+
}
|
|
178
|
+
});
|
|
179
|
+
/**
|
|
180
|
+
* Skin the static root help for a brand: rewrite the visible `agents` command
|
|
181
|
+
* examples to the brand name and drop lines for commands this brand disabled.
|
|
182
|
+
* A no-op for the unbranded `agents` CLI with nothing disabled.
|
|
183
|
+
*/
|
|
184
|
+
function brandRootHelp(raw) {
|
|
185
|
+
let text = raw;
|
|
186
|
+
if (BRAND !== 'agents') {
|
|
187
|
+
text = text
|
|
188
|
+
.replace(/Usage: agents /g, `Usage: ${BRAND} `)
|
|
189
|
+
.replace(/^ {2}agents /gm, ` ${BRAND} `)
|
|
190
|
+
.replace(/Run 'agents /g, `Run '${BRAND} `);
|
|
191
|
+
}
|
|
192
|
+
const disabled = disabledCommandsForActiveBrand();
|
|
193
|
+
if (disabled.size > 0) {
|
|
194
|
+
text = text
|
|
195
|
+
.split('\n')
|
|
196
|
+
.filter((line) => {
|
|
197
|
+
const m = line.match(/^ {2}([a-z][\w-]*)/);
|
|
198
|
+
return !(m && disabled.has(m[1]));
|
|
199
|
+
})
|
|
200
|
+
.join('\n');
|
|
201
|
+
}
|
|
202
|
+
return text;
|
|
203
|
+
}
|
|
204
|
+
// Custom help for the main program only
|
|
205
|
+
const originalHelpInformation = program.helpInformation.bind(program);
|
|
206
|
+
program.helpInformation = function () {
|
|
207
|
+
if (this.name() === BRAND && !this.parent) {
|
|
208
|
+
return brandRootHelp(`Usage: agents [command] [options]
|
|
209
|
+
|
|
210
|
+
Install, configure, run, and dispatch AI coding agents from one place.
|
|
211
|
+
Works with Claude, Codex, Antigravity, Cursor, OpenCode, OpenClaw, and Droid.
|
|
212
|
+
|
|
213
|
+
Quick start:
|
|
214
|
+
agents setup First-time setup (interactive)
|
|
215
|
+
agents view See what's installed
|
|
216
|
+
agents run <agent> ["prompt"] Run an agent (interactive without prompt, headless with)
|
|
217
|
+
agents sessions Browse past sessions across all agents
|
|
218
|
+
|
|
219
|
+
Agent versions:
|
|
220
|
+
add <agent>[@version] Install an agent CLI (e.g. agents add grok or agents add codex)
|
|
221
|
+
import <agent> Adopt an existing global install (npm/homebrew) into agents-cli
|
|
222
|
+
update <agent>[@version] Move an installed agent to a new release, keeping its name (agents-cli itself is 'agents upgrade')
|
|
223
|
+
prune <agent>[@version] Uninstall a version
|
|
224
|
+
remove <agent>[@version] Alias for prune
|
|
225
|
+
use <agent>@<version> Set the default version
|
|
226
|
+
prune cleanup [target] Remove orphan resources and older duplicate version installs
|
|
227
|
+
trash Inspect and restore soft-deleted version directories
|
|
228
|
+
view [agent[@version]] List versions, inspect one in detail, or --merged for the cross-layer resource surface
|
|
229
|
+
inspect <target> Deep details for one agent+version, or a DotAgents repo (user|system|project|alias|path)
|
|
230
|
+
|
|
231
|
+
Agent configuration (synced across versions):
|
|
232
|
+
rules Instructions given to agents (CLAUDE.md, etc.)
|
|
233
|
+
commands Slash commands (/commit, /test, etc.)
|
|
234
|
+
skills Knowledge packs (SKILL.md + supporting files)
|
|
235
|
+
mcp MCP servers (stdio or HTTP)
|
|
236
|
+
permissions Allow/deny rules for tool calls
|
|
237
|
+
hooks Shell scripts that run on agent events (hooks.yaml in agents.yaml)
|
|
238
|
+
subagents Named sub-agent definitions
|
|
239
|
+
plugins Bundles of skills, hooks, and scripts
|
|
240
|
+
|
|
241
|
+
Packages:
|
|
242
|
+
search <query> Find MCP servers and skills in registries
|
|
243
|
+
install <pkg> Install from registry (mcp:name, skill:user/repo)
|
|
244
|
+
|
|
245
|
+
Run and dispatch:
|
|
246
|
+
run <agent|profile> [prompt] Run an agent. Omit prompt for interactive mode.
|
|
247
|
+
config Configure run defaults, project root, and device options
|
|
248
|
+
teams Coordinate multiple agents on shared work
|
|
249
|
+
routines Run agents on a cron schedule (scheduler auto-starts)
|
|
250
|
+
daemon Runtime status/control for the always-on daemon (secrets broker, browser IPC, scheduler)
|
|
251
|
+
webhook Receive signed GitHub/Linear webhooks for trigger routines
|
|
252
|
+
funnel Expose a webhook receiver through Tailscale Funnel
|
|
253
|
+
sessions Browse, search, and replay past runs (live-search in TTY; grouped by workspace)
|
|
254
|
+
logs [id] Show a run's log — host-dispatch task or session; -f to follow
|
|
255
|
+
browser Automate a browser — navigate, click, screenshot, console, network
|
|
256
|
+
pty Drive interactive terminal programs (REPLs, TUIs) via a persistent PTY session
|
|
257
|
+
artifacts Publish what an agent made (plans, reports, visuals) to a shareable link
|
|
258
|
+
|
|
259
|
+
Observe (read the fleet — no store merge; aliases point at the real readers):
|
|
260
|
+
feed / inbox Needs-you inbox (open blocks waiting on you)
|
|
261
|
+
timeline Agent progress stream (= feed --filter updates)
|
|
262
|
+
roster Live agents (= sessions --active)
|
|
263
|
+
events Unified ops + activity event trail
|
|
264
|
+
audit Tamper-evident run-dispatch log (not events)
|
|
265
|
+
snapshot One-process inventory + active sessions poll
|
|
266
|
+
status Sync/drift only (not the live fleet snapshot)
|
|
267
|
+
|
|
268
|
+
Credentials and profiles:
|
|
269
|
+
profile Activate resource profiles across skills, MCP, permissions, and secrets
|
|
270
|
+
profiles Bundles of (host CLI, endpoint, model, auth)
|
|
271
|
+
secrets Keychain-backed env bundles; use 'secrets exec <bundle> -- <cmd>' to inject into a subprocess
|
|
272
|
+
|
|
273
|
+
Diagnostics:
|
|
274
|
+
doctor [agent[@version]] Diagnose CLI availability, sync status, and resource divergence; --check for the CI drift gate
|
|
275
|
+
usage [agent] Show rate-limit and quota usage per agent
|
|
276
|
+
insights How work looks — behaviour (default) or counter mix (insights mix)
|
|
277
|
+
perf Latency rollups (hooks, commands, runs) from the disposable perf warehouse
|
|
278
|
+
|
|
279
|
+
Config sync:
|
|
280
|
+
repo pull [alias] Git pull a repo (system | user | <extra>)
|
|
281
|
+
sync [agent] Re-materialize installed version homes; --local to skip fetching
|
|
282
|
+
repo init --path <dir> Scaffold your own editable repo from a template
|
|
283
|
+
repo add <path|gh:user/repo> Merge an extra repo after the system repo
|
|
284
|
+
|
|
285
|
+
Beta features:
|
|
286
|
+
beta Enable preview features (factory and more)
|
|
287
|
+
|
|
288
|
+
Automation tips:
|
|
289
|
+
Pass explicit names/IDs Avoid pickers: agents sessions <id> --markdown
|
|
290
|
+
Use --yes for defaults Auto-accept sync/default prompts on add/use/pull
|
|
291
|
+
Use --names for central items e.g. agents commands add --names review-pr,debug
|
|
292
|
+
Use agent@version targets e.g. --agents grok@0.1.218,claude@2.1.79,codex@default
|
|
293
|
+
Non-TTY shells apply defaults Omitted required selections fail with a plain hint
|
|
294
|
+
|
|
295
|
+
Options:
|
|
296
|
+
-V, --version Show version number
|
|
297
|
+
-h, --help Show help
|
|
298
|
+
--verbose Show startup self-heal details on stderr
|
|
299
|
+
|
|
300
|
+
System config lives in ~/.agents/.system/. Run 'agents <command> --help' for details.
|
|
301
|
+
`);
|
|
302
|
+
}
|
|
303
|
+
return originalHelpInformation();
|
|
304
|
+
};
|
|
305
|
+
/** Compare two semver version strings. Returns 1 if a > b, -1 if a < b, 0 if equal. */
|
|
306
|
+
function compareVersions(a, b) {
|
|
307
|
+
const partsA = a.split('.').map(Number);
|
|
308
|
+
const partsB = b.split('.').map(Number);
|
|
309
|
+
for (let i = 0; i < 3; i++) {
|
|
310
|
+
if (partsA[i] > partsB[i])
|
|
311
|
+
return 1;
|
|
312
|
+
if (partsA[i] < partsB[i])
|
|
313
|
+
return -1;
|
|
314
|
+
}
|
|
315
|
+
return 0;
|
|
316
|
+
}
|
|
317
|
+
/** Fetch and display changelog entries between two versions from unpkg. */
|
|
318
|
+
async function showWhatsNew(fromVersion, toVersion) {
|
|
319
|
+
try {
|
|
320
|
+
const response = await fetch(`https://unpkg.com/@phnx-labs/agents-cli@${toVersion}/CHANGELOG.md`);
|
|
321
|
+
if (!response.ok)
|
|
322
|
+
return;
|
|
323
|
+
const relevantChanges = renderWhatsNew(await response.text(), fromVersion, toVersion);
|
|
324
|
+
if (relevantChanges.length > 0) {
|
|
325
|
+
console.log(chalk.bold("\nWhat's new:\n"));
|
|
326
|
+
for (const line of relevantChanges) {
|
|
327
|
+
console.log(line);
|
|
328
|
+
}
|
|
329
|
+
console.log(chalk.gray('\nFull notes: https://github.com/phnx-labs/agents-cli/blob/main/CHANGELOG.md'));
|
|
330
|
+
console.log();
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
catch {
|
|
334
|
+
// Silently ignore changelog fetch errors
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
const UPDATE_CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000; // 24 hours
|
|
338
|
+
import { getUpdateCheckPath, getMigratedSentinelPath, getUserAgentsDir, getRuntimeStateDir } from './lib/state.js';
|
|
339
|
+
import { resolveBrandName, disabledCommandsForActiveBrand } from './lib/brand.js';
|
|
340
|
+
import { readUpdateCache, saveUpdateCheck, dismissUpdateVersion, shouldPromptUpgrade, resolveMultiInstallInventory, remediateStaleAgentsCliInstalls, resolveRunningPackageRoot, } from './lib/self-update.js';
|
|
341
|
+
const UPDATE_CHECK_FILE = getUpdateCheckPath();
|
|
342
|
+
// Beside the existing update-check cache (RUSH-2324): short-TTL memo of the
|
|
343
|
+
// multi-install PATH scan so every ordinary CLI invocation does not re-walk
|
|
344
|
+
// PATH + known install roots (~1ms warm).
|
|
345
|
+
const MULTI_INSTALL_SCAN_FILE = path.join(path.dirname(UPDATE_CHECK_FILE), '.multi-install-scan');
|
|
346
|
+
/**
|
|
347
|
+
* Warn once when this machine contains a different agents-cli install than the
|
|
348
|
+
* copy that is currently running (or several). Divergent installs
|
|
349
|
+
* are how self-updates "succeed" without changing the command the user types.
|
|
350
|
+
* The warning re-fires only when the set of install roots or their helper-copy
|
|
351
|
+
* safety changes. Dev builds are included because old dev copies can still
|
|
352
|
+
* overwrite the shared macOS helper bundle non-atomically.
|
|
353
|
+
*/
|
|
354
|
+
function maybeWarnMultiInstall() {
|
|
355
|
+
const sentinel = path.join(getRuntimeStateDir(), 'multi-install-warned');
|
|
356
|
+
let runningRoot;
|
|
357
|
+
try {
|
|
358
|
+
runningRoot = resolveRunningPackageRoot(__dirname);
|
|
359
|
+
}
|
|
360
|
+
catch {
|
|
361
|
+
// Without a real root for the running copy there is nothing to compare
|
|
362
|
+
// against, and a guess here is exactly what produced the phantom
|
|
363
|
+
// "/$bunfs" install. This warning is advisory — stay silent instead.
|
|
364
|
+
return;
|
|
365
|
+
}
|
|
366
|
+
// RUSH-2324: resolve via the short-TTL scan cache beside `.update-check`.
|
|
367
|
+
// Fresh cache → skip findAgentsCliInstalls (~1ms PATH walk).
|
|
368
|
+
const inventory = resolveMultiInstallInventory(runningRoot, VERSION, process.env.PATH || '', MULTI_INSTALL_SCAN_FILE);
|
|
369
|
+
if (inventory.length < 2) {
|
|
370
|
+
try {
|
|
371
|
+
fs.unlinkSync(sentinel);
|
|
372
|
+
}
|
|
373
|
+
catch { /* nothing recorded */ }
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
376
|
+
const key = inventory
|
|
377
|
+
.map((info) => `${info.packageRoot}\t${info.version}\t${info.note}`)
|
|
378
|
+
.sort()
|
|
379
|
+
.join('\n');
|
|
380
|
+
try {
|
|
381
|
+
if (fs.readFileSync(sentinel, 'utf-8') === key)
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
catch { /* not warned for this set yet */ }
|
|
385
|
+
console.error(chalk.yellow('Multiple agents-cli installs detected:'));
|
|
386
|
+
for (const info of inventory) {
|
|
387
|
+
console.error(chalk.gray(` ${info.packageRoot} ${info.version} (${info.note})`));
|
|
388
|
+
}
|
|
389
|
+
console.error(chalk.gray('Upgrades apply to the running copy. Purge npx-cache / legacy / pre-1.22.30 copies with: agents doctor --fix'));
|
|
390
|
+
try {
|
|
391
|
+
fs.mkdirSync(path.dirname(sentinel), { recursive: true });
|
|
392
|
+
fs.writeFileSync(sentinel, key);
|
|
393
|
+
}
|
|
394
|
+
catch { /* best-effort; worst case the warning repeats */ }
|
|
395
|
+
}
|
|
396
|
+
/** Determine whether enough time has elapsed since the last registry fetch. */
|
|
397
|
+
function shouldFetchLatest(cache) {
|
|
398
|
+
if (!cache)
|
|
399
|
+
return true;
|
|
400
|
+
return Date.now() - cache.lastCheck > UPDATE_CHECK_INTERVAL_MS;
|
|
401
|
+
}
|
|
402
|
+
/** Fetch the exact latest npm version plus its registry integrity hash. */
|
|
403
|
+
async function fetchNpmPackageMetadata(versionOrTag = 'latest', timeoutMs = 5000) {
|
|
404
|
+
const response = await fetch(`https://registry.npmjs.org/${NPM_PACKAGE_NAME}/${versionOrTag}`, {
|
|
405
|
+
signal: AbortSignal.timeout(timeoutMs),
|
|
406
|
+
});
|
|
407
|
+
if (!response.ok) {
|
|
408
|
+
if (response.status === 404) {
|
|
409
|
+
throw new Error(`${NPM_PACKAGE_NAME}@${versionOrTag} not found on npm`);
|
|
410
|
+
}
|
|
411
|
+
throw new Error('Could not reach npm registry');
|
|
412
|
+
}
|
|
413
|
+
const data = await response.json();
|
|
414
|
+
if (typeof data.version !== 'string' ||
|
|
415
|
+
typeof data.dist?.integrity !== 'string' ||
|
|
416
|
+
typeof data.dist?.tarball !== 'string') {
|
|
417
|
+
throw new Error('npm registry response did not include version, integrity, and tarball');
|
|
418
|
+
}
|
|
419
|
+
return { version: data.version, integrity: data.dist.integrity, tarball: data.dist.tarball };
|
|
420
|
+
}
|
|
421
|
+
function printResolvedPackage(metadata) {
|
|
422
|
+
console.log(chalk.gray(`Resolved: ${NPM_PACKAGE_NAME}@${metadata.version}`));
|
|
423
|
+
console.log(chalk.gray(`Integrity: ${metadata.integrity}`));
|
|
424
|
+
}
|
|
425
|
+
async function installResolvedPackage(metadata) {
|
|
426
|
+
const packageRoot = resolveRunningPackageRoot(__dirname);
|
|
427
|
+
// Download the published tarball and prove its bytes match the registry
|
|
428
|
+
// integrity BEFORE installing anything. A `name@version` spec would let the
|
|
429
|
+
// package manager fetch and install whatever the registry serves with no
|
|
430
|
+
// hash check on our side; instead we verify here and install the LOCAL, now
|
|
431
|
+
// trusted .tgz. A mismatch throws and nothing below runs — fail closed.
|
|
432
|
+
const tarball = await downloadVerifiedTarball(metadata.tarball, metadata.integrity);
|
|
433
|
+
try {
|
|
434
|
+
// Upgrade with the package manager that owns this install. A bun global
|
|
435
|
+
// install lives at <bunGlobalDir>/node_modules/... (no `lib` segment), so an
|
|
436
|
+
// `npm install --prefix` would write to <bunGlobalDir>/lib/node_modules and
|
|
437
|
+
// never touch the running copy — npm exits 0, the verify below fails.
|
|
438
|
+
if (detectPackageManager(packageRoot) === 'bun') {
|
|
439
|
+
await installPackageWithBun(tarball);
|
|
440
|
+
}
|
|
441
|
+
else {
|
|
442
|
+
await installPackageIntoPrefix(tarball, deriveGlobalPrefix(packageRoot));
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
finally {
|
|
446
|
+
// Best-effort cleanup of the verified tarball and its temp dir.
|
|
447
|
+
try {
|
|
448
|
+
fs.rmSync(path.dirname(tarball), { recursive: true, force: true });
|
|
449
|
+
}
|
|
450
|
+
catch {
|
|
451
|
+
/* leave it for the OS temp sweep */
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
verifyInstalledVersion(packageRoot, metadata.version);
|
|
455
|
+
refreshAliasShims(packageRoot);
|
|
456
|
+
// The npm install above runs with --ignore-scripts, so the postinstall that
|
|
457
|
+
// installs the macOS Keychain helper never fires on upgrade. Force-refresh the
|
|
458
|
+
// helper here so a user upgrading FROM a broken build (e.g. the entitlement-less
|
|
459
|
+
// 1.20.4 helper that fails SecItemAdd with -34018) gets the fixed, signed bundle
|
|
460
|
+
// immediately — instead of waiting for the lazy staleness check in
|
|
461
|
+
// getKeychainHelperPath() to repair it on their next secret operation. The new
|
|
462
|
+
// package is already on disk, so the dynamic import resolves the freshly-installed
|
|
463
|
+
// helper module + bundle. Best-effort: an upgrade must never fail because the
|
|
464
|
+
// helper could not be reinstalled (the lazy staleness check in
|
|
465
|
+
// getKeychainHelperPath() still repairs it on the next secret operation).
|
|
466
|
+
if (process.platform === 'darwin') {
|
|
467
|
+
try {
|
|
468
|
+
const { ensureKeychainHelperInstalled } = await import('./lib/secrets/install-helper.js');
|
|
469
|
+
ensureKeychainHelperInstalled({ forceReinstall: true });
|
|
470
|
+
}
|
|
471
|
+
catch {
|
|
472
|
+
// Non-fatal.
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
/** Present an interactive upgrade prompt (TTY) or a one-line hint (non-TTY). */
|
|
477
|
+
async function promptUpgrade(latestVersion) {
|
|
478
|
+
const { default: ora } = await import('ora');
|
|
479
|
+
const { confirm, select } = await import('@inquirer/prompts');
|
|
480
|
+
const { isInteractiveTerminal, isPromptCancelled } = await import('./commands/utils.js');
|
|
481
|
+
if (!isInteractiveTerminal()) {
|
|
482
|
+
console.error(chalk.yellow(`Update available: ${VERSION} -> ${latestVersion}. Run: agents upgrade --yes`));
|
|
483
|
+
return;
|
|
484
|
+
}
|
|
485
|
+
const answer = await select({
|
|
486
|
+
message: `Update available: ${VERSION} -> ${latestVersion}`,
|
|
487
|
+
choices: [
|
|
488
|
+
{ value: 'now', name: 'Upgrade now' },
|
|
489
|
+
{ value: 'later', name: 'Later' },
|
|
490
|
+
{ value: 'dismiss', name: `Skip ${latestVersion}` },
|
|
491
|
+
],
|
|
492
|
+
});
|
|
493
|
+
if (answer === 'dismiss') {
|
|
494
|
+
dismissUpdateVersion(UPDATE_CHECK_FILE, latestVersion);
|
|
495
|
+
return;
|
|
496
|
+
}
|
|
497
|
+
if (answer === 'now') {
|
|
498
|
+
const { spawnSync } = await import('child_process');
|
|
499
|
+
let spinner = ora('Resolving package metadata...').start();
|
|
500
|
+
try {
|
|
501
|
+
const metadata = await fetchNpmPackageMetadata();
|
|
502
|
+
// The prompt showed the cached latest, which can lag the registry (the
|
|
503
|
+
// 24h window) — sync the cache to what was actually resolved so later
|
|
504
|
+
// prompts and the install agree on the same version.
|
|
505
|
+
saveUpdateCheck(UPDATE_CHECK_FILE, metadata.version);
|
|
506
|
+
spinner.succeed(`Resolved ${NPM_PACKAGE_NAME}@${metadata.version}`);
|
|
507
|
+
printResolvedPackage(metadata);
|
|
508
|
+
const approved = await confirm({
|
|
509
|
+
message: `Install ${NPM_PACKAGE_NAME}@${metadata.version}?`,
|
|
510
|
+
default: false,
|
|
511
|
+
});
|
|
512
|
+
if (!approved) {
|
|
513
|
+
console.log(chalk.gray('Upgrade cancelled'));
|
|
514
|
+
return;
|
|
515
|
+
}
|
|
516
|
+
spinner = ora('Upgrading...').start();
|
|
517
|
+
await installResolvedPackage(metadata);
|
|
518
|
+
spinner.succeed(`Upgraded to ${metadata.version}`);
|
|
519
|
+
await showWhatsNew(VERSION, metadata.version);
|
|
520
|
+
console.log();
|
|
521
|
+
// Re-exec the verified install's entrypoint and exit. PATH lookup of
|
|
522
|
+
// `agents` could resolve a different copy (dev build, another prefix)
|
|
523
|
+
// than the one that was just upgraded. getCliLaunch resolves the JS-vs-
|
|
524
|
+
// standalone shape — never hand-roll `[process.execPath, entrypoint]`,
|
|
525
|
+
// which hands the bun virtual entry to a compiled binary as a bogus arg.
|
|
526
|
+
const { command, args } = getCliLaunch(process.argv.slice(2));
|
|
527
|
+
const result = spawnSync(command, args, {
|
|
528
|
+
stdio: 'inherit',
|
|
529
|
+
shell: false,
|
|
530
|
+
});
|
|
531
|
+
process.exit(result.status ?? 0);
|
|
532
|
+
}
|
|
533
|
+
catch (err) {
|
|
534
|
+
if (isPromptCancelled(err))
|
|
535
|
+
return;
|
|
536
|
+
spinner.fail(`Upgrade failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
537
|
+
console.log(chalk.gray('Run manually: agents upgrade --yes'));
|
|
538
|
+
}
|
|
539
|
+
console.log();
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
/**
|
|
543
|
+
* Background update check — fires once per 24h cache window.
|
|
544
|
+
* Network: GET registry.npmjs.org/@phnx-labs/agents-cli/latest.
|
|
545
|
+
* Disable: set AGENTS_CLI_DISABLE_AUTO_UPDATE=1 in shell rc.
|
|
546
|
+
*
|
|
547
|
+
* Fire-and-forget; never blocks the CLI's foreground operation.
|
|
548
|
+
*/
|
|
549
|
+
function refreshUpdateCacheInBackground() {
|
|
550
|
+
fetch('https://registry.npmjs.org/@phnx-labs/agents-cli/latest', {
|
|
551
|
+
signal: AbortSignal.timeout(2000),
|
|
552
|
+
})
|
|
553
|
+
.then((response) => (response.ok ? response.json() : null))
|
|
554
|
+
.then((data) => {
|
|
555
|
+
if (data && typeof data.version === 'string') {
|
|
556
|
+
saveUpdateCheck(UPDATE_CHECK_FILE, data.version);
|
|
557
|
+
}
|
|
558
|
+
})
|
|
559
|
+
.catch(() => {
|
|
560
|
+
/* network error, try again next invocation */
|
|
561
|
+
});
|
|
562
|
+
}
|
|
563
|
+
/** Check for available updates using the local cache. Triggers a background refresh if stale. */
|
|
564
|
+
async function checkForUpdates() {
|
|
565
|
+
if (process.env.AGENTS_CLI_DISABLE_AUTO_UPDATE)
|
|
566
|
+
return;
|
|
567
|
+
maybeWarnMultiInstall();
|
|
568
|
+
const cache = readUpdateCache(UPDATE_CHECK_FILE);
|
|
569
|
+
// Kick off network refresh in background if stale. Does not block.
|
|
570
|
+
if (shouldFetchLatest(cache)) {
|
|
571
|
+
refreshUpdateCacheInBackground();
|
|
572
|
+
}
|
|
573
|
+
// Prompt based on current cache (may be from a previous run's background refresh).
|
|
574
|
+
// Skip if the user dismissed this exact version — they'll be prompted again when
|
|
575
|
+
// a newer version appears.
|
|
576
|
+
if (shouldPromptUpgrade(cache, VERSION)) {
|
|
577
|
+
try {
|
|
578
|
+
await promptUpgrade(cache.latestVersion);
|
|
579
|
+
}
|
|
580
|
+
catch (err) {
|
|
581
|
+
const { isPromptCancelled } = await import('./commands/utils.js');
|
|
582
|
+
if (isPromptCancelled(err))
|
|
583
|
+
return;
|
|
584
|
+
/* prompt error, ignore */
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
async function maybeBootstrapShimIntegration(requestedCommand, helpOrVersionRequested, verboseStartup) {
|
|
589
|
+
if (!verboseStartup && (!process.stdin.isTTY || !process.stdout.isTTY)) {
|
|
590
|
+
return;
|
|
591
|
+
}
|
|
592
|
+
// Pure documentation paths must never trigger interactive repair — mirrors
|
|
593
|
+
// the helpOrVersionRequested gate around ensureInitialized below. Covers
|
|
594
|
+
// both bare `agents --version` (requestedCommand === undefined) and
|
|
595
|
+
// `agents <subcommand> --help` (requestedCommand === subcommand name).
|
|
596
|
+
if (helpOrVersionRequested) {
|
|
597
|
+
return;
|
|
598
|
+
}
|
|
599
|
+
if (requestedCommand === 'sync' || requestedCommand === 'refresh-rules') {
|
|
600
|
+
return;
|
|
601
|
+
}
|
|
602
|
+
// Past the documentation/non-TTY guards: heal the shim/shadow/PATH conditions
|
|
603
|
+
// through the unified self-heal registry — the SAME checks the daemon runs, but
|
|
604
|
+
// driven silently on this interactive invocation so a user who never starts the
|
|
605
|
+
// daemon still gets healed. Regenerating stale shims, adopting symlink launchers,
|
|
606
|
+
// and adding the shims dir to PATH now happen without any output. The only thing
|
|
607
|
+
// that ever prints is a ONE-TIME notice for what a machine can't silently fix
|
|
608
|
+
// (a real native binary shadowing the shim) or is worth saying once (a PATH entry
|
|
609
|
+
// just added). Suppression is persistent and keyed to the condition — a new
|
|
610
|
+
// terminal no longer re-nags (the old per-PPID sentinel did, every shell).
|
|
611
|
+
const { runInteractiveShimHeal } = await import('./lib/shim-heal.js');
|
|
612
|
+
const { summarizeSelfHeal } = await import('./lib/self-heal/registry.js');
|
|
613
|
+
const { noticeLines, report } = await runInteractiveShimHeal();
|
|
614
|
+
if (verboseStartup) {
|
|
615
|
+
process.stderr.write(`[agents] startup self-heal: ${summarizeSelfHeal(report)}\n`);
|
|
616
|
+
}
|
|
617
|
+
if (noticeLines) {
|
|
618
|
+
for (const line of noticeLines)
|
|
619
|
+
console.log(chalk.gray(line));
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
// --- Inline command registrars ----------------------------------------------
|
|
623
|
+
// These commands are defined here rather than in a command module because they
|
|
624
|
+
// close over entry-point-local state (program re-parsing, VERSION, the npm
|
|
625
|
+
// upgrade helpers). The lazy registrar and the all-commands fallback below both
|
|
626
|
+
// call them, so the behavior is identical to the old eager registration.
|
|
627
|
+
// memory is a first-class resource command (see commands/memory.ts via
|
|
628
|
+
// COMMAND_LOADERS). The old memory→rules tombstone was removed in RUSH-1330.
|
|
629
|
+
/** Deprecated `perms` alias — re-parses as `permissions`. */
|
|
630
|
+
function registerPermsAliasCommand(p) {
|
|
631
|
+
p.command('perms', { hidden: true })
|
|
632
|
+
.allowUnknownOption()
|
|
633
|
+
.allowExcessArguments()
|
|
634
|
+
.action(async () => {
|
|
635
|
+
console.log(chalk.yellow('Deprecated: Use "agents permissions" instead of "agents perms"\n'));
|
|
636
|
+
// Re-parse with 'permissions' command
|
|
637
|
+
const args = process.argv.slice(2);
|
|
638
|
+
args[0] = 'permissions';
|
|
639
|
+
await program.parseAsync(['node', 'agents', ...args]);
|
|
640
|
+
});
|
|
641
|
+
}
|
|
642
|
+
/** Deprecated `exec` alias — re-parses as `run`. */
|
|
643
|
+
function registerExecAliasCommand(p) {
|
|
644
|
+
p.command('exec', { hidden: true })
|
|
645
|
+
.allowUnknownOption()
|
|
646
|
+
.allowExcessArguments()
|
|
647
|
+
.action(async () => {
|
|
648
|
+
console.log(chalk.yellow('Deprecated: Use "agents run" instead of "agents exec"\n'));
|
|
649
|
+
const args = process.argv.slice(2);
|
|
650
|
+
args[0] = 'run';
|
|
651
|
+
await program.parseAsync(['node', 'agents', ...args]);
|
|
652
|
+
});
|
|
653
|
+
}
|
|
654
|
+
/** Deprecated `jobs` / `cron` aliases — re-parse as `routines`. */
|
|
655
|
+
function registerJobsCronAliasCommand(p, alias) {
|
|
656
|
+
p.command(alias, { hidden: true })
|
|
657
|
+
.allowUnknownOption()
|
|
658
|
+
.allowExcessArguments()
|
|
659
|
+
.action(async () => {
|
|
660
|
+
console.log(chalk.yellow(`Deprecated: Use "agents routines" instead of "agents ${alias}"\n`));
|
|
661
|
+
const args = process.argv.slice(2);
|
|
662
|
+
args[0] = 'routines';
|
|
663
|
+
await program.parseAsync(['node', 'agents', ...args]);
|
|
664
|
+
});
|
|
665
|
+
}
|
|
666
|
+
/**
|
|
667
|
+
* Removed `check` command (RUSH-1234) — re-parses as `doctor --check`, forwarding
|
|
668
|
+
* any remaining flags so `check --quiet` / `check --json` / `check --devices` keep
|
|
669
|
+
* working and the drift-gate exit code survives the rename. The notice goes to
|
|
670
|
+
* stderr so `--json` stdout stays clean for CI parsers.
|
|
671
|
+
*/
|
|
672
|
+
function registerCheckTombstoneCommand(p) {
|
|
673
|
+
p.command('check', { hidden: true })
|
|
674
|
+
.allowUnknownOption()
|
|
675
|
+
.allowExcessArguments()
|
|
676
|
+
.action(async () => {
|
|
677
|
+
console.error(chalk.yellow('Deprecated: "agents check" is now "agents doctor --check". Running that for you.\n'));
|
|
678
|
+
const args = process.argv.slice(2);
|
|
679
|
+
args[0] = 'doctor';
|
|
680
|
+
args.splice(1, 0, '--check');
|
|
681
|
+
await program.parseAsync(['node', 'agents', ...args]);
|
|
682
|
+
});
|
|
683
|
+
}
|
|
684
|
+
/**
|
|
685
|
+
* Removed `resources` command (RUSH-1234) — re-parses as `view --merged` (the
|
|
686
|
+
* cross-layer, first-wins resource table now lives there; `agents inspect <target>`
|
|
687
|
+
* covers per-agent / per-repo detail). Forwards remaining flags like `--json`.
|
|
688
|
+
*/
|
|
689
|
+
function registerResourcesTombstoneCommand(p) {
|
|
690
|
+
p.command('resources', { hidden: true })
|
|
691
|
+
.allowUnknownOption()
|
|
692
|
+
.allowExcessArguments()
|
|
693
|
+
.action(async () => {
|
|
694
|
+
console.error(chalk.yellow('Deprecated: "agents resources" is now "agents view --merged" (use "agents inspect <target>" for per-agent/per-repo detail). Running that for you.\n'));
|
|
695
|
+
const args = process.argv.slice(2);
|
|
696
|
+
args[0] = 'view';
|
|
697
|
+
args.splice(1, 0, '--merged');
|
|
698
|
+
await program.parseAsync(['node', 'agents', ...args]);
|
|
699
|
+
});
|
|
700
|
+
}
|
|
701
|
+
/**
|
|
702
|
+
* Removed `hq` command — the JSON bridge for the interactive Agents HQ floor
|
|
703
|
+
* (`agents hq floor --json`). No UI ever consumed it (apps/ext has zero
|
|
704
|
+
* references) and it had no external users, so it is gone with no replacement.
|
|
705
|
+
* Kept as a hidden tombstone so a stale invocation gets a clear message and a
|
|
706
|
+
* non-zero exit instead of commander's raw "unknown command".
|
|
707
|
+
*/
|
|
708
|
+
function registerHqTombstoneCommand(p) {
|
|
709
|
+
p.command('hq', { hidden: true })
|
|
710
|
+
.allowUnknownOption()
|
|
711
|
+
.allowExcessArguments()
|
|
712
|
+
.action(() => {
|
|
713
|
+
die('"agents hq" was removed (internal Agents HQ floor bridge, no longer used).');
|
|
714
|
+
});
|
|
715
|
+
}
|
|
716
|
+
/**
|
|
717
|
+
* Hidden `agents _internal <sub>` namespace for machine-to-machine calls that
|
|
718
|
+
* are not user-facing. The first subcommand is `friction`, used by shell guard
|
|
719
|
+
* hooks (git-guard, rm-guard, …) to self-report a block into the event log
|
|
720
|
+
* before they exit 2 — they run before any `agents` process exists, so they
|
|
721
|
+
* cannot emit in-process.
|
|
722
|
+
*/
|
|
723
|
+
function registerInternalCommand(p) {
|
|
724
|
+
const internal = p.command('_internal', { hidden: true });
|
|
725
|
+
internal
|
|
726
|
+
.command('friction')
|
|
727
|
+
.option('--surface <surface>', 'Subsystem that hit the failure (e.g. guard, teams)')
|
|
728
|
+
.option('--id <failureId>', 'Stable failure slug (e.g. git.reset-hard)')
|
|
729
|
+
.option('--error <message>', 'Human-readable failure reason')
|
|
730
|
+
.option('--command <command>', 'The command that was blocked')
|
|
731
|
+
.action((opts) => {
|
|
732
|
+
if (!opts.surface || !opts.id) {
|
|
733
|
+
process.exit(0); // fail-open: never break the caller
|
|
734
|
+
}
|
|
735
|
+
emitFriction(opts.surface, opts.id, {
|
|
736
|
+
...(opts.error ? { error: opts.error } : {}),
|
|
737
|
+
...(opts.command ? { command: opts.command } : {}),
|
|
738
|
+
});
|
|
739
|
+
process.exit(0);
|
|
740
|
+
});
|
|
741
|
+
}
|
|
742
|
+
/** Runtime action for the shared `agents upgrade [version]` command definition. */
|
|
743
|
+
async function runUpgrade(version, options) {
|
|
744
|
+
const { default: ora } = await import('ora');
|
|
745
|
+
const { confirm } = await import('@inquirer/prompts');
|
|
746
|
+
const { isInteractiveTerminal, isPromptCancelled } = await import('./commands/utils.js');
|
|
747
|
+
const target = version ?? 'latest';
|
|
748
|
+
let spinner = ora(version ? `Resolving ${NPM_PACKAGE_NAME}@${target}...` : 'Checking for updates...').start();
|
|
749
|
+
try {
|
|
750
|
+
const metadata = await fetchNpmPackageMetadata(target);
|
|
751
|
+
const resolvedVersion = metadata.version;
|
|
752
|
+
if (resolvedVersion === VERSION) {
|
|
753
|
+
spinner.succeed(`Already on ${VERSION}`);
|
|
754
|
+
return;
|
|
755
|
+
}
|
|
756
|
+
// For `latest` (no explicit version) skip when already ahead. When a
|
|
757
|
+
// version is named explicitly, honor it even if it's a downgrade.
|
|
758
|
+
if (!version && compareVersions(resolvedVersion, VERSION) <= 0) {
|
|
759
|
+
spinner.succeed(`Already ahead of latest (${VERSION} >= ${resolvedVersion})`);
|
|
760
|
+
return;
|
|
761
|
+
}
|
|
762
|
+
const direction = compareVersions(resolvedVersion, VERSION) < 0 ? 'Downgrade' : 'Upgrade';
|
|
763
|
+
spinner.succeed(`Resolved ${NPM_PACKAGE_NAME}@${resolvedVersion}`);
|
|
764
|
+
printResolvedPackage(metadata);
|
|
765
|
+
if (isInteractiveTerminal() && !options.yes) {
|
|
766
|
+
const approved = await confirm({
|
|
767
|
+
message: `Install ${NPM_PACKAGE_NAME}@${resolvedVersion}?`,
|
|
768
|
+
default: false,
|
|
769
|
+
});
|
|
770
|
+
if (!approved) {
|
|
771
|
+
console.log(chalk.gray('Upgrade cancelled'));
|
|
772
|
+
return;
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
spinner = ora(`${direction === 'Downgrade' ? 'Downgrading' : 'Upgrading'} ${VERSION} -> ${resolvedVersion}...`).start();
|
|
776
|
+
await installResolvedPackage(metadata);
|
|
777
|
+
spinner.succeed(`${direction}d to ${resolvedVersion}`);
|
|
778
|
+
// After a successful upgrade, drop latent pre-fix / npx-cache /
|
|
779
|
+
// unsafe-legacy copies so the new binary is not shadowed (RUSH-2415).
|
|
780
|
+
try {
|
|
781
|
+
const runningRoot = resolveRunningPackageRoot(__dirname);
|
|
782
|
+
const purge = remediateStaleAgentsCliInstalls({
|
|
783
|
+
runningRoot,
|
|
784
|
+
runningVersion: resolvedVersion,
|
|
785
|
+
});
|
|
786
|
+
if (purge.removed.length > 0) {
|
|
787
|
+
console.log(chalk.gray(`Purged ${purge.removed.length} stale agents-cli install${purge.removed.length === 1 ? '' : 's'} (npx-cache / legacy / pre-1.22.30).`));
|
|
788
|
+
}
|
|
789
|
+
if (purge.failed.length > 0) {
|
|
790
|
+
console.log(chalk.yellow(`Could not purge ${purge.failed.length} stale install${purge.failed.length === 1 ? '' : 's'}; re-run agents doctor --fix.`));
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
catch {
|
|
794
|
+
/* best-effort; upgrade already succeeded */
|
|
795
|
+
}
|
|
796
|
+
// Only show the changelog for a genuine upgrade range.
|
|
797
|
+
if (compareVersions(resolvedVersion, VERSION) > 0) {
|
|
798
|
+
await showWhatsNew(VERSION, resolvedVersion);
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
catch (err) {
|
|
802
|
+
if (isPromptCancelled(err))
|
|
803
|
+
return;
|
|
804
|
+
spinner.fail(`Upgrade failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
805
|
+
console.log(chalk.gray(`Run manually: agents upgrade ${version ? version + ' ' : ''}--yes`));
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
function registerUpgradeRuntimeCommand(p) {
|
|
809
|
+
registerUpgradeCommand(p, runUpgrade);
|
|
810
|
+
}
|
|
811
|
+
// --- Lazy registration orchestration -----------------------------------------
|
|
812
|
+
/** Import a command module via its loader and register it on the program. */
|
|
813
|
+
async function reg(loader) {
|
|
814
|
+
(await loader())(program);
|
|
815
|
+
}
|
|
816
|
+
/**
|
|
817
|
+
* Register exactly the command(s) the requested top-level name needs.
|
|
818
|
+
* Returns false when the name maps to no known command (typo / unknown).
|
|
819
|
+
*
|
|
820
|
+
* Lazy commands (sessions/teams/cloud) are intentionally NOT handled here — they
|
|
821
|
+
* must register after applyGlobalHelpConventions to match main's ordering.
|
|
822
|
+
* Inline aliases/tombstones load their target module via COMMAND_LOADERS.
|
|
823
|
+
*/
|
|
824
|
+
async function registerEagerForRequest(name) {
|
|
825
|
+
switch (name) {
|
|
826
|
+
case 'perms':
|
|
827
|
+
// The action re-parses as `permissions`, so that target must exist too.
|
|
828
|
+
registerPermsAliasCommand(program);
|
|
829
|
+
for (const loader of COMMAND_LOADERS['permissions'] ?? [])
|
|
830
|
+
await reg(loader);
|
|
831
|
+
return true;
|
|
832
|
+
case 'exec':
|
|
833
|
+
registerExecAliasCommand(program);
|
|
834
|
+
for (const loader of COMMAND_LOADERS['run'] ?? [])
|
|
835
|
+
await reg(loader);
|
|
836
|
+
return true;
|
|
837
|
+
case 'jobs':
|
|
838
|
+
case 'cron':
|
|
839
|
+
registerJobsCronAliasCommand(program, name);
|
|
840
|
+
for (const loader of COMMAND_LOADERS['routines'] ?? [])
|
|
841
|
+
await reg(loader);
|
|
842
|
+
return true;
|
|
843
|
+
case 'check':
|
|
844
|
+
// The action re-parses as `doctor --check`, so doctor must exist too.
|
|
845
|
+
registerCheckTombstoneCommand(program);
|
|
846
|
+
for (const loader of COMMAND_LOADERS['doctor'] ?? [])
|
|
847
|
+
await reg(loader);
|
|
848
|
+
return true;
|
|
849
|
+
case 'resources':
|
|
850
|
+
// The action re-parses as `view --merged`, so view must exist too.
|
|
851
|
+
registerResourcesTombstoneCommand(program);
|
|
852
|
+
for (const loader of COMMAND_LOADERS['view'] ?? [])
|
|
853
|
+
await reg(loader);
|
|
854
|
+
return true;
|
|
855
|
+
case 'hq':
|
|
856
|
+
registerHqTombstoneCommand(program);
|
|
857
|
+
return true;
|
|
858
|
+
case '_internal':
|
|
859
|
+
registerInternalCommand(program);
|
|
860
|
+
return true;
|
|
861
|
+
case 'upgrade':
|
|
862
|
+
registerUpgradeRuntimeCommand(program);
|
|
863
|
+
return true;
|
|
864
|
+
}
|
|
865
|
+
const loaders = COMMAND_LOADERS[name];
|
|
866
|
+
if (!loaders)
|
|
867
|
+
return false;
|
|
868
|
+
for (const loader of loaders)
|
|
869
|
+
await reg(loader);
|
|
870
|
+
return true;
|
|
871
|
+
}
|
|
872
|
+
// Safety-net for unknown commands that still reach commander (should be rare
|
|
873
|
+
// after the pre-parse spellcheck below). Candidates come from the plain-string
|
|
874
|
+
// KNOWN_TOP_LEVEL_COMMANDS set so this path never depends on every module
|
|
875
|
+
// having been registered (RUSH-2329).
|
|
876
|
+
program.on('command:*', (operands) => {
|
|
877
|
+
const unknown = operands[0];
|
|
878
|
+
const { closest, minDist } = closestTopLevelCommand(unknown, KNOWN_TOP_LEVEL_COMMANDS);
|
|
879
|
+
if (minDist === 1 && closest && !RETIRED_TOP_LEVEL_COMMANDS.has(unknown)) {
|
|
880
|
+
const args = process.argv.slice(2);
|
|
881
|
+
args[0] = closest;
|
|
882
|
+
// The typo'd name was unknown, so the top-level --host router (which ran
|
|
883
|
+
// before commander parsing, against the ORIGINAL name) could not have
|
|
884
|
+
// routed it - it correctly fell through to reach this handler at all
|
|
885
|
+
// (that fallthrough is this ticket's own fix). But falling through to a
|
|
886
|
+
// plain local re-parse means a routing flag on a corrected REAL
|
|
887
|
+
// host-routable command (e.g. `docto --host box`, corrected to `doctor`)
|
|
888
|
+
// silently ran LOCALLY instead of remotely, with no error - worse than
|
|
889
|
+
// the loud "does not support --host" this ticket replaced. Re-run the
|
|
890
|
+
// router with the CORRECTED name before falling through to local parse;
|
|
891
|
+
// it already no-ops when no routing flag is present. RUSH-2022 review r2.
|
|
892
|
+
void (async () => {
|
|
893
|
+
// Register only the corrected command — never the full tree (RUSH-2329).
|
|
894
|
+
if (LAZY_COMMAND_NAMES.has(closest)) {
|
|
895
|
+
for (const loader of COMMAND_LOADERS[closest] ?? [])
|
|
896
|
+
await reg(loader);
|
|
897
|
+
}
|
|
898
|
+
else {
|
|
899
|
+
await registerEagerForRequest(closest);
|
|
900
|
+
}
|
|
901
|
+
// Same RUSH-2374 gate as the main router: typo corrections with no routing
|
|
902
|
+
// flag must not load the hosts graph just to no-op.
|
|
903
|
+
if (hasHostRoutingFlag(args)) {
|
|
904
|
+
const { maybeRunOnHost } = await import('./lib/hosts/passthrough.js');
|
|
905
|
+
if (await maybeRunOnHost(closest, args)) {
|
|
906
|
+
process.exit(process.exitCode ?? 0);
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
program.parse(['node', 'agents', ...args]);
|
|
910
|
+
})();
|
|
911
|
+
return;
|
|
912
|
+
}
|
|
913
|
+
console.error(`error: unknown command '${unknown}'`);
|
|
914
|
+
if (closest && minDist <= 3) {
|
|
915
|
+
console.error(`(Did you mean ${closest}?)`);
|
|
916
|
+
}
|
|
917
|
+
process.exit(1);
|
|
918
|
+
});
|
|
919
|
+
// Parse the invocation shape up front: the first non-flag token is the command,
|
|
920
|
+
// and the doc flags (--version/--help/-h) drive both the registration strategy
|
|
921
|
+
// and whether the update check + background sync run at all.
|
|
922
|
+
const passedArgs = process.argv.slice(2);
|
|
923
|
+
// Commander owns `--version` on the root command and otherwise intercepts it
|
|
924
|
+
// even after `sessions`, before the subcommand can parse its version filter.
|
|
925
|
+
// Rewrite only that value-taking nested form; bare `agents --version` and every
|
|
926
|
+
// other command retain the root documentation flag unchanged.
|
|
927
|
+
if (passedArgs[0] === 'sessions') {
|
|
928
|
+
const nestedVersionIndex = passedArgs.indexOf('--version', 1);
|
|
929
|
+
if (nestedVersionIndex >= 0) {
|
|
930
|
+
const nestedVersion = passedArgs[nestedVersionIndex + 1];
|
|
931
|
+
if (!nestedVersion || nestedVersion.startsWith('-')) {
|
|
932
|
+
console.error("error: option '--version <version>' argument missing");
|
|
933
|
+
process.exit(1);
|
|
934
|
+
}
|
|
935
|
+
passedArgs[nestedVersionIndex] = '--session-version';
|
|
936
|
+
process.argv[nestedVersionIndex + 2] = '--session-version';
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
const requestedCommand = passedArgs.find((arg) => !arg.startsWith('-'));
|
|
940
|
+
const verboseStartup = passedArgs.includes('--verbose');
|
|
941
|
+
// Help and version output are pure documentation — they must never gate on
|
|
942
|
+
// setup, otherwise `agents <cmd> --help` becomes useless on a fresh box.
|
|
943
|
+
const helpOrVersionRequested = passedArgs.some((arg) => arg === '--help' || arg === '-h' || arg === '--version' || arg === '-V');
|
|
944
|
+
// White-label: a brand can hide built-in top-level commands. A hidden command
|
|
945
|
+
// must behave as if it doesn't exist under this brand (unknown-command +
|
|
946
|
+
// spellcheck), while `agents` itself is unaffected. `brandDisabled` is empty for
|
|
947
|
+
// the unbranded CLI, so all of this is a no-op there.
|
|
948
|
+
const brandDisabled = disabledCommandsForActiveBrand();
|
|
949
|
+
const requestedIsDisabled = requestedCommand !== undefined && brandDisabled.has(requestedCommand);
|
|
950
|
+
// `--host` passthrough: run this invocation on a remote machine over SSH instead
|
|
951
|
+
// of locally. Handled before any local command registration / update check /
|
|
952
|
+
// background sync — a remote run needs none of that. Only the allowlisted
|
|
953
|
+
// read-only + config + teams commands route here; `run`/`sessions` are absent
|
|
954
|
+
// from the table and fall through to their own richer `--host` handling below.
|
|
955
|
+
// `--help`/`--version` stay local (docs must work without a reachable host).
|
|
956
|
+
//
|
|
957
|
+
// RUSH-2374: gate the dynamic import on a routing flag actually being present.
|
|
958
|
+
// Without this, every named invocation paid ~187ms to load passthrough.js only
|
|
959
|
+
// for maybeRunOnHost to return false after four flagValue scans. The presence
|
|
960
|
+
// scan itself is the same work those four scans do, at ~0.001ms on an 11-token
|
|
961
|
+
// argv — free next to the module graph it avoids on the majority path.
|
|
962
|
+
if (requestedCommand !== undefined &&
|
|
963
|
+
!helpOrVersionRequested &&
|
|
964
|
+
!requestedIsDisabled &&
|
|
965
|
+
hasHostRoutingFlag(passedArgs)) {
|
|
966
|
+
const { maybeRunOnHost } = await import('./lib/hosts/passthrough.js');
|
|
967
|
+
if (await maybeRunOnHost(requestedCommand, passedArgs)) {
|
|
968
|
+
process.exit(process.exitCode ?? 0);
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
// Register only the command(s) this invocation actually uses. Lazy commands
|
|
972
|
+
// (sessions/teams/cloud) are handled after applyGlobalHelpConventions below.
|
|
973
|
+
const isLazyRequest = requestedCommand !== undefined && LAZY_COMMAND_NAMES.has(requestedCommand);
|
|
974
|
+
// Set when the requested name maps to no command. Spellcheck uses the plain
|
|
975
|
+
// KNOWN_TOP_LEVEL_COMMANDS string set — never registerAllEagerCommands just to
|
|
976
|
+
// build the candidate list (RUSH-2329; was 250-330ms of module evaluation).
|
|
977
|
+
let requestedIsUnknown = false;
|
|
978
|
+
if (requestedIsDisabled) {
|
|
979
|
+
// Brand hid this command: resolve as unknown without loading the full tree.
|
|
980
|
+
requestedIsUnknown = true;
|
|
981
|
+
}
|
|
982
|
+
else if (requestedCommand !== undefined && !isLazyRequest) {
|
|
983
|
+
const known = await registerEagerForRequest(requestedCommand);
|
|
984
|
+
if (!known) {
|
|
985
|
+
requestedIsUnknown = true;
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
// When requestedCommand is undefined (bare invocation, --version, --help, -h) no
|
|
989
|
+
// command modules are needed: --version is built in and the root help text is a
|
|
990
|
+
// static string.
|
|
991
|
+
// Mirror main: help conventions are applied after the eager command tree and
|
|
992
|
+
// before the lazy commands, so the latter inherit the root's custom help
|
|
993
|
+
// formatter instead of getting the per-command recursive pass.
|
|
994
|
+
applyGlobalHelpConventions(program);
|
|
995
|
+
// Lazy commands pull in the SQLite-backed session/cloud stack; register them
|
|
996
|
+
// only when explicitly requested, keeping lightweight commands off that path.
|
|
997
|
+
if (isLazyRequest && !requestedIsDisabled) {
|
|
998
|
+
for (const loader of COMMAND_LOADERS[requestedCommand])
|
|
999
|
+
await reg(loader);
|
|
1000
|
+
}
|
|
1001
|
+
else if (requestedIsUnknown && requestedCommand) {
|
|
1002
|
+
// Spellcheck from the plain-string name set. KNOWN_TOP_LEVEL_COMMANDS already
|
|
1003
|
+
// includes lazy names (sessions/teams/cloud/…) and inline aliases/tombstones,
|
|
1004
|
+
// so `agents session` still suggests `sessions` without loading either module.
|
|
1005
|
+
// Insertion order matches COMMAND_LOADERS key order + INLINE_COMMAND_NAMES,
|
|
1006
|
+
// preserving the historical first-seen tie-break of registerAllEagerCommands.
|
|
1007
|
+
const candidates = [...KNOWN_TOP_LEVEL_COMMANDS].filter((name) => !brandDisabled.has(name));
|
|
1008
|
+
const { closest, minDist } = closestTopLevelCommand(requestedCommand, candidates);
|
|
1009
|
+
if (minDist === 1 &&
|
|
1010
|
+
closest &&
|
|
1011
|
+
!requestedIsDisabled &&
|
|
1012
|
+
!RETIRED_TOP_LEVEL_COMMANDS.has(requestedCommand)) {
|
|
1013
|
+
// Auto-correct: register ONLY the corrected command, then re-route --host
|
|
1014
|
+
// and reparse under the real name (RUSH-2329 + RUSH-2022 review r2).
|
|
1015
|
+
passedArgs[0] = closest;
|
|
1016
|
+
// Keep process.argv in sync for the command:* safety-net and any code that
|
|
1017
|
+
// re-reads argv after this point.
|
|
1018
|
+
const argvCmdIndex = process.argv.findIndex((a, i) => i >= 2 && !a.startsWith('-'));
|
|
1019
|
+
if (argvCmdIndex >= 0)
|
|
1020
|
+
process.argv[argvCmdIndex] = closest;
|
|
1021
|
+
if (LAZY_COMMAND_NAMES.has(closest)) {
|
|
1022
|
+
for (const loader of COMMAND_LOADERS[closest] ?? [])
|
|
1023
|
+
await reg(loader);
|
|
1024
|
+
}
|
|
1025
|
+
else {
|
|
1026
|
+
await registerEagerForRequest(closest);
|
|
1027
|
+
}
|
|
1028
|
+
if (!helpOrVersionRequested && hasHostRoutingFlag(passedArgs)) {
|
|
1029
|
+
const { maybeRunOnHost } = await import('./lib/hosts/passthrough.js');
|
|
1030
|
+
if (await maybeRunOnHost(closest, passedArgs)) {
|
|
1031
|
+
process.exit(process.exitCode ?? 0);
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
else {
|
|
1036
|
+
// No auto-correct: print the suggestion and exit without loading modules.
|
|
1037
|
+
console.error(`error: unknown command '${requestedCommand}'`);
|
|
1038
|
+
if (closest && minDist <= 3) {
|
|
1039
|
+
console.error(`(Did you mean ${closest}?)`);
|
|
1040
|
+
}
|
|
1041
|
+
process.exit(1);
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1044
|
+
// White-label: remove any commands this brand disabled so they resolve as
|
|
1045
|
+
// unknown. Unbranded or nothing-disabled → no-op. After auto-correct we may
|
|
1046
|
+
// have registered a non-disabled command; strip only if it is still listed.
|
|
1047
|
+
if (brandDisabled.size > 0) {
|
|
1048
|
+
const kept = program.commands.filter((c) => !brandDisabled.has(c.name()));
|
|
1049
|
+
if (kept.length !== program.commands.length) {
|
|
1050
|
+
program.commands = kept;
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
1053
|
+
// Pure documentation paths (--version / --help / -h) return immediately: skip
|
|
1054
|
+
// the update check (PATH scan + cache read) and the detached background sync
|
|
1055
|
+
// (spawns a child process) that every other invocation runs.
|
|
1056
|
+
if (!helpOrVersionRequested) {
|
|
1057
|
+
// Run update check before parsing so the upgrade notice/prompt precedes output.
|
|
1058
|
+
await checkForUpdates();
|
|
1059
|
+
// Fire-and-forget the background sync. System repo gets a real fast-forward
|
|
1060
|
+
// pull (read-only locally, safe). User repo and extras get fetch-only + a
|
|
1061
|
+
// status marker that `agents doctor` surfaces as a repo-behind warning.
|
|
1062
|
+
const { spawnDetachedSync } = await import('./lib/auto-pull.js');
|
|
1063
|
+
spawnDetachedSync();
|
|
1064
|
+
}
|
|
1065
|
+
// First-run experience: no args + no config yet + TTY -> launch interactive setup.
|
|
1066
|
+
// Skipped when stdin/stdout isn't a terminal (CI, pipes) or when user passes any args.
|
|
1067
|
+
const metaFilePath = path.join(getUserAgentsDir(), 'agents.yaml');
|
|
1068
|
+
const firstRun = passedArgs.length === 0 &&
|
|
1069
|
+
!fs.existsSync(metaFilePath) &&
|
|
1070
|
+
process.stdin.isTTY &&
|
|
1071
|
+
process.stdout.isTTY;
|
|
1072
|
+
if (firstRun) {
|
|
1073
|
+
try {
|
|
1074
|
+
const { runSetup } = await import('./commands/setup.js');
|
|
1075
|
+
await runSetup(program);
|
|
1076
|
+
}
|
|
1077
|
+
catch (err) {
|
|
1078
|
+
if (!(err instanceof Error && err.name === 'ExitPromptError')) {
|
|
1079
|
+
throw err;
|
|
1080
|
+
}
|
|
1081
|
+
}
|
|
1082
|
+
process.exit(0);
|
|
1083
|
+
}
|
|
1084
|
+
// Every command requires the system repo to be cloned first. `setup` is the
|
|
1085
|
+
// command that does the cloning; `uninstall` is its reverse and must run even
|
|
1086
|
+
// from a broken/half-setup state (that is exactly when you want to tear down).
|
|
1087
|
+
const SETUP_EXEMPT_COMMANDS = new Set(['setup', 'help', 'uninstall']);
|
|
1088
|
+
// Fold legacy ~/.agents-system/ into ~/.agents/.system/ BEFORE ensureInitialized
|
|
1089
|
+
// runs. ensureInitialized checks for .git inside the new path; if the user is
|
|
1090
|
+
// upgrading from a layout where .git lives under the legacy path, the check
|
|
1091
|
+
// would fail and exit before the migrator ever runs. Also runs outside the
|
|
1092
|
+
// sentinel guard below because the sentinel was set by pre-fold releases and
|
|
1093
|
+
// would otherwise skip this step on every existing install. Idempotent —
|
|
1094
|
+
// no-ops when legacy is missing or already a symlink.
|
|
1095
|
+
//
|
|
1096
|
+
// Skipped for --help/--version (RUSH-2454): pure documentation paths must not
|
|
1097
|
+
// load any migration graph. Loaded from migrate-fold.js (leaf: fs + createLink),
|
|
1098
|
+
// not migrate.js, so a real command pays only the fold hop unless the v19
|
|
1099
|
+
// sentinel is missing and runMigration() is required below.
|
|
1100
|
+
if (process.env.AGENTS_SKIP_MIGRATION !== '1' && !helpOrVersionRequested) {
|
|
1101
|
+
try {
|
|
1102
|
+
const { foldLegacySystemRepo } = await import('./lib/migrate-fold.js');
|
|
1103
|
+
foldLegacySystemRepo();
|
|
1104
|
+
}
|
|
1105
|
+
catch { /* must never block CLI startup */ }
|
|
1106
|
+
}
|
|
1107
|
+
if (!firstRun &&
|
|
1108
|
+
requestedCommand &&
|
|
1109
|
+
!SETUP_EXEMPT_COMMANDS.has(requestedCommand) &&
|
|
1110
|
+
!helpOrVersionRequested) {
|
|
1111
|
+
const { ensureInitialized } = await import('./commands/setup.js');
|
|
1112
|
+
await ensureInitialized(program);
|
|
1113
|
+
}
|
|
1114
|
+
// One-shot idempotent migrations (split-layout, legacy file moves).
|
|
1115
|
+
// Each step is internally guarded by existence checks so it's safe to run
|
|
1116
|
+
// every invocation. A sentinel file in the system dir short-circuits the
|
|
1117
|
+
// scan once a migration version has run, so the hot path stays cheap.
|
|
1118
|
+
// AGENTS_SKIP_MIGRATION=1 disables the bootstrap-time run for tests and
|
|
1119
|
+
// scripted invocations that prepare their own legacy fixtures.
|
|
1120
|
+
//
|
|
1121
|
+
// Skipped for --help/--version (RUSH-2454): same pure-docs gate as fold, the
|
|
1122
|
+
// update check, background sync, ensureInitialized, and the menu-bar self-heal.
|
|
1123
|
+
// The sentinel check itself is pure fs and does not load migrate.js — only a
|
|
1124
|
+
// missing/stale sentinel pays for `await import('./lib/migrate.js')` (which
|
|
1125
|
+
// pulls the hosts/routine/teams/daemon/menubar graph).
|
|
1126
|
+
if (process.env.AGENTS_SKIP_MIGRATION !== '1' && !helpOrVersionRequested) {
|
|
1127
|
+
try {
|
|
1128
|
+
const sentinel = getMigratedSentinelPath();
|
|
1129
|
+
// Sentinel is keyed to the migration SCHEMA version, not the binary version.
|
|
1130
|
+
// Bumping the suffix re-runs migrations for every user; binary releases that
|
|
1131
|
+
// don't change the schema must NOT re-run (they would destroy user content
|
|
1132
|
+
// when migration steps overlap with user-authored paths). See issue #20.
|
|
1133
|
+
const sentinelValue = 'v19';
|
|
1134
|
+
let needRun = true;
|
|
1135
|
+
try {
|
|
1136
|
+
if (fs.existsSync(sentinel) && fs.readFileSync(sentinel, 'utf-8').trim() === sentinelValue) {
|
|
1137
|
+
needRun = false;
|
|
1138
|
+
}
|
|
1139
|
+
}
|
|
1140
|
+
catch { /* best-effort — fall through to run */ }
|
|
1141
|
+
if (needRun) {
|
|
1142
|
+
const { runMigration } = await import('./lib/migrate.js');
|
|
1143
|
+
await runMigration();
|
|
1144
|
+
try {
|
|
1145
|
+
fs.mkdirSync(path.dirname(sentinel), { recursive: true });
|
|
1146
|
+
fs.writeFileSync(sentinel, sentinelValue);
|
|
1147
|
+
}
|
|
1148
|
+
catch { /* best-effort */ }
|
|
1149
|
+
}
|
|
1150
|
+
}
|
|
1151
|
+
catch { /* migration must never block CLI startup */ }
|
|
1152
|
+
}
|
|
1153
|
+
// Auto-enable the macOS menu-bar helper once, for every user. Best-effort and
|
|
1154
|
+
// idempotent: installMenubarLaunchAgentOnUpgrade() no-ops when not on darwin,
|
|
1155
|
+
// when the user ran `agents menubar disable` (sticky opt-out), when the service
|
|
1156
|
+
// is already installed, or when no helper bundle ships with this build. This is
|
|
1157
|
+
// a lightweight startup self-heal (two existsSync checks then return) rather
|
|
1158
|
+
// than a migration-sentinel bump, so it covers fresh installs AND upgrades
|
|
1159
|
+
// without re-running the full migration for the whole user base (issue #20).
|
|
1160
|
+
// Skipped for --help/--version: those are pure documentation paths, so they
|
|
1161
|
+
// pay neither the dynamic import (child_process, the version/layout resolver,
|
|
1162
|
+
// the bundle installer) nor the self-heal's filesystem checks — same gate the
|
|
1163
|
+
// update check, background sync, and ensureInitialized above already use.
|
|
1164
|
+
if (process.platform === 'darwin' &&
|
|
1165
|
+
process.env.AGENTS_SKIP_MIGRATION !== '1' &&
|
|
1166
|
+
!helpOrVersionRequested) {
|
|
1167
|
+
try {
|
|
1168
|
+
const { installMenubarLaunchAgentOnUpgrade } = await import('./lib/menubar/install-menubar.js');
|
|
1169
|
+
installMenubarLaunchAgentOnUpgrade();
|
|
1170
|
+
}
|
|
1171
|
+
catch { /* never block CLI startup on the menu bar */ }
|
|
1172
|
+
}
|
|
1173
|
+
// Bare invocation prints the root help. Commander only auto-displays help on
|
|
1174
|
+
// an empty parse when subcommands are registered, and the lazy-startup path
|
|
1175
|
+
// registers none for a bare call — without this branch, `agents` exits
|
|
1176
|
+
// silently. Runs after first-run setup and migrations so those still fire;
|
|
1177
|
+
// exits 0 to match `agents --help` (and the pre-fix exit code).
|
|
1178
|
+
if (passedArgs.length === 0) {
|
|
1179
|
+
program.outputHelp();
|
|
1180
|
+
process.exit(0);
|
|
1181
|
+
}
|
|
1182
|
+
try {
|
|
1183
|
+
await maybeBootstrapShimIntegration(requestedCommand, helpOrVersionRequested, verboseStartup);
|
|
1184
|
+
await program.parseAsync();
|
|
1185
|
+
}
|
|
1186
|
+
catch (err) {
|
|
1187
|
+
if (err instanceof Error && err.name === 'ExitPromptError') {
|
|
1188
|
+
process.exit(130);
|
|
1189
|
+
}
|
|
1190
|
+
// Browser-daemon-not-running and CDP-not-reachable surface as typed errors
|
|
1191
|
+
// from src/lib/browser/. Don't dump a Node stacktrace for these — they are
|
|
1192
|
+
// user-actionable, not engineering bugs. See issues #41 and #43.
|
|
1193
|
+
if (err instanceof Error) {
|
|
1194
|
+
const isBrowserDaemonNotRunning = err.name === 'BrowserDaemonNotRunningError';
|
|
1195
|
+
const isBrowserCdpUnreachable = err.name === 'BrowserCdpConnectionError';
|
|
1196
|
+
const isBrowserIpcDown = err.message.startsWith('IPC error:') &&
|
|
1197
|
+
(err.message.includes('ECONNREFUSED') || err.message.includes('ENOENT'));
|
|
1198
|
+
if (isBrowserDaemonNotRunning || isBrowserCdpUnreachable || isBrowserIpcDown) {
|
|
1199
|
+
console.error(err.message);
|
|
1200
|
+
process.exit(1);
|
|
1201
|
+
}
|
|
1202
|
+
// A --host targeting a password-auth device throws this from resolveHost.
|
|
1203
|
+
// It carries an actionable message (switch to key auth);
|
|
1204
|
+
// handling it here covers every resolveHost caller (run, teams,
|
|
1205
|
+
// secrets --host) at the source instead of a catch at each call site.
|
|
1206
|
+
if (err.name === 'DeviceOffloadUnsupportedError') {
|
|
1207
|
+
console.error(err.message);
|
|
1208
|
+
process.exit(1);
|
|
1209
|
+
}
|
|
1210
|
+
}
|
|
1211
|
+
throw err;
|
|
1212
|
+
}
|