@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
|
@@ -10,13 +10,13 @@ import * as fs from 'fs';
|
|
|
10
10
|
import * as path from 'path';
|
|
11
11
|
import * as os from 'os';
|
|
12
12
|
import * as yaml from 'yaml';
|
|
13
|
-
import { normalizeAliases } from '
|
|
14
|
-
import { AGENTS, ensureSkillsDir, agentConfigDirName } from '
|
|
15
|
-
import { capableAgents, isCapable } from '
|
|
16
|
-
import { getUserSkillsDir, getSkillsDir as getSystemSkillsDir, getProjectAgentsDir, getEnabledExtraRepos, getTrashSkillsDir } from '
|
|
17
|
-
import { getEffectiveHome, getVersionHomePath, listInstalledVersions } from '
|
|
18
|
-
import { listCommandSkillsInVersion } from '
|
|
19
|
-
import { emit } from '
|
|
13
|
+
import { normalizeAliases } from '../resource-aliases.js';
|
|
14
|
+
import { AGENTS, ensureSkillsDir, agentConfigDirName } from '../agents.js';
|
|
15
|
+
import { capableAgents, isCapable } from '../capabilities.js';
|
|
16
|
+
import { getUserSkillsDir, getSkillsDir as getSystemSkillsDir, getProjectAgentsDir, getEnabledExtraRepos, getTrashSkillsDir } from '../state.js';
|
|
17
|
+
import { getEffectiveHome, getVersionHomePath, listInstalledVersions } from '../installations/versions.js';
|
|
18
|
+
import { listCommandSkillsInVersion } from '../command-skills.js';
|
|
19
|
+
import { emit } from '../feed/events.js';
|
|
20
20
|
const HOME = os.homedir();
|
|
21
21
|
/** User-scoped skills dir (~/.agents/skills/). Used for installs. */
|
|
22
22
|
export function getSkillsDir() {
|
|
@@ -18,7 +18,7 @@ export declare function costOfUsage(u: TokenUsage): number;
|
|
|
18
18
|
* USD cost of one usage record priced as if caching were OFF: cache-read and
|
|
19
19
|
* cache-write tokens are billed at the model's full INPUT rate rather than their
|
|
20
20
|
* discounted cache rates. This is the "what would this have cost with no prompt
|
|
21
|
-
* caching?" scenario that `agents output --pricing no-cache` models. Output and
|
|
21
|
+
* caching?" scenario that `agents insights output --pricing no-cache` models. Output and
|
|
22
22
|
* uncached input are unchanged — only the cache tokens are repriced. Returns 0
|
|
23
23
|
* for a missing/unpriced model, exactly like {@link costOfUsage}.
|
|
24
24
|
*/
|
package/dist/lib/pricing/cost.js
CHANGED
|
@@ -35,7 +35,7 @@ export function costOfUsage(u) {
|
|
|
35
35
|
* USD cost of one usage record priced as if caching were OFF: cache-read and
|
|
36
36
|
* cache-write tokens are billed at the model's full INPUT rate rather than their
|
|
37
37
|
* discounted cache rates. This is the "what would this have cost with no prompt
|
|
38
|
-
* caching?" scenario that `agents output --pricing no-cache` models. Output and
|
|
38
|
+
* caching?" scenario that `agents insights output --pricing no-cache` models. Output and
|
|
39
39
|
* uncached input are unchanged — only the cache tokens are repriced. Returns 0
|
|
40
40
|
* for a missing/unpriced model, exactly like {@link costOfUsage}.
|
|
41
41
|
*/
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Built-in profile presets for popular model providers.
|
|
3
3
|
*
|
|
4
4
|
* Each preset bundles a host CLI, API base URL, default model, and provider
|
|
5
|
-
* name so users can `agents
|
|
5
|
+
* name so users can `agents harness add kimi` without manual configuration.
|
|
6
6
|
*/
|
|
7
7
|
import type { AgentId } from './types.js';
|
|
8
8
|
export interface PresetVar {
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Built-in profile presets for popular model providers.
|
|
3
3
|
*
|
|
4
4
|
* Each preset bundles a host CLI, API base URL, default model, and provider
|
|
5
|
-
* name so users can `agents
|
|
5
|
+
* name so users can `agents harness add kimi` without manual configuration.
|
|
6
6
|
*/
|
|
7
7
|
// Model IDs verified against openrouter.ai/api/v1/models on 2026-07-31
|
|
8
8
|
// (spark presets corrected from the never-served meta/claude-spark-1.1 to the
|
package/dist/lib/profiles.js
CHANGED
|
@@ -129,7 +129,7 @@ export function listProfiles() {
|
|
|
129
129
|
profiles.push(readProfile(name));
|
|
130
130
|
}
|
|
131
131
|
catch {
|
|
132
|
-
// Skip malformed profile files; surfacing via `agents
|
|
132
|
+
// Skip malformed profile files; surfacing via `agents harness view <name>`.
|
|
133
133
|
}
|
|
134
134
|
}
|
|
135
135
|
return profiles.sort((a, b) => a.name.localeCompare(b.name));
|
|
@@ -318,7 +318,7 @@ export function profileFromPreset(profileName, preset, version) {
|
|
|
318
318
|
}
|
|
319
319
|
/**
|
|
320
320
|
* Env var each host CLI reads to override its model. Mirror of the read-side
|
|
321
|
-
* `MODEL_ENV_KEYS` above, keyed by
|
|
321
|
+
* `MODEL_ENV_KEYS` above, keyed by agent so a one-shot `--device <agent> --model
|
|
322
322
|
* <id>` writes the model onto the var that host actually honors.
|
|
323
323
|
*/
|
|
324
324
|
const MODEL_ENV_KEY_BY_HOST = {
|
|
@@ -68,7 +68,10 @@ export function buildLinearImportCandidates(projects, existing, deps, opts) {
|
|
|
68
68
|
continue;
|
|
69
69
|
}
|
|
70
70
|
seen.add(name);
|
|
71
|
-
|
|
71
|
+
// The Linear display name is recorded verbatim (`p.name`), not the
|
|
72
|
+
// slugified def name: `agi` is the local id, "AGI" is what the board calls
|
|
73
|
+
// it, and agents read the latter when they name the project.
|
|
74
|
+
const def = { ...prior, name, linear: { projectId: p.id, name: p.name } };
|
|
72
75
|
if (p.url)
|
|
73
76
|
def.linear.url = p.url;
|
|
74
77
|
const dir = matchLocalCheckoutExact(p.name, deps.localDirs);
|
package/dist/lib/project-key.js
CHANGED
|
@@ -55,6 +55,13 @@ export function repoRootForCwd(dir, home = os.homedir()) {
|
|
|
55
55
|
for (;;) {
|
|
56
56
|
if (fs.existsSync(path.join(current, '.git')))
|
|
57
57
|
return current === stop ? undefined : current;
|
|
58
|
+
// Never climb past $HOME: an ancestor of home (/tmp, /, ...) is not part of
|
|
59
|
+
// any project this cwd belongs to, and treating one as the repo root would
|
|
60
|
+
// let unrelated host state (e.g. a stray /tmp/.git) swallow every loose
|
|
61
|
+
// directory under home. Mirrors the shim's own home boundary — see
|
|
62
|
+
// shims.ts shimExecTail.
|
|
63
|
+
if (current === stop)
|
|
64
|
+
return undefined;
|
|
58
65
|
const parent = path.dirname(current);
|
|
59
66
|
if (parent === current)
|
|
60
67
|
return undefined;
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
* change path.
|
|
36
36
|
*/
|
|
37
37
|
import type { AgentId } from './types.js';
|
|
38
|
-
import { pluginInstallDir } from './plugin-marketplace.js';
|
|
38
|
+
import { pluginInstallDir } from './plugins/plugin-marketplace.js';
|
|
39
39
|
export interface LaunchSyncOptions {
|
|
40
40
|
agent: AgentId;
|
|
41
41
|
version: string;
|
|
@@ -40,12 +40,12 @@ import * as os from 'os';
|
|
|
40
40
|
import * as path from 'path';
|
|
41
41
|
import { supports } from './capabilities.js';
|
|
42
42
|
import { getEnabledExtraRepos, getExtraPluginsDir, getPluginsDir, getProjectAgentsDir, getProjectPluginsDir, getSystemPluginsDir, } from './state.js';
|
|
43
|
-
import { getVersionHomePath } from './versions.js';
|
|
43
|
+
import { getVersionHomePath } from './installations/versions.js';
|
|
44
44
|
import { toPortableKey } from './platform/index.js';
|
|
45
45
|
import { compileRulesForProject } from './rules/compile.js';
|
|
46
46
|
import { syncProjectResourcesToAgent } from './project-resources.js';
|
|
47
|
-
import { discoverPluginsInDir, hasPluginExecSurfaces, inspectPluginCapabilities } from './plugins.js';
|
|
48
|
-
import { MARKETPLACE_NAME, PROJECT_MARKETPLACE_NAME, SYSTEM_MARKETPLACE_NAME, addPluginToSettings, copyPluginToMarketplace, marketplaceNameFor, marketplaceRoot, pluginInstallDir, registerMarketplace, removePluginFromSettings, syncMarketplaceManifest, } from './plugin-marketplace.js';
|
|
47
|
+
import { discoverPluginsInDir, hasPluginExecSurfaces, inspectPluginCapabilities } from './plugins/plugins.js';
|
|
48
|
+
import { MARKETPLACE_NAME, PROJECT_MARKETPLACE_NAME, SYSTEM_MARKETPLACE_NAME, addPluginToSettings, copyPluginToMarketplace, marketplaceNameFor, marketplaceRoot, pluginInstallDir, registerMarketplace, removePluginFromSettings, syncMarketplaceManifest, } from './plugins/plugin-marketplace.js';
|
|
49
49
|
/**
|
|
50
50
|
* Run the launch-time project compile. Safe to call on every agent launch:
|
|
51
51
|
* each step is idempotent and skips when its inputs are missing.
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Project pull — fast-forward every fleet checkout of a named project.
|
|
3
|
+
*
|
|
4
|
+
* Safety contract (binding for all paths in this module):
|
|
5
|
+
* - Dirty trees are blocked immediately (no fetch attempted).
|
|
6
|
+
* - Only checkouts on the remote's default branch are fast-forwarded.
|
|
7
|
+
* - Local commits ahead of upstream block the pull.
|
|
8
|
+
* - Fast-forward ONLY — no rebase, no reset, no history rewrite.
|
|
9
|
+
* - Missing checkouts are reported, never cloned.
|
|
10
|
+
* - Malformed or partial peer envelopes fail CLOSED and LOUD (the parse
|
|
11
|
+
* returns `valid: false`, so the peer lands in `parseFailed` and drives a
|
|
12
|
+
* non-zero exit — never an empty result set that reads as "nothing to do").
|
|
13
|
+
* - Git hook symlinks are NEVER installed during a pull.
|
|
14
|
+
*
|
|
15
|
+
* The fan-out extends the `projects status` seam: the same
|
|
16
|
+
* `projectRepoTargetsForDef` expansion drives both commands, so they
|
|
17
|
+
* always operate on exactly the same set of directories.
|
|
18
|
+
*/
|
|
19
|
+
import { type ProjectRepoTarget } from './projects.js';
|
|
20
|
+
import { type RemoteAgentsJsonParseResult } from './remote-agents-json.js';
|
|
21
|
+
export type ProjectPullStatus = 'updated' | 'current' | 'missing' | 'blocked' | 'failed';
|
|
22
|
+
export interface ProjectPullResult {
|
|
23
|
+
/** Machine that ran the pull (from {@link machineId}). */
|
|
24
|
+
host: string;
|
|
25
|
+
/** Home-relative path — re-roots on each fleet device. */
|
|
26
|
+
path: string;
|
|
27
|
+
/** Expected GitHub slug, echoed from the target for reference. */
|
|
28
|
+
expectedSlug?: string;
|
|
29
|
+
status: ProjectPullStatus;
|
|
30
|
+
branch?: string;
|
|
31
|
+
upstream?: string;
|
|
32
|
+
/** Short commit hash HEAD was at before the pull. */
|
|
33
|
+
before?: string;
|
|
34
|
+
/** Short commit hash HEAD is at after the pull. */
|
|
35
|
+
after?: string;
|
|
36
|
+
/** Human-readable status or error; present when status is not `current`. */
|
|
37
|
+
message?: string;
|
|
38
|
+
}
|
|
39
|
+
/** The wire format this machine emits for the fleet fan-out. Fail-closed on parse. */
|
|
40
|
+
export interface ProjectPullEnvelope {
|
|
41
|
+
schemaVersion: 1;
|
|
42
|
+
kind: 'project-pull';
|
|
43
|
+
/** The emitting machine's {@link machineId} — verified by the caller. */
|
|
44
|
+
machine: string;
|
|
45
|
+
/** Hash of the target list the caller sent — verified before trusting results. */
|
|
46
|
+
targetFingerprint: string;
|
|
47
|
+
results: ProjectPullResult[];
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Deterministic hash of a target list so the orchestrator can verify the peer
|
|
51
|
+
* pulled the same set of directories it intended to request. Computed from the
|
|
52
|
+
* canonical `path\0expectedSlug` representation, sorted so insertion order does
|
|
53
|
+
* not matter.
|
|
54
|
+
*/
|
|
55
|
+
export declare function fingerprintTargets(targets: ProjectRepoTarget[]): string;
|
|
56
|
+
/**
|
|
57
|
+
* Encode a target list as the single `--targets` argument the orchestrating
|
|
58
|
+
* `pull` hands to a peer's hidden `pull-local`.
|
|
59
|
+
*
|
|
60
|
+
* A target is a `{ path, expectedSlug }` PAIR, and both halves must survive the
|
|
61
|
+
* hop: `expectedSlug` is what makes the peer refuse to fast-forward a directory
|
|
62
|
+
* whose `origin` is a different repo, and it is also hashed into
|
|
63
|
+
* {@link fingerprintTargets}. Sending bare paths therefore broke the fan-out
|
|
64
|
+
* twice over — slug verification silently became a no-op on every peer, and the
|
|
65
|
+
* peer's slug-less fingerprint could never match the caller's, so
|
|
66
|
+
* {@link parseProjectPullEnvelope} discarded the peer's whole result set. JSON
|
|
67
|
+
* keeps the pair intact; both transports quote it as one argument
|
|
68
|
+
* (`shellQuote` on bash, `powershellQuote` inside a base64 `-EncodedCommand`
|
|
69
|
+
* on Windows), so no escaping is owed here.
|
|
70
|
+
*/
|
|
71
|
+
export declare function encodePullTargets(targets: ProjectRepoTarget[]): string;
|
|
72
|
+
/**
|
|
73
|
+
* The exact `agents …` argv the fleet fan-out runs on each peer. Owned here,
|
|
74
|
+
* beside the decoder, so the two halves of the hop cannot drift apart and a
|
|
75
|
+
* test can exercise the real caller-side arguments rather than a retyped copy.
|
|
76
|
+
*/
|
|
77
|
+
export declare function pullLocalArgs(targets: ProjectRepoTarget[]): string[];
|
|
78
|
+
/**
|
|
79
|
+
* Decode the `--targets` argument back into targets on the peer. THROWS on any
|
|
80
|
+
* malformed input rather than returning a partial list: a peer that cannot tell
|
|
81
|
+
* exactly which directories it was asked to pull must fail loudly, not
|
|
82
|
+
* fast-forward a guessed subset.
|
|
83
|
+
*/
|
|
84
|
+
export declare function decodePullTargets(raw: string): ProjectRepoTarget[];
|
|
85
|
+
/**
|
|
86
|
+
* Pull each target on THIS machine, sequentially (concurrent worktree writes
|
|
87
|
+
* can corrupt the git index). Returns one result per target.
|
|
88
|
+
*
|
|
89
|
+
* Missing checkouts (no directory or no `.git`) are reported as `missing` and
|
|
90
|
+
* skipped — never cloned. A slug mismatch (the directory's `origin` remote does
|
|
91
|
+
* not match the expected slug) is reported as `blocked`.
|
|
92
|
+
*/
|
|
93
|
+
export declare function pullProjectTargets(targets: ProjectRepoTarget[], host?: string): Promise<ProjectPullResult[]>;
|
|
94
|
+
export declare function buildPullEnvelope(results: ProjectPullResult[], targets: ProjectRepoTarget[]): ProjectPullEnvelope;
|
|
95
|
+
/**
|
|
96
|
+
* Parse a peer's `projects pull-local --json` stdout. Fails CLOSED **and
|
|
97
|
+
* LOUD**: any structural anomaly (wrong schema version, wrong kind, machine
|
|
98
|
+
* mismatch, fingerprint mismatch, non-array results, malformed rows) returns
|
|
99
|
+
* `{ items: [], valid: false }` rather than silently accepting a partial or
|
|
100
|
+
* spoofed payload.
|
|
101
|
+
*
|
|
102
|
+
* `valid: false` is what makes the failure visible. A bare `[]` normalizes to
|
|
103
|
+
* `{ items: [], valid: true }` in `normalizeRemoteAgentsJsonParse`, so the peer
|
|
104
|
+
* would be recorded as having answered with nothing to report — indistinguishable
|
|
105
|
+
* from a device that genuinely had no work, even though it had already run
|
|
106
|
+
* `git fetch` + `merge --ff-only` or hit a real `blocked`/`failed`. Returning
|
|
107
|
+
* the result shape instead lands the peer in `parseFailed`, which the caller
|
|
108
|
+
* both prints and treats as a non-zero exit.
|
|
109
|
+
*/
|
|
110
|
+
export declare function parseProjectPullEnvelope(stdout: string, machine: string, opts?: {
|
|
111
|
+
expectedFingerprint?: string;
|
|
112
|
+
}): RemoteAgentsJsonParseResult<ProjectPullResult>;
|
|
113
|
+
/**
|
|
114
|
+
* Returns `true` when every pull completed successfully (`updated` or
|
|
115
|
+
* `current`). `missing` paths are skipped by design — their absence does not
|
|
116
|
+
* count as a failure. `blocked` and `failed` paths drive a non-zero exit.
|
|
117
|
+
*/
|
|
118
|
+
export declare function projectPullComplete(results: ProjectPullResult[]): boolean;
|
|
119
|
+
/**
|
|
120
|
+
* Print a human-readable per-host / per-path pull summary to stdout, followed
|
|
121
|
+
* by a one-line counts footer. Does not exit — the caller controls the exit code.
|
|
122
|
+
*
|
|
123
|
+
* `unavailableDevices` never answered (offline, no CLI, timed out).
|
|
124
|
+
* `unverifiedDevices` DID answer but their envelope failed verification, so
|
|
125
|
+
* their real outcome is unknown — a strictly worse state than silence, and the
|
|
126
|
+
* one the caller turns into a non-zero exit.
|
|
127
|
+
*/
|
|
128
|
+
export declare function printProjectPullSummary(projectName: string, results: ProjectPullResult[], unavailableDevices: string[], unverifiedDevices?: string[]): void;
|
|
@@ -0,0 +1,384 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Project pull — fast-forward every fleet checkout of a named project.
|
|
3
|
+
*
|
|
4
|
+
* Safety contract (binding for all paths in this module):
|
|
5
|
+
* - Dirty trees are blocked immediately (no fetch attempted).
|
|
6
|
+
* - Only checkouts on the remote's default branch are fast-forwarded.
|
|
7
|
+
* - Local commits ahead of upstream block the pull.
|
|
8
|
+
* - Fast-forward ONLY — no rebase, no reset, no history rewrite.
|
|
9
|
+
* - Missing checkouts are reported, never cloned.
|
|
10
|
+
* - Malformed or partial peer envelopes fail CLOSED and LOUD (the parse
|
|
11
|
+
* returns `valid: false`, so the peer lands in `parseFailed` and drives a
|
|
12
|
+
* non-zero exit — never an empty result set that reads as "nothing to do").
|
|
13
|
+
* - Git hook symlinks are NEVER installed during a pull.
|
|
14
|
+
*
|
|
15
|
+
* The fan-out extends the `projects status` seam: the same
|
|
16
|
+
* `projectRepoTargetsForDef` expansion drives both commands, so they
|
|
17
|
+
* always operate on exactly the same set of directories.
|
|
18
|
+
*/
|
|
19
|
+
import * as crypto from 'crypto';
|
|
20
|
+
import * as fs from 'fs';
|
|
21
|
+
import * as path from 'path';
|
|
22
|
+
import chalk from 'chalk';
|
|
23
|
+
import { expandLocalHome } from './project-root.js';
|
|
24
|
+
import { getRepoCommit, getRemoteUrl, pullRepo } from './git.js';
|
|
25
|
+
import { parseOwnerRepoFromRemote } from './registry.js';
|
|
26
|
+
import { machineId } from './machine-id.js';
|
|
27
|
+
// ---------------------------------------------------------------------------
|
|
28
|
+
// Target fingerprint
|
|
29
|
+
// ---------------------------------------------------------------------------
|
|
30
|
+
/**
|
|
31
|
+
* Deterministic hash of a target list so the orchestrator can verify the peer
|
|
32
|
+
* pulled the same set of directories it intended to request. Computed from the
|
|
33
|
+
* canonical `path\0expectedSlug` representation, sorted so insertion order does
|
|
34
|
+
* not matter.
|
|
35
|
+
*/
|
|
36
|
+
export function fingerprintTargets(targets) {
|
|
37
|
+
const lines = targets
|
|
38
|
+
.map((t) => `${t.path}\0${t.expectedSlug ?? ''}`)
|
|
39
|
+
.sort();
|
|
40
|
+
return crypto.createHash('sha256').update(lines.join('\n')).digest('hex').slice(0, 16);
|
|
41
|
+
}
|
|
42
|
+
// ---------------------------------------------------------------------------
|
|
43
|
+
// Target wire encoding (the `pull` → `pull-local` CLI-arg boundary)
|
|
44
|
+
// ---------------------------------------------------------------------------
|
|
45
|
+
/**
|
|
46
|
+
* Encode a target list as the single `--targets` argument the orchestrating
|
|
47
|
+
* `pull` hands to a peer's hidden `pull-local`.
|
|
48
|
+
*
|
|
49
|
+
* A target is a `{ path, expectedSlug }` PAIR, and both halves must survive the
|
|
50
|
+
* hop: `expectedSlug` is what makes the peer refuse to fast-forward a directory
|
|
51
|
+
* whose `origin` is a different repo, and it is also hashed into
|
|
52
|
+
* {@link fingerprintTargets}. Sending bare paths therefore broke the fan-out
|
|
53
|
+
* twice over — slug verification silently became a no-op on every peer, and the
|
|
54
|
+
* peer's slug-less fingerprint could never match the caller's, so
|
|
55
|
+
* {@link parseProjectPullEnvelope} discarded the peer's whole result set. JSON
|
|
56
|
+
* keeps the pair intact; both transports quote it as one argument
|
|
57
|
+
* (`shellQuote` on bash, `powershellQuote` inside a base64 `-EncodedCommand`
|
|
58
|
+
* on Windows), so no escaping is owed here.
|
|
59
|
+
*/
|
|
60
|
+
export function encodePullTargets(targets) {
|
|
61
|
+
return JSON.stringify(targets.map((t) => (t.expectedSlug === undefined ? { path: t.path } : { path: t.path, expectedSlug: t.expectedSlug })));
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* The exact `agents …` argv the fleet fan-out runs on each peer. Owned here,
|
|
65
|
+
* beside the decoder, so the two halves of the hop cannot drift apart and a
|
|
66
|
+
* test can exercise the real caller-side arguments rather than a retyped copy.
|
|
67
|
+
*/
|
|
68
|
+
export function pullLocalArgs(targets) {
|
|
69
|
+
return ['projects', 'pull-local', '--json', '--targets', encodePullTargets(targets)];
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Decode the `--targets` argument back into targets on the peer. THROWS on any
|
|
73
|
+
* malformed input rather than returning a partial list: a peer that cannot tell
|
|
74
|
+
* exactly which directories it was asked to pull must fail loudly, not
|
|
75
|
+
* fast-forward a guessed subset.
|
|
76
|
+
*/
|
|
77
|
+
export function decodePullTargets(raw) {
|
|
78
|
+
let parsed;
|
|
79
|
+
try {
|
|
80
|
+
parsed = JSON.parse(raw);
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
throw new Error('not valid JSON');
|
|
84
|
+
}
|
|
85
|
+
if (!Array.isArray(parsed))
|
|
86
|
+
throw new Error('expected a JSON array of targets');
|
|
87
|
+
return parsed.map((x, i) => {
|
|
88
|
+
if (!x || typeof x !== 'object' || Array.isArray(x))
|
|
89
|
+
throw new Error(`target ${i} is not an object`);
|
|
90
|
+
const o = x;
|
|
91
|
+
if (typeof o.path !== 'string' || o.path.length === 0)
|
|
92
|
+
throw new Error(`target ${i} has no "path"`);
|
|
93
|
+
if (o.expectedSlug !== undefined && typeof o.expectedSlug !== 'string') {
|
|
94
|
+
throw new Error(`target ${i} has a non-string "expectedSlug"`);
|
|
95
|
+
}
|
|
96
|
+
return o.expectedSlug === undefined
|
|
97
|
+
? { path: o.path }
|
|
98
|
+
: { path: o.path, expectedSlug: o.expectedSlug };
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
// ---------------------------------------------------------------------------
|
|
102
|
+
// Local pull
|
|
103
|
+
// ---------------------------------------------------------------------------
|
|
104
|
+
/**
|
|
105
|
+
* Pull each target on THIS machine, sequentially (concurrent worktree writes
|
|
106
|
+
* can corrupt the git index). Returns one result per target.
|
|
107
|
+
*
|
|
108
|
+
* Missing checkouts (no directory or no `.git`) are reported as `missing` and
|
|
109
|
+
* skipped — never cloned. A slug mismatch (the directory's `origin` remote does
|
|
110
|
+
* not match the expected slug) is reported as `blocked`.
|
|
111
|
+
*/
|
|
112
|
+
export async function pullProjectTargets(targets, host = machineId()) {
|
|
113
|
+
const results = [];
|
|
114
|
+
for (const target of targets) {
|
|
115
|
+
const absPath = expandLocalHome(target.path);
|
|
116
|
+
// Missing directory or not a git repo: report and skip.
|
|
117
|
+
if (!fs.existsSync(absPath) || !fs.existsSync(path.join(absPath, '.git'))) {
|
|
118
|
+
results.push({
|
|
119
|
+
host,
|
|
120
|
+
path: target.path,
|
|
121
|
+
expectedSlug: target.expectedSlug,
|
|
122
|
+
status: 'missing',
|
|
123
|
+
message: 'Checkout not present on this device',
|
|
124
|
+
});
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
// Slug verification: if the project def declares a slug, the directory's
|
|
128
|
+
// origin must match it. A mismatch means the path hosts a different repo
|
|
129
|
+
// and we must not fast-forward it under a wrong project's command.
|
|
130
|
+
// An unreadable remote URL or an unrecognised remote shape is also blocked —
|
|
131
|
+
// we cannot confirm the checkout is the right repo, so we fail closed.
|
|
132
|
+
if (target.expectedSlug) {
|
|
133
|
+
const remoteUrl = await getRemoteUrl(absPath);
|
|
134
|
+
if (remoteUrl === null) {
|
|
135
|
+
results.push({
|
|
136
|
+
host,
|
|
137
|
+
path: target.path,
|
|
138
|
+
expectedSlug: target.expectedSlug,
|
|
139
|
+
status: 'blocked',
|
|
140
|
+
message: `Slug verification failed: no origin remote found (expected ${target.expectedSlug})`,
|
|
141
|
+
});
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
const actualSlug = parseOwnerRepoFromRemote(remoteUrl);
|
|
145
|
+
if (actualSlug === null) {
|
|
146
|
+
results.push({
|
|
147
|
+
host,
|
|
148
|
+
path: target.path,
|
|
149
|
+
expectedSlug: target.expectedSlug,
|
|
150
|
+
status: 'blocked',
|
|
151
|
+
message: `Slug verification failed: cannot parse remote URL "${remoteUrl}" (expected ${target.expectedSlug})`,
|
|
152
|
+
});
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
if (actualSlug.toLowerCase() !== target.expectedSlug.toLowerCase()) {
|
|
156
|
+
results.push({
|
|
157
|
+
host,
|
|
158
|
+
path: target.path,
|
|
159
|
+
expectedSlug: target.expectedSlug,
|
|
160
|
+
status: 'blocked',
|
|
161
|
+
message: `Slug mismatch: expected ${target.expectedSlug}, found ${actualSlug}`,
|
|
162
|
+
});
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
// Capture HEAD before the pull for the before→after diff in output.
|
|
167
|
+
// getRepoCommit already returns an 8-char short hash and never throws.
|
|
168
|
+
const beforeRaw = await getRepoCommit(absPath);
|
|
169
|
+
const before = beforeRaw === 'unknown' ? undefined : beforeRaw;
|
|
170
|
+
// Fast-forward in strict mode. The option contract is documented on PullRepoOptions.
|
|
171
|
+
let pull;
|
|
172
|
+
try {
|
|
173
|
+
pull = await pullRepo(absPath, { mode: 'default-branch-fast-forward' });
|
|
174
|
+
}
|
|
175
|
+
catch (err) {
|
|
176
|
+
results.push({
|
|
177
|
+
host,
|
|
178
|
+
path: target.path,
|
|
179
|
+
expectedSlug: target.expectedSlug,
|
|
180
|
+
status: 'failed',
|
|
181
|
+
before,
|
|
182
|
+
message: err.message,
|
|
183
|
+
});
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
186
|
+
if (!pull.success) {
|
|
187
|
+
results.push({
|
|
188
|
+
host,
|
|
189
|
+
path: target.path,
|
|
190
|
+
expectedSlug: target.expectedSlug,
|
|
191
|
+
status: 'blocked',
|
|
192
|
+
branch: pull.branch,
|
|
193
|
+
before,
|
|
194
|
+
message: pull.error,
|
|
195
|
+
});
|
|
196
|
+
continue;
|
|
197
|
+
}
|
|
198
|
+
const after = pull.commit;
|
|
199
|
+
// Both are 8-char short hashes; equal → already up-to-date.
|
|
200
|
+
const status = before !== undefined && before !== after ? 'updated' : 'current';
|
|
201
|
+
results.push({
|
|
202
|
+
host,
|
|
203
|
+
path: target.path,
|
|
204
|
+
expectedSlug: target.expectedSlug,
|
|
205
|
+
status,
|
|
206
|
+
branch: pull.branch,
|
|
207
|
+
before,
|
|
208
|
+
after,
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
return results;
|
|
212
|
+
}
|
|
213
|
+
// ---------------------------------------------------------------------------
|
|
214
|
+
// Envelope builder (used by the hidden `pull-local` peer command)
|
|
215
|
+
// ---------------------------------------------------------------------------
|
|
216
|
+
export function buildPullEnvelope(results, targets) {
|
|
217
|
+
return {
|
|
218
|
+
schemaVersion: 1,
|
|
219
|
+
kind: 'project-pull',
|
|
220
|
+
machine: machineId(),
|
|
221
|
+
targetFingerprint: fingerprintTargets(targets),
|
|
222
|
+
results,
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
// ---------------------------------------------------------------------------
|
|
226
|
+
// Envelope parser (fail-closed — every validation failure returns [])
|
|
227
|
+
// ---------------------------------------------------------------------------
|
|
228
|
+
/**
|
|
229
|
+
* Parse a peer's `projects pull-local --json` stdout. Fails CLOSED **and
|
|
230
|
+
* LOUD**: any structural anomaly (wrong schema version, wrong kind, machine
|
|
231
|
+
* mismatch, fingerprint mismatch, non-array results, malformed rows) returns
|
|
232
|
+
* `{ items: [], valid: false }` rather than silently accepting a partial or
|
|
233
|
+
* spoofed payload.
|
|
234
|
+
*
|
|
235
|
+
* `valid: false` is what makes the failure visible. A bare `[]` normalizes to
|
|
236
|
+
* `{ items: [], valid: true }` in `normalizeRemoteAgentsJsonParse`, so the peer
|
|
237
|
+
* would be recorded as having answered with nothing to report — indistinguishable
|
|
238
|
+
* from a device that genuinely had no work, even though it had already run
|
|
239
|
+
* `git fetch` + `merge --ff-only` or hit a real `blocked`/`failed`. Returning
|
|
240
|
+
* the result shape instead lands the peer in `parseFailed`, which the caller
|
|
241
|
+
* both prints and treats as a non-zero exit.
|
|
242
|
+
*/
|
|
243
|
+
export function parseProjectPullEnvelope(stdout, machine, opts = {}) {
|
|
244
|
+
const rejected = { items: [], valid: false };
|
|
245
|
+
let parsed;
|
|
246
|
+
try {
|
|
247
|
+
parsed = JSON.parse(stdout);
|
|
248
|
+
}
|
|
249
|
+
catch {
|
|
250
|
+
return rejected;
|
|
251
|
+
}
|
|
252
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed))
|
|
253
|
+
return rejected;
|
|
254
|
+
const env = parsed;
|
|
255
|
+
// Every field is load-bearing — a partial envelope is not trusted.
|
|
256
|
+
if (env.schemaVersion !== 1)
|
|
257
|
+
return rejected;
|
|
258
|
+
if (env.kind !== 'project-pull')
|
|
259
|
+
return rejected;
|
|
260
|
+
if (typeof env.machine !== 'string' || env.machine !== machine)
|
|
261
|
+
return rejected;
|
|
262
|
+
if (opts.expectedFingerprint !== undefined && env.targetFingerprint !== opts.expectedFingerprint)
|
|
263
|
+
return rejected;
|
|
264
|
+
if (!Array.isArray(env.results))
|
|
265
|
+
return rejected;
|
|
266
|
+
const validStatuses = new Set(['updated', 'current', 'missing', 'blocked', 'failed']);
|
|
267
|
+
const items = [];
|
|
268
|
+
for (const x of env.results) {
|
|
269
|
+
// A malformed ROW is the same class of failure as a malformed envelope:
|
|
270
|
+
// dropping it would hide one directory's real outcome inside an otherwise
|
|
271
|
+
// healthy-looking answer.
|
|
272
|
+
if (!x || typeof x !== 'object' || Array.isArray(x))
|
|
273
|
+
return rejected;
|
|
274
|
+
const r = x;
|
|
275
|
+
if (typeof r.path !== 'string')
|
|
276
|
+
return rejected;
|
|
277
|
+
if (typeof r.status !== 'string' || !validStatuses.has(r.status))
|
|
278
|
+
return rejected;
|
|
279
|
+
const result = {
|
|
280
|
+
host: machine,
|
|
281
|
+
path: r.path,
|
|
282
|
+
status: r.status,
|
|
283
|
+
};
|
|
284
|
+
if (typeof r.expectedSlug === 'string')
|
|
285
|
+
result.expectedSlug = r.expectedSlug;
|
|
286
|
+
if (typeof r.branch === 'string')
|
|
287
|
+
result.branch = r.branch;
|
|
288
|
+
if (typeof r.upstream === 'string')
|
|
289
|
+
result.upstream = r.upstream;
|
|
290
|
+
if (typeof r.before === 'string')
|
|
291
|
+
result.before = r.before;
|
|
292
|
+
if (typeof r.after === 'string')
|
|
293
|
+
result.after = r.after;
|
|
294
|
+
if (typeof r.message === 'string')
|
|
295
|
+
result.message = r.message;
|
|
296
|
+
items.push(result);
|
|
297
|
+
}
|
|
298
|
+
return { items, valid: true };
|
|
299
|
+
}
|
|
300
|
+
// ---------------------------------------------------------------------------
|
|
301
|
+
// Exit-code predicate
|
|
302
|
+
// ---------------------------------------------------------------------------
|
|
303
|
+
/**
|
|
304
|
+
* Returns `true` when every pull completed successfully (`updated` or
|
|
305
|
+
* `current`). `missing` paths are skipped by design — their absence does not
|
|
306
|
+
* count as a failure. `blocked` and `failed` paths drive a non-zero exit.
|
|
307
|
+
*/
|
|
308
|
+
export function projectPullComplete(results) {
|
|
309
|
+
return results.every((r) => r.status !== 'blocked' && r.status !== 'failed');
|
|
310
|
+
}
|
|
311
|
+
// ---------------------------------------------------------------------------
|
|
312
|
+
// Human-readable output
|
|
313
|
+
// ---------------------------------------------------------------------------
|
|
314
|
+
function statusIcon(s) {
|
|
315
|
+
switch (s) {
|
|
316
|
+
case 'updated': return chalk.green('✓');
|
|
317
|
+
case 'current': return chalk.dim('·');
|
|
318
|
+
case 'missing': return chalk.yellow('?');
|
|
319
|
+
case 'blocked': return chalk.red('✗');
|
|
320
|
+
case 'failed': return chalk.red('✗');
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
function statusLabel(r) {
|
|
324
|
+
switch (r.status) {
|
|
325
|
+
case 'updated': {
|
|
326
|
+
const diff = r.before && r.after ? chalk.dim(` ${r.before}→${r.after}`) : '';
|
|
327
|
+
const br = r.branch ? ` (${r.branch})` : '';
|
|
328
|
+
return `${chalk.green('updated')}${br}${diff}`;
|
|
329
|
+
}
|
|
330
|
+
case 'current': {
|
|
331
|
+
const br = r.branch ? chalk.dim(` (${r.branch})`) : '';
|
|
332
|
+
return `${chalk.dim('already current')}${br}`;
|
|
333
|
+
}
|
|
334
|
+
case 'missing':
|
|
335
|
+
return chalk.yellow('missing — skipped');
|
|
336
|
+
case 'blocked':
|
|
337
|
+
return `${chalk.red('blocked')}${r.message ? chalk.dim(` — ${r.message}`) : ''}`;
|
|
338
|
+
case 'failed':
|
|
339
|
+
return `${chalk.red('failed')}${r.message ? chalk.dim(` — ${r.message}`) : ''}`;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
/**
|
|
343
|
+
* Print a human-readable per-host / per-path pull summary to stdout, followed
|
|
344
|
+
* by a one-line counts footer. Does not exit — the caller controls the exit code.
|
|
345
|
+
*
|
|
346
|
+
* `unavailableDevices` never answered (offline, no CLI, timed out).
|
|
347
|
+
* `unverifiedDevices` DID answer but their envelope failed verification, so
|
|
348
|
+
* their real outcome is unknown — a strictly worse state than silence, and the
|
|
349
|
+
* one the caller turns into a non-zero exit.
|
|
350
|
+
*/
|
|
351
|
+
export function printProjectPullSummary(projectName, results, unavailableDevices, unverifiedDevices = []) {
|
|
352
|
+
const counts = { updated: 0, current: 0, missing: 0, blocked: 0, failed: 0 };
|
|
353
|
+
for (const r of results)
|
|
354
|
+
counts[r.status]++;
|
|
355
|
+
console.log(chalk.bold(`${projectName}`));
|
|
356
|
+
const hosts = [...new Set(results.map((r) => r.host))].sort();
|
|
357
|
+
for (const host of hosts) {
|
|
358
|
+
const hostResults = results.filter((r) => r.host === host);
|
|
359
|
+
const hasProblems = hostResults.some((r) => r.status === 'blocked' || r.status === 'failed');
|
|
360
|
+
const hostLabel = hasProblems ? chalk.red(host) : chalk.cyan(host);
|
|
361
|
+
console.log(` ${hostLabel}`);
|
|
362
|
+
for (const r of hostResults) {
|
|
363
|
+
console.log(` ${statusIcon(r.status)} ${chalk.dim(r.path)} ${statusLabel(r)}`);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
if (unavailableDevices.length > 0) {
|
|
367
|
+
console.log(chalk.gray(` unavailable: ${unavailableDevices.join(', ')}`));
|
|
368
|
+
}
|
|
369
|
+
if (unverifiedDevices.length > 0) {
|
|
370
|
+
console.log(chalk.red(` unverified: ${unverifiedDevices.join(', ')} — answered, but the result could not be verified; their checkouts may have changed`));
|
|
371
|
+
}
|
|
372
|
+
const parts = [];
|
|
373
|
+
if (counts.updated)
|
|
374
|
+
parts.push(chalk.green(`${counts.updated} updated`));
|
|
375
|
+
if (counts.current)
|
|
376
|
+
parts.push(chalk.dim(`${counts.current} current`));
|
|
377
|
+
if (counts.missing)
|
|
378
|
+
parts.push(chalk.yellow(`${counts.missing} missing`));
|
|
379
|
+
if (counts.blocked)
|
|
380
|
+
parts.push(chalk.red(`${counts.blocked} blocked`));
|
|
381
|
+
if (counts.failed)
|
|
382
|
+
parts.push(chalk.red(`${counts.failed} failed`));
|
|
383
|
+
console.log(` ${parts.join(chalk.dim(' · '))}`);
|
|
384
|
+
}
|