@phnx-labs/agents-cli 1.22.35 → 1.22.37
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 +666 -2
- package/README.md +75 -35
- package/dist/bin/agents +0 -0
- package/dist/bootstrap.d.ts +15 -0
- package/dist/bootstrap.js +1211 -0
- package/dist/commands/accounts.d.ts +4 -11
- package/dist/commands/accounts.js +159 -27
- 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 +15 -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 +85 -41
- 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 +4 -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/set.d.ts +1 -1
- package/dist/commands/set.js +2 -2
- 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-share.js +34 -4
- package/dist/commands/setup.d.ts +5 -0
- package/dist/commands/setup.js +26 -0
- package/dist/commands/share.d.ts +67 -0
- package/dist/commands/share.js +275 -12
- 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/ipc.d.ts +30 -0
- package/dist/lib/browser/ipc.js +81 -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 +4 -0
- package/dist/lib/browser/service.js +30 -3
- 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 +19 -0
- 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 +20 -6
- package/dist/lib/git.js +121 -17
- 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 +41 -6
- 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/config.d.ts +4 -0
- package/dist/lib/share/config.js +1 -0
- package/dist/lib/share/provision.d.ts +38 -0
- package/dist/lib/share/provision.js +50 -0
- package/dist/lib/share/publish.d.ts +40 -1
- package/dist/lib/share/publish.js +116 -3
- package/dist/lib/share/worker-template.js +71 -6
- 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 +6 -12
- package/dist/lib/startup/command-registry.js +11 -31
- 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 +320 -0
- package/dist/lib/tmux/orphan-reap.js +644 -0
- package/dist/lib/tmux/session.d.ts +68 -3
- package/dist/lib/tmux/session.js +95 -5
- package/dist/lib/triggers/handlers.d.ts +20 -0
- package/dist/lib/triggers/handlers.js +6 -1
- package/dist/lib/types.d.ts +46 -34
- 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/wallet.d.ts +0 -20
- package/dist/commands/wallet.js +0 -216
- package/dist/commands/worktree.d.ts +0 -19
- package/dist/commands/worktree.js +0 -272
- package/dist/lib/account-labels.d.ts +0 -24
- package/dist/lib/account-labels.js +0 -72
- package/dist/lib/auto-dispatch-linear.d.ts +0 -18
- package/dist/lib/auto-dispatch-linear.js +0 -107
- package/dist/lib/auto-dispatch-provider.d.ts +0 -10
- package/dist/lib/auto-dispatch-provider.js +0 -30
- package/dist/lib/auto-dispatch.d.ts +0 -93
- package/dist/lib/auto-dispatch.js +0 -128
- package/dist/lib/export.d.ts +0 -72
- package/dist/lib/export.js +0 -269
- package/dist/lib/lock.d.ts +0 -93
- package/dist/lib/lock.js +0 -207
- package/dist/lib/wallet/index.d.ts +0 -78
- package/dist/lib/wallet/index.js +0 -253
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One-time migration: fold per-device config from the legacy stores into the
|
|
3
|
+
* central `fleet.devices.<name>.config` block of `~/.agents/agents.yaml`.
|
|
4
|
+
*
|
|
5
|
+
* Only SHARED-visibility keys move — the ones a peer reads. Machine-visibility
|
|
6
|
+
* keys (see lib/config-machine-keys.ts) stay in the per-device doc, which is
|
|
7
|
+
* exactly where the machine-local read path now looks for them.
|
|
8
|
+
*
|
|
9
|
+
* Legacy sources (both pre-unification):
|
|
10
|
+
* - `~/.agents/devices/<name>/agents.yaml` — the `config:` map and the
|
|
11
|
+
* top-level `defaultBrowserProfile:` field. Agent pins (`agents:`,
|
|
12
|
+
* `isolatedAgents:`), `routines:`, and machine-visibility config STAY in the
|
|
13
|
+
* per-device doc — only shared operator config moves.
|
|
14
|
+
* - `~/.agents/.history/devices/auto-launch.json` — AGI EXT auto-launch
|
|
15
|
+
* enabled/preferred flags, becoming `autoLaunchEnabled` /
|
|
16
|
+
* `autoLaunchPreferred` config keys.
|
|
17
|
+
*
|
|
18
|
+
* The fold is ADDITIVE: the central block is written and the legacy stores are
|
|
19
|
+
* left exactly as they were, so a box still on the previous CLI keeps reading
|
|
20
|
+
* what it always read. The central-wins merge makes a re-run a byte no-op.
|
|
21
|
+
*
|
|
22
|
+
* Invoked only from a lifecycle entry point — `runMigration()` (fresh installs)
|
|
23
|
+
* and daemon boot. It must NEVER hang off a config read or write: agents.yaml is
|
|
24
|
+
* tracked and shared by the whole fleet, so a migration on the read path lets an
|
|
25
|
+
* ordinary `agents config get` dirty it on every machine.
|
|
26
|
+
*/
|
|
27
|
+
import type { FleetDeviceOverride, FleetManifest } from '../fleet/types.js';
|
|
28
|
+
/**
|
|
29
|
+
* The `fleet.devices` map a config write should edit. A missing block starts
|
|
30
|
+
* empty; the literal `'all'` upgrades to an explicit map of the
|
|
31
|
+
* currently-registered roster so a config write never silently narrows what
|
|
32
|
+
* `agents apply` targets. (The dynamic "new devices join automatically"
|
|
33
|
+
* semantics of `'all'` do not survive the upgrade — per-device config is
|
|
34
|
+
* inherently a map.)
|
|
35
|
+
*/
|
|
36
|
+
export declare function fleetDevicesMapForWrite(fleet: FleetManifest | undefined): Record<string, FleetDeviceOverride>;
|
|
37
|
+
/**
|
|
38
|
+
* Fold the legacy per-device config stores into the central
|
|
39
|
+
* `fleet.devices.<name>.config` block. Safe to call on every boot / config
|
|
40
|
+
* access: cheap no-op once the legacy stores are stripped.
|
|
41
|
+
*/
|
|
42
|
+
export declare function migrateDeviceConfigToCentral(): void;
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One-time migration: fold per-device config from the legacy stores into the
|
|
3
|
+
* central `fleet.devices.<name>.config` block of `~/.agents/agents.yaml`.
|
|
4
|
+
*
|
|
5
|
+
* Only SHARED-visibility keys move — the ones a peer reads. Machine-visibility
|
|
6
|
+
* keys (see lib/config-machine-keys.ts) stay in the per-device doc, which is
|
|
7
|
+
* exactly where the machine-local read path now looks for them.
|
|
8
|
+
*
|
|
9
|
+
* Legacy sources (both pre-unification):
|
|
10
|
+
* - `~/.agents/devices/<name>/agents.yaml` — the `config:` map and the
|
|
11
|
+
* top-level `defaultBrowserProfile:` field. Agent pins (`agents:`,
|
|
12
|
+
* `isolatedAgents:`), `routines:`, and machine-visibility config STAY in the
|
|
13
|
+
* per-device doc — only shared operator config moves.
|
|
14
|
+
* - `~/.agents/.history/devices/auto-launch.json` — AGI EXT auto-launch
|
|
15
|
+
* enabled/preferred flags, becoming `autoLaunchEnabled` /
|
|
16
|
+
* `autoLaunchPreferred` config keys.
|
|
17
|
+
*
|
|
18
|
+
* The fold is ADDITIVE: the central block is written and the legacy stores are
|
|
19
|
+
* left exactly as they were, so a box still on the previous CLI keeps reading
|
|
20
|
+
* what it always read. The central-wins merge makes a re-run a byte no-op.
|
|
21
|
+
*
|
|
22
|
+
* Invoked only from a lifecycle entry point — `runMigration()` (fresh installs)
|
|
23
|
+
* and daemon boot. It must NEVER hang off a config read or write: agents.yaml is
|
|
24
|
+
* tracked and shared by the whole fleet, so a migration on the read path lets an
|
|
25
|
+
* ordinary `agents config get` dirty it on every machine.
|
|
26
|
+
*/
|
|
27
|
+
import * as fs from 'fs';
|
|
28
|
+
import * as path from 'path';
|
|
29
|
+
import * as yaml from 'yaml';
|
|
30
|
+
import { getDevicesAutoLaunchPath, getUserAgentsDir, updateMeta, } from '../state.js';
|
|
31
|
+
import { loadDevicesSync } from './registry.js';
|
|
32
|
+
import { MACHINE_LOCAL_YAML_KEYS } from '../config-machine-keys.js';
|
|
33
|
+
/**
|
|
34
|
+
* The `fleet.devices` map a config write should edit. A missing block starts
|
|
35
|
+
* empty; the literal `'all'` upgrades to an explicit map of the
|
|
36
|
+
* currently-registered roster so a config write never silently narrows what
|
|
37
|
+
* `agents apply` targets. (The dynamic "new devices join automatically"
|
|
38
|
+
* semantics of `'all'` do not survive the upgrade — per-device config is
|
|
39
|
+
* inherently a map.)
|
|
40
|
+
*/
|
|
41
|
+
export function fleetDevicesMapForWrite(fleet) {
|
|
42
|
+
if (fleet && fleet.devices !== 'all')
|
|
43
|
+
return { ...fleet.devices };
|
|
44
|
+
if (fleet?.devices !== 'all')
|
|
45
|
+
return {};
|
|
46
|
+
// devices === 'all': expand to the registered roster.
|
|
47
|
+
const roster = loadDevicesSync();
|
|
48
|
+
const devices = {};
|
|
49
|
+
for (const name of Object.keys(roster))
|
|
50
|
+
devices[name] = {};
|
|
51
|
+
return devices;
|
|
52
|
+
}
|
|
53
|
+
function mergeFold(folds, device, values) {
|
|
54
|
+
folds[device] = { ...folds[device], ...values };
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Read every legacy device doc, returning the config to fold per device.
|
|
58
|
+
* A doc that fails to parse is LOUDLY skipped (and left untouched) so the next
|
|
59
|
+
* run retries — never silently emptied, same corruption contract the device
|
|
60
|
+
* registry keeps.
|
|
61
|
+
*/
|
|
62
|
+
function collectDeviceDocFolds(devicesRoot) {
|
|
63
|
+
const folds = {};
|
|
64
|
+
const docs = [];
|
|
65
|
+
let entries;
|
|
66
|
+
try {
|
|
67
|
+
entries = fs.readdirSync(devicesRoot, { withFileTypes: true });
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
return { folds, docs }; // no devices/ tree — nothing to fold
|
|
71
|
+
}
|
|
72
|
+
for (const entry of entries) {
|
|
73
|
+
if (!entry.isDirectory())
|
|
74
|
+
continue;
|
|
75
|
+
const docPath = path.join(devicesRoot, entry.name, 'agents.yaml');
|
|
76
|
+
if (!fs.existsSync(docPath))
|
|
77
|
+
continue;
|
|
78
|
+
let parsed;
|
|
79
|
+
try {
|
|
80
|
+
parsed = yaml.parse(fs.readFileSync(docPath, 'utf-8'));
|
|
81
|
+
}
|
|
82
|
+
catch (err) {
|
|
83
|
+
console.error(`device config migration: could not parse ${docPath} (${err.message}); leaving it for a later retry`);
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
if (parsed === null || parsed === undefined)
|
|
87
|
+
continue; // empty doc
|
|
88
|
+
if (typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
89
|
+
console.error(`device config migration: ${docPath} is not a YAML map; leaving it for manual repair`);
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
const doc = parsed;
|
|
93
|
+
const config = {};
|
|
94
|
+
if (doc.config !== undefined) {
|
|
95
|
+
if (typeof doc.config !== 'object' || doc.config === null || Array.isArray(doc.config)) {
|
|
96
|
+
console.error(`device config migration: ${docPath} has a non-map config: block; leaving it for manual repair`);
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
Object.assign(config, doc.config);
|
|
100
|
+
}
|
|
101
|
+
if (typeof doc.defaultBrowserProfile === 'string') {
|
|
102
|
+
config.defaultBrowserProfile = doc.defaultBrowserProfile;
|
|
103
|
+
}
|
|
104
|
+
// Machine-visibility keys are NOT folded. Two reasons, both load-bearing:
|
|
105
|
+
//
|
|
106
|
+
// 1. They already live exactly where they belong. `config:` in this doc is
|
|
107
|
+
// the machine-local store the new read path uses (state.ts overlays it
|
|
108
|
+
// as `deviceConfig`), so folding would only copy them into the shared
|
|
109
|
+
// file this whole change exists to keep clean.
|
|
110
|
+
// 2. Copying a PEER's would be a leak. `browserRemoteControl` is a consent
|
|
111
|
+
// flag gating whether other machines may drive that box's browser;
|
|
112
|
+
// hoisting one box's opt-in into the synced file spreads it to the fleet.
|
|
113
|
+
// Each machine keeps its own, in its own doc.
|
|
114
|
+
for (const key of Object.keys(config)) {
|
|
115
|
+
if (MACHINE_LOCAL_YAML_KEYS.has(key))
|
|
116
|
+
delete config[key];
|
|
117
|
+
}
|
|
118
|
+
if (Object.keys(config).length === 0)
|
|
119
|
+
continue;
|
|
120
|
+
mergeFold(folds, entry.name, config);
|
|
121
|
+
docs.push({ name: entry.name, path: docPath, doc });
|
|
122
|
+
}
|
|
123
|
+
return { folds, docs };
|
|
124
|
+
}
|
|
125
|
+
/** Read the legacy auto-launch.json, returning the flags to fold per device. */
|
|
126
|
+
function collectAutoLaunchFolds(autoLaunchPath) {
|
|
127
|
+
const folds = {};
|
|
128
|
+
let raw;
|
|
129
|
+
try {
|
|
130
|
+
raw = fs.readFileSync(autoLaunchPath, 'utf-8');
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
return folds; // absent — nothing to fold
|
|
134
|
+
}
|
|
135
|
+
let parsed;
|
|
136
|
+
try {
|
|
137
|
+
parsed = JSON.parse(raw);
|
|
138
|
+
}
|
|
139
|
+
catch (err) {
|
|
140
|
+
console.error(`device config migration: could not parse ${autoLaunchPath} (${err.message}); leaving it for a later retry`);
|
|
141
|
+
return folds;
|
|
142
|
+
}
|
|
143
|
+
const devices = parsed?.devices;
|
|
144
|
+
if (devices && typeof devices === 'object' && !Array.isArray(devices)) {
|
|
145
|
+
for (const [name, pref] of Object.entries(devices)) {
|
|
146
|
+
const config = {};
|
|
147
|
+
if (pref?.enabled === false)
|
|
148
|
+
config.autoLaunchEnabled = false;
|
|
149
|
+
if (pref?.preferred === true)
|
|
150
|
+
config.autoLaunchPreferred = true;
|
|
151
|
+
if (Object.keys(config).length > 0)
|
|
152
|
+
mergeFold(folds, name, config);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return folds;
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Fold the legacy per-device config stores into the central
|
|
159
|
+
* `fleet.devices.<name>.config` block. Safe to call on every boot / config
|
|
160
|
+
* access: cheap no-op once the legacy stores are stripped.
|
|
161
|
+
*/
|
|
162
|
+
export function migrateDeviceConfigToCentral() {
|
|
163
|
+
const devicesRoot = path.join(getUserAgentsDir(), 'devices');
|
|
164
|
+
const autoLaunchPath = getDevicesAutoLaunchPath();
|
|
165
|
+
const { folds: docFolds, docs } = collectDeviceDocFolds(devicesRoot);
|
|
166
|
+
const autoFolds = collectAutoLaunchFolds(autoLaunchPath);
|
|
167
|
+
const folds = {};
|
|
168
|
+
for (const [name, config] of Object.entries(docFolds))
|
|
169
|
+
mergeFold(folds, name, config);
|
|
170
|
+
for (const [name, config] of Object.entries(autoFolds))
|
|
171
|
+
mergeFold(folds, name, config);
|
|
172
|
+
if (Object.keys(folds).length === 0)
|
|
173
|
+
return;
|
|
174
|
+
// 1. Central write FIRST. A `fleet.devices: all` declaration upgrades to an
|
|
175
|
+
// explicit map of the currently-registered roster so `agents apply` keeps
|
|
176
|
+
// targeting the same devices. A key already present centrally WINS over
|
|
177
|
+
// the legacy value — a re-fold after a mid-migration crash is then a
|
|
178
|
+
// no-op, and a value written by a newer CLI is never clobbered.
|
|
179
|
+
updateMeta((m) => {
|
|
180
|
+
const devices = fleetDevicesMapForWrite(m.fleet);
|
|
181
|
+
for (const [name, config] of Object.entries(folds)) {
|
|
182
|
+
const prev = devices[name] ?? {};
|
|
183
|
+
devices[name] = { ...prev, config: { ...config, ...prev.config } };
|
|
184
|
+
}
|
|
185
|
+
const fleet = { ...m.fleet, devices };
|
|
186
|
+
return { ...m, fleet };
|
|
187
|
+
});
|
|
188
|
+
// 2. The legacy stores are deliberately LEFT IN PLACE.
|
|
189
|
+
//
|
|
190
|
+
// This migration used to delete them — `fs.rmSync` on the device doc and
|
|
191
|
+
// `fs.rmdirSync` on its directory. Deleting is what made the fold unsafe
|
|
192
|
+
// to run anywhere: it had to win a race against every other machine's copy
|
|
193
|
+
// of the same shared file, and a box on an older CLI that re-created the
|
|
194
|
+
// doc would be stripped again on the next command.
|
|
195
|
+
//
|
|
196
|
+
// An additive fold has neither problem. The central block is written; the
|
|
197
|
+
// source is untouched; a box still running the previous CLI keeps reading
|
|
198
|
+
// what it always read. The now-redundant legacy copy is pruned in a later
|
|
199
|
+
// release by one explicit operator command, not by 13 machines each
|
|
200
|
+
// deciding to delete on their own schedule.
|
|
201
|
+
void docs;
|
|
202
|
+
void autoLaunchPath;
|
|
203
|
+
}
|
|
@@ -22,6 +22,7 @@ import { homeRemainder, remoteCdPrefix } from '../project-root.js';
|
|
|
22
22
|
import { getCacheDir } from '../state.js';
|
|
23
23
|
import { hostKeyCheckingOpts } from './known-hosts.js';
|
|
24
24
|
import { hostNameFor } from './ssh-config.js';
|
|
25
|
+
import { resolveDeviceProfile } from './resolve-profile.js';
|
|
25
26
|
/** Env var the askpass shim reads to know which bundle holds the password. */
|
|
26
27
|
export const ASKPASS_BUNDLE_ENV = 'AGENTS_SSH_BUNDLE';
|
|
27
28
|
/** Env var the askpass shim reads to know which key in the bundle is the password. */
|
|
@@ -39,11 +40,12 @@ export const ASKPASS_AGENT_ONLY_ENV = 'AGENTS_SSH_AGENT_ONLY';
|
|
|
39
40
|
* it against the shared injection guard. Throws if the device has no address.
|
|
40
41
|
*/
|
|
41
42
|
export function sshTargetFor(device) {
|
|
42
|
-
const
|
|
43
|
+
const resolved = resolveDeviceProfile(device);
|
|
44
|
+
const host = hostNameFor(resolved);
|
|
43
45
|
if (!host) {
|
|
44
|
-
throw new Error(`Device '${
|
|
46
|
+
throw new Error(`Device '${resolved.name}' has no address (dnsName/ip). Run \`agents devices sync\` or \`agents devices add\`.`);
|
|
45
47
|
}
|
|
46
|
-
const target =
|
|
48
|
+
const target = resolved.user ? `${resolved.user}@${host}` : host;
|
|
47
49
|
assertValidSshTarget(target);
|
|
48
50
|
return target;
|
|
49
51
|
}
|
|
@@ -64,7 +66,8 @@ export function fleetDialTarget(device) {
|
|
|
64
66
|
return sshTargetFor(device);
|
|
65
67
|
}
|
|
66
68
|
catch {
|
|
67
|
-
|
|
69
|
+
const resolved = resolveDeviceProfile(device);
|
|
70
|
+
return resolved.user ? `${resolved.user}@${resolved.name}` : resolved.name;
|
|
68
71
|
}
|
|
69
72
|
}
|
|
70
73
|
/**
|
|
@@ -127,8 +130,9 @@ export function buildInteractiveShellCommand(device, mirrorCwd) {
|
|
|
127
130
|
}
|
|
128
131
|
/** OpenSSH argv that makes an explicit device key authoritative. */
|
|
129
132
|
export function deviceIdentityArgs(device) {
|
|
130
|
-
|
|
131
|
-
|
|
133
|
+
const resolved = resolveDeviceProfile(device);
|
|
134
|
+
return resolved.auth?.method === 'key' && resolved.auth.identityFile
|
|
135
|
+
? ['-i', resolved.auth.identityFile, '-o', 'IdentitiesOnly=yes']
|
|
132
136
|
: [];
|
|
133
137
|
}
|
|
134
138
|
/**
|
|
@@ -154,6 +158,9 @@ export function deviceIdentityArgs(device) {
|
|
|
154
158
|
* behavior unchanged (RUSH-2412).
|
|
155
159
|
*/
|
|
156
160
|
export function buildSshInvocation(device, cmd, askpassShimPath, hostKey = {}, opts = {}) {
|
|
161
|
+
// The effective profile: central config (ssh.*/platform/user) overlaid on
|
|
162
|
+
// the registry's discovery record.
|
|
163
|
+
device = resolveDeviceProfile(device);
|
|
157
164
|
const target = sshTargetFor(device);
|
|
158
165
|
// No cmd ⇒ interactive login. It may still carry a derived cd+login-shell
|
|
159
166
|
// wrapper (interactiveCwd), which is an interactive login too and still needs
|
|
@@ -169,7 +176,7 @@ export function buildSshInvocation(device, cmd, askpassShimPath, hostKey = {}, o
|
|
|
169
176
|
];
|
|
170
177
|
if (device.auth.method === 'password') {
|
|
171
178
|
if (!device.auth.bundle) {
|
|
172
|
-
throw new Error(`Device '${device.name}' uses password auth but has no secrets bundle. Set one with \`agents devices
|
|
179
|
+
throw new Error(`Device '${device.name}' uses password auth but has no secrets bundle. Set one with \`agents devices config ${device.name} ssh.bundle <name>\`.`);
|
|
173
180
|
}
|
|
174
181
|
env.SSH_ASKPASS = askpassShimPath;
|
|
175
182
|
env.SSH_ASKPASS_REQUIRE = 'force';
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { AgentId } from '../types.js';
|
|
2
2
|
import type { DuplicateVersionHook } from '../hooks.js';
|
|
3
|
+
import type { AgentsBinaryShadow } from '../binary-shadow.js';
|
|
3
4
|
import type { RcSecretFinding } from '../secrets/rc-hygiene.js';
|
|
4
5
|
import type { OwnerSinkStatus } from '../channels/owner-sink.js';
|
|
5
6
|
import { type WindowsSshEnrollmentAudit } from './windows-ssh-enrollment.js';
|
|
@@ -12,7 +13,7 @@ export type FindingSeverity = 'critical' | 'warning';
|
|
|
12
13
|
* the JSON consumer group by kind. */
|
|
13
14
|
/** Every finding class. Severity is NOT annotated here — {@link FINDING_SEVERITY}
|
|
14
15
|
* below owns it, and a second copy in these comments is a fourth place to drift. */
|
|
15
|
-
export declare const ALL_FINDING_KINDS: readonly ["logged-out", "logout-unprovable", "missing-hook", "missing-plugin", "unwired-hook", "hook-runtime-broken", "hook-runtime-visibility-unavailable", "cli-missing", "missing-resource", "content-drift", "never-synced", "stale", "repo-behind", "repo-drift", "fleet-resource-gap", "host-cli-missing", "host-cli-invalid", "version-skew", "orphan", "duplicate-hook", "duplicate-hook-drift", "rc-secret-export", "env-secret-export", "exec-policy", "ssh-key-enrollment", "stale-cli", "owner-sink-unreachable"];
|
|
16
|
+
export declare const ALL_FINDING_KINDS: readonly ["logged-out", "logout-unprovable", "missing-hook", "missing-plugin", "unwired-hook", "hook-runtime-broken", "hook-runtime-visibility-unavailable", "cli-missing", "missing-resource", "content-drift", "never-synced", "stale", "repo-behind", "repo-drift", "fleet-resource-gap", "host-cli-missing", "host-cli-invalid", "version-skew", "orphan", "duplicate-hook", "duplicate-hook-drift", "rc-secret-export", "env-secret-export", "exec-policy", "ssh-key-enrollment", "stale-cli", "binary-shadow", "owner-sink-unreachable"];
|
|
16
17
|
/**
|
|
17
18
|
* The severity each kind is emitted with - the SINGLE source of truth, read by
|
|
18
19
|
* the builders below and asserted against both prose rubrics by
|
|
@@ -117,6 +118,8 @@ export interface LocalFindingInputs {
|
|
|
117
118
|
* `rush` transport, so it stays out of this pure module). Absent → no probe ran
|
|
118
119
|
* → no finding. */
|
|
119
120
|
ownerSink?: OwnerSinkStatus;
|
|
121
|
+
/** `agents` binaries that shadow the currently running CLI (RUSH-2431). */
|
|
122
|
+
binaryShadows?: AgentsBinaryShadow[];
|
|
120
123
|
}
|
|
121
124
|
/**
|
|
122
125
|
* Fold this machine's signals into findings. Missing hooks/plugins and unwired
|
|
@@ -26,7 +26,8 @@
|
|
|
26
26
|
* never-synced · stale · repo-behind · repo-drift · version-skew ·
|
|
27
27
|
* fleet-resource-gap · hook-runtime-visibility-unavailable · orphan · duplicate-hook ·
|
|
28
28
|
* duplicate-hook-drift · host-cli-missing · host-cli-invalid ·
|
|
29
|
-
* rc-secret-export · env-secret-export · exec-policy · stale-cli
|
|
29
|
+
* rc-secret-export · env-secret-export · exec-policy · stale-cli ·
|
|
30
|
+
* binary-shadow.
|
|
30
31
|
* (RUSH-2162 moved never-synced and duplicate-hook-drift to WARNING: both are
|
|
31
32
|
* stale-sync states one `agents sync` resolves, not "needs you now".)
|
|
32
33
|
*
|
|
@@ -104,6 +105,7 @@ export const ALL_FINDING_KINDS = [
|
|
|
104
105
|
'exec-policy', // Windows execution policy blocks agents.ps1
|
|
105
106
|
'ssh-key-enrollment', // Windows OpenSSH public-key path/content/ACL is invalid
|
|
106
107
|
'stale-cli',
|
|
108
|
+
'binary-shadow', // another agents binary on PATH or in a well-known dir shadows the running copy
|
|
107
109
|
'owner-sink-unreachable', // the feed/notify owner-delivery lane can't reach the owner from this box
|
|
108
110
|
];
|
|
109
111
|
/**
|
|
@@ -151,6 +153,7 @@ export const FINDING_SEVERITY = {
|
|
|
151
153
|
'exec-policy': 'warning',
|
|
152
154
|
'ssh-key-enrollment': 'critical',
|
|
153
155
|
'stale-cli': 'warning',
|
|
156
|
+
'binary-shadow': 'warning',
|
|
154
157
|
};
|
|
155
158
|
function agentName(agent) {
|
|
156
159
|
return AGENT_NAMES[agent] || agent;
|
|
@@ -272,6 +275,8 @@ export function remediationFor(finding) {
|
|
|
272
275
|
return 'repair the reported AuthorizedKeysFile, then rerun agents doctor';
|
|
273
276
|
case 'stale-cli':
|
|
274
277
|
return 'upgrade';
|
|
278
|
+
case 'binary-shadow':
|
|
279
|
+
return 'remove or repoint the shadowing agents install(s)';
|
|
275
280
|
case 'owner-sink-unreachable':
|
|
276
281
|
// The lane delivers over the rush-backed owner channel, which needs rush on
|
|
277
282
|
// PATH AND a usable session in THIS context. Non-interactive shells miss a
|
|
@@ -531,6 +536,18 @@ export function buildLocalFindings(input) {
|
|
|
531
536
|
// (warning). Signed-in versions produce no finding — the accounts line shows
|
|
532
537
|
// them. Agents that can't be inspected never yield a logout finding.
|
|
533
538
|
out.push(...signInToFindings(device, input.signIn));
|
|
539
|
+
// agents binary shadows (warning) — another install could be resolved by a
|
|
540
|
+
// scheduled routine or by a different shell PATH, running stale code.
|
|
541
|
+
const shadows = input.binaryShadows ?? [];
|
|
542
|
+
if (shadows.length > 0) {
|
|
543
|
+
const examples = shadows.slice(0, 2).map((s) => `${s.path}${s.version ? ` (${s.version})` : ''}`).join(', ');
|
|
544
|
+
out.push(finding({
|
|
545
|
+
severity: FINDING_SEVERITY['binary-shadow'], kind: 'binary-shadow', device,
|
|
546
|
+
message: shadows.length === 1
|
|
547
|
+
? `agents binary shadowed by ${examples}`
|
|
548
|
+
: `${shadows.length} agents binaries may shadow the running copy (incl. ${examples})`,
|
|
549
|
+
}));
|
|
550
|
+
}
|
|
534
551
|
return collapseAcrossVersions(out, new Set(input.isolatedVersions ?? []));
|
|
535
552
|
}
|
|
536
553
|
/**
|
|
@@ -1013,6 +1030,8 @@ function warningSubject(f) {
|
|
|
1013
1030
|
// so a self-referential subject reads as the local machine in fleet mode.
|
|
1014
1031
|
if (f.kind === 'stale-cli')
|
|
1015
1032
|
return 'agents-cli';
|
|
1033
|
+
if (f.kind === 'binary-shadow')
|
|
1034
|
+
return 'agents-cli';
|
|
1016
1035
|
if (f.kind === 'orphan')
|
|
1017
1036
|
return 'orphans';
|
|
1018
1037
|
if (f.kind === 'rc-secret-export')
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type AccountInfo } from '../agents.js';
|
|
1
2
|
import type { AgentId } from '../types.js';
|
|
2
3
|
import { type UsageSnapshot } from '../usage.js';
|
|
3
4
|
/** An account's usage headroom rolled into one glanceable summary. */
|
|
@@ -6,11 +7,19 @@ export interface QuotaSummary {
|
|
|
6
7
|
* `available` / `rate_limited` from the live usage windows, or `null` when
|
|
7
8
|
* there is no snapshot at all (no data yet, or the agent has no usage source).
|
|
8
9
|
*/
|
|
9
|
-
status: 'available' | 'rate_limited' | null;
|
|
10
|
+
status: 'available' | 'rate_limited' | 'out_of_credits' | null;
|
|
11
|
+
/** Canonical launch verdict. Kept separate from utilization for JSON clients. */
|
|
12
|
+
verdict: 'available' | 'rate_limited' | 'out_of_credits' | 'unavailable';
|
|
10
13
|
/** Max utilization across blocking windows (0-100, rounded), or null when unknown. */
|
|
11
14
|
usedPercent: number | null;
|
|
12
15
|
/** True when the snapshot is a cached `last_seen`, not a fresh `live` read. */
|
|
13
16
|
stale: boolean;
|
|
17
|
+
/** ISO-8601 timestamp for the underlying usage observation. */
|
|
18
|
+
capturedAt: string | null;
|
|
19
|
+
/** Earliest reset across blocking windows, ISO-8601. */
|
|
20
|
+
resetsAt: string | null;
|
|
21
|
+
/** Why quota could not be evaluated; null when a verdict is available. */
|
|
22
|
+
unavailableReason: string | null;
|
|
14
23
|
}
|
|
15
24
|
/** One installed (agent, version) on a host, fully resolved. */
|
|
16
25
|
export interface HarnessRow {
|
|
@@ -55,7 +64,7 @@ export interface AccountGroup {
|
|
|
55
64
|
* `sonnet_week` sub-limit is excluded so hitting it doesn't read as a throttled
|
|
56
65
|
* account) and takes the highest utilization across those windows. Pure.
|
|
57
66
|
*/
|
|
58
|
-
export declare function summarizeQuota(snapshot: UsageSnapshot | null | undefined): QuotaSummary;
|
|
67
|
+
export declare function summarizeQuota(snapshot: UsageSnapshot | null | undefined, unavailableReason?: string | null, accountStatus?: AccountInfo['usageStatus']): QuotaSummary;
|
|
59
68
|
/**
|
|
60
69
|
* "Ready" = signed in AND not rate-limited. A missing quota snapshot does NOT
|
|
61
70
|
* block readiness: the account is signed in and usable, we just have no live
|
|
Binary file
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* of blocking the whole table.
|
|
9
9
|
*
|
|
10
10
|
* The parsers are pure and unit-tested (health.test.ts). They mirror the ones in
|
|
11
|
-
*
|
|
11
|
+
* AGI EXT (apps/ext/src/core/deviceHealth.ts) — kept as a
|
|
12
12
|
* separate copy on purpose: the CLI does not import across packages.
|
|
13
13
|
*/
|
|
14
14
|
import type { DeviceProfile } from './registry.js';
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* of blocking the whole table.
|
|
9
9
|
*
|
|
10
10
|
* The parsers are pure and unit-tested (health.test.ts). They mirror the ones in
|
|
11
|
-
*
|
|
11
|
+
* AGI EXT (apps/ext/src/core/deviceHealth.ts) — kept as a
|
|
12
12
|
* separate copy on purpose: the CLI does not import across packages.
|
|
13
13
|
*/
|
|
14
14
|
import { execFile } from 'child_process';
|
|
@@ -2,14 +2,35 @@ export interface PendingDevice {
|
|
|
2
2
|
name: string;
|
|
3
3
|
platform: string;
|
|
4
4
|
}
|
|
5
|
+
/**
|
|
6
|
+
* Drop pending sentinels for devices that are already registered or ignored.
|
|
7
|
+
* Safe without a live tailscale probe — only removes names we know are no
|
|
8
|
+
* longer "new". Used when soft-fail discovery cannot recompute the full
|
|
9
|
+
* pending set, so a test leak or race cannot leave registered fleet boxes in
|
|
10
|
+
* the menu bar's NEW DEVICES section forever.
|
|
11
|
+
*/
|
|
12
|
+
export declare function pruneDismissedPendingSentinels(): Promise<void>;
|
|
5
13
|
/**
|
|
6
14
|
* Make the sentinel dir exactly match `pending`: create a file per pending
|
|
7
15
|
* device (content = platform), and delete any leftover sentinel whose device is
|
|
8
16
|
* no longer pending (it got registered, ignored, or left the tailnet). Best-
|
|
9
17
|
* effort — a filesystem error here must never crash the daemon, so callers pass
|
|
10
18
|
* this through their existing try/catch.
|
|
19
|
+
*
|
|
20
|
+
* The sentinel writer is the single authoritative choke point every discovery
|
|
21
|
+
* path flows through (the daemon device-probe timer and `agents sync`), so it
|
|
22
|
+
* re-subtracts the persisted ignore-list AND the registered roster rather than
|
|
23
|
+
* trusting the caller's `pending` set. That closes two races:
|
|
24
|
+
* - probe/ignore (RUSH-2495): a probe that computed `pending` BEFORE the user
|
|
25
|
+
* pressed "Ignore" would otherwise re-create the just-dismissed device's
|
|
26
|
+
* sentinel and the menu bar would re-surface it.
|
|
27
|
+
* - registry-empty pollution: a process that had AGENTS_DEVICES_DIR redirected
|
|
28
|
+
* (tests / hermetic runs) but still wrote the live devices-pending dir
|
|
29
|
+
* treated every tailnet node as new — including already-registered fleet
|
|
30
|
+
* boxes. Re-subtracting the live registry means those phantoms are never
|
|
31
|
+
* written and are removed if they already exist.
|
|
11
32
|
*/
|
|
12
|
-
export declare function reconcilePendingSentinels(pending: PendingDevice[]): void
|
|
33
|
+
export declare function reconcilePendingSentinels(pending: PendingDevice[]): Promise<void>;
|
|
13
34
|
/** Remove one device's pending sentinel (after the user registers or ignores it).
|
|
14
35
|
* No-op if it doesn't exist. */
|
|
15
36
|
export declare function clearPendingSentinel(name: string): void;
|
|
@@ -16,21 +16,86 @@
|
|
|
16
16
|
import * as fs from 'fs';
|
|
17
17
|
import * as path from 'path';
|
|
18
18
|
import { getDevicesPendingDir } from '../state.js';
|
|
19
|
+
import { loadDevices, loadIgnored } from './registry.js';
|
|
19
20
|
/** Device-name sentinels must be safe filenames (no path traversal). The device
|
|
20
21
|
* name charset is already the ssh-alias set, but guard defensively. */
|
|
21
22
|
function isSafeName(name) {
|
|
22
23
|
return /^[a-zA-Z0-9][a-zA-Z0-9._-]*$/.test(name);
|
|
23
24
|
}
|
|
25
|
+
/**
|
|
26
|
+
* Best-effort load of names that must never appear as "NEW DEVICES": the
|
|
27
|
+
* ignore-list and the registered roster. A corrupted registry/ignore file
|
|
28
|
+
* (both throw by design) must not crash the daemon loop — treat it as empty
|
|
29
|
+
* and let the next probe recover once the file is fixed.
|
|
30
|
+
*/
|
|
31
|
+
async function loadDismissedNames() {
|
|
32
|
+
const out = new Set();
|
|
33
|
+
try {
|
|
34
|
+
for (const name of await loadIgnored())
|
|
35
|
+
out.add(name);
|
|
36
|
+
}
|
|
37
|
+
catch { /* treat as nothing ignored */ }
|
|
38
|
+
try {
|
|
39
|
+
for (const name of Object.keys(await loadDevices()))
|
|
40
|
+
out.add(name);
|
|
41
|
+
}
|
|
42
|
+
catch { /* treat as nothing registered */ }
|
|
43
|
+
return out;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Drop pending sentinels for devices that are already registered or ignored.
|
|
47
|
+
* Safe without a live tailscale probe — only removes names we know are no
|
|
48
|
+
* longer "new". Used when soft-fail discovery cannot recompute the full
|
|
49
|
+
* pending set, so a test leak or race cannot leave registered fleet boxes in
|
|
50
|
+
* the menu bar's NEW DEVICES section forever.
|
|
51
|
+
*/
|
|
52
|
+
export async function pruneDismissedPendingSentinels() {
|
|
53
|
+
const dir = getDevicesPendingDir();
|
|
54
|
+
const dismissed = await loadDismissedNames();
|
|
55
|
+
if (dismissed.size === 0)
|
|
56
|
+
return;
|
|
57
|
+
let existing;
|
|
58
|
+
try {
|
|
59
|
+
existing = fs.readdirSync(dir).filter((n) => !n.startsWith('.'));
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
for (const name of existing) {
|
|
65
|
+
if (!dismissed.has(name))
|
|
66
|
+
continue;
|
|
67
|
+
try {
|
|
68
|
+
fs.unlinkSync(path.join(dir, name));
|
|
69
|
+
}
|
|
70
|
+
catch { /* already gone */ }
|
|
71
|
+
}
|
|
72
|
+
}
|
|
24
73
|
/**
|
|
25
74
|
* Make the sentinel dir exactly match `pending`: create a file per pending
|
|
26
75
|
* device (content = platform), and delete any leftover sentinel whose device is
|
|
27
76
|
* no longer pending (it got registered, ignored, or left the tailnet). Best-
|
|
28
77
|
* effort — a filesystem error here must never crash the daemon, so callers pass
|
|
29
78
|
* this through their existing try/catch.
|
|
79
|
+
*
|
|
80
|
+
* The sentinel writer is the single authoritative choke point every discovery
|
|
81
|
+
* path flows through (the daemon device-probe timer and `agents sync`), so it
|
|
82
|
+
* re-subtracts the persisted ignore-list AND the registered roster rather than
|
|
83
|
+
* trusting the caller's `pending` set. That closes two races:
|
|
84
|
+
* - probe/ignore (RUSH-2495): a probe that computed `pending` BEFORE the user
|
|
85
|
+
* pressed "Ignore" would otherwise re-create the just-dismissed device's
|
|
86
|
+
* sentinel and the menu bar would re-surface it.
|
|
87
|
+
* - registry-empty pollution: a process that had AGENTS_DEVICES_DIR redirected
|
|
88
|
+
* (tests / hermetic runs) but still wrote the live devices-pending dir
|
|
89
|
+
* treated every tailnet node as new — including already-registered fleet
|
|
90
|
+
* boxes. Re-subtracting the live registry means those phantoms are never
|
|
91
|
+
* written and are removed if they already exist.
|
|
30
92
|
*/
|
|
31
|
-
export function reconcilePendingSentinels(pending) {
|
|
93
|
+
export async function reconcilePendingSentinels(pending) {
|
|
32
94
|
const dir = getDevicesPendingDir();
|
|
33
|
-
const
|
|
95
|
+
const dismissed = await loadDismissedNames();
|
|
96
|
+
const want = new Map(pending
|
|
97
|
+
.filter((p) => isSafeName(p.name) && !dismissed.has(p.name))
|
|
98
|
+
.map((p) => [p.name, p.platform]));
|
|
34
99
|
// Whole body is best-effort: a filesystem error here must never propagate into
|
|
35
100
|
// the daemon loop or `agents sync`. The top-level mkdir/readdir are guarded
|
|
36
101
|
// too, so no caller needs its own try/catch.
|
|
@@ -158,28 +158,3 @@ export declare function addIgnored(name: string): Promise<Set<string>>;
|
|
|
158
158
|
/** Remove a node name from the ignore-list (un-ignore). Returns false if it was
|
|
159
159
|
* not ignored. */
|
|
160
160
|
export declare function removeIgnored(name: string): Promise<boolean>;
|
|
161
|
-
/**
|
|
162
|
-
* Auto-launch preferences: which registered devices are eligible for Factory's
|
|
163
|
-
* auto-host selection, and which are preferred. Stored as a sibling to the
|
|
164
|
-
* registry and ignore-list under ~/.agents/.history/devices/.
|
|
165
|
-
*/
|
|
166
|
-
export interface AutoLaunchPreference {
|
|
167
|
-
enabled?: boolean;
|
|
168
|
-
preferred?: boolean;
|
|
169
|
-
}
|
|
170
|
-
export interface AutoLaunchPreferences {
|
|
171
|
-
devices: Record<string, AutoLaunchPreference>;
|
|
172
|
-
updatedAt: string;
|
|
173
|
-
}
|
|
174
|
-
/** Load auto-launch preferences. Missing or malformed file => empty map. */
|
|
175
|
-
export declare function loadAutoLaunchPreferences(): Promise<Record<string, AutoLaunchPreference>>;
|
|
176
|
-
/** True if the device is enabled for auto-launch. Missing entry defaults to true. */
|
|
177
|
-
export declare function isAutoLaunchEnabled(name: string): Promise<boolean>;
|
|
178
|
-
/** Set whether a device is enabled for auto-launch. Setting to the default
|
|
179
|
-
* (enabled) removes the entry to keep the file minimal. */
|
|
180
|
-
export declare function setAutoLaunchEnabled(name: string, enabled: boolean): Promise<void>;
|
|
181
|
-
/** True if the device is preferred for auto-launch ranking. */
|
|
182
|
-
export declare function isAutoLaunchPreferred(name: string): Promise<boolean>;
|
|
183
|
-
/** Set whether a device is preferred for auto-launch. Setting to the default
|
|
184
|
-
* (not preferred) removes the flag to keep the file minimal. */
|
|
185
|
-
export declare function setAutoLaunchPreferred(name: string, preferred: boolean): Promise<void>;
|