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