@phnx-labs/agents-cli 1.22.35 → 1.22.38
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +708 -2
- package/README.md +83 -43
- package/dist/bin/agents +0 -0
- package/dist/bootstrap.d.ts +15 -0
- package/dist/bootstrap.js +1212 -0
- package/dist/commands/accounts.d.ts +4 -11
- package/dist/commands/accounts.js +159 -27
- package/dist/commands/artifacts-setup.d.ts +53 -0
- package/dist/commands/artifacts-setup.js +161 -0
- package/dist/commands/artifacts.d.ts +18 -0
- package/dist/commands/artifacts.js +58 -0
- package/dist/commands/attach.js +17 -4
- package/dist/commands/audit.d.ts +8 -8
- package/dist/commands/audit.js +57 -47
- package/dist/commands/browser.js +17 -11
- package/dist/commands/cloud.js +1 -1
- package/dist/commands/commands.js +0 -11
- package/dist/commands/computer-actions.js +33 -0
- package/dist/commands/computer-sessions-picker.d.ts +21 -0
- package/dist/commands/computer-sessions-picker.js +141 -0
- package/dist/commands/computer.d.ts +16 -0
- package/dist/commands/computer.js +37 -0
- package/dist/commands/config.d.ts +17 -0
- package/dist/commands/config.js +401 -0
- package/dist/commands/cp.d.ts +84 -0
- package/dist/commands/cp.js +231 -0
- package/dist/commands/daemon.js +243 -14
- package/dist/commands/detach-core.d.ts +1 -1
- package/dist/commands/detach-core.js +12 -2
- package/dist/commands/detach.js +1 -1
- package/dist/commands/doctor.js +64 -5
- package/dist/commands/events.d.ts +24 -12
- package/dist/commands/events.js +144 -28
- package/dist/commands/exec.js +86 -42
- package/dist/commands/feed.d.ts +1 -1
- package/dist/commands/feed.js +16 -8
- package/dist/commands/focus.d.ts +33 -1
- package/dist/commands/focus.js +177 -17
- package/dist/commands/funnel.d.ts +1 -1
- package/dist/commands/go.js +9 -8
- package/dist/commands/harness-wizard.d.ts +1 -0
- package/dist/commands/harness-wizard.js +18 -20
- package/dist/commands/harness.d.ts +2 -0
- package/dist/commands/harness.js +59 -26
- package/dist/commands/hooks.js +0 -11
- package/dist/commands/inspect.d.ts +94 -1
- package/dist/commands/inspect.js +866 -40
- package/dist/commands/lease.d.ts +4 -4
- package/dist/commands/lease.js +6 -6
- package/dist/commands/login.js +0 -13
- package/dist/commands/logs.d.ts +2 -2
- package/dist/commands/logs.js +2 -2
- package/dist/commands/mcp.js +7 -2
- package/dist/commands/memory.js +7 -2
- package/dist/commands/message.d.ts +1 -1
- package/dist/commands/message.js +37 -6
- package/dist/commands/models.js +71 -0
- package/dist/commands/monitors.d.ts +0 -1
- package/dist/commands/monitors.js +199 -16
- package/dist/commands/open.d.ts +18 -0
- package/dist/commands/open.js +57 -0
- package/dist/commands/packages.js +2 -107
- package/dist/commands/plugins.js +6 -3
- package/dist/commands/profiles.d.ts +1 -0
- package/dist/commands/profiles.js +16 -160
- package/dist/commands/projects.d.ts +28 -0
- package/dist/commands/projects.js +125 -5
- package/dist/commands/reconnect.js +9 -3
- package/dist/commands/repo.js +42 -1
- package/dist/commands/resource-view.d.ts +17 -0
- package/dist/commands/resource-view.js +36 -14
- package/dist/commands/resume.js +9 -2
- package/dist/commands/routines.d.ts +0 -2
- package/dist/commands/routines.js +473 -289
- package/dist/commands/secrets.js +52 -9
- package/dist/commands/sessions-export.d.ts +47 -0
- package/dist/commands/sessions-export.js +116 -6
- package/dist/commands/sessions-import.d.ts +17 -0
- package/dist/commands/sessions-import.js +114 -6
- package/dist/commands/sessions-migrate.d.ts +27 -0
- package/dist/commands/sessions-migrate.js +50 -9
- package/dist/commands/sessions-picker.js +23 -4
- package/dist/commands/sessions-resume.d.ts +5 -3
- package/dist/commands/sessions-resume.js +30 -8
- package/dist/commands/sessions-watch.d.ts +2 -0
- package/dist/commands/sessions-watch.js +53 -0
- package/dist/commands/sessions.d.ts +66 -3
- package/dist/commands/sessions.js +298 -47
- package/dist/commands/setup-browser.js +2 -2
- package/dist/commands/setup-mine.js +0 -1
- package/dist/commands/setup-preferences.js +1 -1
- package/dist/commands/setup.d.ts +5 -0
- package/dist/commands/setup.js +31 -5
- package/dist/commands/share.d.ts +87 -7
- package/dist/commands/share.js +320 -58
- package/dist/commands/skills.js +0 -11
- package/dist/commands/snapshot.js +1 -1
- package/dist/commands/ssh.d.ts +18 -0
- package/dist/commands/ssh.js +549 -210
- package/dist/commands/sync.d.ts +5 -0
- package/dist/commands/sync.js +212 -32
- package/dist/commands/teams.d.ts +13 -0
- package/dist/commands/teams.js +187 -30
- package/dist/commands/tickets.d.ts +2 -0
- package/dist/commands/tickets.js +43 -0
- package/dist/commands/update.js +2 -4
- package/dist/commands/usage.js +5 -3
- package/dist/commands/versions.js +11 -5
- package/dist/commands/view.js +15 -7
- package/dist/commands/webhook.d.ts +2 -2
- package/dist/commands/webhook.js +7 -7
- package/dist/index.d.ts +18 -2
- package/dist/index.js +53 -1264
- package/dist/lib/account-catalog.d.ts +18 -0
- package/dist/lib/account-catalog.js +38 -0
- package/dist/lib/account-provider-registry.d.ts +18 -0
- package/dist/lib/account-provider-registry.js +59 -0
- package/dist/lib/account-registry.d.ts +40 -0
- package/dist/lib/account-registry.js +239 -0
- package/dist/lib/account-schema.d.ts +44 -0
- package/dist/lib/account-schema.js +91 -0
- package/dist/lib/account-state-service.d.ts +21 -0
- package/dist/lib/account-state-service.js +60 -0
- package/dist/lib/activity.d.ts +1 -1
- package/dist/lib/activity.js +8 -1
- package/dist/lib/add-dir.d.ts +80 -0
- package/dist/lib/add-dir.js +241 -0
- package/dist/lib/agent-cli-commands.d.ts +11 -0
- package/dist/lib/agent-cli-commands.js +30 -0
- package/dist/lib/agents.js +44 -36
- package/dist/lib/answer-router.js +1 -1
- package/dist/lib/audit/log.d.ts +10 -27
- package/dist/lib/audit/log.js +20 -33
- package/dist/lib/auth-health.d.ts +9 -2
- package/dist/lib/auth-health.js +42 -9
- package/dist/lib/auto-pull-worker.js +7 -3
- package/dist/lib/auto-pull.d.ts +44 -3
- package/dist/lib/auto-pull.js +88 -5
- package/dist/lib/binary-shadow.d.ts +28 -0
- package/dist/lib/binary-shadow.js +121 -0
- package/dist/lib/brand.js +9 -3
- package/dist/lib/browser/hygiene.d.ts +90 -0
- package/dist/lib/browser/hygiene.js +146 -0
- package/dist/lib/browser/ipc.d.ts +30 -0
- package/dist/lib/browser/ipc.js +93 -4
- package/dist/lib/browser/profiles.d.ts +3 -2
- package/dist/lib/browser/profiles.js +65 -22
- package/dist/lib/browser/service.d.ts +79 -1
- package/dist/lib/browser/service.js +231 -14
- package/dist/lib/browser/sessions-list.d.ts +22 -2
- package/dist/lib/browser/sessions-list.js +54 -6
- package/dist/lib/browser/stream.d.ts +2 -0
- package/dist/lib/browser/stream.js +1 -0
- package/dist/lib/browser/types.d.ts +63 -1
- package/dist/lib/byok-usage.d.ts +9 -2
- package/dist/lib/byok-usage.js +133 -31
- package/dist/lib/cli-entry.d.ts +11 -0
- package/dist/lib/cli-entry.js +46 -0
- package/dist/lib/cloud/host.d.ts +1 -1
- package/dist/lib/cloud/host.js +2 -2
- package/dist/lib/cloud/registry.js +1 -1
- package/dist/lib/cloud/types.d.ts +1 -1
- package/dist/lib/commands.d.ts +2 -0
- package/dist/lib/commands.js +2 -0
- package/dist/lib/computer/sessions-list.d.ts +150 -0
- package/dist/lib/computer/sessions-list.js +363 -0
- package/dist/lib/config-keys.d.ts +86 -0
- package/dist/lib/config-keys.js +193 -0
- package/dist/lib/config-machine-keys.d.ts +12 -0
- package/dist/lib/config-machine-keys.js +17 -0
- package/dist/lib/config-transfer.js +3 -4
- package/dist/lib/crabbox/cli.d.ts +2 -2
- package/dist/lib/crabbox/cli.js +3 -3
- package/dist/lib/daemon-health.d.ts +22 -0
- package/dist/lib/daemon-health.js +33 -3
- package/dist/lib/daemon-services.d.ts +50 -0
- package/dist/lib/daemon-services.js +147 -0
- package/dist/lib/daemon-ticks.d.ts +35 -66
- package/dist/lib/daemon-ticks.js +77 -159
- package/dist/lib/daemon.d.ts +72 -4
- package/dist/lib/daemon.js +792 -202
- package/dist/lib/deeplink/register.d.ts +49 -0
- package/dist/lib/deeplink/register.js +253 -0
- package/dist/lib/deeplink/url.d.ts +34 -0
- package/dist/lib/deeplink/url.js +78 -0
- package/dist/lib/device-config.d.ts +124 -29
- package/dist/lib/device-config.js +346 -141
- package/dist/lib/devices/config-migration.d.ts +42 -0
- package/dist/lib/devices/config-migration.js +203 -0
- package/dist/lib/devices/connect.js +14 -7
- package/dist/lib/devices/doctor-findings.d.ts +4 -1
- package/dist/lib/devices/doctor-findings.js +20 -1
- package/dist/lib/devices/harness-inventory.d.ts +11 -2
- package/dist/lib/devices/harness-inventory.js +0 -0
- package/dist/lib/devices/health.d.ts +1 -1
- package/dist/lib/devices/health.js +1 -1
- package/dist/lib/devices/pending.d.ts +22 -1
- package/dist/lib/devices/pending.js +67 -2
- package/dist/lib/devices/registry.d.ts +0 -25
- package/dist/lib/devices/registry.js +2 -83
- package/dist/lib/devices/resolve-profile.d.ts +20 -0
- package/dist/lib/devices/resolve-profile.js +44 -0
- package/dist/lib/devices/rollout-verify.d.ts +101 -0
- package/dist/lib/devices/rollout-verify.js +193 -0
- package/dist/lib/devices/ssh-config.js +10 -6
- package/dist/lib/event-families.d.ts +28 -0
- package/dist/lib/event-families.js +159 -0
- package/dist/lib/event-stream.d.ts +9 -1
- package/dist/lib/event-stream.js +26 -9
- package/dist/lib/events.d.ts +9 -1
- package/dist/lib/events.js +21 -6
- package/dist/lib/exec.d.ts +30 -0
- package/dist/lib/exec.js +219 -121
- package/dist/lib/feed-broadcast.d.ts +25 -0
- package/dist/lib/feed-broadcast.js +33 -0
- package/dist/lib/feed.d.ts +1 -1
- package/dist/lib/feed.js +12 -2
- package/dist/lib/fleet/capture.js +15 -0
- package/dist/lib/fleet/manifest.js +9 -0
- package/dist/lib/fleet/types.d.ts +9 -0
- package/dist/lib/fleet-status.js +10 -8
- package/dist/lib/git.d.ts +21 -7
- package/dist/lib/git.js +122 -18
- package/dist/lib/hosts/dispatch.d.ts +8 -3
- package/dist/lib/hosts/dispatch.js +29 -3
- package/dist/lib/hosts/logs.d.ts +2 -2
- package/dist/lib/hosts/logs.js +2 -2
- package/dist/lib/hosts/option.js +1 -1
- package/dist/lib/hosts/passthrough.d.ts +2 -3
- package/dist/lib/hosts/passthrough.js +10 -24
- package/dist/lib/hosts/progress.js +2 -2
- package/dist/lib/hosts/providers/devices.d.ts +3 -3
- package/dist/lib/hosts/providers/devices.js +18 -12
- package/dist/lib/hosts/ready.d.ts +5 -1
- package/dist/lib/hosts/ready.js +9 -3
- package/dist/lib/hosts/reconcile.d.ts +2 -2
- package/dist/lib/hosts/reconcile.js +2 -2
- package/dist/lib/hosts/registry.js +14 -9
- package/dist/lib/hosts/remote-cmd.js +1 -1
- package/dist/lib/hosts/remote-os.d.ts +9 -6
- package/dist/lib/hosts/remote-os.js +13 -6
- package/dist/lib/hosts/routing-flag.d.ts +23 -0
- package/dist/lib/hosts/routing-flag.js +41 -0
- package/dist/lib/hosts/run-target.js +1 -1
- package/dist/lib/hosts/session-index.js +1 -1
- package/dist/lib/hosts/tasks.d.ts +6 -6
- package/dist/lib/hosts/tasks.js +4 -4
- package/dist/lib/hosts/types.js +1 -2
- package/dist/lib/installations/resolve.js +2 -8
- package/dist/lib/isolation-boundary-report.js +0 -1
- package/dist/lib/linear-cache.d.ts +7 -0
- package/dist/lib/linear-cache.js +31 -4
- package/dist/lib/linear-project-counts.d.ts +1 -1
- package/dist/lib/linear-project-counts.js +2 -3
- package/dist/lib/linear-projects.js +1 -1
- package/dist/lib/loop.d.ts +2 -2
- package/dist/lib/loop.js +1 -1
- package/dist/lib/mailbox-target.d.ts +1 -1
- package/dist/lib/mailbox-target.js +1 -1
- package/dist/lib/manifest.js +7 -4
- package/dist/lib/menubar/MenubarHelper.app/Contents/CodeResources +0 -0
- package/dist/lib/menubar/MenubarHelper.app/Contents/MacOS/MenubarHelper +0 -0
- package/dist/lib/menubar/snapshot.d.ts +3 -2
- package/dist/lib/menubar/snapshot.js +6 -2
- package/dist/lib/migrate-fold.d.ts +33 -0
- package/dist/lib/migrate-fold.js +130 -0
- package/dist/lib/migrate.d.ts +34 -34
- package/dist/lib/migrate.js +106 -128
- package/dist/lib/model-tier-overrides.d.ts +1 -1
- package/dist/lib/model-tier-overrides.js +15 -2
- package/dist/lib/monitors/config.d.ts +17 -3
- package/dist/lib/monitors/config.js +52 -18
- package/dist/lib/monitors/engine.d.ts +32 -1
- package/dist/lib/monitors/engine.js +94 -11
- package/dist/lib/monitors/fingerprint.d.ts +46 -0
- package/dist/lib/monitors/fingerprint.js +100 -0
- package/dist/lib/monitors/remote.d.ts +47 -0
- package/dist/lib/monitors/remote.js +81 -0
- package/dist/lib/monitors/state.d.ts +39 -0
- package/dist/lib/monitors/state.js +56 -0
- package/dist/lib/onepassword.d.ts +17 -0
- package/dist/lib/onepassword.js +52 -22
- package/dist/lib/open-url.js +1 -1
- package/dist/lib/picker.d.ts +14 -2
- package/dist/lib/picker.js +35 -13
- package/dist/lib/placement.d.ts +1 -1
- package/dist/lib/placement.js +1 -1
- package/dist/lib/pr-land-detach.d.ts +32 -0
- package/dist/lib/pr-land-detach.js +79 -0
- package/dist/lib/profiles-presets.js +13 -0
- package/dist/lib/profiles.d.ts +2 -0
- package/dist/lib/profiles.js +44 -1
- package/dist/lib/project-focus.d.ts +1 -1
- package/dist/lib/project-focus.js +1 -1
- package/dist/lib/project-probe.d.ts +4 -1
- package/dist/lib/project-probe.js +5 -4
- package/dist/lib/project-resources.js +2 -18
- package/dist/lib/project-root.d.ts +16 -0
- package/dist/lib/project-root.js +20 -2
- package/dist/lib/projects.d.ts +20 -1
- package/dist/lib/projects.js +78 -1
- package/dist/lib/refresh-coordinator.d.ts +24 -0
- package/dist/lib/refresh-coordinator.js +52 -0
- package/dist/lib/registry.d.ts +2 -2
- package/dist/lib/registry.js +1 -1
- package/dist/lib/resource-aliases.d.ts +13 -0
- package/dist/lib/resource-aliases.js +26 -0
- package/dist/lib/resources/types.d.ts +1 -1
- package/dist/lib/resources.d.ts +16 -0
- package/dist/lib/resources.js +66 -10
- package/dist/lib/rotate.d.ts +3 -4
- package/dist/lib/rotate.js +4 -5
- package/dist/lib/routine-activation.d.ts +22 -2
- package/dist/lib/routine-activation.js +63 -16
- package/dist/lib/routine-context.d.ts +9 -2
- package/dist/lib/routine-context.js +21 -6
- package/dist/lib/routines-project.d.ts +58 -48
- package/dist/lib/routines-project.js +190 -159
- package/dist/lib/routines.d.ts +44 -7
- package/dist/lib/routines.js +92 -34
- package/dist/lib/run-defaults.d.ts +5 -0
- package/dist/lib/run-defaults.js +11 -1
- package/dist/lib/runner.d.ts +10 -8
- package/dist/lib/runner.js +122 -39
- package/dist/lib/secrets/Agents CLI.app/Contents/CodeResources +0 -0
- package/dist/lib/secrets/Agents CLI.app/Contents/MacOS/Agents CLI +0 -0
- package/dist/lib/secrets/agent.d.ts +10 -3
- package/dist/lib/secrets/agent.js +43 -8
- package/dist/lib/secrets/bundles.d.ts +1 -0
- package/dist/lib/secrets/bundles.js +92 -80
- package/dist/lib/secrets/index.js +32 -9
- package/dist/lib/secrets/install-helper.d.ts +4 -4
- package/dist/lib/secrets/install-helper.js +4 -4
- package/dist/lib/secrets/push.d.ts +40 -6
- package/dist/lib/secrets/push.js +58 -11
- package/dist/lib/secrets/read-backoff.d.ts +1 -1
- package/dist/lib/secrets/read-backoff.js +1 -1
- package/dist/lib/secrets/remote.d.ts +24 -0
- package/dist/lib/secrets/remote.js +54 -4
- package/dist/lib/self-update.d.ts +108 -0
- package/dist/lib/self-update.js +276 -1
- package/dist/lib/session/active.d.ts +89 -5
- package/dist/lib/session/active.js +165 -10
- package/dist/lib/session/actor-sidecar.d.ts +1 -1
- package/dist/lib/session/actor-sidecar.js +2 -1
- package/dist/lib/session/db.d.ts +176 -2
- package/dist/lib/session/db.js +484 -17
- package/dist/lib/session/discover.js +27 -4
- package/dist/lib/session/hook-sessions.js +2 -2
- package/dist/lib/session/host-link.d.ts +2 -2
- package/dist/lib/session/host-link.js +2 -2
- package/dist/lib/session/pid-registry.d.ts +21 -0
- package/dist/lib/session/pid-registry.js +57 -0
- package/dist/lib/session/remote-active.js +15 -1
- package/dist/lib/session/remote.js +2 -2
- package/dist/lib/session/session-cache.d.ts +14 -7
- package/dist/lib/session/session-cache.js +51 -9
- package/dist/lib/session/state.d.ts +2 -2
- package/dist/lib/session/state.js +1 -1
- package/dist/lib/session/sync/config.d.ts +10 -10
- package/dist/lib/session/sync/config.js +10 -10
- package/dist/lib/session/sync/r2.d.ts +34 -0
- package/dist/lib/session/sync/r2.js +123 -0
- package/dist/lib/session/throughput.d.ts +1 -1
- package/dist/lib/session/throughput.js +1 -1
- package/dist/lib/session/types.d.ts +23 -1
- package/dist/lib/session/types.js +15 -0
- package/dist/lib/session/viewing-in.d.ts +1 -1
- package/dist/lib/session/viewing-in.js +1 -1
- package/dist/lib/session/watch.d.ts +101 -0
- package/dist/lib/session/watch.js +242 -0
- package/dist/lib/share/analytics.js +1 -1
- package/dist/lib/share/capture.d.ts +1 -1
- package/dist/lib/share/capture.js +3 -3
- package/dist/lib/share/config.d.ts +6 -2
- package/dist/lib/share/config.js +6 -5
- package/dist/lib/share/delete.js +3 -3
- package/dist/lib/share/provision.d.ts +38 -0
- package/dist/lib/share/provision.js +51 -1
- package/dist/lib/share/publish.d.ts +40 -1
- package/dist/lib/share/publish.js +119 -6
- package/dist/lib/share/worker-template.d.ts +12 -1
- package/dist/lib/share/worker-template.js +83 -7
- package/dist/lib/shims.d.ts +17 -3
- package/dist/lib/shims.js +98 -26
- package/dist/lib/signin-badge.js +3 -2
- package/dist/lib/skills.js +2 -0
- package/dist/lib/smart-launch.d.ts +8 -8
- package/dist/lib/smart-launch.js +80 -59
- package/dist/lib/ssh-exec.d.ts +1 -1
- package/dist/lib/ssh-exec.js +1 -1
- package/dist/lib/staleness/prune.d.ts +88 -0
- package/dist/lib/staleness/prune.js +69 -0
- package/dist/lib/staleness/registry.d.ts +1 -1
- package/dist/lib/staleness/writers/commands.js +34 -2
- package/dist/lib/staleness/writers/hooks.js +16 -0
- package/dist/lib/staleness/writers/kinds.d.ts +6 -0
- package/dist/lib/staleness/writers/skills.js +19 -0
- package/dist/lib/staleness/writers/subagents.d.ts +2 -3
- package/dist/lib/staleness/writers/subagents.js +0 -4
- package/dist/lib/staleness/writers/types.d.ts +32 -0
- package/dist/lib/startup/command-registry.d.ts +16 -14
- package/dist/lib/startup/command-registry.js +26 -37
- package/dist/lib/startup/dev-build.d.ts +10 -0
- package/dist/lib/startup/dev-build.js +13 -1
- package/dist/lib/startup/spellcheck.d.ts +18 -0
- package/dist/lib/startup/spellcheck.js +45 -0
- package/dist/lib/state.d.ts +36 -9
- package/dist/lib/state.js +96 -46
- package/dist/lib/subagents-registry.d.ts +0 -7
- package/dist/lib/subagents-registry.js +38 -65
- package/dist/lib/subagents.d.ts +0 -33
- package/dist/lib/subagents.js +0 -75
- package/dist/lib/sync-umbrella.js +1 -1
- package/dist/lib/teams/agents.d.ts +41 -1
- package/dist/lib/teams/agents.js +187 -9
- package/dist/lib/teams/api.d.ts +11 -1
- package/dist/lib/teams/api.js +11 -2
- package/dist/lib/teams/delivery.d.ts +41 -0
- package/dist/lib/teams/delivery.js +60 -0
- package/dist/lib/teams/registry.d.ts +14 -0
- package/dist/lib/teams/registry.js +51 -1
- package/dist/lib/teams/remoteWorktree.d.ts +19 -0
- package/dist/lib/teams/remoteWorktree.js +29 -0
- package/dist/lib/teams/scheduler.d.ts +1 -1
- package/dist/lib/teams/scheduler.js +1 -1
- package/dist/lib/teams/worktree.d.ts +24 -0
- package/dist/lib/teams/worktree.js +42 -0
- package/dist/lib/terminal/backends/vscodium-agent.d.ts +10 -1
- package/dist/lib/terminal/backends/vscodium-agent.js +16 -5
- package/dist/lib/terminal/engine.d.ts +4 -0
- package/dist/lib/terminal/engine.js +8 -2
- package/dist/lib/terminal/types.d.ts +21 -2
- package/dist/lib/testdata/refresh-coordinator-worker.d.ts +1 -0
- package/dist/lib/testdata/refresh-coordinator-worker.js +23 -0
- package/dist/lib/tickets/list.d.ts +53 -0
- package/dist/lib/tickets/list.js +153 -0
- package/dist/lib/tmux/orphan-reap.d.ts +316 -0
- package/dist/lib/tmux/orphan-reap.js +638 -0
- package/dist/lib/tmux/session.d.ts +68 -3
- package/dist/lib/tmux/session.js +96 -5
- package/dist/lib/triggers/handlers.d.ts +20 -0
- package/dist/lib/triggers/handlers.js +16 -1
- package/dist/lib/triggers/webhook.js +10 -0
- package/dist/lib/types.d.ts +47 -35
- package/dist/lib/usage-fleet.d.ts +32 -0
- package/dist/lib/usage-fleet.js +125 -0
- package/dist/lib/usage-refresh.d.ts +3 -3
- package/dist/lib/usage-refresh.js +19 -16
- package/dist/lib/usage.d.ts +51 -47
- package/dist/lib/usage.js +145 -153
- package/dist/lib/version-duplicates.js +2 -2
- package/dist/lib/versions.d.ts +56 -13
- package/dist/lib/versions.js +195 -45
- package/dist/lib/watchdog/log.d.ts +1 -1
- package/dist/lib/watchdog/log.js +3 -3
- package/dist/lib/watchdog/rotate.d.ts +6 -6
- package/dist/lib/watchdog/rotate.js +6 -6
- package/dist/lib/workflows.d.ts +1 -1
- package/dist/lib/yaml-io.d.ts +47 -0
- package/dist/lib/yaml-io.js +55 -0
- package/package.json +2 -1
- package/scripts/install-helper.js +2 -2
- package/scripts/postinstall.js +23 -20
- package/dist/commands/defaults.d.ts +0 -7
- package/dist/commands/defaults.js +0 -107
- package/dist/commands/export.d.ts +0 -11
- package/dist/commands/export.js +0 -215
- package/dist/commands/helper.d.ts +0 -12
- package/dist/commands/helper.js +0 -87
- package/dist/commands/hosts.d.ts +0 -11
- package/dist/commands/hosts.js +0 -330
- package/dist/commands/lock.d.ts +0 -12
- package/dist/commands/lock.js +0 -70
- package/dist/commands/pull.d.ts +0 -17
- package/dist/commands/pull.js +0 -39
- package/dist/commands/push.d.ts +0 -14
- package/dist/commands/push.js +0 -30
- package/dist/commands/set.d.ts +0 -15
- package/dist/commands/set.js +0 -79
- package/dist/commands/setup-share.d.ts +0 -17
- package/dist/commands/setup-share.js +0 -85
- package/dist/commands/wallet.d.ts +0 -20
- package/dist/commands/wallet.js +0 -216
- package/dist/commands/worktree.d.ts +0 -19
- package/dist/commands/worktree.js +0 -272
- package/dist/lib/account-labels.d.ts +0 -24
- package/dist/lib/account-labels.js +0 -72
- package/dist/lib/auto-dispatch-linear.d.ts +0 -18
- package/dist/lib/auto-dispatch-linear.js +0 -107
- package/dist/lib/auto-dispatch-provider.d.ts +0 -10
- package/dist/lib/auto-dispatch-provider.js +0 -30
- package/dist/lib/auto-dispatch.d.ts +0 -93
- package/dist/lib/auto-dispatch.js +0 -128
- package/dist/lib/export.d.ts +0 -72
- package/dist/lib/export.js +0 -269
- package/dist/lib/lock.d.ts +0 -93
- package/dist/lib/lock.js +0 -207
- package/dist/lib/wallet/index.d.ts +0 -78
- package/dist/lib/wallet/index.js +0 -253
|
@@ -10,14 +10,14 @@ import ora from 'ora';
|
|
|
10
10
|
import * as fs from 'fs';
|
|
11
11
|
import * as path from 'path';
|
|
12
12
|
import * as yaml from 'yaml';
|
|
13
|
-
import { isDaemonRunning, signalDaemonReload, startDaemon, stopDaemon, readDaemonLog, getDaemonStatus, } from '../lib/daemon.js';
|
|
13
|
+
import { isDaemonRunning, signalDaemonReload, startDaemon, stopDaemon, readDaemonLog, getDaemonStatus, getDaemonLogPath, } from '../lib/daemon.js';
|
|
14
14
|
import { assertSchedulerEnabled, assertDaemonEnabled, isDaemonEnabled } from '../lib/device-config.js';
|
|
15
15
|
import { resolveAgentName, isAgentHardDeprecated, hardDeprecationError, ROUTINE_AGENT_IDS } from '../lib/agents.js';
|
|
16
16
|
import { humanizeCron, humanizeNextRun, formatRepoLink, REPO_DISPLAY_MAX } from '../lib/routines-format.js';
|
|
17
|
-
import { listJobs as listAllJobs, deleteJob, readJob, validateJob, writeJob, setJobEnabled, listRuns, routineStats, getLatestRun, getRunDir, getJobPath, parseAtTime, hasCompletedOneShotRun, isOneShotLikeSchedule, isOneShotRoutine, isPastOneShotRoutine, jobRunsOnThisDevice, checkJobDeviceEligibility, normalizeTriggerEvent, parseHostStrategy, resolveHostStrategy, HOST_STRATEGIES, computeProjectGroup, computeProjectGroupKind, projectGroupKey, projectGroupTitle, projectGroupOrder, normalizeProjects, } from '../lib/routines.js';
|
|
17
|
+
import { listJobs as listAllJobs, deleteJob, readJob, validateJob, writeJob, setJobEnabled, listRuns, routineStats, getLatestRun, getRunDir, getJobPath, isCanonicalRoutineSource, parseAtTime, hasCompletedOneShotRun, isOneShotLikeSchedule, isOneShotRoutine, isPastOneShotRoutine, jobRunsOnThisDevice, checkJobDeviceEligibility, normalizeTriggerEvent, parseHostStrategy, resolveHostStrategy, HOST_STRATEGIES, computeProjectGroup, computeProjectGroupKind, projectGroupKey, projectGroupTitle, projectGroupOrder, normalizeProjects, } from '../lib/routines.js';
|
|
18
18
|
import { listProjectDefs, isSafeProjectName } from '../lib/projects.js';
|
|
19
19
|
import { evaluateActivationReadinessLive } from '../lib/routine-readiness.js';
|
|
20
|
-
import {
|
|
20
|
+
import { discoverProjectRoutines, findProjectRoutine, materialiseProjectRoutine, syncProjectRoutines, syncAllProjectRoutines, displayProjectPath, } from '../lib/routines-project.js';
|
|
21
21
|
import { fireWebhookJobs, matchJobsToWebhook } from '../lib/triggers/webhook.js';
|
|
22
22
|
import { getRoutinesDir } from '../lib/state.js';
|
|
23
23
|
import { IS_WINDOWS } from '../lib/platform/index.js';
|
|
@@ -27,6 +27,8 @@ import { JobScheduler } from '../lib/scheduler.js';
|
|
|
27
27
|
import { detectOverdueJobs } from '../lib/overdue.js';
|
|
28
28
|
import { runCatchup } from '../lib/catchup.js';
|
|
29
29
|
import { isInteractiveTerminal, requireInteractiveSelection } from './utils.js';
|
|
30
|
+
import { itemPicker } from '../lib/picker.js';
|
|
31
|
+
import { Separator } from '@inquirer/core';
|
|
30
32
|
import { setHelpSections } from '../lib/help.js';
|
|
31
33
|
import { loadDevices, loadDevicesSync } from '../lib/devices/registry.js';
|
|
32
34
|
import { machineId, normalizeHost } from '../lib/machine-id.js';
|
|
@@ -409,12 +411,30 @@ export function buildRunsJson(runs) {
|
|
|
409
411
|
}
|
|
410
412
|
/** Build the exact structured routine rows shared by `routines list --json`
|
|
411
413
|
* and the one-process AGI Menu snapshot. */
|
|
414
|
+
/**
|
|
415
|
+
* Materialised routines plus project routines discoverable from registered
|
|
416
|
+
* projects, deduped by name. The discovered ones are always disabled (available
|
|
417
|
+
* to enable) — this is a DISPLAY-only merge, so `list` shows the full single
|
|
418
|
+
* enabled/disabled picture. Execution paths (`run`/`catchup`/`webhook`) keep
|
|
419
|
+
* using `listAllJobs` so an un-materialised project routine can never fire.
|
|
420
|
+
*/
|
|
421
|
+
function listJobsForDisplay(cwd) {
|
|
422
|
+
const jobs = listAllJobs(cwd);
|
|
423
|
+
const seen = new Set(jobs.map((j) => j.name));
|
|
424
|
+
for (const discovered of discoverProjectRoutines()) {
|
|
425
|
+
if (seen.has(discovered.name))
|
|
426
|
+
continue;
|
|
427
|
+
seen.add(discovered.name);
|
|
428
|
+
jobs.push(discovered.config);
|
|
429
|
+
}
|
|
430
|
+
return jobs;
|
|
431
|
+
}
|
|
412
432
|
export function buildRoutineListJson() {
|
|
413
433
|
try {
|
|
414
434
|
monitorRunningJobs();
|
|
415
435
|
}
|
|
416
436
|
catch { /* best-effort orphan reap */ }
|
|
417
|
-
const jobs =
|
|
437
|
+
const jobs = listJobsForDisplay(process.cwd());
|
|
418
438
|
if (jobs.length === 0)
|
|
419
439
|
return [];
|
|
420
440
|
const scheduler = new JobScheduler(async () => { });
|
|
@@ -521,6 +541,101 @@ async function pickJob(message, filter, alternatives = [], cwd) {
|
|
|
521
541
|
throw err;
|
|
522
542
|
}
|
|
523
543
|
}
|
|
544
|
+
/**
|
|
545
|
+
* Enable a routine on this device. If the name is not yet a user/system routine
|
|
546
|
+
* but names a project routine (in the current project or a registered one), it
|
|
547
|
+
* is materialised into the user layer first — one step, no separate opt-in. The
|
|
548
|
+
* device flag (`meta.deviceRoutines`) is the only thing that turns firing on, so
|
|
549
|
+
* a project YAML can never enable itself.
|
|
550
|
+
*/
|
|
551
|
+
async function enableRoutineAction(name) {
|
|
552
|
+
if (!name) {
|
|
553
|
+
name = await pickJob('Select routine to enable', (job) => !job.enabled, ['agents routines enable <name>']) ?? undefined;
|
|
554
|
+
if (!name) {
|
|
555
|
+
const available = discoverProjectRoutines();
|
|
556
|
+
if (available.length > 0) {
|
|
557
|
+
console.log(chalk.gray(`Project routines available to enable: ${available.map((r) => r.name).join(', ')}`));
|
|
558
|
+
console.log(chalk.gray(' Enable one by name: agents routines enable <name>'));
|
|
559
|
+
}
|
|
560
|
+
return;
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
try {
|
|
564
|
+
// Not a user/system routine yet? Resolve it as a project routine and
|
|
565
|
+
// materialise it before enabling.
|
|
566
|
+
if (!readJob(name)) {
|
|
567
|
+
const found = findProjectRoutine(name);
|
|
568
|
+
if (!found) {
|
|
569
|
+
console.log(chalk.red(`No routine named '${name}'.`));
|
|
570
|
+
const available = discoverProjectRoutines();
|
|
571
|
+
if (available.length > 0) {
|
|
572
|
+
console.log(chalk.gray(` Available project routines: ${available.map((r) => r.name).join(', ')}`));
|
|
573
|
+
}
|
|
574
|
+
process.exit(1);
|
|
575
|
+
}
|
|
576
|
+
if ('ambiguous' in found) {
|
|
577
|
+
console.log(chalk.red(`'${name}' is defined in more than one project: ${found.ambiguous.join(', ')}.`));
|
|
578
|
+
console.log(chalk.gray(' Enable it from inside the project you want, or rename one.'));
|
|
579
|
+
process.exit(1);
|
|
580
|
+
}
|
|
581
|
+
const mat = materialiseProjectRoutine(found.projectRoot, name);
|
|
582
|
+
if ('error' in mat) {
|
|
583
|
+
console.log(chalk.red(`Cannot enable '${name}': ${mat.error}`));
|
|
584
|
+
process.exit(1);
|
|
585
|
+
}
|
|
586
|
+
console.log(chalk.gray(`Materialised '${name}' from ${displayProjectPath(found.projectRoot)}`));
|
|
587
|
+
}
|
|
588
|
+
// Enabling re-runs readiness — it can never bypass a proven blocker. A
|
|
589
|
+
// blocked routine stays disabled.
|
|
590
|
+
const job = readJob(name);
|
|
591
|
+
if (job) {
|
|
592
|
+
const readiness = await evaluateActivationReadinessLive(job);
|
|
593
|
+
if (!readiness.ready) {
|
|
594
|
+
const r = readiness.readiness;
|
|
595
|
+
console.log(chalk.red(`Cannot enable '${name}' — not ready: ${r.code}`));
|
|
596
|
+
console.log(chalk.gray(` ${r.message}`));
|
|
597
|
+
if (r.repair)
|
|
598
|
+
console.log(chalk.gray(` repair: ${r.repair}`));
|
|
599
|
+
console.log(chalk.gray(` fix it, then: agents routines doctor ${name} --fix`));
|
|
600
|
+
process.exit(1);
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
setJobEnabled(name, true);
|
|
604
|
+
console.log(chalk.green(`Routine '${name}' enabled`));
|
|
605
|
+
if (isDaemonRunning()) {
|
|
606
|
+
signalDaemonReload();
|
|
607
|
+
console.log(chalk.gray('Daemon reloaded'));
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
catch (err) {
|
|
611
|
+
console.log(chalk.red(err.message));
|
|
612
|
+
process.exit(1);
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
/** Disable a routine on this device. Its definition stays; only firing stops. */
|
|
616
|
+
async function disableRoutineAction(name) {
|
|
617
|
+
if (!name) {
|
|
618
|
+
name = await pickJob('Select routine to disable', (job) => job.enabled, ['agents routines disable <name>']) ?? undefined;
|
|
619
|
+
if (!name)
|
|
620
|
+
return;
|
|
621
|
+
}
|
|
622
|
+
try {
|
|
623
|
+
if (!readJob(name)) {
|
|
624
|
+
console.log(chalk.red(`No routine named '${name}'.`));
|
|
625
|
+
process.exit(1);
|
|
626
|
+
}
|
|
627
|
+
setJobEnabled(name, false);
|
|
628
|
+
console.log(chalk.green(`Routine '${name}' disabled`));
|
|
629
|
+
if (isDaemonRunning()) {
|
|
630
|
+
signalDaemonReload();
|
|
631
|
+
console.log(chalk.gray('Scheduler reloaded'));
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
catch (err) {
|
|
635
|
+
console.log(chalk.red(err.message));
|
|
636
|
+
process.exit(1);
|
|
637
|
+
}
|
|
638
|
+
}
|
|
524
639
|
/**
|
|
525
640
|
* Parse a comma-separated devices string, normalize, deduplicate, and validate
|
|
526
641
|
* each entry against the registered fleet. Exits nonzero on empty/whitespace
|
|
@@ -543,11 +658,258 @@ async function parseAndValidateDevices(raw) {
|
|
|
543
658
|
}
|
|
544
659
|
return names;
|
|
545
660
|
}
|
|
661
|
+
/**
|
|
662
|
+
* The static routines table. Shared verbatim by `agents routines list` and the
|
|
663
|
+
* non-interactive fall-through of the bare `agents routines` command, so both emit
|
|
664
|
+
* byte-identical `--json` and text output.
|
|
665
|
+
*/
|
|
666
|
+
function runRoutinesList(options) {
|
|
667
|
+
if (options.groupBy && options.groupBy !== 'device' && options.groupBy !== 'project') {
|
|
668
|
+
console.error(chalk.red(`Unsupported --group-by '${options.groupBy}'. Use: project (default) or device`));
|
|
669
|
+
process.exit(1);
|
|
670
|
+
}
|
|
671
|
+
if (options.json) {
|
|
672
|
+
process.stdout.write(JSON.stringify(buildRoutineListJson()) + '\n');
|
|
673
|
+
return;
|
|
674
|
+
}
|
|
675
|
+
try {
|
|
676
|
+
monitorRunningJobs();
|
|
677
|
+
}
|
|
678
|
+
catch { /* best-effort orphan reap */ }
|
|
679
|
+
const jobs = listJobsForDisplay(process.cwd());
|
|
680
|
+
if (jobs.length === 0) {
|
|
681
|
+
console.log(chalk.gray('No jobs configured'));
|
|
682
|
+
console.log(chalk.gray(' Add a job: agents routines add <path-to-job.yml>'));
|
|
683
|
+
return;
|
|
684
|
+
}
|
|
685
|
+
const scheduler = new JobScheduler(async () => { });
|
|
686
|
+
scheduler.loadAll();
|
|
687
|
+
// Build a quick lookup: which jobs are currently overdue?
|
|
688
|
+
const overdueSet = new Set();
|
|
689
|
+
try {
|
|
690
|
+
for (const j of detectOverdueJobs())
|
|
691
|
+
overdueSet.add(j.name);
|
|
692
|
+
}
|
|
693
|
+
catch {
|
|
694
|
+
// Best-effort indicator; never block the list on detection errors.
|
|
695
|
+
}
|
|
696
|
+
console.log(chalk.bold('Scheduled Jobs\n'));
|
|
697
|
+
// OSC 8 hyperlink helper — renders as a clickable link in supporting terminals.
|
|
698
|
+
// Guarded on process.stdout.isTTY so that piped/redirected output never
|
|
699
|
+
// contains raw ESC ] 8 ;; ... BEL escape sequences.
|
|
700
|
+
const link = (label, url) => url && process.stdout.isTTY ? `\x1b]8;;${url}\x07${label}\x1b]8;;\x07` : label;
|
|
701
|
+
const now = new Date();
|
|
702
|
+
if (options.flat) {
|
|
703
|
+
renderRoutineRows({ jobs, scheduler, overdueSet, link, now });
|
|
704
|
+
}
|
|
705
|
+
else if (options.groupBy === 'device') {
|
|
706
|
+
let registry = {};
|
|
707
|
+
try {
|
|
708
|
+
registry = loadDevicesSync();
|
|
709
|
+
}
|
|
710
|
+
catch (err) {
|
|
711
|
+
console.error(chalk.yellow(`Could not read device registry: ${err.message}`));
|
|
712
|
+
}
|
|
713
|
+
const groups = groupRoutineJobsByDevice(jobs, registry);
|
|
714
|
+
for (const group of groups) {
|
|
715
|
+
console.log(chalk.bold(`\n${group.title}`));
|
|
716
|
+
renderRoutineRows({ jobs: group.jobs, scheduler, overdueSet, link, now, local: group.local });
|
|
717
|
+
}
|
|
718
|
+
if (groups.some((group) => !group.local)) {
|
|
719
|
+
console.log();
|
|
720
|
+
console.log(chalk.gray(' Last Status is per-device: rows under another device show "-" — read it there with: agents routines list --device <name>'));
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
else {
|
|
724
|
+
// Default: group by project
|
|
725
|
+
const knownProjectNames = new Set(listProjectDefs().map((p) => p.name));
|
|
726
|
+
const groups = groupRoutineJobsByProject(jobs, knownProjectNames);
|
|
727
|
+
for (const group of groups) {
|
|
728
|
+
console.log(chalk.bold(`\n${group.title}`));
|
|
729
|
+
renderRoutineRows({ jobs: group.jobs, scheduler, overdueSet, link, now });
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
if (overdueSet.size > 0) {
|
|
733
|
+
console.log();
|
|
734
|
+
console.log(chalk.yellow(` ${overdueSet.size} routine(s) overdue — catch up with: agents routines catchup`));
|
|
735
|
+
}
|
|
736
|
+
scheduler.stopAll();
|
|
737
|
+
console.log();
|
|
738
|
+
}
|
|
739
|
+
/** True when a routine matches the browser's live filter query. */
|
|
740
|
+
function routineMatchesQuery(job, q) {
|
|
741
|
+
const kind = job.command ? 'command' : job.workflow ? `wf:${job.workflow}` : job.agent ?? '';
|
|
742
|
+
return [job.name, kind, fireConditionLabel(job), (job.projects ?? []).join(' ')]
|
|
743
|
+
.join(' ')
|
|
744
|
+
.toLowerCase()
|
|
745
|
+
.includes(q);
|
|
746
|
+
}
|
|
747
|
+
/** One compact routine row for the browser list: name · kind · schedule · next · last. */
|
|
748
|
+
function routineBrowserRow(job, scheduler, overdueSet, now) {
|
|
749
|
+
const kind = job.command ? 'command' : job.workflow ? `wf:${job.workflow}` : job.agent ?? '?';
|
|
750
|
+
const name = job.name.length > 24 ? job.name.slice(0, 23) + '…' : job.name.padEnd(24);
|
|
751
|
+
const enabled = job.enabled === false ? chalk.gray('paused') : chalk.green('on');
|
|
752
|
+
const latest = localLatestRun(job);
|
|
753
|
+
const last = latest
|
|
754
|
+
? latest.status === 'completed'
|
|
755
|
+
? chalk.green('ok')
|
|
756
|
+
: latest.status === 'failed' || latest.status === 'timeout'
|
|
757
|
+
? chalk.red(latest.status)
|
|
758
|
+
: chalk.yellow(latest.status)
|
|
759
|
+
: chalk.gray('—');
|
|
760
|
+
const next = overdueSet.has(job.name)
|
|
761
|
+
? chalk.yellow('overdue')
|
|
762
|
+
: chalk.gray(nextRunLabel(job, scheduler, now));
|
|
763
|
+
return `${chalk.bold(name)} ${chalk.cyan(kind.padEnd(12))} ${enabled.padEnd(6)} ${chalk.gray(scheduleLabel(job)).padEnd(24)} next ${next} · last ${last}`;
|
|
764
|
+
}
|
|
765
|
+
/**
|
|
766
|
+
* The four-block routine detail — Definition, Next fire, Recent runs, Stats — shown
|
|
767
|
+
* both live in the picker's preview pane and, in full, when a routine is selected.
|
|
768
|
+
*/
|
|
769
|
+
function buildRoutineDetail(job, scheduler, now) {
|
|
770
|
+
const lines = [];
|
|
771
|
+
// 1. Definition
|
|
772
|
+
lines.push(chalk.bold.underline('Definition'));
|
|
773
|
+
const kind = job.command
|
|
774
|
+
? `command: ${job.command}`
|
|
775
|
+
: job.workflow
|
|
776
|
+
? `workflow: ${job.workflow}`
|
|
777
|
+
: `agent: ${job.agent ?? '?'}`;
|
|
778
|
+
lines.push(` ${kind}`);
|
|
779
|
+
lines.push(` schedule: ${fireConditionLabel(job)}${isOneShotRoutine(job) ? ' (one-shot)' : ''}`);
|
|
780
|
+
const devices = devicesWithRoutineEnabled(job.name);
|
|
781
|
+
lines.push(` devices: ${devices.length > 0 ? devices.join(', ') : chalk.gray('none (paused)')}`);
|
|
782
|
+
if (job.timezone)
|
|
783
|
+
lines.push(` timezone: ${job.timezone}`);
|
|
784
|
+
if (job.repo)
|
|
785
|
+
lines.push(` repo: ${job.repo}`);
|
|
786
|
+
if (job.prompt) {
|
|
787
|
+
const oneLine = job.prompt.replace(/\s+/g, ' ').trim();
|
|
788
|
+
lines.push(` prompt: ${oneLine.length > 120 ? oneLine.slice(0, 119) + '…' : oneLine}`);
|
|
789
|
+
}
|
|
790
|
+
// 2. Next fire
|
|
791
|
+
lines.push('');
|
|
792
|
+
lines.push(chalk.bold.underline('Next fire'));
|
|
793
|
+
const nextDate = nextRunForDisplay(job, scheduler);
|
|
794
|
+
lines.push(` ${nextRunLabel(job, scheduler, now)}${nextDate ? chalk.gray(` (${nextDate.toISOString()})`) : ''}`);
|
|
795
|
+
// 3. Recent runs (most recent last, like `routines runs`)
|
|
796
|
+
lines.push('');
|
|
797
|
+
lines.push(chalk.bold.underline('Recent runs'));
|
|
798
|
+
const runs = listRuns(job.name).slice(-5);
|
|
799
|
+
if (runs.length === 0) {
|
|
800
|
+
lines.push(chalk.gray(' no runs yet'));
|
|
801
|
+
}
|
|
802
|
+
else {
|
|
803
|
+
for (const run of runs) {
|
|
804
|
+
const status = run.status === 'completed'
|
|
805
|
+
? chalk.green(run.status)
|
|
806
|
+
: run.status === 'failed' || run.status === 'timeout'
|
|
807
|
+
? chalk.red(run.status)
|
|
808
|
+
: chalk.yellow(run.status);
|
|
809
|
+
const dur = run.completedAt ? ` ${formatRunDuration(run.startedAt, run.completedAt)}` : '';
|
|
810
|
+
lines.push(` ${run.startedAt} ${status}${dur}`);
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
// 4. Stats
|
|
814
|
+
lines.push('');
|
|
815
|
+
lines.push(chalk.bold.underline('Stats'));
|
|
816
|
+
const stats = routineStats(job.name);
|
|
817
|
+
if (stats.count === 0) {
|
|
818
|
+
lines.push(chalk.gray(' no completed runs'));
|
|
819
|
+
}
|
|
820
|
+
else {
|
|
821
|
+
lines.push(` runs ${stats.count} · failed ${stats.failed} · missed ${stats.missed}`);
|
|
822
|
+
lines.push(` avg ${stats.avgMs}ms · p50 ${stats.p50}ms · p95 ${stats.p95}ms`);
|
|
823
|
+
}
|
|
824
|
+
return lines.join('\n');
|
|
825
|
+
}
|
|
826
|
+
/**
|
|
827
|
+
* Interactive routines browser behind the bare `agents routines` command on a TTY.
|
|
828
|
+
* Reuses {@link itemPicker}, keeping the project/device group headers as inline
|
|
829
|
+
* dividers, with a live four-block detail pane and a full drill-in on select.
|
|
830
|
+
*/
|
|
831
|
+
async function runRoutinesBrowser(options) {
|
|
832
|
+
// Same --group-by validation as the static list, so a bad value fails the same
|
|
833
|
+
// way on a TTY as it does under --json / a pipe (rather than silently defaulting).
|
|
834
|
+
if (options.groupBy && options.groupBy !== 'device' && options.groupBy !== 'project') {
|
|
835
|
+
console.error(chalk.red(`Unsupported --group-by '${options.groupBy}'. Use: project (default) or device`));
|
|
836
|
+
process.exit(1);
|
|
837
|
+
}
|
|
838
|
+
try {
|
|
839
|
+
monitorRunningJobs();
|
|
840
|
+
}
|
|
841
|
+
catch { /* best-effort orphan reap */ }
|
|
842
|
+
const jobs = listJobsForDisplay(process.cwd());
|
|
843
|
+
if (jobs.length === 0) {
|
|
844
|
+
console.log(chalk.gray('No jobs configured'));
|
|
845
|
+
console.log(chalk.gray(' Add a job: agents routines add <path-to-job.yml>'));
|
|
846
|
+
return;
|
|
847
|
+
}
|
|
848
|
+
const scheduler = new JobScheduler(async () => { });
|
|
849
|
+
scheduler.loadAll();
|
|
850
|
+
try {
|
|
851
|
+
const now = new Date();
|
|
852
|
+
const overdueSet = new Set();
|
|
853
|
+
try {
|
|
854
|
+
for (const j of detectOverdueJobs())
|
|
855
|
+
overdueSet.add(j.name);
|
|
856
|
+
}
|
|
857
|
+
catch { /* best-effort indicator */ }
|
|
858
|
+
// Group with the same headers the static list uses, so the picker's dividers
|
|
859
|
+
// read identically to `routines list`.
|
|
860
|
+
let groups;
|
|
861
|
+
if (options.groupBy === 'device') {
|
|
862
|
+
let registry = {};
|
|
863
|
+
try {
|
|
864
|
+
registry = loadDevicesSync();
|
|
865
|
+
}
|
|
866
|
+
catch { /* headers still render without state */ }
|
|
867
|
+
groups = groupRoutineJobsByDevice(jobs, registry);
|
|
868
|
+
}
|
|
869
|
+
else {
|
|
870
|
+
const knownProjectNames = new Set(listProjectDefs().map((p) => p.name));
|
|
871
|
+
groups = groupRoutineJobsByProject(jobs, knownProjectNames);
|
|
872
|
+
}
|
|
873
|
+
const filter = (query) => {
|
|
874
|
+
const q = query.trim().toLowerCase();
|
|
875
|
+
const rows = [];
|
|
876
|
+
for (const group of groups) {
|
|
877
|
+
const matched = q ? group.jobs.filter((j) => routineMatchesQuery(j, q)) : group.jobs;
|
|
878
|
+
if (matched.length === 0)
|
|
879
|
+
continue;
|
|
880
|
+
rows.push(new Separator(chalk.bold(`── ${group.title} ──`)));
|
|
881
|
+
rows.push(...matched);
|
|
882
|
+
}
|
|
883
|
+
return rows;
|
|
884
|
+
};
|
|
885
|
+
const picked = await itemPicker({
|
|
886
|
+
message: 'Routines',
|
|
887
|
+
subtitle: chalk.gray('type to filter · ↑↓ navigate · space toggles detail · ⏎ open'),
|
|
888
|
+
items: filter(''),
|
|
889
|
+
filter,
|
|
890
|
+
labelFor: (job) => routineBrowserRow(job, scheduler, overdueSet, now),
|
|
891
|
+
buildPreview: (job) => buildRoutineDetail(job, scheduler, now),
|
|
892
|
+
shortIdFor: (job) => job.name,
|
|
893
|
+
pageSize: 15,
|
|
894
|
+
enterHint: 'open',
|
|
895
|
+
emptyMessage: 'No routines match.',
|
|
896
|
+
});
|
|
897
|
+
if (picked) {
|
|
898
|
+
// Drill-in: print the full, untruncated four-block detail for the selection.
|
|
899
|
+
console.log();
|
|
900
|
+
console.log(chalk.bold(picked.item.name));
|
|
901
|
+
console.log(buildRoutineDetail(picked.item, scheduler, now));
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
finally {
|
|
905
|
+
scheduler.stopAll();
|
|
906
|
+
}
|
|
907
|
+
}
|
|
546
908
|
/** Register the `agents routines` command tree. */
|
|
547
909
|
export function registerRoutinesCommands(program) {
|
|
548
910
|
const routinesCmd = program
|
|
549
911
|
.command('routines')
|
|
550
|
-
.description('Schedule agents to run on a cron schedule or at a specific time. The
|
|
912
|
+
.description('Schedule agents to run on a cron schedule or at a specific time. The daemon starts at install/upgrade and on setup when daemon.enabled is not false; routines add also ensures it is running.');
|
|
551
913
|
addHostOption(routinesCmd);
|
|
552
914
|
setHelpSections(routinesCmd, {
|
|
553
915
|
examples: `
|
|
@@ -564,9 +926,9 @@ export function registerRoutinesCommands(program) {
|
|
|
564
926
|
agents routines add drain --schedule "0 3 * * *" --agent claude --placement fleet --prompt "Drain queue"
|
|
565
927
|
agents routines add review --schedule "0 9 * * 1" --agent claude --placement cloud --prompt "Review open PRs"
|
|
566
928
|
|
|
567
|
-
#
|
|
568
|
-
agents routines enable-
|
|
569
|
-
agents routines
|
|
929
|
+
# Enable a routine (materialises a project routine on first enable)
|
|
930
|
+
agents routines enable security-sweep
|
|
931
|
+
agents routines disable security-sweep
|
|
570
932
|
|
|
571
933
|
# List all routines and their next run times
|
|
572
934
|
agents routines list
|
|
@@ -593,7 +955,8 @@ export function registerRoutinesCommands(program) {
|
|
|
593
955
|
- what task to give the agent (the prompt)
|
|
594
956
|
- execution constraints (mode, effort, timeout)
|
|
595
957
|
|
|
596
|
-
The
|
|
958
|
+
The always-on agents daemon starts at install/upgrade and on setup (when
|
|
959
|
+
daemon.enabled is not false). Adding a routine also ensures it is running.
|
|
597
960
|
Manage it with 'agents routines start|stop|status'.
|
|
598
961
|
|
|
599
962
|
Version / credit failover (same semantics as 'agents run'):
|
|
@@ -611,6 +974,25 @@ export function registerRoutinesCommands(program) {
|
|
|
611
974
|
run is skipped up front with a re-login hint.
|
|
612
975
|
`,
|
|
613
976
|
});
|
|
977
|
+
// Bare `agents routines`: a HIDDEN default subcommand carries the bare-command's
|
|
978
|
+
// own --json/--group-by/--flat. It must be a subcommand rather than options on the
|
|
979
|
+
// parent `routines`, because commander binds a same-named PARENT option first and
|
|
980
|
+
// would shadow the identical --json on every sibling (`add`/`run`/`runs`/`list`/…).
|
|
981
|
+
// On a TTY it opens the interactive browser; with --json, --flat, or in a
|
|
982
|
+
// non-interactive shell it prints the exact static `routines list` output.
|
|
983
|
+
routinesCmd
|
|
984
|
+
.command('browse', { isDefault: true, hidden: true })
|
|
985
|
+
.description('Interactive routines browser (the default for a bare `agents routines`)')
|
|
986
|
+
.option('--json', 'Emit machine-readable JSON instead of the browser (matches `routines list --json`)')
|
|
987
|
+
.option('--group-by <field>', 'Group by field: project (default) or device', 'project')
|
|
988
|
+
.option('--flat', 'Print the legacy flat table instead of the interactive browser')
|
|
989
|
+
.action(async (options) => {
|
|
990
|
+
if (options.json || options.flat || !isInteractiveTerminal()) {
|
|
991
|
+
runRoutinesList(options);
|
|
992
|
+
return;
|
|
993
|
+
}
|
|
994
|
+
await runRoutinesBrowser(options);
|
|
995
|
+
});
|
|
614
996
|
routinesCmd
|
|
615
997
|
.command('list')
|
|
616
998
|
.description('See all scheduled jobs, when they run next, and their last execution status')
|
|
@@ -618,81 +1000,7 @@ export function registerRoutinesCommands(program) {
|
|
|
618
1000
|
.option('--group-by <field>', 'Group output by field: project (default) or device', 'project')
|
|
619
1001
|
.option('--flat', 'Print the legacy flat table instead of grouped sections')
|
|
620
1002
|
.action((options) => {
|
|
621
|
-
|
|
622
|
-
console.error(chalk.red(`Unsupported --group-by '${options.groupBy}'. Use: project (default) or device`));
|
|
623
|
-
process.exit(1);
|
|
624
|
-
}
|
|
625
|
-
if (options.json) {
|
|
626
|
-
process.stdout.write(JSON.stringify(buildRoutineListJson()) + '\n');
|
|
627
|
-
return;
|
|
628
|
-
}
|
|
629
|
-
try {
|
|
630
|
-
monitorRunningJobs();
|
|
631
|
-
}
|
|
632
|
-
catch { /* best-effort orphan reap */ }
|
|
633
|
-
const jobs = listAllJobs(process.cwd());
|
|
634
|
-
if (jobs.length === 0) {
|
|
635
|
-
if (options.json) {
|
|
636
|
-
process.stdout.write('[]\n');
|
|
637
|
-
return;
|
|
638
|
-
}
|
|
639
|
-
console.log(chalk.gray('No jobs configured'));
|
|
640
|
-
console.log(chalk.gray(' Add a job: agents routines add <path-to-job.yml>'));
|
|
641
|
-
return;
|
|
642
|
-
}
|
|
643
|
-
const scheduler = new JobScheduler(async () => { });
|
|
644
|
-
scheduler.loadAll();
|
|
645
|
-
// Build a quick lookup: which jobs are currently overdue?
|
|
646
|
-
const overdueSet = new Set();
|
|
647
|
-
try {
|
|
648
|
-
for (const j of detectOverdueJobs())
|
|
649
|
-
overdueSet.add(j.name);
|
|
650
|
-
}
|
|
651
|
-
catch {
|
|
652
|
-
// Best-effort indicator; never block the list on detection errors.
|
|
653
|
-
}
|
|
654
|
-
console.log(chalk.bold('Scheduled Jobs\n'));
|
|
655
|
-
// OSC 8 hyperlink helper — renders as a clickable link in supporting terminals.
|
|
656
|
-
// Guarded on process.stdout.isTTY so that piped/redirected output never
|
|
657
|
-
// contains raw ESC ] 8 ;; ... BEL escape sequences.
|
|
658
|
-
const link = (label, url) => url && process.stdout.isTTY ? `\x1b]8;;${url}\x07${label}\x1b]8;;\x07` : label;
|
|
659
|
-
const now = new Date();
|
|
660
|
-
if (options.flat) {
|
|
661
|
-
renderRoutineRows({ jobs, scheduler, overdueSet, link, now });
|
|
662
|
-
}
|
|
663
|
-
else if (options.groupBy === 'device') {
|
|
664
|
-
let registry = {};
|
|
665
|
-
try {
|
|
666
|
-
registry = loadDevicesSync();
|
|
667
|
-
}
|
|
668
|
-
catch (err) {
|
|
669
|
-
console.error(chalk.yellow(`Could not read device registry: ${err.message}`));
|
|
670
|
-
}
|
|
671
|
-
const groups = groupRoutineJobsByDevice(jobs, registry);
|
|
672
|
-
for (const group of groups) {
|
|
673
|
-
console.log(chalk.bold(`\n${group.title}`));
|
|
674
|
-
renderRoutineRows({ jobs: group.jobs, scheduler, overdueSet, link, now, local: group.local });
|
|
675
|
-
}
|
|
676
|
-
if (groups.some((group) => !group.local)) {
|
|
677
|
-
console.log();
|
|
678
|
-
console.log(chalk.gray(' Last Status is per-device: rows under another device show "-" — read it there with: agents routines list --device <name>'));
|
|
679
|
-
}
|
|
680
|
-
}
|
|
681
|
-
else {
|
|
682
|
-
// Default: group by project
|
|
683
|
-
const knownProjectNames = new Set(listProjectDefs().map((p) => p.name));
|
|
684
|
-
const groups = groupRoutineJobsByProject(jobs, knownProjectNames);
|
|
685
|
-
for (const group of groups) {
|
|
686
|
-
console.log(chalk.bold(`\n${group.title}`));
|
|
687
|
-
renderRoutineRows({ jobs: group.jobs, scheduler, overdueSet, link, now });
|
|
688
|
-
}
|
|
689
|
-
}
|
|
690
|
-
if (overdueSet.size > 0) {
|
|
691
|
-
console.log();
|
|
692
|
-
console.log(chalk.yellow(` ${overdueSet.size} routine(s) overdue — catch up with: agents routines catchup`));
|
|
693
|
-
}
|
|
694
|
-
scheduler.stopAll();
|
|
695
|
-
console.log();
|
|
1003
|
+
runRoutinesList(options);
|
|
696
1004
|
});
|
|
697
1005
|
routinesCmd
|
|
698
1006
|
.command('add [nameOrPath]')
|
|
@@ -707,7 +1015,7 @@ export function registerRoutinesCommands(program) {
|
|
|
707
1015
|
.option('-t, --timeout <timeout>', 'Kill the agent if it runs longer than this (e.g., 10m, 2h, 3d, 1w; max 1w)', '10m')
|
|
708
1016
|
.option('--timezone <tz>', 'Interpret schedule in this timezone (e.g., America/Los_Angeles)')
|
|
709
1017
|
.option('--devices <names>', 'Fleet allowlist (comma-separated): only listed devices schedule and fire this routine. Omit for unrestricted.')
|
|
710
|
-
.option('--run-on <name>', 'BODY placement: execute the job body on this machine over SSH (registered host, device, capability tag, or user@host). Sets hostStrategy=host. Same model as agents run --where device:<name> (docs/
|
|
1018
|
+
.option('--run-on <name>', 'BODY placement: execute the job body on this machine over SSH (registered host, device, capability tag, or user@host). Sets hostStrategy=host. Same model as agents run --where device:<name> (docs/concepts.md#placement).')
|
|
711
1019
|
.option('--placement <strategy>', `BODY placement strategy: ${HOST_STRATEGIES.join('|')} (default: local, or host when --run-on is set). Maps to the shared Placement model (local|device|fleet|cloud). Not the same as --host (which manages routines on a remote machine).`)
|
|
712
1020
|
.option('--run-cwd <dir>', 'Working directory on the --run-on host (--remote-cwd is taken by the remote-management passthrough)')
|
|
713
1021
|
.option('--at <time>', 'One-shot mode: run once at this time (e.g., "14:30" or "2026-02-24 09:00"), then disable')
|
|
@@ -887,6 +1195,10 @@ export function registerRoutinesCommands(program) {
|
|
|
887
1195
|
console.log(chalk.gray(` ${r.message}`));
|
|
888
1196
|
if (r.repair)
|
|
889
1197
|
console.log(chalk.gray(` repair: ${r.repair}`));
|
|
1198
|
+
// Name the resume explicitly: a paused routine is otherwise a dead
|
|
1199
|
+
// end for an agent, which is how the release train sat unregistered
|
|
1200
|
+
// (RUSH-2517 / RUSH-2476).
|
|
1201
|
+
console.log(chalk.gray(` then: agents routines enable ${config.name}`));
|
|
890
1202
|
}
|
|
891
1203
|
}
|
|
892
1204
|
if (options.json) {
|
|
@@ -956,17 +1268,38 @@ export function registerRoutinesCommands(program) {
|
|
|
956
1268
|
config.runOnce = true;
|
|
957
1269
|
console.error(chalk.yellow(`Schedule "${config.schedule}" pins minute, hour, day, and month; treating it as one-shot. Prefer --at for one-time routines.`));
|
|
958
1270
|
}
|
|
959
|
-
|
|
1271
|
+
// `add <file>` must never rewrite the file the user pointed at. When the
|
|
1272
|
+
// source already IS the canonical routine file — the normal case for a
|
|
1273
|
+
// definition tracked in the git-backed `~/.agents` repo — writeJob would
|
|
1274
|
+
// re-serialize it in place and drop every key the canonical form omits,
|
|
1275
|
+
// including the legacy `devices:` pin, silently corrupting committed
|
|
1276
|
+
// config (RUSH-2517). The definition is already where it belongs; only
|
|
1277
|
+
// activation is left to do. A source from anywhere else is still copied
|
|
1278
|
+
// in, which is the whole point of passing a path.
|
|
1279
|
+
if (!isCanonicalRoutineSource(resolved, config.name))
|
|
1280
|
+
writeJob(config);
|
|
960
1281
|
const deviceMatch = !config.devices || config.devices.map(normalizeHost).includes(normalizeHost(machineId()));
|
|
961
1282
|
const readiness = await evaluateActivationReadinessLive(config);
|
|
962
1283
|
const activate = config.enabled && deviceMatch && readiness.ready;
|
|
963
1284
|
setJobEnabled(config.name, activate);
|
|
1285
|
+
// A `devices:` pin in a hand-authored definition is legacy input: since
|
|
1286
|
+
// RUSH-2392's split (723182bb5) activation lives in each device's
|
|
1287
|
+
// `agents.yaml`, and `add` only ever applies it to THIS box. Say so
|
|
1288
|
+
// rather than let the routine read `Devices: all` on every peer.
|
|
1289
|
+
if (config.devices && config.devices.length > 0 && !options.json) {
|
|
1290
|
+
console.log(chalk.yellow(`'devices:' pins activation on this box only — it is not propagated to peers.`));
|
|
1291
|
+
console.log(chalk.gray(` pin the fleet with: agents routines devices ${config.name} --set ${config.devices.join(',')}`));
|
|
1292
|
+
}
|
|
964
1293
|
if (config.enabled && deviceMatch && !readiness.ready && !options.json) {
|
|
965
1294
|
const r = readiness.readiness;
|
|
966
1295
|
console.log(chalk.yellow(`Saved paused — not ready to activate: ${r.code}`));
|
|
967
1296
|
console.log(chalk.gray(` ${r.message}`));
|
|
968
1297
|
if (r.repair)
|
|
969
1298
|
console.log(chalk.gray(` repair: ${r.repair}`));
|
|
1299
|
+
// Name the resume explicitly: a paused routine is otherwise a dead end
|
|
1300
|
+
// for an agent, which is how the release train sat unregistered
|
|
1301
|
+
// (RUSH-2517 / RUSH-2476).
|
|
1302
|
+
console.log(chalk.gray(` then: agents routines enable ${config.name}`));
|
|
970
1303
|
}
|
|
971
1304
|
if (options.json) {
|
|
972
1305
|
writeJson({
|
|
@@ -1070,6 +1403,8 @@ export function registerRoutinesCommands(program) {
|
|
|
1070
1403
|
.option('--yaml', 'Open the raw YAML in $EDITOR (the current edit surface)')
|
|
1071
1404
|
.option('--state-to <name>', 'Update the Linear current-state filter before opening the editor')
|
|
1072
1405
|
.option('--state-from <name>', 'Update the Linear previous-state filter before opening the editor')
|
|
1406
|
+
.option('--cwd <path>', 'Set the execution directory and save, without opening an editor')
|
|
1407
|
+
.option('--project-anchor <name>', 'Set the execution project anchor and save, without opening an editor')
|
|
1073
1408
|
.action(async (name, options) => {
|
|
1074
1409
|
if (!name) {
|
|
1075
1410
|
name = await pickJob('Select job to edit', undefined, ['agents routines edit <name>']) ?? undefined;
|
|
@@ -1077,6 +1412,40 @@ export function registerRoutinesCommands(program) {
|
|
|
1077
1412
|
return;
|
|
1078
1413
|
}
|
|
1079
1414
|
const existing = readJob(name);
|
|
1415
|
+
// Headless context repair. Both readiness blockers print
|
|
1416
|
+
// `agents routines edit <name> --project-anchor <name> # or --cwd <path>`
|
|
1417
|
+
// as their repair (routine-context.ts:215,334), but neither flag existed
|
|
1418
|
+
// and the only edit surface opened $EDITOR — so an agent could never
|
|
1419
|
+
// follow its own repair hint (RUSH-2517). Applying and saving directly is
|
|
1420
|
+
// what makes that hint true.
|
|
1421
|
+
if (options.cwd !== undefined || options.projectAnchor !== undefined) {
|
|
1422
|
+
if (!existing) {
|
|
1423
|
+
console.error(chalk.red(`Routine '${name}' not found. Create it first: agents routines add ${name} --schedule "..." --agent claude --prompt "..."`));
|
|
1424
|
+
process.exit(1);
|
|
1425
|
+
}
|
|
1426
|
+
if (options.cwd !== undefined)
|
|
1427
|
+
existing.cwd = options.cwd;
|
|
1428
|
+
if (options.projectAnchor !== undefined)
|
|
1429
|
+
existing.project = options.projectAnchor;
|
|
1430
|
+
const errors = validateJob(existing);
|
|
1431
|
+
if (errors.length > 0) {
|
|
1432
|
+
console.error(chalk.red('Validation errors:'));
|
|
1433
|
+
for (const err of errors)
|
|
1434
|
+
console.error(chalk.red(` - ${err}`));
|
|
1435
|
+
process.exit(1);
|
|
1436
|
+
}
|
|
1437
|
+
writeJob(existing);
|
|
1438
|
+
const readiness = await evaluateActivationReadinessLive(existing);
|
|
1439
|
+
console.log(chalk.green(`Routine '${name}' updated`));
|
|
1440
|
+
if (!readiness.ready && readiness.readiness) {
|
|
1441
|
+
console.log(chalk.yellow(`Still not ready: ${readiness.readiness.code}`));
|
|
1442
|
+
console.log(chalk.gray(` ${readiness.readiness.message}`));
|
|
1443
|
+
}
|
|
1444
|
+
else {
|
|
1445
|
+
console.log(chalk.gray(` enable it with: agents routines enable ${name}`));
|
|
1446
|
+
}
|
|
1447
|
+
return;
|
|
1448
|
+
}
|
|
1080
1449
|
if (existing && (options.stateTo !== undefined || options.stateFrom !== undefined)) {
|
|
1081
1450
|
if (!existing.trigger || existing.trigger.type !== 'linear_event') {
|
|
1082
1451
|
console.error(chalk.red(`'${name}' does not have a Linear trigger; --state-to/--state-from only apply to linear triggers`));
|
|
@@ -1612,65 +1981,18 @@ export function registerRoutinesCommands(program) {
|
|
|
1612
1981
|
}
|
|
1613
1982
|
});
|
|
1614
1983
|
routinesCmd
|
|
1615
|
-
.command('
|
|
1616
|
-
.
|
|
1984
|
+
.command('enable [name]')
|
|
1985
|
+
.aliases(['resume'])
|
|
1986
|
+
.description('Enable a routine so the daemon schedules it. Also materialises a project routine (from the current project or a registered one) on first enable — one step, no separate opt-in.')
|
|
1617
1987
|
.action(async (name) => {
|
|
1618
|
-
|
|
1619
|
-
// Only show paused jobs
|
|
1620
|
-
name = await pickJob('Select job to resume', (job) => !job.enabled, ['agents routines resume <name>']) ?? undefined;
|
|
1621
|
-
if (!name)
|
|
1622
|
-
return;
|
|
1623
|
-
}
|
|
1624
|
-
try {
|
|
1625
|
-
// Resume re-runs readiness — it can never bypass a proven blocker (the
|
|
1626
|
-
// plan: "resume cannot bypass readiness"). A blocked routine stays paused.
|
|
1627
|
-
const job = readJob(name);
|
|
1628
|
-
if (job) {
|
|
1629
|
-
const readiness = await evaluateActivationReadinessLive(job);
|
|
1630
|
-
if (!readiness.ready) {
|
|
1631
|
-
const r = readiness.readiness;
|
|
1632
|
-
console.log(chalk.red(`Cannot resume '${name}' — not ready: ${r.code}`));
|
|
1633
|
-
console.log(chalk.gray(` ${r.message}`));
|
|
1634
|
-
if (r.repair)
|
|
1635
|
-
console.log(chalk.gray(` repair: ${r.repair}`));
|
|
1636
|
-
console.log(chalk.gray(` fix it, then: agents routines doctor ${name} --fix`));
|
|
1637
|
-
process.exit(1);
|
|
1638
|
-
}
|
|
1639
|
-
}
|
|
1640
|
-
setJobEnabled(name, true);
|
|
1641
|
-
console.log(chalk.green(`Job '${name}' resumed`));
|
|
1642
|
-
if (isDaemonRunning()) {
|
|
1643
|
-
signalDaemonReload();
|
|
1644
|
-
console.log(chalk.gray('Daemon reloaded'));
|
|
1645
|
-
}
|
|
1646
|
-
}
|
|
1647
|
-
catch (err) {
|
|
1648
|
-
console.log(chalk.red(err.message));
|
|
1649
|
-
process.exit(1);
|
|
1650
|
-
}
|
|
1988
|
+
await enableRoutineAction(name);
|
|
1651
1989
|
});
|
|
1652
1990
|
routinesCmd
|
|
1653
|
-
.command('
|
|
1654
|
-
.
|
|
1991
|
+
.command('disable [name]')
|
|
1992
|
+
.aliases(['pause'])
|
|
1993
|
+
.description('Disable a routine. Stops scheduling future runs; turn it back on with: agents routines enable <name>.')
|
|
1655
1994
|
.action(async (name) => {
|
|
1656
|
-
|
|
1657
|
-
// Only show enabled jobs
|
|
1658
|
-
name = await pickJob('Select job to pause', (job) => job.enabled, ['agents routines pause <name>']) ?? undefined;
|
|
1659
|
-
if (!name)
|
|
1660
|
-
return;
|
|
1661
|
-
}
|
|
1662
|
-
try {
|
|
1663
|
-
setJobEnabled(name, false);
|
|
1664
|
-
console.log(chalk.green(`Job '${name}' paused`));
|
|
1665
|
-
if (isDaemonRunning()) {
|
|
1666
|
-
signalDaemonReload();
|
|
1667
|
-
console.log(chalk.gray('Scheduler reloaded'));
|
|
1668
|
-
}
|
|
1669
|
-
}
|
|
1670
|
-
catch (err) {
|
|
1671
|
-
console.log(chalk.red(err.message));
|
|
1672
|
-
process.exit(1);
|
|
1673
|
-
}
|
|
1995
|
+
await disableRoutineAction(name);
|
|
1674
1996
|
});
|
|
1675
1997
|
// Device activation management for a single routine. Each mutation executes
|
|
1676
1998
|
// on the target device so only that host writes devices/<hostname>/agents.yaml.
|
|
@@ -1874,122 +2196,13 @@ export function registerRoutinesCommands(program) {
|
|
|
1874
2196
|
console.log(chalk.gray('\n Start the scheduler to begin firing routines: agents routines start'));
|
|
1875
2197
|
}
|
|
1876
2198
|
});
|
|
1877
|
-
routinesCmd
|
|
1878
|
-
.command('enable-project [path]')
|
|
1879
|
-
.description('Opt a project\'s .agents/routines/*.yml into daemon firing. Requires explicit approval — project routines never auto-fire from a cloned repo. Materialises copies into ~/.agents/routines/ with source provenance.')
|
|
1880
|
-
.option('--yes', 'Skip the interactive confirmation prompt')
|
|
1881
|
-
.option('--json', 'Emit machine-readable JSON')
|
|
1882
|
-
.action(async (projectPath, options) => {
|
|
1883
|
-
const root = projectPath
|
|
1884
|
-
? path.resolve(projectPath)
|
|
1885
|
-
: resolveProjectRoot(process.cwd());
|
|
1886
|
-
if (!root) {
|
|
1887
|
-
console.error(chalk.red('No project .agents/ directory found from the current directory.'));
|
|
1888
|
-
console.error(chalk.gray('Run from inside a project, or pass the project path: agents routines enable-project /path/to/repo'));
|
|
1889
|
-
process.exit(1);
|
|
1890
|
-
}
|
|
1891
|
-
const files = listProjectRoutineFiles(root);
|
|
1892
|
-
if (files.length === 0) {
|
|
1893
|
-
console.error(chalk.red(`No routines found under ${path.join(root, '.agents', 'routines')}`));
|
|
1894
|
-
process.exit(1);
|
|
1895
|
-
}
|
|
1896
|
-
if (!options.yes) {
|
|
1897
|
-
if (!isInteractiveTerminal()) {
|
|
1898
|
-
console.error(chalk.red('Refusing to enable project routines non-interactively without --yes.'));
|
|
1899
|
-
console.error(chalk.gray(`Found ${files.length} routine(s) in ${displayProjectPath(root)}. Re-run with --yes to confirm.`));
|
|
1900
|
-
process.exit(1);
|
|
1901
|
-
}
|
|
1902
|
-
try {
|
|
1903
|
-
const { confirm } = await import('@inquirer/prompts');
|
|
1904
|
-
const ok = await confirm({
|
|
1905
|
-
message: `Enable daemon firing for ${files.length} project routine(s) in ${displayProjectPath(root)}?`,
|
|
1906
|
-
default: false,
|
|
1907
|
-
});
|
|
1908
|
-
if (!ok) {
|
|
1909
|
-
console.log(chalk.gray('Cancelled'));
|
|
1910
|
-
return;
|
|
1911
|
-
}
|
|
1912
|
-
}
|
|
1913
|
-
catch (err) {
|
|
1914
|
-
if (isPromptCancelled(err)) {
|
|
1915
|
-
console.log(chalk.gray('Cancelled'));
|
|
1916
|
-
return;
|
|
1917
|
-
}
|
|
1918
|
-
throw err;
|
|
1919
|
-
}
|
|
1920
|
-
}
|
|
1921
|
-
const newly = enableProjectRoutines(root);
|
|
1922
|
-
const sync = syncProjectRoutines(root);
|
|
1923
|
-
if (isDaemonRunning())
|
|
1924
|
-
signalDaemonReload();
|
|
1925
|
-
if (options.json) {
|
|
1926
|
-
writeJson({
|
|
1927
|
-
ok: true,
|
|
1928
|
-
projectRoot: root,
|
|
1929
|
-
newlyEnabled: newly,
|
|
1930
|
-
synced: sync.synced,
|
|
1931
|
-
skipped: sync.skipped,
|
|
1932
|
-
removed: sync.removed,
|
|
1933
|
-
errors: sync.errors,
|
|
1934
|
-
});
|
|
1935
|
-
return;
|
|
1936
|
-
}
|
|
1937
|
-
console.log(chalk.green(newly
|
|
1938
|
-
? `Enabled project routines for ${displayProjectPath(root)}`
|
|
1939
|
-
: `Project routines already enabled for ${displayProjectPath(root)}`));
|
|
1940
|
-
if (sync.synced.length > 0) {
|
|
1941
|
-
console.log(chalk.gray(` Synced: ${sync.synced.join(', ')}`));
|
|
1942
|
-
}
|
|
1943
|
-
for (const s of sync.skipped) {
|
|
1944
|
-
console.log(chalk.yellow(` Skipped ${s.name}: ${s.reason}`));
|
|
1945
|
-
}
|
|
1946
|
-
for (const e of sync.errors) {
|
|
1947
|
-
console.log(chalk.red(` Error ${e.name}: ${e.error}`));
|
|
1948
|
-
}
|
|
1949
|
-
console.log(chalk.gray('Daemon will fire these after reload. Re-sync later with: agents routines sync'));
|
|
1950
|
-
});
|
|
1951
|
-
routinesCmd
|
|
1952
|
-
.command('disable-project [path]')
|
|
1953
|
-
.description('Remove a project from the project-routines allowlist. Use --remove-synced to also delete the user-layer copies.')
|
|
1954
|
-
.option('--remove-synced', 'Delete user-layer routines that were materialised from this project')
|
|
1955
|
-
.option('--json', 'Emit machine-readable JSON')
|
|
1956
|
-
.action(async (projectPath, options) => {
|
|
1957
|
-
const root = projectPath
|
|
1958
|
-
? path.resolve(projectPath)
|
|
1959
|
-
: resolveProjectRoot(process.cwd());
|
|
1960
|
-
if (!root) {
|
|
1961
|
-
console.error(chalk.red('No project .agents/ directory found from the current directory.'));
|
|
1962
|
-
process.exit(1);
|
|
1963
|
-
}
|
|
1964
|
-
const result = disableProjectRoutines(root, { removeSynced: options.removeSynced });
|
|
1965
|
-
if (isDaemonRunning())
|
|
1966
|
-
signalDaemonReload();
|
|
1967
|
-
if (options.json) {
|
|
1968
|
-
writeJson({ ok: true, projectRoot: root, ...result });
|
|
1969
|
-
return;
|
|
1970
|
-
}
|
|
1971
|
-
if (!result.removed) {
|
|
1972
|
-
console.log(chalk.gray(`Project ${displayProjectPath(root)} was not on the allowlist`));
|
|
1973
|
-
}
|
|
1974
|
-
else {
|
|
1975
|
-
console.log(chalk.green(`Disabled project routines for ${displayProjectPath(root)}`));
|
|
1976
|
-
}
|
|
1977
|
-
if (result.deletedJobs.length > 0) {
|
|
1978
|
-
console.log(chalk.gray(` Removed user-layer copies: ${result.deletedJobs.join(', ')}`));
|
|
1979
|
-
}
|
|
1980
|
-
});
|
|
1981
2199
|
routinesCmd
|
|
1982
2200
|
.command('sync [path]')
|
|
1983
|
-
.description('Refresh
|
|
2201
|
+
.description('Refresh materialised project routines from their .agents/routines/*.yml sources. Definition-only — never changes what is enabled. With no path, refreshes every project you have enabled a routine from. Also runs automatically on daemon reload (SIGHUP).')
|
|
1984
2202
|
.option('--json', 'Emit machine-readable JSON')
|
|
1985
2203
|
.action(async (projectPath, options) => {
|
|
1986
2204
|
if (projectPath) {
|
|
1987
2205
|
const root = path.resolve(projectPath);
|
|
1988
|
-
const isEnabled = listEnabledProjectRoots().some((p) => p === root);
|
|
1989
|
-
if (!isEnabled) {
|
|
1990
|
-
console.error(chalk.red(`Project ${displayProjectPath(root)} is not enabled. Run: agents routines enable-project ${root}`));
|
|
1991
|
-
process.exit(1);
|
|
1992
|
-
}
|
|
1993
2206
|
const sync = syncProjectRoutines(root);
|
|
1994
2207
|
if (isDaemonRunning())
|
|
1995
2208
|
signalDaemonReload();
|
|
@@ -1997,6 +2210,11 @@ export function registerRoutinesCommands(program) {
|
|
|
1997
2210
|
writeJson({ ok: true, ...sync });
|
|
1998
2211
|
return;
|
|
1999
2212
|
}
|
|
2213
|
+
if (sync.synced.length === 0 && sync.skipped.length === 0 && sync.removed.length === 0 && sync.errors.length === 0) {
|
|
2214
|
+
console.log(chalk.gray(`No enabled routines materialised from ${displayProjectPath(root)}.`));
|
|
2215
|
+
console.log(chalk.gray(' Enable one with: agents routines enable <name>'));
|
|
2216
|
+
return;
|
|
2217
|
+
}
|
|
2000
2218
|
printSyncResult(sync);
|
|
2001
2219
|
return;
|
|
2002
2220
|
}
|
|
@@ -2008,48 +2226,15 @@ export function registerRoutinesCommands(program) {
|
|
|
2008
2226
|
return;
|
|
2009
2227
|
}
|
|
2010
2228
|
if (all.projects.length === 0 && all.missing.length === 0) {
|
|
2011
|
-
console.log(chalk.gray('No project
|
|
2012
|
-
console.log(chalk.gray(' Enable one with: agents routines enable
|
|
2229
|
+
console.log(chalk.gray('No project routines materialised yet.'));
|
|
2230
|
+
console.log(chalk.gray(' Enable one with: agents routines enable <name>'));
|
|
2013
2231
|
return;
|
|
2014
2232
|
}
|
|
2015
2233
|
for (const p of all.projects)
|
|
2016
2234
|
printSyncResult(p);
|
|
2017
2235
|
for (const m of all.missing) {
|
|
2018
|
-
console.log(chalk.yellow(`Missing project root
|
|
2019
|
-
}
|
|
2020
|
-
});
|
|
2021
|
-
routinesCmd
|
|
2022
|
-
.command('projects')
|
|
2023
|
-
.description('List project roots opted into daemon-fired project routines')
|
|
2024
|
-
.option('--json', 'Emit machine-readable JSON')
|
|
2025
|
-
.action((options) => {
|
|
2026
|
-
const roots = listEnabledProjectRoots();
|
|
2027
|
-
if (options.json) {
|
|
2028
|
-
writeJson(roots.map((r) => ({
|
|
2029
|
-
path: r,
|
|
2030
|
-
display: displayProjectPath(r),
|
|
2031
|
-
routines: listProjectRoutineFiles(r).map((f) => f.name),
|
|
2032
|
-
})));
|
|
2033
|
-
return;
|
|
2236
|
+
console.log(chalk.yellow(`Missing project root for a materialised routine: ${displayProjectPath(m)}`));
|
|
2034
2237
|
}
|
|
2035
|
-
if (roots.length === 0) {
|
|
2036
|
-
console.log(chalk.gray('No projects enabled. Use: agents routines enable-project'));
|
|
2037
|
-
// Offer a discovery hint for the current project.
|
|
2038
|
-
const discovered = discoverProjectRoutinesAt(process.cwd());
|
|
2039
|
-
if (discovered) {
|
|
2040
|
-
console.log(chalk.gray(` Found ${discovered.files.length} routine(s) in ${displayProjectPath(discovered.projectRoot)} — enable with: agents routines enable-project`));
|
|
2041
|
-
}
|
|
2042
|
-
return;
|
|
2043
|
-
}
|
|
2044
|
-
console.log(chalk.bold('Enabled project routines\n'));
|
|
2045
|
-
for (const r of roots) {
|
|
2046
|
-
const files = listProjectRoutineFiles(r);
|
|
2047
|
-
console.log(` ${chalk.cyan(displayProjectPath(r))} ${chalk.gray(`(${files.length} routine${files.length === 1 ? '' : 's'})`)}`);
|
|
2048
|
-
for (const f of files) {
|
|
2049
|
-
console.log(chalk.gray(` - ${f.name}`));
|
|
2050
|
-
}
|
|
2051
|
-
}
|
|
2052
|
-
console.log();
|
|
2053
2238
|
});
|
|
2054
2239
|
routinesCmd
|
|
2055
2240
|
.command('scheduler-logs')
|
|
@@ -2058,9 +2243,8 @@ export function registerRoutinesCommands(program) {
|
|
|
2058
2243
|
.option('-f, --follow', 'Stream log output in real time (like tail -f)')
|
|
2059
2244
|
.action(async (options) => {
|
|
2060
2245
|
if (options.follow) {
|
|
2061
|
-
const { getDaemonDir } = await import('../lib/state.js');
|
|
2062
2246
|
const { followFile } = await import('../lib/log-follow.js');
|
|
2063
|
-
const logPath =
|
|
2247
|
+
const logPath = getDaemonLogPath();
|
|
2064
2248
|
const recent = readDaemonLog(parseInt(options.lines, 10));
|
|
2065
2249
|
if (recent)
|
|
2066
2250
|
console.log(recent);
|