@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
|
@@ -40,6 +40,7 @@ import { SYNC_GET_CMD, SYNC_PING_CMD, SYNC_LOCK_CMD } from './sync-commands.js';
|
|
|
40
40
|
import { MAX_LEASE_MS, MIN_LEASE_MS } from './lease.js';
|
|
41
41
|
import { selectLeasedEnv } from './lease.js';
|
|
42
42
|
import { emitSecretAudit } from './audit.js';
|
|
43
|
+
import { isDaemonServiceEnabled } from '../daemon-services.js';
|
|
43
44
|
// Re-exported so callers already reaching for agent.js keep one obvious home for
|
|
44
45
|
// the scope vocabulary; the definitions live in the leaf module scope.ts because
|
|
45
46
|
// agent.ts and session-store.ts import each other and a cyclic `const` read can
|
|
@@ -50,6 +51,14 @@ export { GLOBAL_HARNESS, bundleScopeChain };
|
|
|
50
51
|
const PROTOCOL_VERSION = 3;
|
|
51
52
|
/** Default lifetime of an unlocked bundle when `--ttl` is not given. */
|
|
52
53
|
export const DEFAULT_TTL_MS = 7 * 24 * 60 * 60 * 1000; // 7d
|
|
54
|
+
/**
|
|
55
|
+
* Whether the secrets broker service is enabled in the daemon service config.
|
|
56
|
+
* Off-darwin this is irrelevant (the broker is never used), but the helper is
|
|
57
|
+
* kept synchronous and safe everywhere so callers can fail loud without a prompt.
|
|
58
|
+
*/
|
|
59
|
+
export function isSecretsBrokerEnabled() {
|
|
60
|
+
return isDaemonServiceEnabled('secrets-broker');
|
|
61
|
+
}
|
|
53
62
|
/**
|
|
54
63
|
* Reserved store-key prefix for the `secrets list` metadata snapshot cache.
|
|
55
64
|
* The broker holds the resolved bundle-metadata array (names/policy/timestamps,
|
|
@@ -361,7 +370,12 @@ export function realBundleCount(store) {
|
|
|
361
370
|
export function scopedBundleKey(name, harness) {
|
|
362
371
|
return `${harness}:${name}`;
|
|
363
372
|
}
|
|
364
|
-
export function handleAgentRequest(store, req, now = Date.now()
|
|
373
|
+
export function handleAgentRequest(store, req, now = Date.now(),
|
|
374
|
+
// Eviction tombstones: `lock` records when each bundle was wiped so a load
|
|
375
|
+
// whose snapshot predates the eviction (a detached auto-load that raced a
|
|
376
|
+
// mutating write) is rejected instead of re-populating the store with a
|
|
377
|
+
// pre-mutation copy. Broker-lifetime state, owned by the caller like `store`.
|
|
378
|
+
evictedAt = new Map()) {
|
|
365
379
|
switch (req.cmd) {
|
|
366
380
|
case 'ping':
|
|
367
381
|
// Report the version of the code this broker is RUNNING (getCliVersion
|
|
@@ -390,6 +404,13 @@ export function handleAgentRequest(store, req, now = Date.now()) {
|
|
|
390
404
|
return { ok: true, cmd: 'get', hit: false };
|
|
391
405
|
}
|
|
392
406
|
case 'load': {
|
|
407
|
+
const evictedTs = evictedAt.get(req.name);
|
|
408
|
+
if (evictedTs !== undefined && req.snapshotAt !== undefined && req.snapshotAt <= evictedTs) {
|
|
409
|
+
// The loader captured its bundle/env before a mutating write evicted
|
|
410
|
+
// this name; accepting it would serve (and later re-persist) stale
|
|
411
|
+
// state. A load with no snapshotAt (older client) is accepted as before.
|
|
412
|
+
return { ok: false, error: `stale load for '${req.name}': snapshot predates an eviction` };
|
|
413
|
+
}
|
|
393
414
|
const harness = req.harness || GLOBAL_HARNESS;
|
|
394
415
|
let env = req.env;
|
|
395
416
|
if (req.lease) {
|
|
@@ -409,9 +430,12 @@ export function handleAgentRequest(store, req, now = Date.now()) {
|
|
|
409
430
|
for (const [key, entry] of store)
|
|
410
431
|
if (entry.bundle.name === req.name && store.delete(key))
|
|
411
432
|
wiped++;
|
|
433
|
+
evictedAt.set(req.name, now);
|
|
412
434
|
return { ok: true, cmd: 'lock', wiped };
|
|
413
435
|
}
|
|
414
436
|
const wiped = store.size;
|
|
437
|
+
for (const entry of store.values())
|
|
438
|
+
evictedAt.set(entry.bundle.name, now);
|
|
415
439
|
store.clear();
|
|
416
440
|
return { ok: true, cmd: 'lock', wiped };
|
|
417
441
|
}
|
|
@@ -721,8 +745,9 @@ export async function runSecretsAgent(opts = {}) {
|
|
|
721
745
|
emptySince = now;
|
|
722
746
|
}
|
|
723
747
|
};
|
|
748
|
+
const evictedAt = new Map();
|
|
724
749
|
const handle = (req) => {
|
|
725
|
-
const resp = handleAgentRequest(store, req);
|
|
750
|
+
const resp = handleAgentRequest(store, req, Date.now(), evictedAt);
|
|
726
751
|
if (realBundleCount(store) > 0)
|
|
727
752
|
emptySince = Date.now();
|
|
728
753
|
return resp;
|
|
@@ -854,7 +879,8 @@ export async function startHostedBroker() {
|
|
|
854
879
|
return null;
|
|
855
880
|
const store = rehydrateStore();
|
|
856
881
|
const sock = socketPath(); // agentDir() creates the 0700 dir as a side effect
|
|
857
|
-
const
|
|
882
|
+
const evictedAt = new Map();
|
|
883
|
+
const handle = (req) => handleAgentRequest(store, req, Date.now(), evictedAt);
|
|
858
884
|
const onConn = makeConnectionHandler(handle, readAgentToken);
|
|
859
885
|
const server = await bindBrokerSocket(sock, onConn);
|
|
860
886
|
if (!server)
|
|
@@ -1039,6 +1065,8 @@ function syncClient(sub, timeout) {
|
|
|
1039
1065
|
* (soft — caller falls through to the real keychain). macOS only.
|
|
1040
1066
|
*/
|
|
1041
1067
|
export function agentGetSync(name, harness = GLOBAL_HARNESS) {
|
|
1068
|
+
if (!isSecretsBrokerEnabled())
|
|
1069
|
+
return null;
|
|
1042
1070
|
if (!agentSocketExists())
|
|
1043
1071
|
return null;
|
|
1044
1072
|
const r = syncClient([SYNC_GET_CMD, name, harness], SYNC_GET_TIMEOUT_MS);
|
|
@@ -1270,10 +1298,15 @@ export function clampHoldMs(v) {
|
|
|
1270
1298
|
* The worker reuses the robust `ensureAgentRunning` path (spawn-then-ping) rather
|
|
1271
1299
|
* than a tight inline retry loop. Best-effort; never throws. macOS only.
|
|
1272
1300
|
*/
|
|
1273
|
-
export function agentAutoLoadSync(name, bundle, env, ttlMs, harness = GLOBAL_HARNESS, lease
|
|
1301
|
+
export function agentAutoLoadSync(name, bundle, env, ttlMs, harness = GLOBAL_HARNESS, lease,
|
|
1302
|
+
// When the caller read the bundle from the keychain — lets the broker
|
|
1303
|
+
// reject this load if an eviction lands in between (tombstones, above).
|
|
1304
|
+
snapshotAt) {
|
|
1274
1305
|
if (!onDarwin())
|
|
1275
1306
|
return;
|
|
1276
|
-
|
|
1307
|
+
if (!isSecretsBrokerEnabled())
|
|
1308
|
+
return;
|
|
1309
|
+
const payload = JSON.stringify({ name, bundle, env, ttlMs, harness, lease, snapshotAt });
|
|
1277
1310
|
// Broker actually LISTENING → deterministic synchronous warm (bounded; the read
|
|
1278
1311
|
// already paid a Touch ID, so <1s here is invisible). We gate on a real liveness
|
|
1279
1312
|
// ping, NOT mere socket-file existence: a broker that died leaving its socket
|
|
@@ -1340,13 +1373,13 @@ export async function runAgentLoadFromStdin() {
|
|
|
1340
1373
|
process.exitCode = 1; // broker couldn't be brought up — did NOT load
|
|
1341
1374
|
return;
|
|
1342
1375
|
}
|
|
1343
|
-
const loaded = await agentLoad(payload.name, payload.bundle, payload.env, payload.ttlMs ?? DEFAULT_TTL_MS, payload.harness ?? GLOBAL_HARNESS, payload.lease);
|
|
1376
|
+
const loaded = await agentLoad(payload.name, payload.bundle, payload.env, payload.ttlMs ?? DEFAULT_TTL_MS, payload.harness ?? GLOBAL_HARNESS, payload.lease, payload.snapshotAt);
|
|
1344
1377
|
if (!loaded)
|
|
1345
1378
|
process.exitCode = 1; // transport failed — did NOT load
|
|
1346
1379
|
}
|
|
1347
1380
|
/** Store a resolved bundle in the broker. Returns false on transport failure. */
|
|
1348
|
-
export async function agentLoad(name, bundle, env, ttlMs, harness = GLOBAL_HARNESS, lease) {
|
|
1349
|
-
const r = await request({ cmd: 'load', name, bundle, env, ttlMs, harness, lease });
|
|
1381
|
+
export async function agentLoad(name, bundle, env, ttlMs, harness = GLOBAL_HARNESS, lease, snapshotAt) {
|
|
1382
|
+
const r = await request({ cmd: 'load', name, bundle, env, ttlMs, harness, lease, snapshotAt });
|
|
1350
1383
|
return r?.ok === true && r.cmd === 'load';
|
|
1351
1384
|
}
|
|
1352
1385
|
/** Wipe one bundle (or all if name omitted) from the broker. Returns the count
|
|
@@ -1390,6 +1423,8 @@ export async function agentPing() {
|
|
|
1390
1423
|
export async function ensureAgentRunning(timeoutMs = 5000) {
|
|
1391
1424
|
if (!onDarwin())
|
|
1392
1425
|
return false;
|
|
1426
|
+
if (!isSecretsBrokerEnabled())
|
|
1427
|
+
return false;
|
|
1393
1428
|
// Self-heal: if a broker is reachable but running pre-upgrade code (its
|
|
1394
1429
|
// reported version != the version on disk now), tear it down so the paths
|
|
1395
1430
|
// below bring up a fresh one on current code. A current, reachable broker is
|
|
@@ -211,6 +211,7 @@ export interface BundleEntryInfo {
|
|
|
211
211
|
detail: string;
|
|
212
212
|
}
|
|
213
213
|
export declare function describeBundle(bundle: SecretsBundle): BundleEntryInfo[];
|
|
214
|
+
export declare function stampLastUsed(bundle: SecretsBundle): void;
|
|
214
215
|
/** Options for resolveBundleEnv. */
|
|
215
216
|
export interface ResolveBundleOptions {
|
|
216
217
|
/**
|
|
@@ -31,7 +31,7 @@ import { emit } from '../events.js';
|
|
|
31
31
|
import { emitSecretAudit } from './audit.js';
|
|
32
32
|
import { readMeta, getHelpersDir } from '../state.js';
|
|
33
33
|
import { assertNameActiveInResourceProfile, filterNamesForActiveResourceProfile } from '../resource-profiles.js';
|
|
34
|
-
import { agentGetSync, agentAutoLoadSync, agentGetMetaSync, agentAutoLoadMetaSync, agentEvictSync, secretsAgentAutoEnabled, secretsHoldMs } from './agent.js';
|
|
34
|
+
import { agentGetSync, agentAutoLoadSync, agentGetMetaSync, agentAutoLoadMetaSync, agentEvictSync, secretsAgentAutoEnabled, secretsHoldMs, isSecretsBrokerEnabled } from './agent.js';
|
|
35
35
|
import { GLOBAL_HARNESS } from './scope.js';
|
|
36
36
|
import { resolveSession, deleteSession } from './session-store.js';
|
|
37
37
|
import { createHash } from 'node:crypto';
|
|
@@ -907,11 +907,20 @@ export function describeBundle(bundle) {
|
|
|
907
907
|
}
|
|
908
908
|
return out;
|
|
909
909
|
}
|
|
910
|
-
// Bump `
|
|
911
|
-
//
|
|
912
|
-
//
|
|
910
|
+
// Bump `last_used` and persist it, but no more than once per throttle window
|
|
911
|
+
// so we don't pay a keychain write on every agent run. Failures are swallowed —
|
|
912
|
+
// usage tracking is never allowed to break secret resolution.
|
|
913
913
|
// Set AGENTS_NO_USAGE_TRACK=1 to disable the stamp entirely (used by tests).
|
|
914
|
-
|
|
914
|
+
//
|
|
915
|
+
// The passed bundle is often the BROKER'S snapshot (this fires on every broker
|
|
916
|
+
// hit), which can be stale — a detached auto-load captured before a mutating
|
|
917
|
+
// write can land after its eviction. Persisting that snapshot wholesale used to
|
|
918
|
+
// write its whole `vars` map back over the authoritative store, resurrecting
|
|
919
|
+
// removed keys (or, for a since-deleted bundle, the bundle itself). The stamp
|
|
920
|
+
// is telemetry: it re-reads the store's own current copy (bundle metadata is a
|
|
921
|
+
// silent no-ACL read) and writes ONLY the timestamp onto that.
|
|
922
|
+
// Exported for regression coverage, like shouldEvictAfterBundleWrite.
|
|
923
|
+
export function stampLastUsed(bundle) {
|
|
915
924
|
if (process.env.AGENTS_NO_USAGE_TRACK)
|
|
916
925
|
return;
|
|
917
926
|
const nowMs = Date.now();
|
|
@@ -921,10 +930,14 @@ function stampLastUsed(bundle) {
|
|
|
921
930
|
return;
|
|
922
931
|
}
|
|
923
932
|
try {
|
|
924
|
-
|
|
933
|
+
const fresh = readBundle(bundle.name); // throws if the bundle is gone — swallowed below
|
|
934
|
+
const stamp = new Date(nowMs).toISOString();
|
|
935
|
+
fresh.last_used = stamp;
|
|
936
|
+
// Keep the caller's (possibly broker-held) copy throttling correctly.
|
|
937
|
+
bundle.last_used = stamp;
|
|
925
938
|
// skipBrokerEviction: this stamp fires on every broker HIT; letting it
|
|
926
939
|
// evict would make the cache destroy itself on first use.
|
|
927
|
-
writeBundle(
|
|
940
|
+
writeBundle(fresh, { skipBrokerEviction: true });
|
|
928
941
|
}
|
|
929
942
|
catch {
|
|
930
943
|
// Swallow — telemetry must never block secret resolution.
|
|
@@ -1272,6 +1285,10 @@ export function readAndResolveBundleEnv(name, opts = {}) {
|
|
|
1272
1285
|
// `agents secrets status` is honest. Re-warm under the scope the grant was
|
|
1273
1286
|
// MADE in (resolved.harness), never the asking scope — re-warming a global
|
|
1274
1287
|
// grant as `claude` would silently narrow it for every other harness.
|
|
1288
|
+
// No snapshotAt: the session's bundle was read at unlock time, not now —
|
|
1289
|
+
// claiming freshness here would defeat the broker's eviction tombstones.
|
|
1290
|
+
// An undated load is accepted (legacy behavior); the durable-session
|
|
1291
|
+
// staleness window itself is a known, separate concern.
|
|
1275
1292
|
agentAutoLoadSync(name, session.bundle, session.env, Math.max(1, session.expiresAt - Date.now()), resolved.harness, session.lease);
|
|
1276
1293
|
emitSecretAudit({
|
|
1277
1294
|
event: 'secrets.get',
|
|
@@ -1303,40 +1320,49 @@ export function readAndResolveBundleEnv(name, opts = {}) {
|
|
|
1303
1320
|
`never raises a Touch ID sheet on its own.`);
|
|
1304
1321
|
}
|
|
1305
1322
|
}
|
|
1323
|
+
// If the secrets broker is explicitly disabled and this bundle would otherwise
|
|
1324
|
+
// fall through to a keychain read (Touch ID prompt), fail loud now. Never-policy
|
|
1325
|
+
// bundles are verified below and remain silent; vault/file backends are unaffected.
|
|
1326
|
+
if (backend === 'keychain' && !isSecretsBrokerEnabled() && process.env.AGENTS_SECRETS_NO_AGENT !== '1') {
|
|
1327
|
+
throw new Error(`Secrets broker is disabled — re-enable with 'agents daemon services enable secrets-broker'. ` +
|
|
1328
|
+
`If you meant to read directly from the keychain, set AGENTS_SECRETS_NO_AGENT=1.`);
|
|
1329
|
+
}
|
|
1306
1330
|
if (backend === 'vault')
|
|
1307
1331
|
assertVaultBackendUsable(name);
|
|
1308
1332
|
const store = itemStore(backend);
|
|
1309
1333
|
const metaItem = bundleMetaItem(name);
|
|
1310
1334
|
const bundleSecretPrefix = `${SECRETS_ITEM_PREFIX}${name}.`;
|
|
1311
|
-
let
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1335
|
+
let enumeratedSecretItems = [];
|
|
1336
|
+
// Agent launches must never enumerate the macOS Keychain: a per-bundle
|
|
1337
|
+
// prefix becomes a broad `agents-cli.` scan after service-name hashing and
|
|
1338
|
+
// macOS evaluates unrelated biometry ACLs during that scan (RUSH-2440).
|
|
1339
|
+
// Interactive reads retain the existing enumeration side of the union so
|
|
1340
|
+
// legacy/aliased items keep their established behavior.
|
|
1341
|
+
if (backend !== 'keychain' || !opts.agentOnly) {
|
|
1342
|
+
try {
|
|
1343
|
+
enumeratedSecretItems = store.list(bundleSecretPrefix);
|
|
1344
|
+
}
|
|
1345
|
+
catch {
|
|
1346
|
+
enumeratedSecretItems = [];
|
|
1347
|
+
}
|
|
1317
1348
|
}
|
|
1318
1349
|
const reason = opts.caller
|
|
1319
1350
|
? `read ${name} secrets (for ${opts.caller})`
|
|
1320
1351
|
: `read ${name} secrets`;
|
|
1321
|
-
//
|
|
1322
|
-
//
|
|
1323
|
-
//
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
forceDuration: Boolean(opts.duration),
|
|
1336
|
-
silentNoAcl: verifiedNoAclBundle,
|
|
1337
|
-
})
|
|
1338
|
-
: store.getBatch([...new Set([metaItem, ...secretItems])]);
|
|
1339
|
-
const json = fetched.get(metaItem);
|
|
1352
|
+
// Captured BEFORE the first keychain read: if a mutating write evicts this
|
|
1353
|
+
// bundle while the read is in flight, the broker's tombstone must beat this
|
|
1354
|
+
// snapshot, so the conservative (earliest) timestamp is the correct one.
|
|
1355
|
+
const snapshotAt = Date.now();
|
|
1356
|
+
// Fetch metadata first (it's always no-ACL), then derive secret item names
|
|
1357
|
+
// from its declared keys instead of enumerating. This eliminates the broad
|
|
1358
|
+
// Keychain scan that triggered Touch ID on every run (RUSH-2440). The
|
|
1359
|
+
// metadata is authoritative for declared keys; undeclared keys (union with
|
|
1360
|
+
// legacy/orphaned items) are not supported in agent-only mode and would
|
|
1361
|
+
// fail the agentOnly gate anyway.
|
|
1362
|
+
const metaFetched = backend === 'keychain'
|
|
1363
|
+
? getKeychainTokens([metaItem], { silentNoAcl: true })
|
|
1364
|
+
: store.getBatch([...new Set([metaItem, ...enumeratedSecretItems])]);
|
|
1365
|
+
const json = metaFetched.get(metaItem);
|
|
1340
1366
|
if (json === undefined) {
|
|
1341
1367
|
if (vaultExists() && !getVaultSession().loggedIn) {
|
|
1342
1368
|
throw new Error(`Synced secrets are locked. Run: agents login`);
|
|
@@ -1353,6 +1379,39 @@ export function readAndResolveBundleEnv(name, opts = {}) {
|
|
|
1353
1379
|
if (!parsed || typeof parsed !== 'object') {
|
|
1354
1380
|
throw new Error(`Bundle '${name}' is malformed.`);
|
|
1355
1381
|
}
|
|
1382
|
+
// Compute exact storage names from declared keychain references. The env key
|
|
1383
|
+
// and stored item name may differ (`TOKEN=keychain:actual-token`), so deriving
|
|
1384
|
+
// item names from Object.keys(vars) would silently read the wrong secret.
|
|
1385
|
+
const declaredSecretItems = [];
|
|
1386
|
+
if (parsed.vars && typeof parsed.vars === 'object') {
|
|
1387
|
+
for (const raw of Object.values(parsed.vars)) {
|
|
1388
|
+
const value = parseBundleValue(raw);
|
|
1389
|
+
if ('ref' in value && value.ref.provider === 'keychain') {
|
|
1390
|
+
declaredSecretItems.push(secretsKeychainItem(name, value.ref.value));
|
|
1391
|
+
}
|
|
1392
|
+
}
|
|
1393
|
+
}
|
|
1394
|
+
const secretItems = [...new Set([...enumeratedSecretItems, ...declaredSecretItems])];
|
|
1395
|
+
// Now fetch both metadata and secret values in one batch.
|
|
1396
|
+
// The interactive path keeps the enumeration + declared-item union; the
|
|
1397
|
+
// agent-only path contains declared items only and therefore never scans.
|
|
1398
|
+
const fetched = backend === 'keychain'
|
|
1399
|
+
? getKeychainTokens([...new Set([metaItem, ...secretItems])], {
|
|
1400
|
+
agent: opts.agent || process.env.AGENTS_AGENT_NAME || 'Agents CLI',
|
|
1401
|
+
bundle: name,
|
|
1402
|
+
// The session that triggered the read, so a Touch ID prompt is
|
|
1403
|
+
// attributable when several agents run at once. Exported by exec.ts.
|
|
1404
|
+
sessionId: process.env.AGENT_SESSION_ID || process.env.AGENTS_SESSION_ID,
|
|
1405
|
+
reason: opts.caller ? `to ${opts.caller}` : reason,
|
|
1406
|
+
duration: opts.duration || humanUnlockDuration(secretsHoldMs()),
|
|
1407
|
+
defaultPolicy: secretsDefaultPolicy(),
|
|
1408
|
+
forceDuration: Boolean(opts.duration),
|
|
1409
|
+
silentNoAcl: verifiedNoAclBundle,
|
|
1410
|
+
})
|
|
1411
|
+
// File/vault enumeration is complete and prompt-free. Reuse its initial
|
|
1412
|
+
// batch so metadata-first Keychain safety does not double-decrypt every
|
|
1413
|
+
// file-backed credential read.
|
|
1414
|
+
: metaFetched;
|
|
1356
1415
|
const bundle = {
|
|
1357
1416
|
name,
|
|
1358
1417
|
description: parsed.description,
|
|
@@ -1393,53 +1452,6 @@ export function readAndResolveBundleEnv(name, opts = {}) {
|
|
|
1393
1452
|
}
|
|
1394
1453
|
const keys = [...selectedKeys].sort();
|
|
1395
1454
|
keychainKeys.sort();
|
|
1396
|
-
// RUSH-2252: complete the read set from the bundle's DECLARED keys, not only
|
|
1397
|
-
// from the enumeration above. `store.list()` derives the batch by enumerating
|
|
1398
|
-
// the bundle's namespace, and that enumeration is lossy by construction — the
|
|
1399
|
-
// macOS helper's `list` omits every biometry-ACL'd item
|
|
1400
|
-
// (`kSecUseAuthenticationUISkip`) and skips the whole data-protection pass when
|
|
1401
|
-
// the keychain is locked, so a `hold`-policy bundle's value items never appear
|
|
1402
|
-
// and a present secret reads as "not found" (RUSH-2248). A declared key whose
|
|
1403
|
-
// item did not enumerate is therefore absent from `fetched`; read those exact
|
|
1404
|
-
// items directly — a point read DOES evaluate the ACL, so it triggers Touch ID
|
|
1405
|
-
// and returns the value the enumeration could not see. The enumeration still
|
|
1406
|
-
// earns its place (it catches hashed/aliased storage names and stale leftovers
|
|
1407
|
-
// not named in the metadata), so this is a UNION, not a replacement.
|
|
1408
|
-
//
|
|
1409
|
-
// One Touch ID sheet is preserved: the items missing from `fetched` are exactly
|
|
1410
|
-
// the ACL'd ones the enumeration dropped, so the first batch (metadata plus any
|
|
1411
|
-
// no-ACL / `never` items) raised no sheet, and this second batch raises the
|
|
1412
|
-
// single sheet that covers all of them. When the enumeration is healthy every
|
|
1413
|
-
// declared item is already in `fetched`, `missingDeclared` is empty, and this
|
|
1414
|
-
// is skipped entirely — zero behavior change and no extra spawn on the hot path.
|
|
1415
|
-
if (backend === 'keychain') {
|
|
1416
|
-
const missingDeclared = [];
|
|
1417
|
-
for (const key of keychainKeys) {
|
|
1418
|
-
const p = parsedByKey.get(key);
|
|
1419
|
-
if (!('ref' in p) || p.ref.provider !== 'keychain')
|
|
1420
|
-
continue;
|
|
1421
|
-
const item = secretsKeychainItem(bundle.name, p.ref.value);
|
|
1422
|
-
if (fetched.get(item) === undefined && fetched.get(keychainServiceAlias(item)) === undefined) {
|
|
1423
|
-
missingDeclared.push(item);
|
|
1424
|
-
}
|
|
1425
|
-
}
|
|
1426
|
-
if (missingDeclared.length > 0) {
|
|
1427
|
-
const declaredFetched = getKeychainTokens([...new Set(missingDeclared)], {
|
|
1428
|
-
agent: opts.agent || process.env.AGENTS_AGENT_NAME || 'Agents CLI',
|
|
1429
|
-
bundle: name,
|
|
1430
|
-
sessionId: process.env.AGENT_SESSION_ID || process.env.AGENTS_SESSION_ID,
|
|
1431
|
-
reason: opts.caller ? `to ${opts.caller}` : reason,
|
|
1432
|
-
duration: opts.duration || humanUnlockDuration(secretsHoldMs()),
|
|
1433
|
-
// The policy is known now (metadata parsed), so the prompt names the real
|
|
1434
|
-
// duration instead of the pre-read default the first batch had to guess.
|
|
1435
|
-
defaultPolicy: bundlePolicy(bundle),
|
|
1436
|
-
forceDuration: Boolean(opts.duration),
|
|
1437
|
-
silentNoAcl: verifiedNoAclBundle,
|
|
1438
|
-
});
|
|
1439
|
-
for (const [k, v] of declaredFetched)
|
|
1440
|
-
fetched.set(k, v);
|
|
1441
|
-
}
|
|
1442
|
-
}
|
|
1443
1455
|
const emitReadAudit = (status, err) => {
|
|
1444
1456
|
emitSecretAudit({
|
|
1445
1457
|
event: 'secrets.get',
|
|
@@ -1474,7 +1486,7 @@ export function readAndResolveBundleEnv(name, opts = {}) {
|
|
|
1474
1486
|
bundlePolicy(bundle) === 'hold' &&
|
|
1475
1487
|
secretsAgentAutoEnabled() &&
|
|
1476
1488
|
canCacheResolvedEnv(bundle, selectedKeys, opts.keyMode)) {
|
|
1477
|
-
agentAutoLoadSync(name, bundle, env, secretsHoldMs(), opts.agent || process.env.AGENTS_AGENT_NAME || GLOBAL_HARNESS);
|
|
1489
|
+
agentAutoLoadSync(name, bundle, env, secretsHoldMs(), opts.agent || process.env.AGENTS_AGENT_NAME || GLOBAL_HARNESS, undefined, snapshotAt);
|
|
1478
1490
|
}
|
|
1479
1491
|
return { bundle, env };
|
|
1480
1492
|
}
|
|
@@ -298,7 +298,34 @@ export function readHmacKeyRecord() {
|
|
|
298
298
|
catch {
|
|
299
299
|
return null;
|
|
300
300
|
}
|
|
301
|
-
|
|
301
|
+
const record = parseHmacKeyRecord(raw);
|
|
302
|
+
// Converge a stale-ACL'd hmackey to silent, on the HOT read path (RUSH-2441 /
|
|
303
|
+
// restore of v1.22.7 / 391017461). An old helper re-stamped this
|
|
304
|
+
// contractually-no-ACL item with a biometry ACL, so the read just above pops
|
|
305
|
+
// the generic "Agents CLI needs to authenticate" sheet on EVERY hashed
|
|
306
|
+
// lookup — the `agents devices list` stats probe the SessionStart hook runs,
|
|
307
|
+
// and every other background hashed read. `maybeAutoRekey`'s one-shot heal
|
|
308
|
+
// only fires on a cleartext-bundle resolve and is bypassed for the
|
|
309
|
+
// hmackey/hashed-name path (`prepareServiceName` returns early for
|
|
310
|
+
// HMAC_KEY_ITEM before maybeAutoRekey), so it never converged exactly these
|
|
311
|
+
// reads and the machine prompted forever. Re-store the record no-ACL once
|
|
312
|
+
// per machine here (the read that produced it has already happened — and
|
|
313
|
+
// already prompted if the item was ACL'd); every subsequent read, in this
|
|
314
|
+
// process and all future ones, is silent.
|
|
315
|
+
//
|
|
316
|
+
// v1.22.10 (bf79dc885 / #1995) moved the heal back into maybeAutoRekey and
|
|
317
|
+
// left the changelog claim in 1.22.7 false until this restore.
|
|
318
|
+
if (record && !record.healedNoAcl) {
|
|
319
|
+
try {
|
|
320
|
+
healHmacKeyNoAclOnce(record);
|
|
321
|
+
record.healedNoAcl = true;
|
|
322
|
+
}
|
|
323
|
+
catch {
|
|
324
|
+
// A failed no-ACL re-store leaves the item still ACL'd (a still-prompting
|
|
325
|
+
// read) rather than a silent wrong state; the next process retries.
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
return record;
|
|
302
329
|
}
|
|
303
330
|
function writeHmacKeyRecord(rec) {
|
|
304
331
|
// JSON.stringify drops undefined fields (used to clear pendingDeletes).
|
|
@@ -471,14 +498,10 @@ export function maybeAutoRekey() {
|
|
|
471
498
|
return;
|
|
472
499
|
const st = resolveHashState();
|
|
473
500
|
if (st.active) {
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
catch {
|
|
479
|
-
/* the next explicit unlock retries */
|
|
480
|
-
}
|
|
481
|
-
}
|
|
501
|
+
// The stale-ACL'd-hmackey heal runs on the hot read path
|
|
502
|
+
// (readHmacKeyRecord), which resolveHashState() above just went through —
|
|
503
|
+
// so st.record is already healed here regardless of how this machine
|
|
504
|
+
// reached "hashing active". Nothing to do but finish any pending deletes.
|
|
482
505
|
if (st.record?.pendingDeletes?.length) {
|
|
483
506
|
try {
|
|
484
507
|
finishPendingDeletes(st.record);
|
|
@@ -33,14 +33,14 @@ export declare function ensureKeychainHelperInstalled(opts?: {
|
|
|
33
33
|
* Return the absolute path to the helper executable. If the installed bundle
|
|
34
34
|
* is missing, or is stale relative to the bundled source helper, performs a
|
|
35
35
|
* lazy (re)install first. The staleness check is what lets an upgraded CLI
|
|
36
|
-
* replace a helper a previous version installed —
|
|
37
|
-
*
|
|
38
|
-
*
|
|
36
|
+
* replace a helper a previous version installed — the install never runs on
|
|
37
|
+
* `npm i -g`, so this call site is the only one every machine is guaranteed to
|
|
38
|
+
* pass through.
|
|
39
39
|
*
|
|
40
40
|
* Throws on non-darwin.
|
|
41
41
|
*/
|
|
42
42
|
export declare function getKeychainHelperPath(): string;
|
|
43
|
-
/** Diagnostic snapshot
|
|
43
|
+
/** Diagnostic snapshot of the installed Keychain helper. */
|
|
44
44
|
export interface KeychainHelperStatus {
|
|
45
45
|
source: string | null;
|
|
46
46
|
destination: string;
|
|
@@ -140,7 +140,7 @@ export function ensureKeychainHelperInstalled(opts = {}) {
|
|
|
140
140
|
const verify = codesignVerify(dest);
|
|
141
141
|
if (!verify.ok) {
|
|
142
142
|
throw new Error(`Installed helper failed codesign verification at ${dest}.\n${verify.output}\n` +
|
|
143
|
-
'The bundle may be corrupted.
|
|
143
|
+
'The bundle may be corrupted. Reinstall agents-cli to restore it; the helper reinstalls automatically on the next secrets use.');
|
|
144
144
|
}
|
|
145
145
|
heartbeat(); // codesign done; spctl does a network Gatekeeper lookup — refresh again
|
|
146
146
|
const assess = spctlAssess(dest);
|
|
@@ -156,9 +156,9 @@ export function ensureKeychainHelperInstalled(opts = {}) {
|
|
|
156
156
|
* Return the absolute path to the helper executable. If the installed bundle
|
|
157
157
|
* is missing, or is stale relative to the bundled source helper, performs a
|
|
158
158
|
* lazy (re)install first. The staleness check is what lets an upgraded CLI
|
|
159
|
-
* replace a helper a previous version installed —
|
|
160
|
-
*
|
|
161
|
-
*
|
|
159
|
+
* replace a helper a previous version installed — the install never runs on
|
|
160
|
+
* `npm i -g`, so this call site is the only one every machine is guaranteed to
|
|
161
|
+
* pass through.
|
|
162
162
|
*
|
|
163
163
|
* Throws on non-darwin.
|
|
164
164
|
*/
|
|
@@ -33,6 +33,12 @@ export interface PushBundleOptions {
|
|
|
33
33
|
passphrase?: string;
|
|
34
34
|
/** Label for the audit trail — `export --host` vs `fleet apply`. */
|
|
35
35
|
operation: string;
|
|
36
|
+
/** Preserve an automation account's permanent prompt-free policy remotely. */
|
|
37
|
+
policyNever?: boolean;
|
|
38
|
+
/** Permit a human-invoked push to read locally without requiring the agent broker. */
|
|
39
|
+
agentOnly?: boolean;
|
|
40
|
+
/** Non-secret literals whose bundle value kind must survive the dotenv transport. */
|
|
41
|
+
literalValues?: Record<string, string>;
|
|
36
42
|
}
|
|
37
43
|
export interface PushBundleResult {
|
|
38
44
|
ok: boolean;
|
|
@@ -42,14 +48,34 @@ export interface PushBundleResult {
|
|
|
42
48
|
/** One line for the caller to render. Never contains a secret value. */
|
|
43
49
|
message: string;
|
|
44
50
|
}
|
|
51
|
+
export interface LiteralRestorePlan {
|
|
52
|
+
key: string;
|
|
53
|
+
removeArgs: string[];
|
|
54
|
+
addArgs: string[];
|
|
55
|
+
}
|
|
56
|
+
/** Preserve the literal-vs-secret schema that dotenv transport cannot encode. */
|
|
57
|
+
export declare function planLiteralRestoration(bundle: string, literalValues?: Record<string, string>): LiteralRestorePlan[];
|
|
45
58
|
/**
|
|
46
59
|
* Read and resolve a bundle once, for pushing to one or more hosts.
|
|
47
60
|
*
|
|
48
|
-
* `
|
|
49
|
-
*
|
|
50
|
-
*
|
|
61
|
+
* `keyMode: 'storage'` is fixed — a push always sends storage-shaped values.
|
|
62
|
+
*
|
|
63
|
+
* `agentOnly` is the caller's to decide, and it is NOT a security level: it
|
|
64
|
+
* selects the broker-only read (fail fast with the `agents secrets unlock` hint)
|
|
65
|
+
* over the interactive read (macOS may raise a Touch ID sheet). The headless
|
|
66
|
+
* guard exists because an AGENT LAUNCH must never enumerate the keychain — a
|
|
67
|
+
* per-bundle prefix becomes a broad `agents-cli.` scan after service-name
|
|
68
|
+
* hashing, and macOS evaluates unrelated biometry ACLs during it, one sheet per
|
|
69
|
+
* installed version (bundles.ts, RUSH-2440). A human at a TTY is not that case:
|
|
70
|
+
* they can answer one prompt, and the interactive sibling reads (`view --reveal`,
|
|
71
|
+
* `exec`) already let them. NOT `get` — it is unconditionally `agentOnly` on
|
|
72
|
+
* purpose, because prompting would hang a `$(agents secrets get …)` capture
|
|
73
|
+
* mid-pipeline (SEC-13b). It defaults to `true` so an automated caller that says
|
|
74
|
+
* nothing stays broker-only.
|
|
51
75
|
*/
|
|
52
|
-
export declare function resolveBundleForPush(bundle: string, caller: string
|
|
76
|
+
export declare function resolveBundleForPush(bundle: string, caller: string, opts?: {
|
|
77
|
+
agentOnly?: boolean;
|
|
78
|
+
}): ResolvedBundleForPush;
|
|
53
79
|
/**
|
|
54
80
|
* WHICH transport a (backend, target-OS) pair selects, and the exact bytes it
|
|
55
81
|
* will send. One of four outcomes, and picking the wrong one is silent: a
|
|
@@ -68,17 +94,25 @@ export type PushTransport =
|
|
|
68
94
|
kind: 'refuse';
|
|
69
95
|
message: string;
|
|
70
96
|
}
|
|
71
|
-
/**
|
|
97
|
+
/**
|
|
98
|
+
* A command sent over the raw ssh engine, with the .env on stdin. `multiplex`
|
|
99
|
+
* is always `false`: a push ships credential bytes, so its connection must not
|
|
100
|
+
* reuse — or leave behind — a persistent authenticated control master to the
|
|
101
|
+
* destination the way the default fan-out connections do (RUSH-2527). This
|
|
102
|
+
* mirrors the `--copy-creds` dispatch posture (`multiplex: !opts.copyCreds`).
|
|
103
|
+
*/
|
|
72
104
|
| {
|
|
73
105
|
kind: 'ssh';
|
|
74
106
|
remoteCmd: string;
|
|
75
107
|
input: string;
|
|
108
|
+
multiplex: false;
|
|
76
109
|
}
|
|
77
|
-
/** The OS-aware `agents secrets` wrapper, the READ inverse's own path. */
|
|
110
|
+
/** The OS-aware `agents secrets` wrapper, the READ inverse's own path. Never multiplexed — see the `ssh` kind. */
|
|
78
111
|
| {
|
|
79
112
|
kind: 'remote-secrets';
|
|
80
113
|
args: string[];
|
|
81
114
|
input: string;
|
|
115
|
+
multiplex: false;
|
|
82
116
|
};
|
|
83
117
|
/** Choose the transport for one push. Pure: registry read in, plan out. */
|
|
84
118
|
export declare function planPushTransport(resolved: ResolvedBundleForPush, bundle: string, host: string, opts: PushBundleOptions): PushTransport;
|