@phnx-labs/agents-cli 1.22.35 → 1.22.37
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +666 -2
- package/README.md +75 -35
- package/dist/bin/agents +0 -0
- package/dist/bootstrap.d.ts +15 -0
- package/dist/bootstrap.js +1211 -0
- package/dist/commands/accounts.d.ts +4 -11
- package/dist/commands/accounts.js +159 -27
- package/dist/commands/attach.js +17 -4
- package/dist/commands/audit.d.ts +8 -8
- package/dist/commands/audit.js +57 -47
- package/dist/commands/browser.js +15 -11
- package/dist/commands/cloud.js +1 -1
- package/dist/commands/commands.js +0 -11
- package/dist/commands/computer-actions.js +33 -0
- package/dist/commands/computer-sessions-picker.d.ts +21 -0
- package/dist/commands/computer-sessions-picker.js +141 -0
- package/dist/commands/computer.d.ts +16 -0
- package/dist/commands/computer.js +37 -0
- package/dist/commands/config.d.ts +17 -0
- package/dist/commands/config.js +401 -0
- package/dist/commands/cp.d.ts +84 -0
- package/dist/commands/cp.js +231 -0
- package/dist/commands/daemon.js +243 -14
- package/dist/commands/detach-core.d.ts +1 -1
- package/dist/commands/detach-core.js +12 -2
- package/dist/commands/detach.js +1 -1
- package/dist/commands/doctor.js +64 -5
- package/dist/commands/events.d.ts +24 -12
- package/dist/commands/events.js +144 -28
- package/dist/commands/exec.js +85 -41
- package/dist/commands/feed.d.ts +1 -1
- package/dist/commands/feed.js +16 -8
- package/dist/commands/focus.d.ts +33 -1
- package/dist/commands/focus.js +177 -17
- package/dist/commands/funnel.d.ts +1 -1
- package/dist/commands/go.js +9 -8
- package/dist/commands/harness-wizard.d.ts +1 -0
- package/dist/commands/harness-wizard.js +18 -20
- package/dist/commands/harness.d.ts +2 -0
- package/dist/commands/harness.js +59 -26
- package/dist/commands/hooks.js +0 -11
- package/dist/commands/inspect.d.ts +94 -1
- package/dist/commands/inspect.js +866 -40
- package/dist/commands/lease.d.ts +4 -4
- package/dist/commands/lease.js +6 -6
- package/dist/commands/login.js +0 -13
- package/dist/commands/logs.d.ts +2 -2
- package/dist/commands/logs.js +2 -2
- package/dist/commands/mcp.js +7 -2
- package/dist/commands/memory.js +7 -2
- package/dist/commands/message.d.ts +1 -1
- package/dist/commands/message.js +37 -6
- package/dist/commands/models.js +4 -0
- package/dist/commands/monitors.d.ts +0 -1
- package/dist/commands/monitors.js +199 -16
- package/dist/commands/open.d.ts +18 -0
- package/dist/commands/open.js +57 -0
- package/dist/commands/packages.js +2 -107
- package/dist/commands/plugins.js +6 -3
- package/dist/commands/profiles.d.ts +1 -0
- package/dist/commands/profiles.js +16 -160
- package/dist/commands/projects.d.ts +28 -0
- package/dist/commands/projects.js +125 -5
- package/dist/commands/reconnect.js +9 -3
- package/dist/commands/repo.js +42 -1
- package/dist/commands/resource-view.d.ts +17 -0
- package/dist/commands/resource-view.js +36 -14
- package/dist/commands/resume.js +9 -2
- package/dist/commands/routines.d.ts +0 -2
- package/dist/commands/routines.js +473 -289
- package/dist/commands/secrets.js +52 -9
- package/dist/commands/sessions-export.d.ts +47 -0
- package/dist/commands/sessions-export.js +116 -6
- package/dist/commands/sessions-import.d.ts +17 -0
- package/dist/commands/sessions-import.js +114 -6
- package/dist/commands/sessions-migrate.d.ts +27 -0
- package/dist/commands/sessions-migrate.js +50 -9
- package/dist/commands/sessions-picker.js +23 -4
- package/dist/commands/sessions-resume.d.ts +5 -3
- package/dist/commands/sessions-resume.js +30 -8
- package/dist/commands/sessions-watch.d.ts +2 -0
- package/dist/commands/sessions-watch.js +53 -0
- package/dist/commands/sessions.d.ts +66 -3
- package/dist/commands/sessions.js +298 -47
- package/dist/commands/set.d.ts +1 -1
- package/dist/commands/set.js +2 -2
- package/dist/commands/setup-browser.js +2 -2
- package/dist/commands/setup-mine.js +0 -1
- package/dist/commands/setup-preferences.js +1 -1
- package/dist/commands/setup-share.js +34 -4
- package/dist/commands/setup.d.ts +5 -0
- package/dist/commands/setup.js +26 -0
- package/dist/commands/share.d.ts +67 -0
- package/dist/commands/share.js +275 -12
- package/dist/commands/skills.js +0 -11
- package/dist/commands/snapshot.js +1 -1
- package/dist/commands/ssh.d.ts +18 -0
- package/dist/commands/ssh.js +549 -210
- package/dist/commands/sync.d.ts +5 -0
- package/dist/commands/sync.js +212 -32
- package/dist/commands/teams.d.ts +13 -0
- package/dist/commands/teams.js +187 -30
- package/dist/commands/tickets.d.ts +2 -0
- package/dist/commands/tickets.js +43 -0
- package/dist/commands/update.js +2 -4
- package/dist/commands/usage.js +5 -3
- package/dist/commands/versions.js +11 -5
- package/dist/commands/view.js +15 -7
- package/dist/commands/webhook.d.ts +2 -2
- package/dist/commands/webhook.js +7 -7
- package/dist/index.d.ts +18 -2
- package/dist/index.js +53 -1264
- package/dist/lib/account-catalog.d.ts +18 -0
- package/dist/lib/account-catalog.js +38 -0
- package/dist/lib/account-provider-registry.d.ts +18 -0
- package/dist/lib/account-provider-registry.js +59 -0
- package/dist/lib/account-registry.d.ts +40 -0
- package/dist/lib/account-registry.js +239 -0
- package/dist/lib/account-schema.d.ts +44 -0
- package/dist/lib/account-schema.js +91 -0
- package/dist/lib/account-state-service.d.ts +21 -0
- package/dist/lib/account-state-service.js +60 -0
- package/dist/lib/activity.d.ts +1 -1
- package/dist/lib/activity.js +8 -1
- package/dist/lib/add-dir.d.ts +80 -0
- package/dist/lib/add-dir.js +241 -0
- package/dist/lib/agent-cli-commands.d.ts +11 -0
- package/dist/lib/agent-cli-commands.js +30 -0
- package/dist/lib/agents.js +44 -36
- package/dist/lib/answer-router.js +1 -1
- package/dist/lib/audit/log.d.ts +10 -27
- package/dist/lib/audit/log.js +20 -33
- package/dist/lib/auth-health.d.ts +9 -2
- package/dist/lib/auth-health.js +42 -9
- package/dist/lib/auto-pull-worker.js +7 -3
- package/dist/lib/auto-pull.d.ts +44 -3
- package/dist/lib/auto-pull.js +88 -5
- package/dist/lib/binary-shadow.d.ts +28 -0
- package/dist/lib/binary-shadow.js +121 -0
- package/dist/lib/brand.js +9 -3
- package/dist/lib/browser/ipc.d.ts +30 -0
- package/dist/lib/browser/ipc.js +81 -4
- package/dist/lib/browser/profiles.d.ts +3 -2
- package/dist/lib/browser/profiles.js +65 -22
- package/dist/lib/browser/service.d.ts +4 -0
- package/dist/lib/browser/service.js +30 -3
- package/dist/lib/browser/sessions-list.d.ts +22 -2
- package/dist/lib/browser/sessions-list.js +54 -6
- package/dist/lib/browser/stream.d.ts +2 -0
- package/dist/lib/browser/stream.js +1 -0
- package/dist/lib/browser/types.d.ts +19 -0
- package/dist/lib/byok-usage.d.ts +9 -2
- package/dist/lib/byok-usage.js +133 -31
- package/dist/lib/cli-entry.d.ts +11 -0
- package/dist/lib/cli-entry.js +46 -0
- package/dist/lib/cloud/host.d.ts +1 -1
- package/dist/lib/cloud/host.js +2 -2
- package/dist/lib/cloud/registry.js +1 -1
- package/dist/lib/cloud/types.d.ts +1 -1
- package/dist/lib/commands.d.ts +2 -0
- package/dist/lib/commands.js +2 -0
- package/dist/lib/computer/sessions-list.d.ts +150 -0
- package/dist/lib/computer/sessions-list.js +363 -0
- package/dist/lib/config-keys.d.ts +86 -0
- package/dist/lib/config-keys.js +193 -0
- package/dist/lib/config-machine-keys.d.ts +12 -0
- package/dist/lib/config-machine-keys.js +17 -0
- package/dist/lib/config-transfer.js +3 -4
- package/dist/lib/crabbox/cli.d.ts +2 -2
- package/dist/lib/crabbox/cli.js +3 -3
- package/dist/lib/daemon-health.d.ts +22 -0
- package/dist/lib/daemon-health.js +33 -3
- package/dist/lib/daemon-services.d.ts +50 -0
- package/dist/lib/daemon-services.js +147 -0
- package/dist/lib/daemon-ticks.d.ts +35 -66
- package/dist/lib/daemon-ticks.js +77 -159
- package/dist/lib/daemon.d.ts +72 -4
- package/dist/lib/daemon.js +792 -202
- package/dist/lib/deeplink/register.d.ts +49 -0
- package/dist/lib/deeplink/register.js +253 -0
- package/dist/lib/deeplink/url.d.ts +34 -0
- package/dist/lib/deeplink/url.js +78 -0
- package/dist/lib/device-config.d.ts +124 -29
- package/dist/lib/device-config.js +346 -141
- package/dist/lib/devices/config-migration.d.ts +42 -0
- package/dist/lib/devices/config-migration.js +203 -0
- package/dist/lib/devices/connect.js +14 -7
- package/dist/lib/devices/doctor-findings.d.ts +4 -1
- package/dist/lib/devices/doctor-findings.js +20 -1
- package/dist/lib/devices/harness-inventory.d.ts +11 -2
- package/dist/lib/devices/harness-inventory.js +0 -0
- package/dist/lib/devices/health.d.ts +1 -1
- package/dist/lib/devices/health.js +1 -1
- package/dist/lib/devices/pending.d.ts +22 -1
- package/dist/lib/devices/pending.js +67 -2
- package/dist/lib/devices/registry.d.ts +0 -25
- package/dist/lib/devices/registry.js +2 -83
- package/dist/lib/devices/resolve-profile.d.ts +20 -0
- package/dist/lib/devices/resolve-profile.js +44 -0
- package/dist/lib/devices/rollout-verify.d.ts +101 -0
- package/dist/lib/devices/rollout-verify.js +193 -0
- package/dist/lib/devices/ssh-config.js +10 -6
- package/dist/lib/event-families.d.ts +28 -0
- package/dist/lib/event-families.js +159 -0
- package/dist/lib/event-stream.d.ts +9 -1
- package/dist/lib/event-stream.js +26 -9
- package/dist/lib/events.d.ts +9 -1
- package/dist/lib/events.js +21 -6
- package/dist/lib/exec.d.ts +30 -0
- package/dist/lib/exec.js +219 -121
- package/dist/lib/feed-broadcast.d.ts +25 -0
- package/dist/lib/feed-broadcast.js +33 -0
- package/dist/lib/feed.d.ts +1 -1
- package/dist/lib/feed.js +12 -2
- package/dist/lib/fleet/capture.js +15 -0
- package/dist/lib/fleet/manifest.js +9 -0
- package/dist/lib/fleet/types.d.ts +9 -0
- package/dist/lib/fleet-status.js +10 -8
- package/dist/lib/git.d.ts +20 -6
- package/dist/lib/git.js +121 -17
- package/dist/lib/hosts/dispatch.d.ts +8 -3
- package/dist/lib/hosts/dispatch.js +29 -3
- package/dist/lib/hosts/logs.d.ts +2 -2
- package/dist/lib/hosts/logs.js +2 -2
- package/dist/lib/hosts/option.js +1 -1
- package/dist/lib/hosts/passthrough.d.ts +2 -3
- package/dist/lib/hosts/passthrough.js +10 -24
- package/dist/lib/hosts/progress.js +2 -2
- package/dist/lib/hosts/providers/devices.d.ts +3 -3
- package/dist/lib/hosts/providers/devices.js +18 -12
- package/dist/lib/hosts/ready.d.ts +5 -1
- package/dist/lib/hosts/ready.js +9 -3
- package/dist/lib/hosts/reconcile.d.ts +2 -2
- package/dist/lib/hosts/reconcile.js +2 -2
- package/dist/lib/hosts/registry.js +14 -9
- package/dist/lib/hosts/remote-cmd.js +1 -1
- package/dist/lib/hosts/remote-os.d.ts +9 -6
- package/dist/lib/hosts/remote-os.js +13 -6
- package/dist/lib/hosts/routing-flag.d.ts +23 -0
- package/dist/lib/hosts/routing-flag.js +41 -0
- package/dist/lib/hosts/run-target.js +1 -1
- package/dist/lib/hosts/session-index.js +1 -1
- package/dist/lib/hosts/tasks.d.ts +6 -6
- package/dist/lib/hosts/tasks.js +4 -4
- package/dist/lib/hosts/types.js +1 -2
- package/dist/lib/installations/resolve.js +2 -8
- package/dist/lib/isolation-boundary-report.js +0 -1
- package/dist/lib/linear-cache.d.ts +7 -0
- package/dist/lib/linear-cache.js +31 -4
- package/dist/lib/linear-project-counts.d.ts +1 -1
- package/dist/lib/linear-project-counts.js +2 -3
- package/dist/lib/linear-projects.js +1 -1
- package/dist/lib/loop.d.ts +2 -2
- package/dist/lib/loop.js +1 -1
- package/dist/lib/mailbox-target.d.ts +1 -1
- package/dist/lib/mailbox-target.js +1 -1
- package/dist/lib/manifest.js +7 -4
- package/dist/lib/menubar/MenubarHelper.app/Contents/CodeResources +0 -0
- package/dist/lib/menubar/MenubarHelper.app/Contents/MacOS/MenubarHelper +0 -0
- package/dist/lib/menubar/snapshot.d.ts +3 -2
- package/dist/lib/menubar/snapshot.js +6 -2
- package/dist/lib/migrate-fold.d.ts +33 -0
- package/dist/lib/migrate-fold.js +130 -0
- package/dist/lib/migrate.d.ts +34 -34
- package/dist/lib/migrate.js +106 -128
- package/dist/lib/model-tier-overrides.d.ts +1 -1
- package/dist/lib/model-tier-overrides.js +15 -2
- package/dist/lib/monitors/config.d.ts +17 -3
- package/dist/lib/monitors/config.js +52 -18
- package/dist/lib/monitors/engine.d.ts +32 -1
- package/dist/lib/monitors/engine.js +94 -11
- package/dist/lib/monitors/fingerprint.d.ts +46 -0
- package/dist/lib/monitors/fingerprint.js +100 -0
- package/dist/lib/monitors/remote.d.ts +47 -0
- package/dist/lib/monitors/remote.js +81 -0
- package/dist/lib/monitors/state.d.ts +39 -0
- package/dist/lib/monitors/state.js +56 -0
- package/dist/lib/onepassword.d.ts +17 -0
- package/dist/lib/onepassword.js +52 -22
- package/dist/lib/open-url.js +1 -1
- package/dist/lib/picker.d.ts +14 -2
- package/dist/lib/picker.js +35 -13
- package/dist/lib/placement.d.ts +1 -1
- package/dist/lib/placement.js +1 -1
- package/dist/lib/pr-land-detach.d.ts +32 -0
- package/dist/lib/pr-land-detach.js +79 -0
- package/dist/lib/profiles-presets.js +13 -0
- package/dist/lib/profiles.d.ts +2 -0
- package/dist/lib/profiles.js +44 -1
- package/dist/lib/project-focus.d.ts +1 -1
- package/dist/lib/project-focus.js +1 -1
- package/dist/lib/project-probe.d.ts +4 -1
- package/dist/lib/project-probe.js +5 -4
- package/dist/lib/project-resources.js +2 -18
- package/dist/lib/project-root.d.ts +16 -0
- package/dist/lib/project-root.js +20 -2
- package/dist/lib/projects.d.ts +20 -1
- package/dist/lib/projects.js +78 -1
- package/dist/lib/refresh-coordinator.d.ts +24 -0
- package/dist/lib/refresh-coordinator.js +52 -0
- package/dist/lib/registry.d.ts +2 -2
- package/dist/lib/registry.js +1 -1
- package/dist/lib/resource-aliases.d.ts +13 -0
- package/dist/lib/resource-aliases.js +26 -0
- package/dist/lib/resources/types.d.ts +1 -1
- package/dist/lib/resources.d.ts +16 -0
- package/dist/lib/resources.js +66 -10
- package/dist/lib/rotate.d.ts +3 -4
- package/dist/lib/rotate.js +4 -5
- package/dist/lib/routine-activation.d.ts +22 -2
- package/dist/lib/routine-activation.js +63 -16
- package/dist/lib/routine-context.d.ts +9 -2
- package/dist/lib/routine-context.js +21 -6
- package/dist/lib/routines-project.d.ts +58 -48
- package/dist/lib/routines-project.js +190 -159
- package/dist/lib/routines.d.ts +41 -6
- package/dist/lib/routines.js +92 -34
- package/dist/lib/run-defaults.d.ts +5 -0
- package/dist/lib/run-defaults.js +11 -1
- package/dist/lib/runner.d.ts +10 -8
- package/dist/lib/runner.js +122 -39
- package/dist/lib/secrets/Agents CLI.app/Contents/CodeResources +0 -0
- package/dist/lib/secrets/Agents CLI.app/Contents/MacOS/Agents CLI +0 -0
- package/dist/lib/secrets/agent.d.ts +10 -3
- package/dist/lib/secrets/agent.js +43 -8
- package/dist/lib/secrets/bundles.d.ts +1 -0
- package/dist/lib/secrets/bundles.js +92 -80
- package/dist/lib/secrets/index.js +32 -9
- package/dist/lib/secrets/install-helper.d.ts +4 -4
- package/dist/lib/secrets/install-helper.js +4 -4
- package/dist/lib/secrets/push.d.ts +40 -6
- package/dist/lib/secrets/push.js +58 -11
- package/dist/lib/secrets/read-backoff.d.ts +1 -1
- package/dist/lib/secrets/read-backoff.js +1 -1
- package/dist/lib/secrets/remote.d.ts +24 -0
- package/dist/lib/secrets/remote.js +54 -4
- package/dist/lib/self-update.d.ts +108 -0
- package/dist/lib/self-update.js +276 -1
- package/dist/lib/session/active.d.ts +89 -5
- package/dist/lib/session/active.js +165 -10
- package/dist/lib/session/actor-sidecar.d.ts +1 -1
- package/dist/lib/session/actor-sidecar.js +2 -1
- package/dist/lib/session/db.d.ts +176 -2
- package/dist/lib/session/db.js +484 -17
- package/dist/lib/session/discover.js +27 -4
- package/dist/lib/session/hook-sessions.js +2 -2
- package/dist/lib/session/host-link.d.ts +2 -2
- package/dist/lib/session/host-link.js +2 -2
- package/dist/lib/session/pid-registry.d.ts +21 -0
- package/dist/lib/session/pid-registry.js +57 -0
- package/dist/lib/session/remote-active.js +15 -1
- package/dist/lib/session/remote.js +2 -2
- package/dist/lib/session/session-cache.d.ts +14 -7
- package/dist/lib/session/session-cache.js +51 -9
- package/dist/lib/session/state.d.ts +2 -2
- package/dist/lib/session/state.js +1 -1
- package/dist/lib/session/sync/config.d.ts +10 -10
- package/dist/lib/session/sync/config.js +10 -10
- package/dist/lib/session/sync/r2.d.ts +34 -0
- package/dist/lib/session/sync/r2.js +123 -0
- package/dist/lib/session/throughput.d.ts +1 -1
- package/dist/lib/session/throughput.js +1 -1
- package/dist/lib/session/types.d.ts +23 -1
- package/dist/lib/session/types.js +15 -0
- package/dist/lib/session/viewing-in.d.ts +1 -1
- package/dist/lib/session/viewing-in.js +1 -1
- package/dist/lib/session/watch.d.ts +101 -0
- package/dist/lib/session/watch.js +242 -0
- package/dist/lib/share/config.d.ts +4 -0
- package/dist/lib/share/config.js +1 -0
- package/dist/lib/share/provision.d.ts +38 -0
- package/dist/lib/share/provision.js +50 -0
- package/dist/lib/share/publish.d.ts +40 -1
- package/dist/lib/share/publish.js +116 -3
- package/dist/lib/share/worker-template.js +71 -6
- package/dist/lib/shims.d.ts +17 -3
- package/dist/lib/shims.js +98 -26
- package/dist/lib/signin-badge.js +3 -2
- package/dist/lib/skills.js +2 -0
- package/dist/lib/smart-launch.d.ts +8 -8
- package/dist/lib/smart-launch.js +80 -59
- package/dist/lib/ssh-exec.d.ts +1 -1
- package/dist/lib/ssh-exec.js +1 -1
- package/dist/lib/staleness/prune.d.ts +88 -0
- package/dist/lib/staleness/prune.js +69 -0
- package/dist/lib/staleness/registry.d.ts +1 -1
- package/dist/lib/staleness/writers/commands.js +34 -2
- package/dist/lib/staleness/writers/hooks.js +16 -0
- package/dist/lib/staleness/writers/kinds.d.ts +6 -0
- package/dist/lib/staleness/writers/skills.js +19 -0
- package/dist/lib/staleness/writers/subagents.d.ts +2 -3
- package/dist/lib/staleness/writers/subagents.js +0 -4
- package/dist/lib/staleness/writers/types.d.ts +32 -0
- package/dist/lib/startup/command-registry.d.ts +6 -12
- package/dist/lib/startup/command-registry.js +11 -31
- package/dist/lib/startup/dev-build.d.ts +10 -0
- package/dist/lib/startup/dev-build.js +13 -1
- package/dist/lib/startup/spellcheck.d.ts +18 -0
- package/dist/lib/startup/spellcheck.js +45 -0
- package/dist/lib/state.d.ts +36 -9
- package/dist/lib/state.js +96 -46
- package/dist/lib/subagents-registry.d.ts +0 -7
- package/dist/lib/subagents-registry.js +38 -65
- package/dist/lib/subagents.d.ts +0 -33
- package/dist/lib/subagents.js +0 -75
- package/dist/lib/sync-umbrella.js +1 -1
- package/dist/lib/teams/agents.d.ts +41 -1
- package/dist/lib/teams/agents.js +187 -9
- package/dist/lib/teams/api.d.ts +11 -1
- package/dist/lib/teams/api.js +11 -2
- package/dist/lib/teams/delivery.d.ts +41 -0
- package/dist/lib/teams/delivery.js +60 -0
- package/dist/lib/teams/registry.d.ts +14 -0
- package/dist/lib/teams/registry.js +51 -1
- package/dist/lib/teams/remoteWorktree.d.ts +19 -0
- package/dist/lib/teams/remoteWorktree.js +29 -0
- package/dist/lib/teams/scheduler.d.ts +1 -1
- package/dist/lib/teams/scheduler.js +1 -1
- package/dist/lib/teams/worktree.d.ts +24 -0
- package/dist/lib/teams/worktree.js +42 -0
- package/dist/lib/terminal/backends/vscodium-agent.d.ts +10 -1
- package/dist/lib/terminal/backends/vscodium-agent.js +16 -5
- package/dist/lib/terminal/engine.d.ts +4 -0
- package/dist/lib/terminal/engine.js +8 -2
- package/dist/lib/terminal/types.d.ts +21 -2
- package/dist/lib/testdata/refresh-coordinator-worker.d.ts +1 -0
- package/dist/lib/testdata/refresh-coordinator-worker.js +23 -0
- package/dist/lib/tickets/list.d.ts +53 -0
- package/dist/lib/tickets/list.js +153 -0
- package/dist/lib/tmux/orphan-reap.d.ts +320 -0
- package/dist/lib/tmux/orphan-reap.js +644 -0
- package/dist/lib/tmux/session.d.ts +68 -3
- package/dist/lib/tmux/session.js +95 -5
- package/dist/lib/triggers/handlers.d.ts +20 -0
- package/dist/lib/triggers/handlers.js +6 -1
- package/dist/lib/types.d.ts +46 -34
- package/dist/lib/usage-fleet.d.ts +32 -0
- package/dist/lib/usage-fleet.js +125 -0
- package/dist/lib/usage-refresh.d.ts +3 -3
- package/dist/lib/usage-refresh.js +19 -16
- package/dist/lib/usage.d.ts +51 -47
- package/dist/lib/usage.js +145 -153
- package/dist/lib/version-duplicates.js +2 -2
- package/dist/lib/versions.d.ts +56 -13
- package/dist/lib/versions.js +195 -45
- package/dist/lib/watchdog/log.d.ts +1 -1
- package/dist/lib/watchdog/log.js +3 -3
- package/dist/lib/watchdog/rotate.d.ts +6 -6
- package/dist/lib/watchdog/rotate.js +6 -6
- package/dist/lib/workflows.d.ts +1 -1
- package/dist/lib/yaml-io.d.ts +47 -0
- package/dist/lib/yaml-io.js +55 -0
- package/package.json +2 -1
- package/scripts/install-helper.js +2 -2
- package/scripts/postinstall.js +23 -20
- package/dist/commands/defaults.d.ts +0 -7
- package/dist/commands/defaults.js +0 -107
- package/dist/commands/export.d.ts +0 -11
- package/dist/commands/export.js +0 -215
- package/dist/commands/helper.d.ts +0 -12
- package/dist/commands/helper.js +0 -87
- package/dist/commands/hosts.d.ts +0 -11
- package/dist/commands/hosts.js +0 -330
- package/dist/commands/lock.d.ts +0 -12
- package/dist/commands/lock.js +0 -70
- package/dist/commands/pull.d.ts +0 -17
- package/dist/commands/pull.js +0 -39
- package/dist/commands/push.d.ts +0 -14
- package/dist/commands/push.js +0 -30
- package/dist/commands/wallet.d.ts +0 -20
- package/dist/commands/wallet.js +0 -216
- package/dist/commands/worktree.d.ts +0 -19
- package/dist/commands/worktree.js +0 -272
- package/dist/lib/account-labels.d.ts +0 -24
- package/dist/lib/account-labels.js +0 -72
- package/dist/lib/auto-dispatch-linear.d.ts +0 -18
- package/dist/lib/auto-dispatch-linear.js +0 -107
- package/dist/lib/auto-dispatch-provider.d.ts +0 -10
- package/dist/lib/auto-dispatch-provider.js +0 -30
- package/dist/lib/auto-dispatch.d.ts +0 -93
- package/dist/lib/auto-dispatch.js +0 -128
- package/dist/lib/export.d.ts +0 -72
- package/dist/lib/export.js +0 -269
- package/dist/lib/lock.d.ts +0 -93
- package/dist/lib/lock.js +0 -207
- package/dist/lib/wallet/index.d.ts +0 -78
- package/dist/lib/wallet/index.js +0 -253
|
@@ -13,6 +13,21 @@ export const SESSION_AGENTS = ['claude', 'codex', 'gemini', 'antigravity', 'open
|
|
|
13
13
|
* of {@link SESSION_AGENTS}). The single predicate every session-index writer
|
|
14
14
|
* gates on, so "is this a trackable agent?" is decided in exactly one place.
|
|
15
15
|
*/
|
|
16
|
+
/**
|
|
17
|
+
* The tmux session name the CLI mints for an agent run: `ag-<agent>-<shortid>`.
|
|
18
|
+
*
|
|
19
|
+
* ONE definition. This shape was independently re-written as a regex literal in
|
|
20
|
+
* five places (focus's selector test, sessions-resume's direct-selector test,
|
|
21
|
+
* active's name parsers, actor-sidecar, and the deeplink parser); they drifted —
|
|
22
|
+
* focus accepted a 6+ hex suffix while the others required exactly 8 — which is
|
|
23
|
+
* how the same alias could be an "identity" to one code path and a keyword query
|
|
24
|
+
* to the next.
|
|
25
|
+
*/
|
|
26
|
+
export const AG_TMUX_NAME_RE = /^ag-([a-z][a-z0-9-]*?)-([0-9a-f]{8})$/i;
|
|
27
|
+
/** Whether `name` is an `ag-<agent>-<shortid>` tmux session name. */
|
|
28
|
+
export function isAgentTmuxAlias(name) {
|
|
29
|
+
return AG_TMUX_NAME_RE.test(name);
|
|
30
|
+
}
|
|
16
31
|
export function isSessionTrackedAgent(agent) {
|
|
17
32
|
return SESSION_AGENTS.includes(agent);
|
|
18
33
|
}
|
|
@@ -41,7 +41,7 @@ export interface ViewingIn {
|
|
|
41
41
|
* BOTH "no client attached" and "could not locate the pane", so the pane's resolved
|
|
42
42
|
* `tmuxTarget` is what separates them: without it there is no evidence of absence,
|
|
43
43
|
* only absence of evidence. Claiming `'detached'` there would invent an orphaned
|
|
44
|
-
* session — and a consumer acts on that claim (
|
|
44
|
+
* session — and a consumer acts on that claim (the ext's picker pre-ticks every
|
|
45
45
|
* detached row for rescue), so the wrong answer resumes a session nobody asked for.
|
|
46
46
|
*
|
|
47
47
|
* Shared by the `--active` row renderer and the `--json` serializer so a machine
|
|
@@ -40,7 +40,7 @@ const execFileAsync = promisify(execFile);
|
|
|
40
40
|
* BOTH "no client attached" and "could not locate the pane", so the pane's resolved
|
|
41
41
|
* `tmuxTarget` is what separates them: without it there is no evidence of absence,
|
|
42
42
|
* only absence of evidence. Claiming `'detached'` there would invent an orphaned
|
|
43
|
-
* session — and a consumer acts on that claim (
|
|
43
|
+
* session — and a consumer acts on that claim (the ext's picker pre-ticks every
|
|
44
44
|
* detached row for rescue), so the wrong answer resumes a session nobody asked for.
|
|
45
45
|
*
|
|
46
46
|
* Shared by the `--active` row renderer and the `--json` serializer so a machine
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import type { ActiveSession } from './active.js';
|
|
2
|
+
import { readActiveSessionsCache } from './session-cache.js';
|
|
3
|
+
export declare const SESSION_WATCH_VERSION: 1;
|
|
4
|
+
export declare const SESSION_WATCH_HEARTBEAT_MS = 15000;
|
|
5
|
+
export type SessionWatchScopeStatus = 'available' | 'unavailable';
|
|
6
|
+
export type SessionWatchEnvelope = {
|
|
7
|
+
version: 1;
|
|
8
|
+
type: 'reset';
|
|
9
|
+
streamId: string;
|
|
10
|
+
sequence: number;
|
|
11
|
+
capturedAt: number;
|
|
12
|
+
scope: string;
|
|
13
|
+
rows: SessionWatchRow[];
|
|
14
|
+
} | {
|
|
15
|
+
version: 1;
|
|
16
|
+
type: 'upsert';
|
|
17
|
+
streamId: string;
|
|
18
|
+
sequence: number;
|
|
19
|
+
capturedAt: number;
|
|
20
|
+
scope: string;
|
|
21
|
+
rowKey: string;
|
|
22
|
+
row: SessionWatchRow;
|
|
23
|
+
} | {
|
|
24
|
+
version: 1;
|
|
25
|
+
type: 'remove';
|
|
26
|
+
streamId: string;
|
|
27
|
+
sequence: number;
|
|
28
|
+
capturedAt: number;
|
|
29
|
+
scope: string;
|
|
30
|
+
rowKey: string;
|
|
31
|
+
} | {
|
|
32
|
+
version: 1;
|
|
33
|
+
type: 'scope';
|
|
34
|
+
streamId: string;
|
|
35
|
+
sequence: number;
|
|
36
|
+
capturedAt: number;
|
|
37
|
+
scope: string;
|
|
38
|
+
status: SessionWatchScopeStatus;
|
|
39
|
+
reason?: string;
|
|
40
|
+
} | {
|
|
41
|
+
version: 1;
|
|
42
|
+
type: 'heartbeat';
|
|
43
|
+
streamId: string;
|
|
44
|
+
sequence: number;
|
|
45
|
+
scope: string;
|
|
46
|
+
capturedAt: number;
|
|
47
|
+
};
|
|
48
|
+
export interface SessionWatchRow extends Omit<ActiveSession, 'viewingIn'> {
|
|
49
|
+
rowKey: string;
|
|
50
|
+
sourceDevice: string;
|
|
51
|
+
resumable: boolean;
|
|
52
|
+
unwatched: boolean;
|
|
53
|
+
viewingIn: string | null;
|
|
54
|
+
recovery: {
|
|
55
|
+
command: 'agents';
|
|
56
|
+
args: string[];
|
|
57
|
+
cwd?: string;
|
|
58
|
+
} | null;
|
|
59
|
+
}
|
|
60
|
+
/** Stable, opaque identity for one row within one device scope. */
|
|
61
|
+
export declare function sessionWatchRowKey(scope: string, row: ActiveSession): string;
|
|
62
|
+
export declare function toSessionWatchRow(scope: string, row: ActiveSession): SessionWatchRow;
|
|
63
|
+
/** Stream-local sequencer and convergent row diff. */
|
|
64
|
+
export declare class SessionWatchState {
|
|
65
|
+
readonly streamId: string;
|
|
66
|
+
private sequence;
|
|
67
|
+
private readonly rows;
|
|
68
|
+
constructor(streamId?: string);
|
|
69
|
+
private base;
|
|
70
|
+
reset(scope: string, sourceRows: ActiveSession[]): SessionWatchEnvelope;
|
|
71
|
+
update(scope: string, sourceRows: ActiveSession[]): SessionWatchEnvelope[];
|
|
72
|
+
patch(scope: string, upserts: ActiveSession[], removes: string[]): SessionWatchEnvelope[];
|
|
73
|
+
scope(scope: string, status: SessionWatchScopeStatus, reason?: string): SessionWatchEnvelope;
|
|
74
|
+
heartbeat(scope: string, capturedAt?: number): SessionWatchEnvelope;
|
|
75
|
+
}
|
|
76
|
+
export interface WatchLocalOptions {
|
|
77
|
+
scope: string;
|
|
78
|
+
signal: AbortSignal;
|
|
79
|
+
emit: (event: SessionWatchEnvelope) => void;
|
|
80
|
+
refreshMs?: number;
|
|
81
|
+
heartbeatMs?: number;
|
|
82
|
+
readCache?: typeof readActiveSessionsCache;
|
|
83
|
+
journalPath?: string;
|
|
84
|
+
journalPollMs?: number;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Keep one local subscription alive. Startup reads one canonical reset snapshot;
|
|
88
|
+
* steady state tails the canonical writer journal and never invokes a gather.
|
|
89
|
+
*/
|
|
90
|
+
export declare function watchLocalSessions(options: WatchLocalOptions): Promise<void>;
|
|
91
|
+
export interface WatchFleetOptions {
|
|
92
|
+
signal: AbortSignal;
|
|
93
|
+
emit: (event: SessionWatchEnvelope) => void;
|
|
94
|
+
reconnectMs?: number;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Subscribe to every dialable compute device with one persistent SSH process.
|
|
98
|
+
* A peer disconnect emits `scope: unavailable` but no removes; reconnecting
|
|
99
|
+
* peer resets only its own scope. There is no recurring fleet list command.
|
|
100
|
+
*/
|
|
101
|
+
export declare function watchFleetSessions(options: WatchFleetOptions): Promise<void>;
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
import { createHash, randomUUID } from 'node:crypto';
|
|
2
|
+
import { spawn } from 'node:child_process';
|
|
3
|
+
import { createInterface } from 'node:readline';
|
|
4
|
+
import * as fs from 'node:fs';
|
|
5
|
+
import * as path from 'node:path';
|
|
6
|
+
import { loadDevices, isControlDevice, isDialableDevice } from '../devices/registry.js';
|
|
7
|
+
import { deviceIdentityArgs, sshTargetFor } from '../devices/connect.js';
|
|
8
|
+
import { machineId, normalizeHost } from '../machine-id.js';
|
|
9
|
+
import { SSH_OPTS, controlOpts, shellQuote } from '../ssh-exec.js';
|
|
10
|
+
import { buildWindowsAgentsCommand, remoteShellFor } from '../hosts/remote-cmd.js';
|
|
11
|
+
import { activeSessionsJournalPath, activeSessionJournalIdentity, readActiveSessionsCache, } from './session-cache.js';
|
|
12
|
+
export const SESSION_WATCH_VERSION = 1;
|
|
13
|
+
export const SESSION_WATCH_HEARTBEAT_MS = 15_000;
|
|
14
|
+
/** Stable, opaque identity for one row within one device scope. */
|
|
15
|
+
export function sessionWatchRowKey(scope, row) {
|
|
16
|
+
const identity = activeSessionJournalIdentity(row);
|
|
17
|
+
return createHash('sha256').update(`${scope}\0${identity}`).digest('base64url').slice(0, 22);
|
|
18
|
+
}
|
|
19
|
+
export function toSessionWatchRow(scope, row) {
|
|
20
|
+
const rowKey = sessionWatchRowKey(scope, row);
|
|
21
|
+
const resumable = Boolean(row.sessionId);
|
|
22
|
+
const viewingIn = row.viewingIn
|
|
23
|
+
? [row.viewingIn.app, row.viewingIn.tab ? `tab ${row.viewingIn.tab}` : undefined].filter(Boolean).join(' ')
|
|
24
|
+
: null;
|
|
25
|
+
return {
|
|
26
|
+
...row,
|
|
27
|
+
rowKey,
|
|
28
|
+
sourceDevice: scope,
|
|
29
|
+
resumable,
|
|
30
|
+
unwatched: !viewingIn,
|
|
31
|
+
viewingIn,
|
|
32
|
+
recovery: resumable
|
|
33
|
+
? { command: 'agents', args: ['sessions', 'resume', row.sessionId, '--device', scope], ...(row.cwd ? { cwd: row.cwd } : {}) }
|
|
34
|
+
: null,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
function sameRow(a, b) {
|
|
38
|
+
return JSON.stringify(a) === JSON.stringify(b);
|
|
39
|
+
}
|
|
40
|
+
/** Stream-local sequencer and convergent row diff. */
|
|
41
|
+
export class SessionWatchState {
|
|
42
|
+
streamId;
|
|
43
|
+
sequence = 0;
|
|
44
|
+
rows = new Map();
|
|
45
|
+
constructor(streamId = randomUUID()) { this.streamId = streamId; }
|
|
46
|
+
base(type) {
|
|
47
|
+
return { version: SESSION_WATCH_VERSION, type, streamId: this.streamId, sequence: ++this.sequence, capturedAt: Date.now() };
|
|
48
|
+
}
|
|
49
|
+
reset(scope, sourceRows) {
|
|
50
|
+
const rows = sourceRows.map((row) => toSessionWatchRow(scope, row));
|
|
51
|
+
this.rows.set(scope, new Map(rows.map((row) => [row.rowKey, row])));
|
|
52
|
+
return { ...this.base('reset'), scope, rows };
|
|
53
|
+
}
|
|
54
|
+
update(scope, sourceRows) {
|
|
55
|
+
const previous = this.rows.get(scope) ?? new Map();
|
|
56
|
+
const nextRows = sourceRows.map((row) => toSessionWatchRow(scope, row));
|
|
57
|
+
const next = new Map(nextRows.map((row) => [row.rowKey, row]));
|
|
58
|
+
const events = [];
|
|
59
|
+
for (const [rowKey, row] of next) {
|
|
60
|
+
if (!previous.has(rowKey) || !sameRow(previous.get(rowKey), row)) {
|
|
61
|
+
events.push({ ...this.base('upsert'), scope, rowKey, row });
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
for (const rowKey of previous.keys()) {
|
|
65
|
+
if (!next.has(rowKey))
|
|
66
|
+
events.push({ ...this.base('remove'), scope, rowKey });
|
|
67
|
+
}
|
|
68
|
+
this.rows.set(scope, next);
|
|
69
|
+
return events;
|
|
70
|
+
}
|
|
71
|
+
patch(scope, upserts, removes) {
|
|
72
|
+
const current = new Map(this.rows.get(scope) ?? []);
|
|
73
|
+
const events = [];
|
|
74
|
+
for (const source of upserts) {
|
|
75
|
+
const row = toSessionWatchRow(scope, source);
|
|
76
|
+
if (!current.has(row.rowKey) || !sameRow(current.get(row.rowKey), row)) {
|
|
77
|
+
current.set(row.rowKey, row);
|
|
78
|
+
events.push({ ...this.base('upsert'), scope, rowKey: row.rowKey, row });
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
for (const identity of removes) {
|
|
82
|
+
const rowKey = createHash('sha256').update(`${scope}\0${identity}`).digest('base64url').slice(0, 22);
|
|
83
|
+
if (current.delete(rowKey))
|
|
84
|
+
events.push({ ...this.base('remove'), scope, rowKey });
|
|
85
|
+
}
|
|
86
|
+
this.rows.set(scope, current);
|
|
87
|
+
return events;
|
|
88
|
+
}
|
|
89
|
+
scope(scope, status, reason) {
|
|
90
|
+
// Deliberately retain rows while unavailable. A reconnecting scope replaces
|
|
91
|
+
// them with a reset; transient fleet loss must not look like session death.
|
|
92
|
+
return { ...this.base('scope'), scope, status, ...(reason ? { reason } : {}) };
|
|
93
|
+
}
|
|
94
|
+
heartbeat(scope, capturedAt = Date.now()) {
|
|
95
|
+
return { ...this.base('heartbeat'), scope, capturedAt };
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Keep one local subscription alive. Startup reads one canonical reset snapshot;
|
|
100
|
+
* steady state tails the canonical writer journal and never invokes a gather.
|
|
101
|
+
*/
|
|
102
|
+
export async function watchLocalSessions(options) {
|
|
103
|
+
const state = new SessionWatchState();
|
|
104
|
+
const readCache = options.readCache ?? readActiveSessionsCache;
|
|
105
|
+
const journal = options.journalPath ?? activeSessionsJournalPath();
|
|
106
|
+
const heartbeatMs = options.heartbeatMs ?? SESSION_WATCH_HEARTBEAT_MS;
|
|
107
|
+
let offset = 0;
|
|
108
|
+
try {
|
|
109
|
+
offset = fs.statSync(journal).size;
|
|
110
|
+
}
|
|
111
|
+
catch { /* first publication */ }
|
|
112
|
+
const initial = readCache('local');
|
|
113
|
+
options.emit(state.reset(options.scope, initial?.sessions ?? []));
|
|
114
|
+
options.emit(state.scope(options.scope, initial ? 'available' : 'unavailable', initial ? undefined : 'awaiting publisher'));
|
|
115
|
+
if (options.signal.aborted)
|
|
116
|
+
return;
|
|
117
|
+
await new Promise((resolve) => {
|
|
118
|
+
let partial = '';
|
|
119
|
+
let reading = false;
|
|
120
|
+
const readAppended = () => {
|
|
121
|
+
if (reading || options.signal.aborted)
|
|
122
|
+
return;
|
|
123
|
+
reading = true;
|
|
124
|
+
try {
|
|
125
|
+
const size = fs.statSync(journal).size;
|
|
126
|
+
if (size < offset) {
|
|
127
|
+
offset = 0;
|
|
128
|
+
partial = '';
|
|
129
|
+
}
|
|
130
|
+
if (size === offset)
|
|
131
|
+
return;
|
|
132
|
+
const fd = fs.openSync(journal, 'r');
|
|
133
|
+
try {
|
|
134
|
+
const buffer = Buffer.alloc(size - offset);
|
|
135
|
+
fs.readSync(fd, buffer, 0, buffer.length, offset);
|
|
136
|
+
offset = size;
|
|
137
|
+
const lines = (partial + buffer.toString('utf8')).split('\n');
|
|
138
|
+
partial = lines.pop() ?? '';
|
|
139
|
+
for (const line of lines) {
|
|
140
|
+
if (!line)
|
|
141
|
+
continue;
|
|
142
|
+
try {
|
|
143
|
+
const record = JSON.parse(line);
|
|
144
|
+
if (record.version !== 1 || record.scope !== 'local' || !Array.isArray(record.upserts) || !Array.isArray(record.removes))
|
|
145
|
+
continue;
|
|
146
|
+
for (const event of state.patch(options.scope, record.upserts, record.removes))
|
|
147
|
+
options.emit(event);
|
|
148
|
+
options.emit(state.scope(options.scope, 'available'));
|
|
149
|
+
}
|
|
150
|
+
catch { /* partial/corrupt journal lines are not state */ }
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
finally {
|
|
154
|
+
fs.closeSync(fd);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
catch { /* journal may not exist until the first publisher write */ }
|
|
158
|
+
finally {
|
|
159
|
+
reading = false;
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
fs.mkdirSync(path.dirname(journal), { recursive: true });
|
|
163
|
+
const journalListener = () => readAppended();
|
|
164
|
+
fs.watchFile(journal, { interval: options.journalPollMs ?? 250 }, journalListener);
|
|
165
|
+
const heartbeatTimer = setInterval(() => options.emit(state.heartbeat(options.scope)), heartbeatMs);
|
|
166
|
+
const stop = () => { fs.unwatchFile(journal, journalListener); clearInterval(heartbeatTimer); resolve(); };
|
|
167
|
+
options.signal.addEventListener('abort', stop, { once: true });
|
|
168
|
+
// Close the offset/read/watch handoff: a publisher may append after the
|
|
169
|
+
// startup offset is captured but before watchFile is registered.
|
|
170
|
+
readAppended();
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
function remoteWatchCommand(os) {
|
|
174
|
+
const args = ['sessions', 'watch', '--json', '--local'];
|
|
175
|
+
return remoteShellFor(os) === 'powershell'
|
|
176
|
+
? buildWindowsAgentsCommand({ args })
|
|
177
|
+
: `bash -lc ${shellQuote(`agents ${args.map(shellQuote).join(' ')}`)}`;
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Subscribe to every dialable compute device with one persistent SSH process.
|
|
181
|
+
* A peer disconnect emits `scope: unavailable` but no removes; reconnecting
|
|
182
|
+
* peer resets only its own scope. There is no recurring fleet list command.
|
|
183
|
+
*/
|
|
184
|
+
export async function watchFleetSessions(options) {
|
|
185
|
+
const local = watchLocalSessions({ scope: machineId(), signal: options.signal, emit: options.emit });
|
|
186
|
+
let devices;
|
|
187
|
+
try {
|
|
188
|
+
devices = await loadDevices();
|
|
189
|
+
}
|
|
190
|
+
catch {
|
|
191
|
+
await local;
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
const self = machineId();
|
|
195
|
+
const peers = Object.values(devices).filter((device) => isDialableDevice(device)
|
|
196
|
+
&& !isControlDevice(device)
|
|
197
|
+
&& normalizeHost(device.name) !== self
|
|
198
|
+
&& ['windows', 'linux', 'macos'].includes(device.platform));
|
|
199
|
+
const reconnectMs = options.reconnectMs ?? 2_000;
|
|
200
|
+
const peerTasks = peers.map(async (device) => {
|
|
201
|
+
const scope = normalizeHost(device.name);
|
|
202
|
+
const state = new SessionWatchState();
|
|
203
|
+
while (!options.signal.aborted) {
|
|
204
|
+
let target;
|
|
205
|
+
try {
|
|
206
|
+
target = sshTargetFor(device);
|
|
207
|
+
}
|
|
208
|
+
catch (error) {
|
|
209
|
+
options.emit(state.scope(scope, 'unavailable', error instanceof Error ? error.message : String(error)));
|
|
210
|
+
break;
|
|
211
|
+
}
|
|
212
|
+
const child = spawn('ssh', [
|
|
213
|
+
...SSH_OPTS, ...controlOpts(), ...deviceIdentityArgs(device), target, remoteWatchCommand(device.platform),
|
|
214
|
+
], { stdio: ['ignore', 'pipe', 'ignore'] });
|
|
215
|
+
const stop = () => child.kill('SIGTERM');
|
|
216
|
+
options.signal.addEventListener('abort', stop, { once: true });
|
|
217
|
+
const reader = createInterface({ input: child.stdout });
|
|
218
|
+
reader.on('line', (line) => {
|
|
219
|
+
try {
|
|
220
|
+
const event = JSON.parse(line);
|
|
221
|
+
if (event.version === SESSION_WATCH_VERSION && typeof event.sequence === 'number')
|
|
222
|
+
options.emit(event);
|
|
223
|
+
}
|
|
224
|
+
catch { /* incomplete/non-protocol peer output is not state */ }
|
|
225
|
+
});
|
|
226
|
+
const code = await new Promise((resolve) => {
|
|
227
|
+
child.once('error', () => resolve(null));
|
|
228
|
+
child.once('close', resolve);
|
|
229
|
+
});
|
|
230
|
+
reader.close();
|
|
231
|
+
options.signal.removeEventListener('abort', stop);
|
|
232
|
+
if (options.signal.aborted)
|
|
233
|
+
break;
|
|
234
|
+
options.emit(state.scope(scope, 'unavailable', code === null ? 'ssh failed' : `ssh exited ${code}`));
|
|
235
|
+
await new Promise((resolve) => {
|
|
236
|
+
const timer = setTimeout(resolve, reconnectMs);
|
|
237
|
+
options.signal.addEventListener('abort', () => { clearTimeout(timer); resolve(); }, { once: true });
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
});
|
|
241
|
+
await Promise.all([local, ...peerTasks]);
|
|
242
|
+
}
|
|
@@ -8,6 +8,10 @@ export interface ShareConfig {
|
|
|
8
8
|
domain?: string;
|
|
9
9
|
/** Cloudflare Web Analytics token injected into published HTML pages. */
|
|
10
10
|
analyticsToken?: string;
|
|
11
|
+
/** sha256 of the Worker script deployed at the last provision/update. Absent
|
|
12
|
+
* on any config written before this field existed — treat that as "unknown",
|
|
13
|
+
* never as "outdated" (there is nothing to compare against). */
|
|
14
|
+
templateHash?: string;
|
|
11
15
|
}
|
|
12
16
|
export declare const SHARE_BUNDLE = "share";
|
|
13
17
|
export declare const SHARE_TOKEN_KEY = "WRITE_TOKEN";
|
package/dist/lib/share/config.js
CHANGED
|
@@ -51,6 +51,44 @@ export declare function mergeShareLifecycleRule(existing?: R2LifecycleRule[], ru
|
|
|
51
51
|
export declare function configureBucketLifecycle(apiToken: string, accountId: string, bucketName: string, opts?: ProvisionOptions): Promise<void>;
|
|
52
52
|
/** Upload the module Worker with an R2 binding (`BUCKET`). Secrets are set via the Workers Secrets API. */
|
|
53
53
|
export declare function deployWorker(apiToken: string, accountId: string, workerName: string, script: string, bucketName: string, opts?: ProvisionOptions): Promise<void>;
|
|
54
|
+
/** sha256 of the rendered Worker script, so a deployed endpoint can be compared
|
|
55
|
+
* against the current `worker-template.ts` without redeploying to find out. */
|
|
56
|
+
export declare function hashWorkerScript(script: string): string;
|
|
57
|
+
export interface UpdateWorkerResult {
|
|
58
|
+
/** sha256 of `script` (the hash the endpoint now matches, whether or not this
|
|
59
|
+
* call actually redeployed). */
|
|
60
|
+
templateHash: string;
|
|
61
|
+
/** True when the upload was skipped because `previousHash` already matched. */
|
|
62
|
+
skipped: boolean;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Re-deploy the Worker script against an ALREADY-provisioned endpoint (same
|
|
66
|
+
* account/worker/bucket `deployWorker` was first called with) and idempotently
|
|
67
|
+
* no-op when the template hasn't changed.
|
|
68
|
+
*
|
|
69
|
+
* Cloudflare's script-upload endpoint (`deployWorker`, above) replaces the
|
|
70
|
+
* Worker's bindings/secrets wholesale on every call — there is no documented,
|
|
71
|
+
* reliable way to tell it "keep the existing secret" (the `keep_bindings`
|
|
72
|
+
* metadata field some third-party guides mention is absent from Cloudflare's
|
|
73
|
+
* current Multipart upload metadata reference, and the community has reported
|
|
74
|
+
* it not preventing binding loss even when present:
|
|
75
|
+
* https://developers.cloudflare.com/workers/configuration/multipart-upload-metadata/,
|
|
76
|
+
* https://community.cloudflare.com/t/upload-worker-module-endpoint-removes-existing-bindings-despite-keep-bindings/766447).
|
|
77
|
+
* So instead this immediately re-applies WRITE_TOKEN via the documented Secrets
|
|
78
|
+
* API right after the upload — the same two-call sequence first-time
|
|
79
|
+
* provisioning already uses (`deployWorker` then `setWorkerSecret`), except
|
|
80
|
+
* `writeToken` here is the caller's EXISTING token, never a freshly generated
|
|
81
|
+
* one. Cloudflare's own docs describe exactly this "secrets survive a
|
|
82
|
+
* subsequent write" contract for the sibling `wrangler versions upload
|
|
83
|
+
* --secrets-file` flow ("Secrets not included in the file are preserved from
|
|
84
|
+
* the previous version" —
|
|
85
|
+
* https://developers.cloudflare.com/workers/configuration/secrets/#upload-secrets-alongside-code);
|
|
86
|
+
* re-setting the secret to its own value after every deploy gets the same
|
|
87
|
+
* outcome without depending on an unverified upload-time flag.
|
|
88
|
+
*/
|
|
89
|
+
export declare function updateWorker(apiToken: string, accountId: string, workerName: string, bucketName: string, script: string, writeToken: string, previousHash: string | undefined, opts?: ProvisionOptions & {
|
|
90
|
+
force?: boolean;
|
|
91
|
+
}): Promise<UpdateWorkerResult>;
|
|
54
92
|
/** Add/update the WRITE_TOKEN binding using Cloudflare's Workers Secrets API. */
|
|
55
93
|
export declare function setWorkerSecret(apiToken: string, accountId: string, workerName: string, writeToken: string, opts?: ProvisionOptions): Promise<void>;
|
|
56
94
|
/** Enable the free `*.workers.dev` route for the script, and return the account subdomain. */
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
// lifecycle, uploads the Worker (with an R2 binding), sets the WRITE_TOKEN secret,
|
|
4
4
|
// enables the free `*.workers.dev` subdomain, and — when the token owns the zone —
|
|
5
5
|
// maps a custom domain.
|
|
6
|
+
import { createHash } from 'node:crypto';
|
|
6
7
|
const CF_API = 'https://api.cloudflare.com/client/v4';
|
|
7
8
|
export const SHARE_LIFECYCLE_RULE_ID = 'agents-share-expire-objects';
|
|
8
9
|
export const SHARE_LIFECYCLE_RETENTION_DAYS = 366;
|
|
@@ -95,6 +96,55 @@ export async function deployWorker(apiToken, accountId, workerName, script, buck
|
|
|
95
96
|
form,
|
|
96
97
|
});
|
|
97
98
|
}
|
|
99
|
+
/** sha256 of the rendered Worker script, so a deployed endpoint can be compared
|
|
100
|
+
* against the current `worker-template.ts` without redeploying to find out. */
|
|
101
|
+
export function hashWorkerScript(script) {
|
|
102
|
+
return createHash('sha256').update(script, 'utf8').digest('hex');
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Re-deploy the Worker script against an ALREADY-provisioned endpoint (same
|
|
106
|
+
* account/worker/bucket `deployWorker` was first called with) and idempotently
|
|
107
|
+
* no-op when the template hasn't changed.
|
|
108
|
+
*
|
|
109
|
+
* Cloudflare's script-upload endpoint (`deployWorker`, above) replaces the
|
|
110
|
+
* Worker's bindings/secrets wholesale on every call — there is no documented,
|
|
111
|
+
* reliable way to tell it "keep the existing secret" (the `keep_bindings`
|
|
112
|
+
* metadata field some third-party guides mention is absent from Cloudflare's
|
|
113
|
+
* current Multipart upload metadata reference, and the community has reported
|
|
114
|
+
* it not preventing binding loss even when present:
|
|
115
|
+
* https://developers.cloudflare.com/workers/configuration/multipart-upload-metadata/,
|
|
116
|
+
* https://community.cloudflare.com/t/upload-worker-module-endpoint-removes-existing-bindings-despite-keep-bindings/766447).
|
|
117
|
+
* So instead this immediately re-applies WRITE_TOKEN via the documented Secrets
|
|
118
|
+
* API right after the upload — the same two-call sequence first-time
|
|
119
|
+
* provisioning already uses (`deployWorker` then `setWorkerSecret`), except
|
|
120
|
+
* `writeToken` here is the caller's EXISTING token, never a freshly generated
|
|
121
|
+
* one. Cloudflare's own docs describe exactly this "secrets survive a
|
|
122
|
+
* subsequent write" contract for the sibling `wrangler versions upload
|
|
123
|
+
* --secrets-file` flow ("Secrets not included in the file are preserved from
|
|
124
|
+
* the previous version" —
|
|
125
|
+
* https://developers.cloudflare.com/workers/configuration/secrets/#upload-secrets-alongside-code);
|
|
126
|
+
* re-setting the secret to its own value after every deploy gets the same
|
|
127
|
+
* outcome without depending on an unverified upload-time flag.
|
|
128
|
+
*/
|
|
129
|
+
export async function updateWorker(apiToken, accountId, workerName, bucketName, script, writeToken, previousHash, opts = {}) {
|
|
130
|
+
const templateHash = hashWorkerScript(script);
|
|
131
|
+
if (!opts.force && previousHash === templateHash) {
|
|
132
|
+
return { templateHash, skipped: true };
|
|
133
|
+
}
|
|
134
|
+
await deployWorker(apiToken, accountId, workerName, script, bucketName, opts);
|
|
135
|
+
// Script upload clears bindings/secrets (see JSDoc above). If re-applying
|
|
136
|
+
// WRITE_TOKEN fails here, the live Worker has no write token — every
|
|
137
|
+
// `agents share` publish/delete 401s until a re-run of `agents share update`
|
|
138
|
+
// completes both steps. Surface that explicitly instead of the raw CF error.
|
|
139
|
+
try {
|
|
140
|
+
await setWorkerSecret(apiToken, accountId, workerName, writeToken, opts);
|
|
141
|
+
}
|
|
142
|
+
catch (e) {
|
|
143
|
+
const detail = e instanceof Error ? e.message : String(e);
|
|
144
|
+
throw new Error(`Worker deployed but the write token failed to re-apply — re-run \`agents share update\` to fix this before publishing/deleting anything. (${detail})`);
|
|
145
|
+
}
|
|
146
|
+
return { templateHash, skipped: false };
|
|
147
|
+
}
|
|
98
148
|
/** Add/update the WRITE_TOKEN binding using Cloudflare's Workers Secrets API. */
|
|
99
149
|
export async function setWorkerSecret(apiToken, accountId, workerName, writeToken, opts = {}) {
|
|
100
150
|
const request = opts.request ?? defaultCloudflareRequester;
|
|
@@ -10,9 +10,25 @@ export interface PublishEndpoint {
|
|
|
10
10
|
}
|
|
11
11
|
export interface PublishOptions {
|
|
12
12
|
slug?: string;
|
|
13
|
-
/**
|
|
13
|
+
/**
|
|
14
|
+
* Auto-expire window. Relative (`30d`, `12h`), absolute (`2026-08-01`), or
|
|
15
|
+
* `never` / `none` / `permanent` for no expiry. When omitted, publishes default
|
|
16
|
+
* to {@link DEFAULT_SHARE_EXPIRE} so an accidental share decays instead of
|
|
17
|
+
* living forever (RUSH-2443).
|
|
18
|
+
*/
|
|
14
19
|
expire?: string;
|
|
15
20
|
contentType?: string;
|
|
21
|
+
/**
|
|
22
|
+
* Hide this page from the public `/<user>` gallery and `agents share list`
|
|
23
|
+
* (metadata `visibility=unlisted`). The direct URL is still world-readable —
|
|
24
|
+
* unlisted, not secret (RUSH-2443). Alias of `--private` on the CLI.
|
|
25
|
+
*/
|
|
26
|
+
unlisted?: boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Bypass the pre-publish sensitive-content scan (emails / credential-shaped
|
|
29
|
+
* strings). Required when the page intentionally carries those patterns.
|
|
30
|
+
*/
|
|
31
|
+
force?: boolean;
|
|
16
32
|
/** Generate + attach an OG cover for HTML pages (default true). */
|
|
17
33
|
cover?: boolean;
|
|
18
34
|
/** Inject the Cloudflare Web Analytics beacon (default true for HTML). */
|
|
@@ -34,9 +50,32 @@ export interface PublishResult {
|
|
|
34
50
|
url: string;
|
|
35
51
|
expiresAt?: string;
|
|
36
52
|
coverUrl?: string;
|
|
53
|
+
/** True when the page was published with `visibility=unlisted`. */
|
|
54
|
+
unlisted?: boolean;
|
|
37
55
|
}
|
|
56
|
+
/** Default auto-expire for unflagged publishes — accidental links decay (RUSH-2443). */
|
|
57
|
+
export declare const DEFAULT_SHARE_EXPIRE = "30d";
|
|
38
58
|
/** `30d` / `12h` / `2026-08-01` → an absolute ISO timestamp (or undefined). */
|
|
39
59
|
export declare function parseExpire(spec: string | undefined): string | undefined;
|
|
60
|
+
/**
|
|
61
|
+
* Resolve the publish expiry. Omitted → {@link DEFAULT_SHARE_EXPIRE}. Explicit
|
|
62
|
+
* `never` / `none` / `permanent` → no expiry. Anything else → {@link parseExpire}.
|
|
63
|
+
*/
|
|
64
|
+
export declare function resolveExpire(spec: string | undefined): string | undefined;
|
|
65
|
+
export type SensitiveHitKind = 'email' | 'credential';
|
|
66
|
+
export interface SensitiveHit {
|
|
67
|
+
kind: SensitiveHitKind;
|
|
68
|
+
/** Short redacted sample so the error names *what* was found without dumping it. */
|
|
69
|
+
sample: string;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Scan a text body for email addresses and credential-shaped strings. Returns
|
|
73
|
+
* the first few hits (deduped by kind+sample). Binary / non-text bodies yield
|
|
74
|
+
* nothing — the gate is for HTML/text reports, not screenshots.
|
|
75
|
+
*/
|
|
76
|
+
export declare function scanShareContent(body: string | Buffer): SensitiveHit[];
|
|
77
|
+
/** Build the refuse message when the pre-publish scan finds sensitive content. */
|
|
78
|
+
export declare function formatSensitiveContentError(hits: SensitiveHit[]): string;
|
|
40
79
|
/** Derive a URL-safe slug from a filename (or pass one through). */
|
|
41
80
|
export declare function slugify(name: string): string;
|
|
42
81
|
/** The project the file belongs to — git repo name, else the cwd's basename. */
|