@phnx-labs/agents-cli 1.22.39 → 1.22.41
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 +316 -0
- package/README.md +116 -70
- package/dist/bin/agents +0 -0
- package/dist/bootstrap.js +45 -27
- package/dist/browser.js +2 -2
- package/dist/cli/command-registry.d.ts +135 -0
- package/dist/cli/command-registry.js +259 -0
- package/dist/commands/accounts.d.ts +20 -0
- package/dist/commands/accounts.js +186 -25
- package/dist/commands/apply.d.ts +3 -3
- package/dist/commands/apply.js +20 -63
- package/dist/commands/browser.d.ts +15 -0
- package/dist/commands/browser.js +318 -131
- package/dist/commands/budget.d.ts +7 -6
- package/dist/commands/budget.js +8 -7
- package/dist/commands/cloud.js +17 -20
- package/dist/commands/commands.js +1 -1
- package/dist/commands/computer-actions.d.ts +1 -35
- package/dist/commands/computer-actions.js +13 -181
- package/dist/commands/computer.d.ts +28 -3
- package/dist/commands/computer.js +93 -65
- package/dist/commands/config.js +16 -3
- package/dist/commands/cost.d.ts +2 -1
- package/dist/commands/cost.js +7 -6
- package/dist/commands/daemon.js +151 -4
- package/dist/commands/detach.js +1 -1
- package/dist/commands/doctor.js +19 -11
- package/dist/commands/events.js +1 -1
- package/dist/commands/exec.d.ts +2 -50
- package/dist/commands/exec.js +160 -215
- package/dist/commands/factory.js +1 -1
- package/dist/commands/feed.d.ts +2 -2
- package/dist/commands/feed.js +12 -23
- package/dist/commands/fleet-capture.js +25 -27
- package/dist/commands/focus.d.ts +2 -3
- package/dist/commands/focus.js +34 -9
- package/dist/commands/go.js +15 -5
- package/dist/commands/harness.d.ts +3 -3
- package/dist/commands/harness.js +53 -5
- package/dist/commands/hooks.js +1 -1
- package/dist/commands/import.js +2 -2
- package/dist/commands/insights.d.ts +5 -5
- package/dist/commands/insights.js +15 -9
- package/dist/commands/inspect.d.ts +2 -3
- package/dist/commands/inspect.js +4 -16
- package/dist/commands/logs.d.ts +2 -2
- package/dist/commands/logs.js +9 -9
- package/dist/commands/mcp.js +2 -2
- package/dist/commands/memory.js +1 -1
- package/dist/commands/message.d.ts +1 -1
- package/dist/commands/message.js +7 -7
- package/dist/commands/mine.d.ts +11 -8
- package/dist/commands/mine.js +11 -10
- package/dist/commands/models.js +1 -1
- package/dist/commands/modes.js +1 -1
- package/dist/commands/monitors.js +23 -6
- package/dist/commands/output.d.ts +7 -6
- package/dist/commands/output.js +11 -10
- package/dist/commands/packages.js +4 -4
- package/dist/commands/perf.js +1 -1
- package/dist/commands/permissions.js +49 -32
- package/dist/commands/plugins.d.ts +1 -1
- package/dist/commands/plugins.js +3 -3
- package/dist/commands/profiles.d.ts +6 -9
- package/dist/commands/profiles.js +8 -321
- package/dist/commands/projects.d.ts +9 -0
- package/dist/commands/projects.js +140 -8
- package/dist/commands/prune.js +5 -13
- package/dist/commands/refresh-rules.js +1 -1
- package/dist/commands/repo.d.ts +5 -7
- package/dist/commands/repo.js +34 -18
- package/dist/commands/resource-view.js +4 -0
- package/dist/commands/resume.d.ts +11 -7
- package/dist/commands/resume.js +82 -98
- package/dist/commands/route.d.ts +12 -0
- package/dist/commands/route.js +261 -0
- package/dist/commands/routines.d.ts +2 -3
- package/dist/commands/routines.js +120 -145
- package/dist/commands/rules.js +1 -1
- package/dist/commands/run-account-picker.d.ts +1 -1
- package/dist/commands/run-account-picker.js +2 -2
- package/dist/commands/run-broadcast.d.ts +21 -0
- package/dist/commands/run-broadcast.js +89 -0
- package/dist/commands/run-cloud.js +1 -1
- package/dist/commands/secrets-vault.d.ts +10 -0
- package/dist/commands/{login.js → secrets-vault.js} +34 -14
- package/dist/commands/secrets.d.ts +4 -4
- package/dist/commands/secrets.js +158 -159
- package/dist/commands/sessions-backfill.js +1 -1
- package/dist/commands/sessions-browser.d.ts +1 -1
- package/dist/commands/sessions-browser.js +9 -5
- package/dist/commands/sessions-export.js +2 -2
- package/dist/commands/sessions-inject.js +4 -5
- package/dist/commands/sessions-migrate.js +16 -27
- package/dist/commands/sessions-picker.d.ts +14 -0
- package/dist/commands/sessions-picker.js +209 -13
- package/dist/commands/sessions-resume.d.ts +7 -3
- package/dist/commands/sessions-resume.js +81 -33
- package/dist/commands/sessions-share.d.ts +25 -0
- package/dist/commands/sessions-share.js +166 -0
- package/dist/commands/sessions-stats.js +2 -2
- package/dist/commands/sessions.d.ts +75 -68
- package/dist/commands/sessions.js +144 -180
- package/dist/commands/setup-browser.js +1 -1
- package/dist/commands/setup-computer.js +1 -1
- package/dist/commands/setup-fleet.js +3 -0
- package/dist/commands/setup-mine.d.ts +7 -3
- package/dist/commands/setup-mine.js +13 -8
- package/dist/commands/setup-preferences.js +2 -2
- package/dist/commands/setup-secrets.js +2 -2
- package/dist/commands/setup-watchdog.js +2 -2
- package/dist/commands/setup.js +7 -2
- package/dist/commands/share.d.ts +65 -0
- package/dist/commands/share.js +253 -20
- package/dist/commands/skills.js +3 -3
- package/dist/commands/snapshot.d.ts +2 -2
- package/dist/commands/snapshot.js +9 -9
- package/dist/commands/ssh.d.ts +1 -1
- package/dist/commands/ssh.js +186 -64
- package/dist/commands/subagents.js +1 -1
- package/dist/commands/sync.js +24 -9
- package/dist/commands/teams.d.ts +2 -2
- package/dist/commands/teams.js +9 -18
- package/dist/commands/tmux.js +5 -1
- package/dist/commands/trash.js +1 -1
- package/dist/commands/usage.d.ts +1 -1
- package/dist/commands/usage.js +2 -2
- package/dist/commands/utils.d.ts +4 -14
- package/dist/commands/utils.js +6 -27
- package/dist/commands/versions.js +2 -2
- package/dist/commands/view.d.ts +9 -77
- package/dist/commands/view.js +23 -8
- package/dist/commands/watchdog.js +1 -1
- package/dist/commands/webhook.js +25 -34
- package/dist/commands/workflows.js +1 -1
- package/dist/index.js +1 -1
- package/dist/lib/account-capabilities.d.ts +39 -0
- package/dist/lib/account-capabilities.js +59 -0
- package/dist/lib/account-catalog.js +1 -1
- package/dist/lib/account-registry.d.ts +66 -0
- package/dist/lib/account-registry.js +151 -5
- package/dist/lib/{rotate.d.ts → accounting/rotate.d.ts} +4 -4
- package/dist/lib/{rotate.js → accounting/rotate.js} +8 -8
- package/dist/lib/{usage.d.ts → accounting/usage.d.ts} +4 -4
- package/dist/lib/{usage.js → accounting/usage.js} +11 -11
- package/dist/lib/agent-spec/provider.js +1 -1
- package/dist/lib/agents.d.ts +72 -4
- package/dist/lib/agents.js +231 -199
- package/dist/lib/answer-router.d.ts +2 -2
- package/dist/lib/ask-classifier.d.ts +1 -1
- package/dist/lib/ask-classifier.js +1 -1
- package/dist/lib/audit/log.js +1 -1
- package/dist/lib/auth-health.d.ts +1 -1
- package/dist/lib/auth-health.js +2 -2
- package/dist/lib/brand.js +1 -1
- package/dist/lib/browser/caller-identity.d.ts +28 -0
- package/dist/lib/browser/caller-identity.js +156 -0
- package/dist/lib/browser/cdp.js +5 -1
- package/dist/lib/browser/chrome.js +12 -1
- package/dist/lib/browser/drivers/ssh.d.ts +1 -1
- package/dist/lib/browser/drivers/ssh.js +3 -2
- package/dist/lib/browser/hygiene.d.ts +10 -2
- package/dist/lib/browser/hygiene.js +7 -4
- package/dist/lib/browser/ipc.d.ts +22 -2
- package/dist/lib/browser/ipc.js +185 -21
- package/dist/lib/browser/profiles.d.ts +69 -5
- package/dist/lib/browser/profiles.js +135 -13
- package/dist/lib/browser/remote-control.d.ts +2 -2
- package/dist/lib/browser/remote-control.js +3 -3
- package/dist/lib/browser/runtime-state.d.ts +79 -0
- package/dist/lib/browser/runtime-state.js +119 -1
- package/dist/lib/browser/service.d.ts +137 -2
- package/dist/lib/browser/service.js +492 -54
- package/dist/lib/browser/sessions-list.d.ts +2 -1
- package/dist/lib/browser/sessions-list.js +4 -13
- package/dist/lib/browser/types.d.ts +19 -1
- package/dist/lib/budget/config.js +1 -1
- package/dist/lib/budget/ledger.js +1 -1
- package/dist/lib/byok-usage.js +1 -1
- package/dist/lib/capabilities.js +3 -15
- package/dist/lib/catchup.d.ts +1 -1
- package/dist/lib/catchup.js +1 -1
- package/dist/lib/cloud/dispatch.js +1 -1
- package/dist/lib/cloud/host.d.ts +3 -3
- package/dist/lib/cloud/host.js +5 -5
- package/dist/lib/cloud/rush.d.ts +9 -21
- package/dist/lib/cloud/rush.js +33 -120
- package/dist/lib/cloud/types.d.ts +2 -2
- package/dist/lib/commands.js +3 -13
- package/dist/lib/computer/actions.d.ts +36 -0
- package/dist/lib/computer/actions.js +162 -0
- package/dist/lib/computer/dispatch.d.ts +2 -2
- package/dist/lib/computer/dispatch.js +2 -2
- package/dist/lib/computer/sessions-list.d.ts +3 -3
- package/dist/lib/computer/sessions-list.js +2 -2
- package/dist/lib/config-keys.d.ts +2 -1
- package/dist/lib/config-keys.js +8 -4
- package/dist/lib/config-machine-keys.d.ts +4 -2
- package/dist/lib/config-machine-keys.js +5 -2
- package/dist/lib/crabbox/lease.js +9 -1
- package/dist/lib/crabbox/runtimes.d.ts +32 -7
- package/dist/lib/crabbox/runtimes.js +72 -30
- package/dist/lib/{daemon.d.ts → daemon/daemon.d.ts} +59 -1
- package/dist/lib/{daemon.js → daemon/daemon.js} +237 -49
- package/dist/lib/daemon-services.d.ts +1 -1
- package/dist/lib/daemon-services.js +5 -0
- package/dist/lib/daemon-ticks.d.ts +25 -0
- package/dist/lib/daemon-ticks.js +31 -1
- package/dist/lib/daemon-webhooks.d.ts +104 -0
- package/dist/lib/daemon-webhooks.js +215 -0
- package/dist/lib/device-config.d.ts +85 -75
- package/dist/lib/device-config.js +280 -142
- package/dist/lib/devices/config-migration.d.ts +29 -34
- package/dist/lib/devices/config-migration.js +209 -130
- package/dist/lib/devices/connect.d.ts +2 -2
- package/dist/lib/devices/connect.js +2 -2
- package/dist/lib/devices/discovery-policy.d.ts +30 -0
- package/dist/lib/devices/discovery-policy.js +101 -0
- package/dist/lib/devices/fleet-inventory.js +8 -3
- package/dist/lib/devices/harness-inventory.d.ts +1 -1
- package/dist/lib/devices/harness-inventory.js +12 -3
- package/dist/lib/devices/known-hosts.js +1 -1
- package/dist/lib/devices/pool.d.ts +10 -5
- package/dist/lib/devices/pool.js +5 -7
- package/dist/lib/devices/registry.d.ts +1 -1
- package/dist/lib/devices/registry.js +1 -1
- package/dist/lib/devices/resolve-profile.d.ts +2 -2
- package/dist/lib/devices/resolve-profile.js +2 -2
- package/dist/lib/devices/resolve-target.d.ts +1 -1
- package/dist/lib/devices/resolve-target.js +4 -4
- package/dist/lib/devices/self-host.d.ts +1 -1
- package/dist/lib/devices/self-host.js +3 -3
- package/dist/lib/devices/ssh-config.d.ts +1 -1
- package/dist/lib/devices/stats-cache.js +1 -1
- package/dist/lib/devices/sync.d.ts +3 -1
- package/dist/lib/devices/sync.js +3 -3
- package/dist/lib/doctor-diff.js +4 -4
- package/dist/lib/drift-sync.js +2 -2
- package/dist/lib/drift.js +2 -2
- package/dist/lib/event-families.d.ts +1 -1
- package/dist/lib/event-stream.d.ts +1 -1
- package/dist/lib/event-stream.js +2 -2
- package/dist/lib/events-ingest.js +2 -2
- package/dist/lib/exec.d.ts +4 -4
- package/dist/lib/exec.js +11 -11
- package/dist/lib/factory/snapshot.d.ts +1 -1
- package/dist/lib/factory/snapshot.js +1 -1
- package/dist/lib/{activity.d.ts → feed/activity.d.ts} +1 -1
- package/dist/lib/{activity.js → feed/activity.js} +7 -7
- package/dist/lib/{events.d.ts → feed/events.d.ts} +2 -2
- package/dist/lib/{events.js → feed/events.js} +5 -5
- package/dist/lib/{feed.js → feed/feed.js} +4 -4
- package/dist/lib/feed-outcome.d.ts +1 -1
- package/dist/lib/feed-policy.d.ts +1 -1
- package/dist/lib/feed-policy.js +1 -1
- package/dist/lib/feed-post.d.ts +1 -1
- package/dist/lib/feed-post.js +1 -1
- package/dist/lib/feed-ranking.d.ts +1 -1
- package/dist/lib/fleet/apply.d.ts +0 -6
- package/dist/lib/fleet/apply.js +18 -45
- package/dist/lib/fleet/auth-sync.d.ts +16 -35
- package/dist/lib/fleet/auth-sync.js +16 -75
- package/dist/lib/fleet/capture.js +8 -6
- package/dist/lib/fleet/manifest.js +23 -11
- package/dist/lib/fleet/types.d.ts +19 -7
- package/dist/lib/fleet-cache.d.ts +1 -1
- package/dist/lib/fleet-cache.js +1 -1
- package/dist/lib/format.d.ts +19 -0
- package/dist/lib/format.js +44 -1
- package/dist/lib/friction-heuristics.d.ts +1 -1
- package/dist/lib/git.d.ts +24 -1
- package/dist/lib/git.js +82 -38
- package/dist/lib/heal.js +3 -3
- package/dist/lib/hooks.d.ts +18 -0
- package/dist/lib/hooks.js +113 -1
- package/dist/lib/hosts/credentials.d.ts +30 -10
- package/dist/lib/hosts/credentials.js +38 -26
- package/dist/lib/hosts/dispatch.d.ts +3 -3
- package/dist/lib/hosts/dispatch.js +3 -3
- package/dist/lib/hosts/option.d.ts +3 -3
- package/dist/lib/hosts/option.js +7 -8
- package/dist/lib/hosts/passthrough.d.ts +17 -17
- package/dist/lib/hosts/passthrough.js +67 -50
- package/dist/lib/hosts/providers/devices.d.ts +1 -1
- package/dist/lib/hosts/providers/devices.js +1 -1
- package/dist/lib/hosts/reconcile.d.ts +1 -1
- package/dist/lib/hosts/reconcile.js +1 -1
- package/dist/lib/hosts/reconnect.d.ts +1 -1
- package/dist/lib/hosts/reconnect.js +2 -2
- package/dist/lib/hosts/registry.d.ts +5 -5
- package/dist/lib/hosts/registry.js +5 -5
- package/dist/lib/hosts/remote-cmd.d.ts +10 -10
- package/dist/lib/hosts/remote-cmd.js +25 -18
- package/dist/lib/hosts/remote-os.d.ts +4 -4
- package/dist/lib/hosts/remote-os.js +4 -4
- package/dist/lib/hosts/remote-session-id.d.ts +2 -2
- package/dist/lib/hosts/remote-session-id.js +2 -2
- package/dist/lib/hosts/routing-flag.d.ts +3 -3
- package/dist/lib/hosts/routing-flag.js +4 -5
- package/dist/lib/hosts/run-target.d.ts +4 -4
- package/dist/lib/hosts/run-target.js +4 -4
- package/dist/lib/hosts/session-index.d.ts +2 -2
- package/dist/lib/hosts/session-index.js +1 -1
- package/dist/lib/hosts/session-marker.d.ts +1 -1
- package/dist/lib/hosts/session-marker.js +1 -1
- package/dist/lib/hosts/types.d.ts +2 -2
- package/dist/lib/hosts/types.js +2 -2
- package/dist/lib/import.js +1 -1
- package/dist/lib/{migrate.d.ts → installations/migrate.d.ts} +13 -1
- package/dist/lib/{migrate.js → installations/migrate.js} +160 -40
- package/dist/lib/installations/resolve.js +1 -1
- package/dist/lib/installations/strategies.js +1 -1
- package/dist/lib/installations/update.js +2 -2
- package/dist/lib/{versions.d.ts → installations/versions.d.ts} +12 -3
- package/dist/lib/{versions.js → installations/versions.js} +51 -31
- package/dist/lib/isolation-boundary-report.js +1 -1
- package/dist/lib/linear-projects.d.ts +28 -0
- package/dist/lib/linear-projects.js +30 -0
- package/dist/lib/loop.js +1 -1
- package/dist/lib/mailbox-gc.js +1 -1
- package/dist/lib/mailbox.js +1 -1
- package/dist/lib/mcp-registry.d.ts +62 -0
- package/dist/lib/mcp-registry.js +177 -0
- package/dist/lib/mcp.d.ts +5 -0
- package/dist/lib/mcp.js +124 -307
- package/dist/lib/menubar/MenubarHelper.app/Contents/CodeResources +0 -0
- package/dist/lib/menubar/MenubarHelper.app/Contents/MacOS/MenubarHelper +0 -0
- package/dist/lib/menubar/install-menubar.d.ts +7 -0
- package/dist/lib/menubar/install-menubar.js +24 -6
- package/dist/lib/menubar/snapshot.d.ts +7 -0
- package/dist/lib/menubar/snapshot.js +8 -6
- package/dist/lib/models.js +1 -1
- package/dist/lib/monitors/config.d.ts +12 -0
- package/dist/lib/monitors/config.js +3 -0
- package/dist/lib/monitors/dispatch.js +26 -5
- package/dist/lib/monitors/engine.js +10 -0
- package/dist/lib/monitors/sources/webhook.d.ts +1 -1
- package/dist/lib/monitors/state.d.ts +42 -0
- package/dist/lib/monitors/state.js +30 -0
- package/dist/lib/notify.d.ts +1 -1
- package/dist/lib/observe-aliases.d.ts +3 -3
- package/dist/lib/observe-aliases.js +3 -21
- package/dist/lib/output/git-output.js +2 -2
- package/dist/lib/overdue.d.ts +1 -1
- package/dist/lib/overdue.js +1 -1
- package/dist/lib/permissions-registry.d.ts +65 -0
- package/dist/lib/permissions-registry.js +599 -0
- package/dist/lib/permissions.d.ts +24 -13
- package/dist/lib/permissions.js +52 -271
- package/dist/lib/picker.d.ts +16 -0
- package/dist/lib/picker.js +33 -3
- package/dist/lib/placement.d.ts +1 -1
- package/dist/lib/placement.js +4 -4
- package/dist/lib/platform/paths.d.ts +8 -0
- package/dist/lib/platform/paths.js +11 -0
- package/dist/lib/platform/posixpath.js +3 -3
- package/dist/lib/{plugin-marketplace.d.ts → plugins/plugin-marketplace.d.ts} +1 -1
- package/dist/lib/{plugin-marketplace.js → plugins/plugin-marketplace.js} +2 -2
- package/dist/lib/{plugins.d.ts → plugins/plugins.d.ts} +1 -1
- package/dist/lib/{plugins.js → plugins/plugins.js} +7 -7
- package/dist/lib/{skills.d.ts → plugins/skills.d.ts} +1 -1
- package/dist/lib/{skills.js → plugins/skills.js} +7 -7
- package/dist/lib/pricing/cost.d.ts +1 -1
- package/dist/lib/pricing/cost.js +1 -1
- package/dist/lib/profiles-presets.d.ts +1 -1
- package/dist/lib/profiles-presets.js +1 -1
- package/dist/lib/profiles.js +2 -2
- package/dist/lib/project-import.js +4 -1
- package/dist/lib/project-key.js +7 -0
- package/dist/lib/project-launch.d.ts +1 -1
- package/dist/lib/project-launch.js +3 -3
- package/dist/lib/project-pull.d.ts +128 -0
- package/dist/lib/project-pull.js +384 -0
- package/dist/lib/project-root.d.ts +2 -2
- package/dist/lib/project-root.js +2 -2
- package/dist/lib/project-status.js +1 -1
- package/dist/lib/projects.d.ts +23 -0
- package/dist/lib/projects.js +29 -6
- package/dist/lib/refresh.d.ts +12 -1
- package/dist/lib/refresh.js +16 -9
- package/dist/lib/registry.d.ts +9 -2
- package/dist/lib/registry.js +11 -4
- package/dist/lib/resources.d.ts +2 -2
- package/dist/lib/resources.js +2 -2
- package/dist/lib/routers.d.ts +81 -0
- package/dist/lib/routers.js +153 -0
- package/dist/lib/routine-context.d.ts +1 -0
- package/dist/lib/routine-context.js +7 -2
- package/dist/lib/routine-notify-owner.d.ts +1 -1
- package/dist/lib/routine-notify.d.ts +1 -1
- package/dist/lib/routine-notify.js +1 -1
- package/dist/lib/routine-process-cleanup.d.ts +1 -1
- package/dist/lib/routine-readiness.d.ts +1 -1
- package/dist/lib/routine-readiness.js +11 -4
- package/dist/lib/routines-placement.d.ts +12 -2
- package/dist/lib/routines-placement.js +20 -3
- package/dist/lib/routines-project.d.ts +1 -1
- package/dist/lib/routines-project.js +1 -1
- package/dist/lib/rules/compile.d.ts +1 -0
- package/dist/lib/rules/compile.js +10 -2
- package/dist/lib/rules/rules.js +1 -1
- package/dist/lib/run-notify.js +1 -1
- package/dist/lib/runner.d.ts +80 -2
- package/dist/lib/runner.js +261 -67
- package/dist/lib/sandbox.d.ts +1 -1
- package/dist/lib/scheduler.d.ts +1 -1
- package/dist/lib/scheduler.js +1 -1
- package/dist/lib/{routines.d.ts → scheduling/routines.d.ts} +54 -9
- package/dist/lib/{routines.js → scheduling/routines.js} +173 -14
- package/dist/lib/secrets/Agents CLI.app/Contents/CodeResources +0 -0
- package/dist/lib/secrets/Agents CLI.app/Contents/MacOS/Agents CLI +0 -0
- package/dist/lib/secrets/agent.js +2 -2
- package/dist/lib/secrets/audit.js +1 -1
- package/dist/lib/secrets/bundles.d.ts +4 -4
- package/dist/lib/secrets/bundles.js +8 -8
- package/dist/lib/secrets/headless.d.ts +16 -0
- package/dist/lib/secrets/headless.js +21 -0
- package/dist/lib/secrets/index.js +1 -1
- package/dist/lib/secrets/list-filter.d.ts +1 -1
- package/dist/lib/secrets/list-filter.js +2 -2
- package/dist/lib/secrets/push.d.ts +1 -1
- package/dist/lib/secrets/push.js +10 -12
- package/dist/lib/secrets/remote.d.ts +21 -17
- package/dist/lib/secrets/remote.js +39 -23
- package/dist/lib/secrets/vault.js +2 -2
- package/dist/lib/self-heal/checks/shadowing.js +1 -1
- package/dist/lib/self-heal/checks/shims.js +1 -1
- package/dist/lib/self-update.d.ts +39 -4
- package/dist/lib/self-update.js +78 -25
- package/dist/lib/serve/control.d.ts +1 -1
- package/dist/lib/serve/control.js +4 -4
- package/dist/lib/serve/data.d.ts +1 -1
- package/dist/lib/serve/data.js +1 -1
- package/dist/lib/serve/stream.js +1 -1
- package/dist/lib/service-manifest.d.ts +29 -0
- package/dist/lib/service-manifest.js +80 -0
- package/dist/lib/session/active.d.ts +59 -7
- package/dist/lib/session/active.js +158 -17
- package/dist/lib/session/bash-command.js +1 -1
- package/dist/lib/session/claude-accounts.d.ts +1 -1
- package/dist/lib/session/claude-accounts.js +1 -1
- package/dist/lib/session/db.d.ts +10 -2
- package/dist/lib/session/db.js +25 -6
- package/dist/lib/session/discover.d.ts +77 -0
- package/dist/lib/session/discover.js +130 -25
- package/dist/lib/session/fork.js +1 -1
- package/dist/lib/session/inject.d.ts +1 -1
- package/dist/lib/session/insights.d.ts +1 -1
- package/dist/lib/session/insights.js +1 -1
- package/dist/lib/session/live-metadata.d.ts +15 -0
- package/dist/lib/session/live-metadata.js +76 -0
- package/dist/lib/session/migrate-targets.d.ts +1 -1
- package/dist/lib/session/recovery.d.ts +1 -1
- package/dist/lib/session/recovery.js +2 -2
- package/dist/lib/session/remote-active.d.ts +10 -1
- package/dist/lib/session/remote-active.js +8 -3
- package/dist/lib/session/remote-bundle.d.ts +1 -1
- package/dist/lib/session/remote-bundle.js +2 -2
- package/dist/lib/session/remote-list.d.ts +15 -4
- package/dist/lib/session/remote-list.js +40 -3
- package/dist/lib/session/remote.d.ts +5 -5
- package/dist/lib/session/remote.js +13 -17
- package/dist/lib/session/resume-command.d.ts +3 -2
- package/dist/lib/session/resume-command.js +4 -3
- package/dist/lib/session/resume-owner.d.ts +3 -3
- package/dist/lib/session/resume-owner.js +3 -3
- package/dist/lib/session/session-cache.d.ts +12 -0
- package/dist/lib/session/session-cache.js +2 -0
- package/dist/lib/session/share-html.d.ts +55 -0
- package/dist/lib/session/share-html.js +319 -0
- package/dist/lib/session/shell-programs.js +1 -1
- package/dist/lib/session/state.js +5 -5
- package/dist/lib/session/team-filter.d.ts +1 -1
- package/dist/lib/session/team-filter.js +1 -1
- package/dist/lib/session/types.d.ts +3 -3
- package/dist/lib/session/width.d.ts +2 -2
- package/dist/lib/session/width.js +2 -2
- package/dist/lib/settings-manifest.d.ts +2 -0
- package/dist/lib/settings-manifest.js +81 -3
- package/dist/lib/share/delete.d.ts +21 -0
- package/dist/lib/share/delete.js +42 -0
- package/dist/lib/share/publish.d.ts +120 -0
- package/dist/lib/share/publish.js +243 -18
- package/dist/lib/share/worker-template.js +172 -9
- package/dist/lib/shims.js +35 -7
- package/dist/lib/smart-launch.d.ts +4 -4
- package/dist/lib/smart-launch.js +6 -4
- package/dist/lib/snapshot.d.ts +4 -4
- package/dist/lib/snapshot.js +2 -2
- package/dist/lib/ssh-exec.d.ts +1 -1
- package/dist/lib/ssh-exec.js +2 -2
- package/dist/lib/ssh-tunnel.d.ts +10 -10
- package/dist/lib/ssh-tunnel.js +12 -12
- package/dist/lib/staleness/checkers/hooks.d.ts +1 -1
- package/dist/lib/staleness/checkers/hooks.js +1 -1
- package/dist/lib/staleness/detectors/permissions.js +0 -23
- package/dist/lib/staleness/detectors/plugins.js +1 -1
- package/dist/lib/staleness/fingerprint.js +1 -1
- package/dist/lib/staleness/layers.d.ts +1 -1
- package/dist/lib/staleness/layers.js +1 -1
- package/dist/lib/staleness/writers/mcp.js +3 -1
- package/dist/lib/staleness/writers/plugins.js +1 -1
- package/dist/lib/staleness/writers/types.d.ts +8 -0
- package/dist/lib/startup/command-registry.d.ts +6 -147
- package/dist/lib/startup/command-registry.js +40 -303
- package/dist/lib/startup/dev-build.d.ts +0 -21
- package/dist/lib/startup/dev-build.js +29 -8
- package/dist/lib/state.d.ts +19 -1
- package/dist/lib/state.js +120 -51
- package/dist/lib/subagents.js +1 -1
- package/dist/lib/sync-status.js +1 -1
- package/dist/lib/sync-umbrella.d.ts +6 -1
- package/dist/lib/sync-umbrella.js +9 -4
- package/dist/lib/teams/agents.js +2 -2
- package/dist/lib/teams/registry.js +1 -1
- package/dist/lib/terminal/backends/vscodium-agent.js +1 -1
- package/dist/lib/terminal/engine.js +1 -1
- package/dist/lib/terminal/index.d.ts +4 -0
- package/dist/lib/terminal/index.js +3 -0
- package/dist/lib/terminal/inject.d.ts +2 -2
- package/dist/lib/terminal/inject.js +2 -2
- package/dist/lib/terminal/transport.js +1 -1
- package/dist/lib/tmux/index.d.ts +1 -1
- package/dist/lib/tmux/index.js +1 -1
- package/dist/lib/tmux/session.d.ts +21 -5
- package/dist/lib/tmux/session.js +76 -22
- package/dist/lib/triggers/handlers.d.ts +38 -3
- package/dist/lib/triggers/handlers.js +58 -7
- package/dist/lib/triggers/webhook.d.ts +113 -5
- package/dist/lib/triggers/webhook.js +223 -44
- package/dist/lib/types.d.ts +19 -7
- package/dist/lib/usage-fleet.d.ts +1 -1
- package/dist/lib/usage-fleet.js +1 -1
- package/dist/lib/usage-refresh.d.ts +1 -1
- package/dist/lib/usage-refresh.js +3 -3
- package/dist/lib/version-duplicates.js +2 -2
- package/dist/lib/view-types.d.ts +56 -0
- package/dist/lib/view-types.js +1 -0
- package/dist/lib/watchdog/rotate.d.ts +2 -2
- package/dist/lib/watchdog/rotate.js +3 -3
- package/dist/lib/watchdog/runner.d.ts +2 -3
- package/dist/lib/watchdog/runner.js +3 -4
- package/dist/lib/workflows.js +1 -1
- package/dist/lib/wrap.d.ts +33 -0
- package/dist/lib/wrap.js +70 -0
- package/package.json +2 -1
- package/scripts/postinstall.js +2 -2
- package/dist/commands/bench.d.ts +0 -2
- package/dist/commands/bench.js +0 -101
- package/dist/commands/cp.d.ts +0 -84
- package/dist/commands/cp.js +0 -231
- package/dist/commands/login.d.ts +0 -2
- package/dist/lib/resources/commands.d.ts +0 -46
- package/dist/lib/resources/commands.js +0 -211
- package/dist/lib/resources/hooks.d.ts +0 -16
- package/dist/lib/resources/hooks.js +0 -153
- package/dist/lib/resources/mcp.d.ts +0 -34
- package/dist/lib/resources/mcp.js +0 -250
- package/dist/lib/resources/memory.d.ts +0 -15
- package/dist/lib/resources/memory.js +0 -46
- package/dist/lib/resources/permissions.d.ts +0 -13
- package/dist/lib/resources/permissions.js +0 -202
- package/dist/lib/resources/rules.d.ts +0 -46
- package/dist/lib/resources/rules.js +0 -179
- package/dist/lib/resources/skills.d.ts +0 -37
- package/dist/lib/resources/skills.js +0 -247
- package/dist/lib/resources/subagents.d.ts +0 -46
- package/dist/lib/resources/subagents.js +0 -202
- /package/dist/lib/{feed.d.ts → feed/feed.d.ts} +0 -0
package/dist/commands/secrets.js
CHANGED
|
@@ -14,11 +14,11 @@ import * as os from 'os';
|
|
|
14
14
|
import * as path from 'path';
|
|
15
15
|
import { SSH_TARGET_RE, assertValidSshTarget } from '../lib/ssh-exec.js';
|
|
16
16
|
import { quoteWin32ExecArg, composeWin32CommandLine } from '../lib/platform/index.js';
|
|
17
|
-
import { ensureDaemonStarted, isDaemonRunning } from '../lib/daemon.js';
|
|
17
|
+
import { ensureDaemonStarted, isDaemonRunning } from '../lib/daemon/daemon.js';
|
|
18
18
|
import { parseHostsOption, remoteResolveEnv, remoteSecretsRaw, remoteSecretsStream, resolveHostSshTarget, } from '../lib/secrets/remote.js';
|
|
19
19
|
import { resolveBundleForPush, pushResolvedBundleToHost, bundleEnvToDotenv } from '../lib/secrets/push.js';
|
|
20
20
|
export { bundleEnvToDotenv };
|
|
21
|
-
import { bundleBackend, bundleExists, bundleItemStore, bundlePolicy, deleteBundle, describeBundle, keychainItemsForBundle, keychainRef, listBundles, healKeychainBundleMetadataAclOnce, isHeadlessSecretsContext, migrateLegacyBundles, parseDotenv, readAndResolveBundleEnv, readBundle, readBundleIfDecryptable, reAclBundleItems, renameBundle, rotateBundleSecret, sanitizeProcessEnv, validateBundleName, validateEnvKey, validateExpiresFutureDated, validateSecretType, writeBundle, writeBundleWithItems, SECRET_TYPES, } from '../lib/secrets/bundles.js';
|
|
21
|
+
import { bundleBackend, bundleExists, bundleItemStore, bundlePolicy, deleteBundle, describeBundle, keychainItemsForBundle, keychainRef, listBundles, healKeychainBundleMetadataAclOnce, isHeadlessSecretsContext, isAgentInvocationContext, migrateLegacyBundles, parseDotenv, readAndResolveBundleEnv, readBundle, readBundleIfDecryptable, reAclBundleItems, renameBundle, rotateBundleSecret, sanitizeProcessEnv, validateBundleName, validateEnvKey, validateExpiresFutureDated, validateSecretType, writeBundle, writeBundleWithItems, SECRET_TYPES, } from '../lib/secrets/bundles.js';
|
|
22
22
|
import { parseListFilters, bundleMatchesFilter, bundleExpiry, filterIsActive, describeFilter, parseSortField, sortBundles, SORT_FIELDS, REF_KINDS, DEFAULT_EXPIRING_DAYS, } from '../lib/secrets/list-filter.js';
|
|
23
23
|
import { encryptForFallback, decryptForFallback } from '../lib/secrets/filestore.js';
|
|
24
24
|
import { getKeychainToken, hasKeychainToken, secretsKeychainItem, setKeychainToken, maybeAutoRekey, } from '../lib/secrets/index.js';
|
|
@@ -30,7 +30,7 @@ import { saveSession, deleteBundleSessions, deleteAllSessions } from '../lib/sec
|
|
|
30
30
|
import { getCliVersionFresh } from '../lib/version.js';
|
|
31
31
|
import { readMeta } from '../lib/state.js';
|
|
32
32
|
import { parseDuration } from '../lib/hooks/cache.js';
|
|
33
|
-
import { emit, query } from '../lib/events.js';
|
|
33
|
+
import { emit, query } from '../lib/feed/events.js';
|
|
34
34
|
import { emitSecretAudit } from '../lib/secrets/audit.js';
|
|
35
35
|
import { SYNC_PASSPHRASE_ENV, resolveSyncPassphraseFromEnv } from '../lib/secrets/sync-passphrase.js';
|
|
36
36
|
import { getBundleUsage, getAllBundleUsage, getUsageHistory, SECRET_USAGE_EVENTS, } from '../lib/secrets/usage-db.js';
|
|
@@ -38,11 +38,11 @@ import { frequentlyPromptedBundles } from '../lib/secrets/unlock-hints.js';
|
|
|
38
38
|
import { registerCommandGroups, setHelpSections } from '../lib/help.js';
|
|
39
39
|
import { isInteractiveTerminal, isPromptCancelled } from './utils.js';
|
|
40
40
|
import { discoverSyncedBundles, importSyncedBundle, } from '../lib/secrets/icloud-import.js';
|
|
41
|
-
import { getVaultSession, vaultExists } from '../lib/secrets/vault.js';
|
|
42
41
|
import { registerSecretsSyncCommands } from './secrets-sync.js';
|
|
43
42
|
import { registerSecretsMigrateAclCommand } from './secrets-migrate.js';
|
|
44
43
|
import { registerSecretsImportKeyringCommand } from './secrets-import.js';
|
|
45
44
|
import { registerSecretsRotatePassphraseCommand } from './secrets-rotate-passphrase.js';
|
|
45
|
+
import { registerSecretsVaultCommands } from './secrets-vault.js';
|
|
46
46
|
/** Prompt the user for a secret value with masked input. Requires an interactive TTY. */
|
|
47
47
|
async function promptForSecret(message) {
|
|
48
48
|
if (!isInteractiveTerminal()) {
|
|
@@ -144,7 +144,7 @@ async function resolveVault(vaultOpt) {
|
|
|
144
144
|
/** Read all available data from stdin synchronously, trimmed. */
|
|
145
145
|
/**
|
|
146
146
|
* Read the raw `.env` text for `import --from <path|->`. A `-` reads the .env
|
|
147
|
-
* from stdin (the SSH push path: `export --
|
|
147
|
+
* from stdin (the SSH push path: `export --device` pipes the resolved dotenv over
|
|
148
148
|
* ssh stdin, which has no `/dev/stdin` on a Windows remote); any other value is
|
|
149
149
|
* a filesystem path.
|
|
150
150
|
*/
|
|
@@ -262,7 +262,7 @@ function maybePrintSyncedHint(name, stillPresent) {
|
|
|
262
262
|
}
|
|
263
263
|
}
|
|
264
264
|
/**
|
|
265
|
-
* Build the remote `agents secrets unlock` argv for `unlock --
|
|
265
|
+
* Build the remote `agents secrets unlock` argv for `unlock --device`. `--all`
|
|
266
266
|
* forwards verbatim; otherwise the explicit bundle names. A `--ttl` is passed
|
|
267
267
|
* through as-is so the REMOTE parses its own duration (its platform rules, its
|
|
268
268
|
* defaults). Shared with the command action so the wiring is unit-testable
|
|
@@ -323,10 +323,10 @@ export function buildRemoteUnlockArgs(names, opts) {
|
|
|
323
323
|
];
|
|
324
324
|
}
|
|
325
325
|
/**
|
|
326
|
-
* Build the remote `agents secrets list` argv for `list --
|
|
326
|
+
* Build the remote `agents secrets list` argv for `list --device`. Every filter
|
|
327
327
|
* must be forwarded: `browseRemote` sends this argv verbatim, so a flag left out
|
|
328
328
|
* here is not an error — the remote just lists everything, and
|
|
329
|
-
* `secrets list --
|
|
329
|
+
* `secrets list --device zion --expired` reports every bundle on zion as expired.
|
|
330
330
|
* Silent and wrong beats loud and wrong only for the person who wrote the bug.
|
|
331
331
|
*
|
|
332
332
|
* Values pass through unparsed so the REMOTE validates them under its own rules,
|
|
@@ -923,14 +923,17 @@ export function registerSecretsCommands(program) {
|
|
|
923
923
|
# Stop a noisy automation bundle from prompting every run: ask once a week
|
|
924
924
|
agents secrets policy prod hold
|
|
925
925
|
|
|
926
|
-
#
|
|
927
|
-
|
|
926
|
+
# Run a one-off command with secrets injected (values ride the child env, never stdout)
|
|
927
|
+
agents secrets exec prod -- ./deploy.sh
|
|
928
928
|
|
|
929
|
-
#
|
|
930
|
-
agents secrets
|
|
929
|
+
# Need one value inside a script? Read it under injection, not with a printer
|
|
930
|
+
agents secrets exec prod -- printenv STRIPE_API_KEY
|
|
931
931
|
|
|
932
|
-
#
|
|
933
|
-
agents secrets
|
|
932
|
+
# Deliberately reveal values at your terminal (interactive only)
|
|
933
|
+
agents secrets view prod --reveal
|
|
934
|
+
|
|
935
|
+
# Push the bundle to remote machine(s) over SSH (lands as a native bundle there)
|
|
936
|
+
agents secrets export prod --device yosemite-s0 --device yosemite-s1 --force
|
|
934
937
|
`,
|
|
935
938
|
notes: `
|
|
936
939
|
Bundles are containers; secrets are the variables inside them. Keychain values
|
|
@@ -983,15 +986,15 @@ export function registerSecretsCommands(program) {
|
|
|
983
986
|
{ title: 'Raw item commands', names: ['get', 'set'] },
|
|
984
987
|
{ title: 'Sync commands', names: ['push', 'pull', 'remote-list'] },
|
|
985
988
|
{ title: 'Utilities', names: ['exec', 'mcp', 'generate', 'migrate-acl'] },
|
|
989
|
+
{ title: 'Synced vault', names: ['vault'] },
|
|
986
990
|
]);
|
|
991
|
+
registerSecretsVaultCommands(cmd);
|
|
987
992
|
const listCmd = cmd
|
|
988
993
|
.command('list [query]')
|
|
989
994
|
.alias('ls')
|
|
990
|
-
.description('List configured secrets bundles, optionally filtered (use --
|
|
991
|
-
.option('--
|
|
992
|
-
.option('--
|
|
993
|
-
.option('--device <target>', 'Alias for --host')
|
|
994
|
-
.option('--devices <list>', 'Alias for --hosts')
|
|
995
|
+
.description('List configured secrets bundles, optionally filtered (use --device/--devices for other machines over SSH)')
|
|
996
|
+
.option('--device <target>', 'List bundles on a remote device over SSH (enrolled `agents hosts` name, ssh-config alias, or user@host)')
|
|
997
|
+
.option('--devices <list>', 'Comma-separated devices to list in one shot, e.g. yosemite-s0,yosemite-s1')
|
|
995
998
|
.option('--json', 'Emit machine-readable JSON (bundle metadata only — never secret values) instead of the table')
|
|
996
999
|
.option('--policy <list>', "Only these prompt policies (comma-separated): hold, always, never. '--policy never' is the audit for bundles that read with no Touch ID at all")
|
|
997
1000
|
.option('--backend <list>', 'Only these backends (comma-separated): keychain, file, vault')
|
|
@@ -1005,7 +1008,7 @@ export function registerSecretsCommands(program) {
|
|
|
1005
1008
|
.addOption(new Option('--sort <field>', 'Sort by: name (default), used (most recently used), uses (most frequently accessed), created, updated, expiry').choices([...SORT_FIELDS]))
|
|
1006
1009
|
.option('-n, --limit <n>', 'Show at most this many bundles (after filtering and sorting)')
|
|
1007
1010
|
.action(async (query, opts) => {
|
|
1008
|
-
const targets = parseHostsOption(opts);
|
|
1011
|
+
const targets = parseHostsOption({ device: opts.device, devices: opts.devices });
|
|
1009
1012
|
if (targets.length > 0) {
|
|
1010
1013
|
// Forward the filters — browseRemote sends this argv verbatim, so a
|
|
1011
1014
|
// dropped flag would make the remote silently list everything.
|
|
@@ -1028,7 +1031,7 @@ export function registerSecretsCommands(program) {
|
|
|
1028
1031
|
// hold-state filter would answer from no data — every bundle would look
|
|
1029
1032
|
// not-held. Refuse rather than return a confidently wrong list.
|
|
1030
1033
|
if (filter.held !== undefined && process.platform !== 'darwin') {
|
|
1031
|
-
throw new Error('--held/--not-held need the secrets-agent, which is macOS-only. Run it on a Mac, or with --
|
|
1034
|
+
throw new Error('--held/--not-held need the secrets-agent, which is macOS-only. Run it on a Mac, or with --device <mac>.');
|
|
1032
1035
|
}
|
|
1033
1036
|
}
|
|
1034
1037
|
catch (err) {
|
|
@@ -1160,16 +1163,16 @@ export function registerSecretsCommands(program) {
|
|
|
1160
1163
|
agents secrets list --policy hold --backend file --expiring
|
|
1161
1164
|
|
|
1162
1165
|
# Same filters, on another machine, machine-readable
|
|
1163
|
-
agents secrets list --expired --
|
|
1166
|
+
agents secrets list --expired --device mac-mini --json
|
|
1164
1167
|
`,
|
|
1165
1168
|
notes: `
|
|
1166
1169
|
- Filters compose: every flag you add narrows the list further.
|
|
1167
1170
|
- An unknown value is an error, not an empty list — '--policy hodl' names the valid set.
|
|
1168
|
-
- --held/--not-held read live broker state, so they need macOS. Use --
|
|
1171
|
+
- --held/--not-held read live broker state, so they need macOS. Use --device <mac> from elsewhere.
|
|
1169
1172
|
- --expired and --expiring are different questions: already lapsed vs coming due. The EXPIRING column counts both and turns red once anything has lapsed.
|
|
1170
1173
|
- --unused matches bundles never read at all, not just old ones.
|
|
1171
1174
|
- Filters apply before --json, so the JSON is the exact twin of the table.
|
|
1172
|
-
- Filters are forwarded over --
|
|
1175
|
+
- Filters are forwarded over --device, so a remote list narrows the same way.
|
|
1173
1176
|
- --sort used|uses read the value-free usage read-model (~/.agents/secrets/secrets.db); see 'agents secrets activity'.
|
|
1174
1177
|
`,
|
|
1175
1178
|
});
|
|
@@ -1221,16 +1224,23 @@ export function registerSecretsCommands(program) {
|
|
|
1221
1224
|
.command('view [name]')
|
|
1222
1225
|
.alias('show')
|
|
1223
1226
|
.description('Show a bundle. Keychain values are masked by default — pass --reveal to see them.')
|
|
1224
|
-
.option('--reveal', 'Print keychain-backed values in the clear (
|
|
1225
|
-
.option('--plaintext', 'Allow --reveal in non-interactive shells (use with care)')
|
|
1227
|
+
.option('--reveal', 'Print keychain-backed values in the clear (interactive terminal only)')
|
|
1226
1228
|
.option('--json', 'Emit machine-readable JSON (values masked unless --reveal) instead of the human view')
|
|
1227
|
-
.option('--
|
|
1228
|
-
.option('--
|
|
1229
|
-
.option('--device <target>', 'Alias for --host')
|
|
1230
|
-
.option('--devices <list>', 'Alias for --hosts')
|
|
1229
|
+
.option('--device <target>', 'Show a bundle on a remote device over SSH (enrolled `agents hosts` name, ssh-config alias, or user@host)')
|
|
1230
|
+
.option('--devices <list>', 'Comma-separated devices to show in one shot, e.g. yosemite-s0,yosemite-s1')
|
|
1231
1231
|
.action(async (name, opts) => {
|
|
1232
1232
|
try {
|
|
1233
|
-
|
|
1233
|
+
// `--reveal` is a deliberate HUMAN reveal: interactive terminal only, and
|
|
1234
|
+
// never inside an agent session (an agent under tmux has a TTY, so the
|
|
1235
|
+
// env markers are the load-bearing check). The old `--plaintext` escape
|
|
1236
|
+
// that allowed a piped/non-TTY reveal is removed (RUSH-2774) — printed
|
|
1237
|
+
// values land in an agent's context and session transcript.
|
|
1238
|
+
if (opts.reveal && (isAgentInvocationContext() || !isInteractiveTerminal())) {
|
|
1239
|
+
console.error(chalk.red('--reveal prints values in the clear and needs a real interactive terminal outside an agent session.'));
|
|
1240
|
+
console.error(chalk.dim('Run the consuming command under injection instead: agents secrets exec <bundle> -- <cmd>'));
|
|
1241
|
+
process.exit(1);
|
|
1242
|
+
}
|
|
1243
|
+
const targets = parseHostsOption({ device: opts.device, devices: opts.devices });
|
|
1234
1244
|
if (targets.length > 0) {
|
|
1235
1245
|
if (!name) {
|
|
1236
1246
|
console.error(chalk.red('A bundle name is required when viewing a remote host (interactive pick needs a local terminal).'));
|
|
@@ -1239,14 +1249,13 @@ export function registerSecretsCommands(program) {
|
|
|
1239
1249
|
const args = ['view', name];
|
|
1240
1250
|
if (opts.reveal)
|
|
1241
1251
|
args.push('--reveal');
|
|
1242
|
-
if (opts.plaintext)
|
|
1243
|
-
args.push('--plaintext');
|
|
1244
1252
|
if (opts.json)
|
|
1245
1253
|
args.push('--json');
|
|
1246
1254
|
// With --reveal, force a TTY so the remote keychain prompt can surface
|
|
1247
|
-
//
|
|
1248
|
-
//
|
|
1249
|
-
|
|
1255
|
+
// and the remote's own interactive-terminal gate is satisfied. The
|
|
1256
|
+
// early gate above already proved this side is an interactive,
|
|
1257
|
+
// non-agent terminal.
|
|
1258
|
+
const tty = Boolean(opts.reveal);
|
|
1250
1259
|
// `--reveal` streams the plaintext value back over ssh stdout — a
|
|
1251
1260
|
// secret-bearing read that must pin the host key and not multiplex,
|
|
1252
1261
|
// whether it runs on the interactive (tty) or `--plaintext` non-tty path.
|
|
@@ -1289,14 +1298,10 @@ export function registerSecretsCommands(program) {
|
|
|
1289
1298
|
emitSecretAudit({ event: 'secrets.view', bundle: bundle.name, operation: opts.json ? 'view --json' : 'view', source: 'view', status: 'success' });
|
|
1290
1299
|
if (opts.json) {
|
|
1291
1300
|
// Machine-readable discovery for agents. Values are null unless --reveal
|
|
1292
|
-
// (
|
|
1293
|
-
//
|
|
1294
|
-
//
|
|
1301
|
+
// (already gated above: interactive terminal, outside an agent session).
|
|
1302
|
+
// Gated on the explicit flag, not stdout.isTTY, so a piped human view
|
|
1303
|
+
// never silently swaps formats.
|
|
1295
1304
|
const reveal = Boolean(opts.reveal);
|
|
1296
|
-
if (reveal && !isInteractiveTerminal() && !opts.plaintext) {
|
|
1297
|
-
console.error(chalk.red('--reveal in a non-TTY requires --plaintext.'));
|
|
1298
|
-
process.exit(1);
|
|
1299
|
-
}
|
|
1300
1305
|
const revealed = new Map();
|
|
1301
1306
|
if (reveal) {
|
|
1302
1307
|
const { env } = readAndResolveBundleEnv(bundle.name, {
|
|
@@ -1382,7 +1387,7 @@ export function registerSecretsCommands(program) {
|
|
|
1382
1387
|
if (bundle.backend === 'file')
|
|
1383
1388
|
console.log(chalk.gray('backend: file (encrypted at rest; headless reads via a machine-local key, or AGENTS_SECRETS_PASSPHRASE if set — no Touch ID)'));
|
|
1384
1389
|
if (bundle.backend === 'vault')
|
|
1385
|
-
console.log(chalk.gray('storage: synced (age-encrypted ~/.agents/vault.age; needs agents
|
|
1390
|
+
console.log(chalk.gray('storage: synced (age-encrypted ~/.agents/vault.age; needs `agents secrets vault unlock`)'));
|
|
1386
1391
|
if (bundlePolicy(bundle) === 'never') {
|
|
1387
1392
|
console.log(chalk.red.bold('policy: never — NO biometry ACL; reads are silent (no Touch ID, no user-presence check). Automation-only.'));
|
|
1388
1393
|
}
|
|
@@ -1416,10 +1421,6 @@ export function registerSecretsCommands(program) {
|
|
|
1416
1421
|
return;
|
|
1417
1422
|
}
|
|
1418
1423
|
const reveal = Boolean(opts.reveal);
|
|
1419
|
-
if (reveal && !isInteractiveTerminal() && !opts.plaintext) {
|
|
1420
|
-
console.error(chalk.red('--reveal in a non-TTY requires --plaintext.'));
|
|
1421
|
-
process.exit(1);
|
|
1422
|
-
}
|
|
1423
1424
|
// An explicit human `view --reveal` at a terminal is a deliberate value
|
|
1424
1425
|
// access: resolve interactively (one Touch ID) so a locked bundle shows
|
|
1425
1426
|
// its values. Under an agent (AGENTS_RUNTIME) or headless (no TTY) it
|
|
@@ -1492,50 +1493,38 @@ export function registerSecretsCommands(program) {
|
|
|
1492
1493
|
});
|
|
1493
1494
|
cmd
|
|
1494
1495
|
.command('get <item> [key]')
|
|
1495
|
-
.description('Print one
|
|
1496
|
+
.description('Print one raw keychain item by name, for shell hooks/automation. Cross-platform. Bundle values are never printed — run commands under `agents secrets exec` instead.')
|
|
1496
1497
|
.action((item, key) => {
|
|
1497
|
-
if (key
|
|
1498
|
-
//
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
const value = getKeychainToken(item);
|
|
1506
|
-
// Raw item reads bypass readAndResolveBundleEnv, so audit here too.
|
|
1507
|
-
// `item` is the keychain service name, never the value.
|
|
1508
|
-
emitSecretAudit({ event: 'secrets.get', item, source: 'raw-item', status: 'success' });
|
|
1509
|
-
process.stdout.write(value.endsWith('\n') ? value : `${value}\n`);
|
|
1510
|
-
}
|
|
1511
|
-
catch {
|
|
1512
|
-
// Missing item is a normal, quiet outcome for a hook probe: exit 1,
|
|
1513
|
-
// print nothing to stdout. Callers test the exit code / empty capture.
|
|
1514
|
-
process.exit(1);
|
|
1515
|
-
}
|
|
1516
|
-
return;
|
|
1498
|
+
if (key !== undefined) {
|
|
1499
|
+
// The bundle-key form (`get <bundle> <KEY>`) is REMOVED (RUSH-2774): a
|
|
1500
|
+
// one-liner that prints a bundle credential is the exfiltration path
|
|
1501
|
+
// this change closes, for every caller. The injection replacement is
|
|
1502
|
+
// just as short for a human script and never touches this stdout.
|
|
1503
|
+
console.error(chalk.red(`'secrets get <bundle> <KEY>' has been removed — printing a bundle value to stdout is the exfiltration path this blocks.`));
|
|
1504
|
+
console.error(chalk.dim(`Use: agents secrets exec ${item} -- printenv ${key} (or run the consuming command directly under exec)`));
|
|
1505
|
+
process.exit(1);
|
|
1517
1506
|
}
|
|
1518
|
-
//
|
|
1519
|
-
//
|
|
1520
|
-
//
|
|
1507
|
+
// Raw keychain item path — a single ad-hoc token, not a credential bundle.
|
|
1508
|
+
// Deliberately NOT agent-gated (unlike the removed bundle-key form): shell
|
|
1509
|
+
// hooks that run inside agent sessions inherit the session env markers and
|
|
1510
|
+
// legitimately capture a raw item into their own variables (e.g. the
|
|
1511
|
+
// posthog analytics hook), where the value never reaches the transcript.
|
|
1512
|
+
// A single ad-hoc token is the accepted narrower residual (RUSH-2774).
|
|
1521
1513
|
try {
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
//
|
|
1527
|
-
|
|
1528
|
-
//
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
console.error(chalk.red(`Key '${key}' not in bundle '${item}'.`));
|
|
1532
|
-
process.exit(1);
|
|
1533
|
-
}
|
|
1534
|
-
const value = env[key];
|
|
1514
|
+
// Routes through the platform keychain layer: macOS reads bare items
|
|
1515
|
+
// via /usr/bin/security (no Touch ID), Linux via secret-tool with the
|
|
1516
|
+
// encrypted-file fallback. The value goes to stdout (newline-terminated
|
|
1517
|
+
// so `$(agents secrets get NAME)` captures it cleanly); diagnostics go
|
|
1518
|
+
// to stderr so they never pollute the captured value.
|
|
1519
|
+
const value = getKeychainToken(item);
|
|
1520
|
+
// Raw item reads bypass readAndResolveBundleEnv, so audit here too.
|
|
1521
|
+
// `item` is the keychain service name, never the value.
|
|
1522
|
+
emitSecretAudit({ event: 'secrets.get', item, source: 'raw-item', status: 'success' });
|
|
1535
1523
|
process.stdout.write(value.endsWith('\n') ? value : `${value}\n`);
|
|
1536
1524
|
}
|
|
1537
|
-
catch
|
|
1538
|
-
|
|
1525
|
+
catch {
|
|
1526
|
+
// Missing item is a normal, quiet outcome for a hook probe: exit 1,
|
|
1527
|
+
// print nothing to stdout. Callers test the exit code / empty capture.
|
|
1539
1528
|
process.exit(1);
|
|
1540
1529
|
}
|
|
1541
1530
|
});
|
|
@@ -2019,12 +2008,28 @@ Examples:
|
|
|
2019
2008
|
.option('--backend <backend>', 'When creating the bundle: keychain or file (defaults to agents.yaml secrets.backend)')
|
|
2020
2009
|
.option('--synced', 'When creating the bundle, store it in the age-encrypted synced secrets file')
|
|
2021
2010
|
.option('--force', 'Overwrite an existing key in the bundle')
|
|
2011
|
+
.option('--policy <policy>', 'Set the bundle prompt policy as part of this import (always, hold, or never)')
|
|
2012
|
+
.option('--i-understand', 'Confirm an import with policy never (no biometry ACL)')
|
|
2022
2013
|
.option('--purge', 'With --from icloud: delete the iCloud copies after a successful import (iCloud propagates the deletion to your other devices)')
|
|
2023
2014
|
.option('--from-file <path>', `Import from an AES-256-GCM encrypted offline bundle file (needs ${SYNC_PASSPHRASE_ENV}; symmetric counterpart of export --to-file)`)
|
|
2024
|
-
.option('--from-ssh', 'Pull the bundle from a fleet peer over SSH and import it locally (requires --
|
|
2025
|
-
.option('--
|
|
2015
|
+
.option('--from-ssh', 'Pull the bundle from a fleet peer over SSH and import it locally (requires --device)')
|
|
2016
|
+
.option('--device <peer>', 'SSH peer to pull from when using --from-ssh (device alias or user@host)')
|
|
2026
2017
|
.action(async (bundleName, opts) => {
|
|
2027
2018
|
try {
|
|
2019
|
+
const importPolicy = opts.policy ? parsePolicyOpt(opts.policy) : undefined;
|
|
2020
|
+
const policyAck = assertNeverPolicyAcknowledged(importPolicy, {
|
|
2021
|
+
iUnderstand: opts.iUnderstand,
|
|
2022
|
+
interactive: isInteractiveTerminal(),
|
|
2023
|
+
});
|
|
2024
|
+
if (policyAck === 'prompt' && !(await confirmNeverPolicyInteractive(bundleName ?? 'imported bundle'))) {
|
|
2025
|
+
console.error(chalk.yellow('Aborted.'));
|
|
2026
|
+
return;
|
|
2027
|
+
}
|
|
2028
|
+
const applyImportPolicy = (bundle) => {
|
|
2029
|
+
if (importPolicy)
|
|
2030
|
+
bundle.policy = importPolicy;
|
|
2031
|
+
return bundle;
|
|
2032
|
+
};
|
|
2028
2033
|
// A single import can name only one source. --from-file / --from-ssh are
|
|
2029
2034
|
// early-return paths, so guard them against each other and the --from /
|
|
2030
2035
|
// --from-1password pair (which parseImportSource guards on its own).
|
|
@@ -2043,24 +2048,24 @@ Examples:
|
|
|
2043
2048
|
}
|
|
2044
2049
|
const env = importBundleFromFile(opts.fromFile, passphrase);
|
|
2045
2050
|
const resolvedBundleName = bundleName ?? (await pickBundleName('import into'));
|
|
2046
|
-
const bundle = resolveImportBundle(resolvedBundleName, opts.backend, opts.synced, opts.force);
|
|
2051
|
+
const bundle = applyImportPolicy(resolveImportBundle(resolvedBundleName, opts.backend, opts.synced, opts.force));
|
|
2047
2052
|
const { added, skipped } = applyEnvToBundle(bundle, env, opts);
|
|
2048
2053
|
emitSecretAudit({ event: 'secrets.import', bundle: bundle.name, operation: 'import --from-file', source: 'file', status: 'success', keyCount: added });
|
|
2049
2054
|
console.log(chalk.green(`Imported ${added} key(s) from file${skipped ? `, skipped ${skipped} (already set, pass --force)` : ''}.`));
|
|
2050
2055
|
return;
|
|
2051
2056
|
}
|
|
2052
2057
|
if (opts.fromSsh) {
|
|
2053
|
-
if (!opts.
|
|
2054
|
-
throw new Error('--from-ssh requires --
|
|
2058
|
+
if (!opts.device) {
|
|
2059
|
+
throw new Error('--from-ssh requires --device <peer>.');
|
|
2055
2060
|
}
|
|
2056
|
-
assertValidSshTarget(opts.
|
|
2061
|
+
assertValidSshTarget(opts.device);
|
|
2057
2062
|
const resolvedBundleName = bundleName ?? (await pickBundleName('import into'));
|
|
2058
|
-
const target = await resolveHostSshTarget(opts.
|
|
2059
|
-
const env = await remoteResolveEnv(target, resolvedBundleName, { osLookupName: opts.
|
|
2060
|
-
const bundle = resolveImportBundle(resolvedBundleName, opts.backend, opts.synced, opts.force);
|
|
2063
|
+
const target = await resolveHostSshTarget(opts.device);
|
|
2064
|
+
const env = await remoteResolveEnv(target, resolvedBundleName, { osLookupName: opts.device });
|
|
2065
|
+
const bundle = applyImportPolicy(resolveImportBundle(resolvedBundleName, opts.backend, opts.synced, opts.force));
|
|
2061
2066
|
const { added, skipped } = applyEnvToBundle(bundle, env, opts);
|
|
2062
|
-
emitSecretAudit({ event: 'secrets.import', bundle: bundle.name, operation: 'import --from-ssh', source: 'ssh', host: opts.
|
|
2063
|
-
console.log(chalk.green(`Imported ${added} key(s) from ${opts.
|
|
2067
|
+
emitSecretAudit({ event: 'secrets.import', bundle: bundle.name, operation: 'import --from-ssh', source: 'ssh', host: opts.device, status: 'success', keyCount: added });
|
|
2068
|
+
console.log(chalk.green(`Imported ${added} key(s) from ${opts.device}${skipped ? `, skipped ${skipped} (already set, pass --force)` : ''}.`));
|
|
2064
2069
|
return;
|
|
2065
2070
|
}
|
|
2066
2071
|
const source = parseImportSource(opts);
|
|
@@ -2083,9 +2088,9 @@ Examples:
|
|
|
2083
2088
|
const resolvedBundleName = bundleName ?? (await pickBundleName('import into'));
|
|
2084
2089
|
// resolveImportBundle inherits an existing bundle's backend (and refuses
|
|
2085
2090
|
// to downgrade keychain -> file) or creates it with the requested backend
|
|
2086
|
-
// so a single `import --backend file` works (what `export --
|
|
2091
|
+
// so a single `import --backend file` works (what `export --device ...
|
|
2087
2092
|
// --remote-backend file` drives on the remote).
|
|
2088
|
-
const bundle = resolveImportBundle(resolvedBundleName, opts.backend, opts.synced, opts.force);
|
|
2093
|
+
const bundle = applyImportPolicy(resolveImportBundle(resolvedBundleName, opts.backend, opts.synced, opts.force));
|
|
2089
2094
|
if (source.kind === '1password') {
|
|
2090
2095
|
assertOpAvailable();
|
|
2091
2096
|
const vault = await resolveVault(source.vault);
|
|
@@ -2118,24 +2123,23 @@ Examples:
|
|
|
2118
2123
|
});
|
|
2119
2124
|
cmd
|
|
2120
2125
|
.command('export [bundle]')
|
|
2121
|
-
.description('
|
|
2122
|
-
|
|
2126
|
+
.description('Move a bundle without exposing it: push to remote machine(s) over SSH with --device, to a 1Password vault with --to-1password, or to an encrypted file with --to-file.')
|
|
2127
|
+
// Both transport flags are hidden, not documented: the ONLY caller is the
|
|
2128
|
+
// machine-to-machine SSH resolve (remoteResolveEnv / verifyRemoteKeychainPush),
|
|
2129
|
+
// which sets AGENTS_SECRETS_REMOTE_TRANSPORT=1 on the remote invocation. The
|
|
2130
|
+
// old public shell-eval mode (`eval "$(agents secrets export … --plaintext)"`)
|
|
2131
|
+
// was the top agent-exfiltration path and is removed (RUSH-2774).
|
|
2132
|
+
.addOption(new Option('--plaintext', 'internal remote-resolve transport').hideHelp())
|
|
2133
|
+
.addOption(new Option('--format <json>', 'internal remote-resolve transport').hideHelp())
|
|
2123
2134
|
.option('--to-1password', 'Push every key in the bundle as a PASSWORD item in a 1Password vault')
|
|
2124
2135
|
.option('--vault <name>', '1Password vault name (used with --to-1password)')
|
|
2125
|
-
.option('--
|
|
2126
|
-
.option('--
|
|
2127
|
-
.option('--
|
|
2128
|
-
.option('--force', 'Overwrite existing keys/items on the target (used with --to-1password and --host)')
|
|
2129
|
-
.option('--format <shell|json>', 'Output for --plaintext export: shell (default) or json (lossless, machine-readable; used by remote resolve)', 'shell')
|
|
2136
|
+
.option('--device <target...>', 'Push the bundle over SSH to this device (host alias or user@host); repeatable for multiple machines')
|
|
2137
|
+
.option('--remote-backend <backend>', 'Backend for the bundle on the remote device (with --device): keychain (default) or file. file is headless-readable via the remote\'s machine-local key; it forwards AGENTS_SECRETS_PASSPHRASE over stdin only if set (opt-in).', 'keychain')
|
|
2138
|
+
.option('--force', 'Overwrite existing keys/items on the target (used with --to-1password and --device)')
|
|
2130
2139
|
.option('--to-file <path>', `Write the bundle as an AES-256-GCM encrypted offline file (needs ${SYNC_PASSPHRASE_ENV}; symmetric counterpart of import --from-file)`)
|
|
2131
2140
|
.action(async (bundleName, opts) => {
|
|
2132
2141
|
try {
|
|
2133
|
-
|
|
2134
|
-
// `agents run --device`); fold it into the host list up front so
|
|
2135
|
-
// every downstream branch sees one resolved target list.
|
|
2136
|
-
if (opts.device?.length)
|
|
2137
|
-
opts.host = [...(opts.host ?? []), ...opts.device];
|
|
2138
|
-
const { readAndResolveBundleEnv, bundleToEnvPrefix, isReservedEnvName } = await import('../lib/secrets/bundles.js');
|
|
2142
|
+
const { readAndResolveBundleEnv } = await import('../lib/secrets/bundles.js');
|
|
2139
2143
|
const resolvedBundleName = bundleName ?? (await pickBundleName('export'));
|
|
2140
2144
|
if (opts.toFile) {
|
|
2141
2145
|
// Transport, not the local store's master key — see lib/secrets/sync-passphrase.ts.
|
|
@@ -2150,10 +2154,10 @@ Examples:
|
|
|
2150
2154
|
console.log(chalk.green(`Exported ${Object.keys(env).length} key(s) to ${opts.toFile}`));
|
|
2151
2155
|
return;
|
|
2152
2156
|
}
|
|
2153
|
-
// The presence of --
|
|
2157
|
+
// The presence of --device selects SSH push: --device is the destination
|
|
2154
2158
|
// and carries the mode (no separate --to-ssh needed — it would be
|
|
2155
|
-
// strictly redundant since SSH always requires at least one
|
|
2156
|
-
const hosts = opts.
|
|
2159
|
+
// strictly redundant since SSH always requires at least one device).
|
|
2160
|
+
const hosts = opts.device ?? [];
|
|
2157
2161
|
if (hosts.length > 0) {
|
|
2158
2162
|
for (const h of hosts)
|
|
2159
2163
|
assertValidSshTarget(h);
|
|
@@ -2203,7 +2207,7 @@ Examples:
|
|
|
2203
2207
|
}
|
|
2204
2208
|
console.log(chalk.green(`${host} -> '${resolvedBundleName}': ${out.message}`));
|
|
2205
2209
|
}
|
|
2206
|
-
emitSecretAudit({ event: 'secrets.export', bundle: resolvedBundleName, operation: `export --
|
|
2210
|
+
emitSecretAudit({ event: 'secrets.export', bundle: resolvedBundleName, operation: `export --device ${hosts.join(',')}`, source: 'ssh', host: hosts.join(','), status: failures > 0 ? 'error' : 'success', keyCount });
|
|
2207
2211
|
if (failures > 0)
|
|
2208
2212
|
process.exit(1);
|
|
2209
2213
|
return;
|
|
@@ -2242,41 +2246,36 @@ Examples:
|
|
|
2242
2246
|
console.log(chalk.green(`Exported to 1Password vault '${vault}': ${parts.join(', ')}.`));
|
|
2243
2247
|
return;
|
|
2244
2248
|
}
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2249
|
+
// The shell-eval print mode (`eval "$(agents secrets export … --plaintext)"`)
|
|
2250
|
+
// is REMOVED (RUSH-2774): a whole bundle printed to stdout lands in a
|
|
2251
|
+
// coding agent's context and session transcript, and it was the single
|
|
2252
|
+
// most-copied exfiltration one-liner on the fleet. The sole surviving
|
|
2253
|
+
// stdout emitter is the machine-to-machine SSH resolve transport, which
|
|
2254
|
+
// requires the remote-invocation marker AND a non-agent context — both
|
|
2255
|
+
// set only by remoteResolveEnv / verifyRemoteKeychainPush building the
|
|
2256
|
+
// remote command. Everything else gets the refusal with the paved paths.
|
|
2257
|
+
const isTransportCall = process.env.AGENTS_SECRETS_REMOTE_TRANSPORT === '1' &&
|
|
2258
|
+
Boolean(opts.plaintext) &&
|
|
2259
|
+
opts.format === 'json' &&
|
|
2260
|
+
!isAgentInvocationContext();
|
|
2261
|
+
if (!isTransportCall) {
|
|
2262
|
+
console.error(chalk.red('export no longer prints values. Pick a destination:'));
|
|
2263
|
+
console.error(chalk.red(' --device <host> | --to-1password | --to-file <path>'));
|
|
2264
|
+
console.error(chalk.dim(`Run a command with the values injected instead: agents secrets exec ${resolvedBundleName} -- <cmd>`));
|
|
2265
|
+
console.error(chalk.dim(`Reveal one value at your terminal: agents secrets view ${resolvedBundleName} --reveal`));
|
|
2251
2266
|
process.exit(1);
|
|
2252
2267
|
}
|
|
2253
|
-
//
|
|
2254
|
-
//
|
|
2255
|
-
//
|
|
2268
|
+
// Machine-readable form consumed by `remoteResolveEnv` over SSH: single
|
|
2269
|
+
// object of injected env KEY -> value; values verbatim. Broker-only, so a
|
|
2270
|
+
// headless remote read fails fast rather than raising Touch ID. The
|
|
2271
|
+
// resolve emits its own `secrets.get` audit on the remote; no export
|
|
2272
|
+
// event here — that would tally every peer pull as an export.
|
|
2256
2273
|
const { env } = readAndResolveBundleEnv(resolvedBundleName, {
|
|
2257
|
-
caller:
|
|
2274
|
+
caller: 'remote resolve transport',
|
|
2258
2275
|
keyMode: 'process',
|
|
2259
2276
|
agentOnly: true,
|
|
2260
2277
|
});
|
|
2261
|
-
|
|
2262
|
-
// Machine-readable form consumed by `remoteResolveEnv` over SSH.
|
|
2263
|
-
// Single object of injected env KEY -> value; values verbatim.
|
|
2264
|
-
process.stdout.write(JSON.stringify(env));
|
|
2265
|
-
return;
|
|
2266
|
-
}
|
|
2267
|
-
const prefix = bundleToEnvPrefix(resolvedBundleName);
|
|
2268
|
-
for (const [k, v] of Object.entries(env)) {
|
|
2269
|
-
const exportKey = isReservedEnvName(k) ? `${prefix}_${k}` : k;
|
|
2270
|
-
const needsQuotes = /[\s$`"'\\|&;<>(){}[\]!#~]/.test(v);
|
|
2271
|
-
const output = needsQuotes ? `'${v.replace(/'/g, `'\\''`)}'` : v;
|
|
2272
|
-
process.stdout.write(`export ${exportKey}=${output}\n`);
|
|
2273
|
-
}
|
|
2274
|
-
// Record the shell export (the `eval "$(...)"` path). The `--format json`
|
|
2275
|
-
// branch above is the machine-to-machine remote-resolve transport (already
|
|
2276
|
-
// counted as an access on this machine by the resolve above), so it is
|
|
2277
|
-
// deliberately not counted here as an export — that would tally every peer
|
|
2278
|
-
// pull as an export.
|
|
2279
|
-
emitSecretAudit({ event: 'secrets.export', bundle: resolvedBundleName, operation: 'export --plaintext', source: 'shell', status: 'success', keyCount: Object.keys(env).length });
|
|
2278
|
+
process.stdout.write(JSON.stringify(env));
|
|
2280
2279
|
}
|
|
2281
2280
|
catch (err) {
|
|
2282
2281
|
if (isPromptCancelled(err))
|
|
@@ -2287,8 +2286,8 @@ Examples:
|
|
|
2287
2286
|
});
|
|
2288
2287
|
cmd
|
|
2289
2288
|
.command('exec <bundle> [command...]')
|
|
2290
|
-
.description('Run a command with the bundle\'s secrets injected into the environment (use --
|
|
2291
|
-
.option('--
|
|
2289
|
+
.description('Run a command with the bundle\'s secrets injected into the environment (use --device to resolve the bundle from a remote machine, ephemerally)')
|
|
2290
|
+
.option('--device <target>', 'Resolve <bundle> on a remote device over SSH and inject it (ephemeral — never stored on this machine)')
|
|
2292
2291
|
.option('--keys <keys>', 'Inject only this comma-separated subset of keys (e.g. KEY1,KEY2). Missing keys are an error.')
|
|
2293
2292
|
.option('--allow-expired', 'Inject keys even if their expiry date has passed (overrides the pre-run expiry abort).')
|
|
2294
2293
|
.allowUnknownOption()
|
|
@@ -2303,14 +2302,14 @@ Examples:
|
|
|
2303
2302
|
? execOpts.keys.split(',').map((k) => k.trim()).filter(Boolean)
|
|
2304
2303
|
: undefined;
|
|
2305
2304
|
let secretEnv;
|
|
2306
|
-
if (execOpts.
|
|
2305
|
+
if (execOpts.device) {
|
|
2307
2306
|
// Least-privilege flags do not yet cross the SSH resolver —
|
|
2308
2307
|
// silently applying them would inject the full remote env or an
|
|
2309
2308
|
// expired key. Fail loud so the user can drop the flag or run
|
|
2310
2309
|
// locally instead.
|
|
2311
2310
|
const { assertRemoteBundleFlagsUnsupported } = await import('../lib/secrets/bundles.js');
|
|
2312
|
-
assertRemoteBundleFlagsUnsupported(bundleName, execOpts.
|
|
2313
|
-
secretEnv = await remoteResolveEnv(await resolveHostSshTarget(execOpts.
|
|
2311
|
+
assertRemoteBundleFlagsUnsupported(bundleName, execOpts.device, { keys: keysSubset, allowExpired: execOpts.allowExpired }, { keysFlag: '--keys', allowExpiredFlag: '--allow-expired' });
|
|
2312
|
+
secretEnv = await remoteResolveEnv(await resolveHostSshTarget(execOpts.device), bundleName, { osLookupName: execOpts.device });
|
|
2314
2313
|
}
|
|
2315
2314
|
else {
|
|
2316
2315
|
const { readAndResolveBundleEnv } = await import('../lib/secrets/bundles.js');
|
|
@@ -2497,14 +2496,14 @@ Examples:
|
|
|
2497
2496
|
});
|
|
2498
2497
|
cmd
|
|
2499
2498
|
.command('unlock [names...]')
|
|
2500
|
-
.description('Hold a bundle in the secrets-agent after one Touch ID, so concurrent runs read it without re-prompting (macOS). With --
|
|
2499
|
+
.description('Hold a bundle in the secrets-agent after one Touch ID, so concurrent runs read it without re-prompting (macOS). With --device, unlock FILE-backed bundle(s) on a remote (the passphrase prompt surfaces over the SSH TTY); keychain/biometry bundles are GUI-only and can\'t be remote-unlocked.')
|
|
2501
2500
|
.option('--ttl <duration>', 'How long to hold it (e.g. 30m, 8h, 3d). Default 7d.')
|
|
2502
2501
|
.option('--until <date>', 'Hold until this absolute date or timestamp (for example 2026-08-06T12:00:00Z). Mutually exclusive with --ttl.')
|
|
2503
2502
|
.option('--durable', 'Keep the unlock across sleep + reboot too (default: survives upgrade/restart but re-locks on sleep). Set secrets.agent.durable in agents.yaml to make this the default.')
|
|
2504
2503
|
.option('--agent <agent>', 'Narrow the unlock to ONE harness type (for example claude, codex, or kimi). Default: the grant is global — every harness and a plain shell can read it, so one Touch ID covers them all.')
|
|
2505
2504
|
.option('--keys <keys>', 'Hold ONLY this comma-separated subset of the bundle\'s keys instead of the whole bundle. Scopes exactly one bundle; fails closed on an unknown key. `secrets status` shows which keys a scoped hold covers.')
|
|
2506
2505
|
.option('--all', 'Unlock every configured bundle')
|
|
2507
|
-
.option('--
|
|
2506
|
+
.option('--device <target>', 'Unlock the bundle(s) on this remote device over SSH instead of locally (file-backed bundles only — the remote\'s passphrase prompt surfaces on your terminal over a -tt session). Single-valued (NOT variadic) so it never swallows the bundle name: `unlock <name> --device <machine>`.')
|
|
2508
2507
|
.action(async (names, opts) => {
|
|
2509
2508
|
if (opts.ttl && opts.until) {
|
|
2510
2509
|
console.error(chalk.red('--ttl and --until are mutually exclusive.'));
|
|
@@ -2512,10 +2511,10 @@ Examples:
|
|
|
2512
2511
|
}
|
|
2513
2512
|
if (opts.keys !== undefined) {
|
|
2514
2513
|
// --keys scopes ONE bundle to an explicit subset. It is local-only (a
|
|
2515
|
-
// remote --
|
|
2514
|
+
// remote --device unlock holds the whole file-backed bundle) and cannot
|
|
2516
2515
|
// combine with --all, which would hold every bundle whole.
|
|
2517
|
-
if (opts.
|
|
2518
|
-
console.error(chalk.red('--keys is local-only; a remote (--
|
|
2516
|
+
if (opts.device) {
|
|
2517
|
+
console.error(chalk.red('--keys is local-only; a remote (--device) unlock holds the whole file-backed bundle.'));
|
|
2519
2518
|
process.exit(1);
|
|
2520
2519
|
}
|
|
2521
2520
|
if (opts.all || !names || names.length !== 1) {
|
|
@@ -2523,10 +2522,10 @@ Examples:
|
|
|
2523
2522
|
process.exit(1);
|
|
2524
2523
|
}
|
|
2525
2524
|
}
|
|
2526
|
-
// Single-valued (not variadic): a variadic --
|
|
2527
|
-
// positional bundle name (`unlock --
|
|
2525
|
+
// Single-valued (not variadic): a variadic --device greedily consumes the
|
|
2526
|
+
// positional bundle name (`unlock --device mac wztest` -> device=[mac,wztest],
|
|
2528
2527
|
// names=[]). Unlock targets one remote at a time anyway.
|
|
2529
|
-
const hosts = opts.
|
|
2528
|
+
const hosts = opts.device ? [opts.device] : [];
|
|
2530
2529
|
if (hosts.length > 0) {
|
|
2531
2530
|
// Remote unlock: the REMOTE enforces its own platform rules, so the
|
|
2532
2531
|
// local darwin-only guard below does NOT apply. Only file-backed
|
|
@@ -204,7 +204,7 @@ export function registerSessionsBackfillCommand(sessionsCmd) {
|
|
|
204
204
|
tools.action(async (_options, command) => {
|
|
205
205
|
const options = command.optsWithGlobals();
|
|
206
206
|
if (options.local && (options.fleet || mergeHosts(options).length > 0)) {
|
|
207
|
-
console.error(chalk.red('--local cannot be combined with --fleet
|
|
207
|
+
console.error(chalk.red('--local cannot be combined with --fleet or --device.'));
|
|
208
208
|
process.exitCode = 1;
|
|
209
209
|
return;
|
|
210
210
|
}
|