@phnx-labs/agents-cli 1.22.38 → 1.22.40
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 +302 -0
- package/README.md +111 -70
- package/dist/bin/agents +0 -0
- package/dist/bootstrap.js +46 -27
- package/dist/browser.js +2 -2
- package/dist/cli/command-registry.d.ts +135 -0
- package/dist/cli/command-registry.js +260 -0
- package/dist/commands/accounts.d.ts +20 -0
- package/dist/commands/accounts.js +186 -25
- package/dist/commands/apply.d.ts +3 -3
- package/dist/commands/apply.js +20 -63
- package/dist/commands/browser.js +206 -94
- package/dist/commands/budget.d.ts +7 -6
- package/dist/commands/budget.js +8 -7
- package/dist/commands/cloud.js +17 -20
- package/dist/commands/commands.js +1 -1
- package/dist/commands/computer-actions.d.ts +1 -35
- package/dist/commands/computer-actions.js +13 -181
- package/dist/commands/computer.d.ts +28 -3
- package/dist/commands/computer.js +93 -65
- package/dist/commands/config.js +47 -4
- package/dist/commands/cost.d.ts +2 -1
- package/dist/commands/cost.js +7 -6
- package/dist/commands/daemon.js +151 -4
- package/dist/commands/detach.js +1 -1
- package/dist/commands/doctor.js +19 -11
- package/dist/commands/events.js +1 -1
- package/dist/commands/exec.d.ts +2 -50
- package/dist/commands/exec.js +161 -216
- package/dist/commands/factory.js +1 -1
- package/dist/commands/feed.d.ts +2 -2
- package/dist/commands/feed.js +9 -12
- package/dist/commands/fleet-capture.js +25 -27
- package/dist/commands/focus.d.ts +2 -3
- package/dist/commands/focus.js +34 -9
- package/dist/commands/go.js +15 -5
- package/dist/commands/harness.d.ts +3 -3
- package/dist/commands/harness.js +53 -5
- package/dist/commands/hooks.js +1 -1
- package/dist/commands/import.js +2 -2
- package/dist/commands/insights.d.ts +5 -5
- package/dist/commands/insights.js +15 -9
- package/dist/commands/inspect.d.ts +2 -3
- package/dist/commands/inspect.js +4 -16
- package/dist/commands/logs.d.ts +2 -2
- package/dist/commands/logs.js +9 -9
- package/dist/commands/mcp.js +2 -2
- package/dist/commands/memory.js +1 -1
- package/dist/commands/message.d.ts +1 -1
- package/dist/commands/message.js +7 -7
- package/dist/commands/mine.d.ts +11 -8
- package/dist/commands/mine.js +11 -10
- package/dist/commands/models.js +1 -1
- package/dist/commands/modes.js +1 -1
- package/dist/commands/monitors.js +23 -6
- package/dist/commands/output.d.ts +7 -6
- package/dist/commands/output.js +11 -10
- package/dist/commands/packages.js +4 -4
- package/dist/commands/perf.js +1 -1
- package/dist/commands/permissions.js +49 -32
- package/dist/commands/plugins.d.ts +1 -1
- package/dist/commands/plugins.js +3 -3
- package/dist/commands/profiles.d.ts +6 -9
- package/dist/commands/profiles.js +8 -321
- package/dist/commands/projects.d.ts +9 -0
- package/dist/commands/projects.js +140 -8
- package/dist/commands/prune.js +5 -13
- package/dist/commands/refresh-rules.js +1 -1
- package/dist/commands/repo.d.ts +5 -7
- package/dist/commands/repo.js +34 -18
- package/dist/commands/resource-view.js +4 -0
- package/dist/commands/resume.d.ts +11 -7
- package/dist/commands/resume.js +82 -98
- package/dist/commands/route.d.ts +12 -0
- package/dist/commands/route.js +261 -0
- package/dist/commands/routines.d.ts +2 -3
- package/dist/commands/routines.js +120 -145
- package/dist/commands/rules.js +1 -1
- package/dist/commands/run-account-picker.d.ts +1 -1
- package/dist/commands/run-account-picker.js +2 -2
- package/dist/commands/run-broadcast.d.ts +21 -0
- package/dist/commands/run-broadcast.js +89 -0
- package/dist/commands/run-cloud.js +1 -1
- package/dist/commands/secrets-vault.d.ts +10 -0
- package/dist/commands/{login.js → secrets-vault.js} +34 -14
- package/dist/commands/secrets.d.ts +4 -4
- package/dist/commands/secrets.js +72 -63
- package/dist/commands/sessions-backfill.js +1 -1
- package/dist/commands/sessions-browser.d.ts +1 -1
- package/dist/commands/sessions-browser.js +9 -5
- package/dist/commands/sessions-export.js +2 -2
- package/dist/commands/sessions-inject.js +4 -5
- package/dist/commands/sessions-migrate.js +16 -27
- package/dist/commands/sessions-picker.d.ts +13 -0
- package/dist/commands/sessions-picker.js +182 -8
- package/dist/commands/sessions-resume.d.ts +7 -3
- package/dist/commands/sessions-resume.js +81 -33
- package/dist/commands/sessions-stats.js +2 -2
- package/dist/commands/sessions.d.ts +75 -68
- package/dist/commands/sessions.js +142 -180
- package/dist/commands/setup-computer.js +1 -1
- package/dist/commands/setup-fleet.js +3 -0
- package/dist/commands/setup-mine.d.ts +7 -3
- package/dist/commands/setup-mine.js +13 -8
- package/dist/commands/setup-secrets.js +2 -2
- package/dist/commands/setup-watchdog.js +2 -2
- package/dist/commands/setup.js +7 -2
- package/dist/commands/share.d.ts +65 -0
- package/dist/commands/share.js +253 -20
- package/dist/commands/skills.js +3 -3
- package/dist/commands/snapshot.d.ts +2 -2
- package/dist/commands/snapshot.js +9 -9
- package/dist/commands/ssh.d.ts +1 -1
- package/dist/commands/ssh.js +318 -48
- package/dist/commands/subagents.js +1 -1
- package/dist/commands/sync.js +24 -9
- package/dist/commands/teams.d.ts +2 -2
- package/dist/commands/teams.js +9 -18
- package/dist/commands/tmux.js +5 -1
- package/dist/commands/trash.js +1 -1
- package/dist/commands/usage.d.ts +1 -1
- package/dist/commands/usage.js +2 -2
- package/dist/commands/utils.d.ts +4 -14
- package/dist/commands/utils.js +6 -27
- package/dist/commands/versions.js +2 -2
- package/dist/commands/view.d.ts +9 -77
- package/dist/commands/view.js +23 -8
- package/dist/commands/watchdog.js +1 -1
- package/dist/commands/webhook.js +11 -25
- package/dist/commands/workflows.js +1 -1
- package/dist/index.js +1 -1
- package/dist/lib/account-capabilities.d.ts +39 -0
- package/dist/lib/account-capabilities.js +59 -0
- package/dist/lib/account-catalog.js +1 -1
- package/dist/lib/account-registry.d.ts +66 -0
- package/dist/lib/account-registry.js +151 -5
- package/dist/lib/{rotate.d.ts → accounting/rotate.d.ts} +4 -4
- package/dist/lib/{rotate.js → accounting/rotate.js} +8 -8
- package/dist/lib/{usage.d.ts → accounting/usage.d.ts} +4 -4
- package/dist/lib/{usage.js → accounting/usage.js} +11 -11
- package/dist/lib/agent-spec/provider.js +1 -1
- package/dist/lib/agents.d.ts +72 -4
- package/dist/lib/agents.js +231 -199
- package/dist/lib/answer-router.d.ts +2 -2
- package/dist/lib/ask-classifier.d.ts +1 -1
- package/dist/lib/ask-classifier.js +1 -1
- package/dist/lib/audit/log.js +1 -1
- package/dist/lib/auth-health.d.ts +1 -1
- package/dist/lib/auth-health.js +2 -2
- package/dist/lib/brand.js +1 -1
- package/dist/lib/browser/caller-identity.d.ts +28 -0
- package/dist/lib/browser/caller-identity.js +156 -0
- package/dist/lib/browser/cdp.js +1 -1
- package/dist/lib/browser/drivers/ssh.d.ts +1 -1
- package/dist/lib/browser/drivers/ssh.js +2 -2
- package/dist/lib/browser/hygiene.d.ts +10 -2
- package/dist/lib/browser/hygiene.js +7 -4
- package/dist/lib/browser/ipc.d.ts +22 -2
- package/dist/lib/browser/ipc.js +185 -21
- package/dist/lib/browser/profiles.d.ts +67 -3
- package/dist/lib/browser/profiles.js +129 -7
- package/dist/lib/browser/remote-control.d.ts +2 -2
- package/dist/lib/browser/remote-control.js +3 -3
- package/dist/lib/browser/runtime-state.d.ts +79 -0
- package/dist/lib/browser/runtime-state.js +119 -1
- package/dist/lib/browser/service.d.ts +113 -1
- package/dist/lib/browser/service.js +454 -40
- package/dist/lib/browser/sessions-list.d.ts +2 -1
- package/dist/lib/browser/sessions-list.js +4 -13
- package/dist/lib/browser/types.d.ts +18 -0
- package/dist/lib/budget/config.js +1 -1
- package/dist/lib/budget/ledger.js +1 -1
- package/dist/lib/byok-usage.js +1 -1
- package/dist/lib/capabilities.js +3 -15
- package/dist/lib/catchup.d.ts +1 -1
- package/dist/lib/catchup.js +1 -1
- package/dist/lib/cloud/dispatch.js +1 -1
- package/dist/lib/cloud/host.d.ts +3 -3
- package/dist/lib/cloud/host.js +5 -5
- package/dist/lib/cloud/rush.d.ts +9 -21
- package/dist/lib/cloud/rush.js +33 -120
- package/dist/lib/cloud/types.d.ts +2 -2
- package/dist/lib/commands.js +3 -13
- package/dist/lib/computer/actions.d.ts +36 -0
- package/dist/lib/computer/actions.js +162 -0
- package/dist/lib/computer/dispatch.d.ts +2 -2
- package/dist/lib/computer/dispatch.js +2 -2
- package/dist/lib/computer/sessions-list.d.ts +3 -3
- package/dist/lib/computer/sessions-list.js +2 -2
- package/dist/lib/config-keys.d.ts +12 -3
- package/dist/lib/config-keys.js +29 -6
- package/dist/lib/config-machine-keys.d.ts +4 -2
- package/dist/lib/config-machine-keys.js +6 -2
- package/dist/lib/crabbox/lease.js +9 -1
- package/dist/lib/crabbox/runtimes.d.ts +32 -7
- package/dist/lib/crabbox/runtimes.js +72 -30
- package/dist/lib/{daemon.d.ts → daemon/daemon.d.ts} +59 -1
- package/dist/lib/{daemon.js → daemon/daemon.js} +237 -49
- package/dist/lib/daemon-services.d.ts +1 -1
- package/dist/lib/daemon-services.js +5 -0
- package/dist/lib/daemon-ticks.d.ts +25 -0
- package/dist/lib/daemon-ticks.js +31 -1
- package/dist/lib/daemon-webhooks.d.ts +102 -0
- package/dist/lib/daemon-webhooks.js +210 -0
- package/dist/lib/device-config.d.ts +116 -72
- package/dist/lib/device-config.js +366 -132
- package/dist/lib/devices/config-migration.d.ts +29 -34
- package/dist/lib/devices/config-migration.js +209 -130
- package/dist/lib/devices/connect.d.ts +2 -2
- package/dist/lib/devices/connect.js +2 -2
- package/dist/lib/devices/discovery-policy.d.ts +30 -0
- package/dist/lib/devices/discovery-policy.js +101 -0
- package/dist/lib/devices/fleet-inventory.js +8 -3
- package/dist/lib/devices/harness-inventory.d.ts +1 -1
- package/dist/lib/devices/harness-inventory.js +12 -3
- package/dist/lib/devices/known-hosts.js +1 -1
- package/dist/lib/devices/pool.d.ts +61 -0
- package/dist/lib/devices/pool.js +83 -0
- package/dist/lib/devices/registry.d.ts +1 -1
- package/dist/lib/devices/registry.js +1 -1
- package/dist/lib/devices/resolve-profile.d.ts +2 -2
- package/dist/lib/devices/resolve-profile.js +2 -2
- package/dist/lib/devices/resolve-target.d.ts +1 -1
- package/dist/lib/devices/resolve-target.js +4 -4
- package/dist/lib/devices/self-host.d.ts +1 -1
- package/dist/lib/devices/self-host.js +3 -3
- package/dist/lib/devices/ssh-config.d.ts +1 -1
- package/dist/lib/devices/stats-cache.js +1 -1
- package/dist/lib/devices/sync.d.ts +3 -1
- package/dist/lib/devices/sync.js +3 -3
- package/dist/lib/doctor-diff.js +4 -4
- package/dist/lib/drift-sync.js +2 -2
- package/dist/lib/drift.js +2 -2
- package/dist/lib/event-families.d.ts +1 -1
- package/dist/lib/event-stream.d.ts +1 -1
- package/dist/lib/event-stream.js +2 -2
- package/dist/lib/events-ingest.js +2 -2
- package/dist/lib/exec.d.ts +8 -5
- package/dist/lib/exec.js +19 -13
- package/dist/lib/factory/snapshot.d.ts +1 -1
- package/dist/lib/factory/snapshot.js +1 -1
- package/dist/lib/{activity.d.ts → feed/activity.d.ts} +1 -1
- package/dist/lib/{activity.js → feed/activity.js} +7 -7
- package/dist/lib/{events.d.ts → feed/events.d.ts} +2 -2
- package/dist/lib/{events.js → feed/events.js} +5 -5
- package/dist/lib/{feed.js → feed/feed.js} +4 -4
- package/dist/lib/feed-outcome.d.ts +1 -1
- package/dist/lib/feed-policy.d.ts +1 -1
- package/dist/lib/feed-policy.js +1 -1
- package/dist/lib/feed-post.d.ts +1 -1
- package/dist/lib/feed-post.js +1 -1
- package/dist/lib/feed-ranking.d.ts +1 -1
- package/dist/lib/fleet/apply.d.ts +0 -6
- package/dist/lib/fleet/apply.js +18 -45
- package/dist/lib/fleet/auth-sync.d.ts +16 -35
- package/dist/lib/fleet/auth-sync.js +16 -75
- package/dist/lib/fleet/capture.js +8 -6
- package/dist/lib/fleet/manifest.js +23 -11
- package/dist/lib/fleet/types.d.ts +19 -7
- package/dist/lib/fleet-cache.d.ts +1 -1
- package/dist/lib/fleet-cache.js +1 -1
- package/dist/lib/format.d.ts +19 -0
- package/dist/lib/format.js +44 -1
- package/dist/lib/friction-heuristics.d.ts +1 -1
- package/dist/lib/git.d.ts +24 -1
- package/dist/lib/git.js +82 -38
- package/dist/lib/heal.js +3 -3
- package/dist/lib/hooks.d.ts +18 -0
- package/dist/lib/hooks.js +113 -1
- package/dist/lib/hosts/credentials.d.ts +30 -10
- package/dist/lib/hosts/credentials.js +38 -26
- package/dist/lib/hosts/dispatch.d.ts +3 -3
- package/dist/lib/hosts/dispatch.js +3 -3
- package/dist/lib/hosts/option.d.ts +3 -3
- package/dist/lib/hosts/option.js +7 -8
- package/dist/lib/hosts/passthrough.d.ts +17 -17
- package/dist/lib/hosts/passthrough.js +67 -50
- package/dist/lib/hosts/providers/devices.d.ts +1 -1
- package/dist/lib/hosts/providers/devices.js +1 -1
- package/dist/lib/hosts/reconcile.d.ts +1 -1
- package/dist/lib/hosts/reconcile.js +1 -1
- package/dist/lib/hosts/reconnect.d.ts +1 -1
- package/dist/lib/hosts/reconnect.js +2 -2
- package/dist/lib/hosts/registry.d.ts +5 -5
- package/dist/lib/hosts/registry.js +5 -5
- package/dist/lib/hosts/remote-cmd.d.ts +10 -10
- package/dist/lib/hosts/remote-cmd.js +25 -18
- package/dist/lib/hosts/remote-os.d.ts +4 -4
- package/dist/lib/hosts/remote-os.js +4 -4
- package/dist/lib/hosts/remote-session-id.d.ts +2 -2
- package/dist/lib/hosts/remote-session-id.js +2 -2
- package/dist/lib/hosts/routing-flag.d.ts +3 -3
- package/dist/lib/hosts/routing-flag.js +4 -5
- package/dist/lib/hosts/run-target.d.ts +4 -4
- package/dist/lib/hosts/run-target.js +4 -4
- package/dist/lib/hosts/session-index.d.ts +2 -2
- package/dist/lib/hosts/session-index.js +1 -1
- package/dist/lib/hosts/session-marker.d.ts +1 -1
- package/dist/lib/hosts/session-marker.js +1 -1
- package/dist/lib/hosts/types.d.ts +2 -2
- package/dist/lib/hosts/types.js +2 -2
- package/dist/lib/import.js +1 -1
- package/dist/lib/{migrate.d.ts → installations/migrate.d.ts} +49 -1
- package/dist/lib/{migrate.js → installations/migrate.js} +265 -38
- package/dist/lib/installations/resolve.js +1 -1
- package/dist/lib/installations/strategies.js +1 -1
- package/dist/lib/installations/update.js +2 -2
- package/dist/lib/{versions.d.ts → installations/versions.d.ts} +12 -3
- package/dist/lib/{versions.js → installations/versions.js} +51 -31
- package/dist/lib/isolation-boundary-report.js +1 -1
- package/dist/lib/linear-projects.d.ts +28 -0
- package/dist/lib/linear-projects.js +30 -0
- package/dist/lib/loop.js +1 -1
- package/dist/lib/mailbox-gc.js +1 -1
- package/dist/lib/mailbox.js +1 -1
- package/dist/lib/mcp-registry.d.ts +62 -0
- package/dist/lib/mcp-registry.js +177 -0
- package/dist/lib/mcp.d.ts +5 -0
- package/dist/lib/mcp.js +124 -307
- package/dist/lib/menubar/MenubarHelper.app/Contents/CodeResources +0 -0
- package/dist/lib/menubar/MenubarHelper.app/Contents/MacOS/MenubarHelper +0 -0
- package/dist/lib/menubar/install-menubar.d.ts +7 -0
- package/dist/lib/menubar/install-menubar.js +24 -6
- package/dist/lib/menubar/snapshot.d.ts +7 -0
- package/dist/lib/menubar/snapshot.js +8 -6
- package/dist/lib/models.js +1 -1
- package/dist/lib/monitors/config.d.ts +12 -0
- package/dist/lib/monitors/config.js +3 -0
- package/dist/lib/monitors/dispatch.js +26 -5
- package/dist/lib/monitors/engine.js +10 -0
- package/dist/lib/monitors/sources/webhook.d.ts +1 -1
- package/dist/lib/monitors/state.d.ts +42 -0
- package/dist/lib/monitors/state.js +30 -0
- package/dist/lib/notify.d.ts +1 -1
- package/dist/lib/observe-aliases.d.ts +2 -2
- package/dist/lib/observe-aliases.js +2 -11
- package/dist/lib/output/git-output.js +2 -2
- package/dist/lib/overdue.d.ts +1 -1
- package/dist/lib/overdue.js +1 -1
- package/dist/lib/permissions-registry.d.ts +65 -0
- package/dist/lib/permissions-registry.js +599 -0
- package/dist/lib/permissions.d.ts +24 -13
- package/dist/lib/permissions.js +52 -271
- package/dist/lib/picker.d.ts +16 -0
- package/dist/lib/picker.js +33 -3
- package/dist/lib/placement.d.ts +1 -1
- package/dist/lib/placement.js +4 -4
- package/dist/lib/platform/paths.d.ts +8 -0
- package/dist/lib/platform/paths.js +11 -0
- package/dist/lib/platform/posixpath.js +3 -3
- package/dist/lib/{plugin-marketplace.d.ts → plugins/plugin-marketplace.d.ts} +1 -1
- package/dist/lib/{plugin-marketplace.js → plugins/plugin-marketplace.js} +2 -2
- package/dist/lib/{plugins.d.ts → plugins/plugins.d.ts} +1 -1
- package/dist/lib/{plugins.js → plugins/plugins.js} +7 -7
- package/dist/lib/{skills.d.ts → plugins/skills.d.ts} +1 -1
- package/dist/lib/{skills.js → plugins/skills.js} +7 -7
- package/dist/lib/pricing/cost.d.ts +1 -1
- package/dist/lib/pricing/cost.js +1 -1
- package/dist/lib/profiles-presets.d.ts +1 -1
- package/dist/lib/profiles-presets.js +1 -1
- package/dist/lib/profiles.js +2 -2
- package/dist/lib/project-import.js +4 -1
- package/dist/lib/project-launch.d.ts +1 -1
- package/dist/lib/project-launch.js +3 -3
- package/dist/lib/project-pull.d.ts +128 -0
- package/dist/lib/project-pull.js +384 -0
- package/dist/lib/project-root.d.ts +2 -2
- package/dist/lib/project-root.js +2 -2
- package/dist/lib/project-status.js +1 -1
- package/dist/lib/projects.d.ts +23 -0
- package/dist/lib/projects.js +29 -6
- package/dist/lib/refresh.d.ts +12 -1
- package/dist/lib/refresh.js +16 -9
- package/dist/lib/registry.d.ts +9 -2
- package/dist/lib/registry.js +11 -4
- package/dist/lib/resources.d.ts +2 -2
- package/dist/lib/resources.js +2 -2
- package/dist/lib/routers.d.ts +81 -0
- package/dist/lib/routers.js +153 -0
- package/dist/lib/routine-context.d.ts +1 -0
- package/dist/lib/routine-context.js +7 -2
- package/dist/lib/routine-notify-owner.d.ts +1 -1
- package/dist/lib/routine-notify.d.ts +1 -1
- package/dist/lib/routine-notify.js +1 -1
- package/dist/lib/routine-process-cleanup.d.ts +1 -1
- package/dist/lib/routine-readiness.d.ts +1 -1
- package/dist/lib/routine-readiness.js +11 -4
- package/dist/lib/routines-placement.d.ts +12 -2
- package/dist/lib/routines-placement.js +20 -3
- package/dist/lib/routines-project.d.ts +1 -1
- package/dist/lib/routines-project.js +1 -1
- package/dist/lib/rules/compile.d.ts +1 -0
- package/dist/lib/rules/compile.js +10 -2
- package/dist/lib/rules/rules.js +1 -1
- package/dist/lib/run-notify.js +1 -1
- package/dist/lib/runner.d.ts +80 -2
- package/dist/lib/runner.js +241 -62
- package/dist/lib/sandbox.d.ts +1 -1
- package/dist/lib/scheduler.d.ts +1 -1
- package/dist/lib/scheduler.js +1 -1
- package/dist/lib/{routines.d.ts → scheduling/routines.d.ts} +54 -9
- package/dist/lib/{routines.js → scheduling/routines.js} +173 -14
- package/dist/lib/secrets/Agents CLI.app/Contents/CodeResources +0 -0
- package/dist/lib/secrets/Agents CLI.app/Contents/MacOS/Agents CLI +0 -0
- package/dist/lib/secrets/agent.js +2 -2
- package/dist/lib/secrets/audit.js +1 -1
- package/dist/lib/secrets/bundles.d.ts +3 -3
- package/dist/lib/secrets/bundles.js +7 -7
- package/dist/lib/secrets/index.js +1 -1
- package/dist/lib/secrets/list-filter.d.ts +1 -1
- package/dist/lib/secrets/list-filter.js +2 -2
- package/dist/lib/secrets/push.d.ts +1 -1
- package/dist/lib/secrets/push.js +10 -12
- package/dist/lib/secrets/remote.d.ts +12 -10
- package/dist/lib/secrets/remote.js +21 -14
- package/dist/lib/secrets/vault.js +2 -2
- package/dist/lib/self-heal/checks/shadowing.js +1 -1
- package/dist/lib/self-heal/checks/shims.js +1 -1
- package/dist/lib/self-update.d.ts +39 -4
- package/dist/lib/self-update.js +78 -25
- package/dist/lib/serve/control.d.ts +1 -1
- package/dist/lib/serve/control.js +4 -4
- package/dist/lib/serve/data.d.ts +1 -1
- package/dist/lib/serve/data.js +1 -1
- package/dist/lib/serve/stream.js +1 -1
- package/dist/lib/service-manifest.d.ts +29 -0
- package/dist/lib/service-manifest.js +80 -0
- package/dist/lib/session/active.d.ts +59 -7
- package/dist/lib/session/active.js +158 -17
- package/dist/lib/session/bash-command.js +1 -1
- package/dist/lib/session/claude-accounts.d.ts +1 -1
- package/dist/lib/session/claude-accounts.js +1 -1
- package/dist/lib/session/db.d.ts +10 -2
- package/dist/lib/session/db.js +25 -6
- package/dist/lib/session/discover.d.ts +77 -0
- package/dist/lib/session/discover.js +130 -25
- package/dist/lib/session/fork.js +1 -1
- package/dist/lib/session/inject.d.ts +1 -1
- package/dist/lib/session/insights.d.ts +1 -1
- package/dist/lib/session/insights.js +1 -1
- package/dist/lib/session/live-metadata.d.ts +15 -0
- package/dist/lib/session/live-metadata.js +76 -0
- package/dist/lib/session/migrate-targets.d.ts +1 -1
- package/dist/lib/session/recovery.d.ts +1 -1
- package/dist/lib/session/recovery.js +2 -2
- package/dist/lib/session/remote-active.d.ts +10 -1
- package/dist/lib/session/remote-active.js +8 -3
- package/dist/lib/session/remote-bundle.d.ts +1 -1
- package/dist/lib/session/remote-bundle.js +2 -2
- package/dist/lib/session/remote-list.d.ts +15 -4
- package/dist/lib/session/remote-list.js +40 -3
- package/dist/lib/session/remote.d.ts +5 -5
- package/dist/lib/session/remote.js +13 -17
- package/dist/lib/session/resume-command.d.ts +3 -2
- package/dist/lib/session/resume-command.js +4 -3
- package/dist/lib/session/resume-owner.d.ts +3 -3
- package/dist/lib/session/resume-owner.js +3 -3
- package/dist/lib/session/session-cache.d.ts +12 -0
- package/dist/lib/session/session-cache.js +2 -0
- package/dist/lib/session/shell-programs.js +1 -1
- package/dist/lib/session/state.js +5 -5
- package/dist/lib/session/team-filter.d.ts +1 -1
- package/dist/lib/session/team-filter.js +1 -1
- package/dist/lib/session/types.d.ts +3 -3
- package/dist/lib/session/width.d.ts +2 -2
- package/dist/lib/session/width.js +2 -2
- package/dist/lib/share/delete.d.ts +21 -0
- package/dist/lib/share/delete.js +42 -0
- package/dist/lib/share/publish.d.ts +82 -0
- package/dist/lib/share/publish.js +170 -18
- package/dist/lib/share/worker-template.js +172 -9
- package/dist/lib/shims.js +35 -7
- package/dist/lib/smart-launch.d.ts +36 -6
- package/dist/lib/smart-launch.js +65 -8
- package/dist/lib/snapshot.d.ts +4 -4
- package/dist/lib/snapshot.js +2 -2
- package/dist/lib/ssh-exec.d.ts +1 -1
- package/dist/lib/ssh-exec.js +2 -2
- package/dist/lib/ssh-tunnel.d.ts +10 -10
- package/dist/lib/ssh-tunnel.js +12 -12
- package/dist/lib/staleness/checkers/hooks.d.ts +1 -1
- package/dist/lib/staleness/checkers/hooks.js +1 -1
- package/dist/lib/staleness/detectors/permissions.js +0 -23
- package/dist/lib/staleness/detectors/plugins.js +1 -1
- package/dist/lib/staleness/fingerprint.js +1 -1
- package/dist/lib/staleness/layers.d.ts +1 -1
- package/dist/lib/staleness/layers.js +1 -1
- package/dist/lib/staleness/writers/mcp.js +3 -1
- package/dist/lib/staleness/writers/plugins.js +1 -1
- package/dist/lib/staleness/writers/types.d.ts +8 -0
- package/dist/lib/startup/command-registry.d.ts +5 -147
- package/dist/lib/startup/command-registry.js +38 -303
- package/dist/lib/startup/dev-build.d.ts +0 -21
- package/dist/lib/startup/dev-build.js +29 -8
- package/dist/lib/state.d.ts +19 -1
- package/dist/lib/state.js +120 -51
- package/dist/lib/subagents.js +1 -1
- package/dist/lib/sync-status.js +1 -1
- package/dist/lib/sync-umbrella.d.ts +6 -1
- package/dist/lib/sync-umbrella.js +9 -4
- package/dist/lib/teams/agents.js +2 -2
- package/dist/lib/teams/registry.js +1 -1
- package/dist/lib/terminal/backends/vscodium-agent.js +1 -1
- package/dist/lib/terminal/engine.js +1 -1
- package/dist/lib/terminal/index.d.ts +4 -0
- package/dist/lib/terminal/index.js +3 -0
- package/dist/lib/terminal/inject.d.ts +2 -2
- package/dist/lib/terminal/inject.js +2 -2
- package/dist/lib/terminal/transport.js +1 -1
- package/dist/lib/tmux/index.d.ts +1 -1
- package/dist/lib/tmux/index.js +1 -1
- package/dist/lib/tmux/session.d.ts +21 -5
- package/dist/lib/tmux/session.js +76 -22
- package/dist/lib/triggers/handlers.d.ts +1 -1
- package/dist/lib/triggers/handlers.js +2 -2
- package/dist/lib/triggers/webhook.d.ts +36 -2
- package/dist/lib/triggers/webhook.js +97 -42
- package/dist/lib/types.d.ts +18 -6
- package/dist/lib/usage-fleet.d.ts +1 -1
- package/dist/lib/usage-fleet.js +1 -1
- package/dist/lib/usage-refresh.d.ts +1 -1
- package/dist/lib/usage-refresh.js +3 -3
- package/dist/lib/version-duplicates.js +2 -2
- package/dist/lib/view-types.d.ts +56 -0
- package/dist/lib/view-types.js +1 -0
- package/dist/lib/watchdog/rotate.d.ts +2 -2
- package/dist/lib/watchdog/rotate.js +3 -3
- package/dist/lib/watchdog/runner.d.ts +2 -3
- package/dist/lib/watchdog/runner.js +3 -4
- package/dist/lib/workflows.js +1 -1
- package/package.json +2 -1
- package/scripts/postinstall.js +2 -2
- package/dist/commands/bench.d.ts +0 -2
- package/dist/commands/bench.js +0 -101
- package/dist/commands/cp.d.ts +0 -84
- package/dist/commands/cp.js +0 -231
- package/dist/commands/login.d.ts +0 -2
- package/dist/lib/resources/commands.d.ts +0 -46
- package/dist/lib/resources/commands.js +0 -211
- package/dist/lib/resources/hooks.d.ts +0 -16
- package/dist/lib/resources/hooks.js +0 -153
- package/dist/lib/resources/mcp.d.ts +0 -34
- package/dist/lib/resources/mcp.js +0 -250
- package/dist/lib/resources/memory.d.ts +0 -15
- package/dist/lib/resources/memory.js +0 -46
- package/dist/lib/resources/permissions.d.ts +0 -13
- package/dist/lib/resources/permissions.js +0 -202
- package/dist/lib/resources/rules.d.ts +0 -46
- package/dist/lib/resources/rules.js +0 -179
- package/dist/lib/resources/skills.d.ts +0 -37
- package/dist/lib/resources/skills.js +0 -247
- package/dist/lib/resources/subagents.d.ts +0 -46
- package/dist/lib/resources/subagents.js +0 -202
- /package/dist/lib/{feed.d.ts → feed/feed.d.ts} +0 -0
|
@@ -2,19 +2,20 @@ import * as fs from 'fs';
|
|
|
2
2
|
import * as os from 'os';
|
|
3
3
|
import * as path from 'path';
|
|
4
4
|
import { BrowserCdpConnectionError, CDPClient, discoverBrowserWsUrl, verifyBrowserIdentity, } from './cdp.js';
|
|
5
|
-
import { getProfile, getProfileRuntimeDir, getProfileDownloadsDir, getProfileSessionsDir, getBrowserRuntimeDir, listProfiles, extractConfiguredPort, resolveEndpoint, } from './profiles.js';
|
|
5
|
+
import { getProfile, getProfileRuntimeDir, getProfileDownloadsDir, getProfileSessionsDir, getBrowserRuntimeDir, listProfiles, extractConfiguredPort, resolveEndpoint, parseEndpointUrl, } from './profiles.js';
|
|
6
6
|
import { killChrome, getRunningChromeInfo, launchBrowser, allocatePort } from './chrome.js';
|
|
7
7
|
import { connectLocal } from './drivers/local.js';
|
|
8
8
|
import { connectSSH, shellQuote } from './drivers/ssh.js';
|
|
9
9
|
import { clearProfileRuntime, listProfileCacheDirs, readProfileRuntimeMeta, isProcessAlive } from './runtime-state.js';
|
|
10
10
|
import { resolveDomainSkill } from './domain-skills.js';
|
|
11
|
-
import { generateTaskId, generateShortId,
|
|
11
|
+
import { generateTaskId, generateShortId, } from './types.js';
|
|
12
12
|
import { reapAbandonedTasks, resolveLiveIdentities, taskOwnerIsGone, } from './hygiene.js';
|
|
13
|
+
import { taskMatchesCaller } from './caller-identity.js';
|
|
13
14
|
import { getRefs, resolveRefToCoords, describeRefs, healRef } from './refs.js';
|
|
14
15
|
import { clickAtCoords, hoverAtCoords, scrollAtCoords, typeText, pressKey, focusNode } from './input.js';
|
|
15
16
|
import { typeEditorText } from './editor.js';
|
|
16
17
|
import { detectUploadPattern, stageUploadFile, uploadToDropTarget, uploadToFileInput, uploadViaFileChooser, } from './upload.js';
|
|
17
|
-
import { emit } from '../events.js';
|
|
18
|
+
import { emit } from '../feed/events.js';
|
|
18
19
|
import { resolveActor } from '../actor.js';
|
|
19
20
|
import { recordBrowserSession } from '../session/db.js';
|
|
20
21
|
import { sshExecAsync } from '../ssh-exec.js';
|
|
@@ -238,6 +239,36 @@ async function isConnHealthy(conn, timeoutMs = 1000) {
|
|
|
238
239
|
return false;
|
|
239
240
|
}
|
|
240
241
|
}
|
|
242
|
+
/** Join error lines so callers get a next command, not a dead-end message. */
|
|
243
|
+
export function actionable(...lines) {
|
|
244
|
+
return lines.filter((l) => l != null && l !== '').join('\n');
|
|
245
|
+
}
|
|
246
|
+
/** Derive a human label from an explicit title or a navigated URL. */
|
|
247
|
+
export function deriveTaskLabel(opts) {
|
|
248
|
+
if (opts.title?.trim())
|
|
249
|
+
return opts.title.trim();
|
|
250
|
+
if (opts.existing && opts.existing !== 'untitled')
|
|
251
|
+
return opts.existing;
|
|
252
|
+
if (opts.url) {
|
|
253
|
+
try {
|
|
254
|
+
const host = new URL(opts.url).hostname.replace(/^www\./, '');
|
|
255
|
+
if (host)
|
|
256
|
+
return host;
|
|
257
|
+
}
|
|
258
|
+
catch {
|
|
259
|
+
/* unparseable */
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
return opts.existing ?? 'untitled';
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* True when `key` is the bare profile name or one of its composite endpoint
|
|
266
|
+
* keys (`name@endpoint`). Used by status/stop so `comet-local` matches
|
|
267
|
+
* `comet-local@endpoint-0`.
|
|
268
|
+
*/
|
|
269
|
+
export function connectionKeyMatchesProfile(key, profileName) {
|
|
270
|
+
return key === profileName || key.startsWith(`${profileName}@`) || key.startsWith(`${profileName}.`);
|
|
271
|
+
}
|
|
241
272
|
/**
|
|
242
273
|
* Resolve the identity stamped on a task at start: WHO (`owner`) and WHICH run
|
|
243
274
|
* (`launchId`). The forwarded values come from the caller's own CLI process and
|
|
@@ -280,14 +311,20 @@ export class BrowserService {
|
|
|
280
311
|
// passed an unknown name. The composite identifier `<profile>@<endpoint>`
|
|
281
312
|
// is what the connection map + per-profile runtime dirs are keyed on, so
|
|
282
313
|
// a single YAML profile can run at multiple endpoints concurrently.
|
|
314
|
+
//
|
|
315
|
+
// The YAML profile's bare `name` stays on `profile.name` for status
|
|
316
|
+
// matching; only the connection/runtime key uses the composite.
|
|
283
317
|
const resolved = resolveEndpoint(profile, opts.endpointName);
|
|
284
318
|
const composite = `${profileName}@${resolved.name}`;
|
|
285
319
|
const effectiveProfile = {
|
|
286
320
|
...profile,
|
|
321
|
+
// Runtime/connect path key — NOT the bare YAML name. Connections and
|
|
322
|
+
// tasks.json live under this composite so multi-endpoint is safe.
|
|
287
323
|
name: composite,
|
|
288
324
|
binary: resolved.binary,
|
|
289
325
|
targetFilter: resolved.targetFilter,
|
|
290
326
|
};
|
|
327
|
+
const taskId = generateTaskId();
|
|
291
328
|
let taskName;
|
|
292
329
|
if (opts.taskName) {
|
|
293
330
|
if (this.hasTaskNamed(opts.taskName)) {
|
|
@@ -296,9 +333,10 @@ export class BrowserService {
|
|
|
296
333
|
taskName = opts.taskName;
|
|
297
334
|
}
|
|
298
335
|
else {
|
|
299
|
-
|
|
336
|
+
// Address by the short machine id — no word-salad. Status shows `label`.
|
|
337
|
+
taskName = taskId;
|
|
300
338
|
}
|
|
301
|
-
const
|
|
339
|
+
const taskLabel = deriveTaskLabel({ title: opts.title, url: opts.url });
|
|
302
340
|
let conn = this.connections.get(composite);
|
|
303
341
|
let effectiveProfileName = composite;
|
|
304
342
|
// If we have a cached connection, confirm it's still usable before any
|
|
@@ -344,9 +382,12 @@ export class BrowserService {
|
|
|
344
382
|
else if (!conn) {
|
|
345
383
|
conn = await this.connectProfile(effectiveProfile, resolved.target);
|
|
346
384
|
conn.profileName = composite;
|
|
385
|
+
conn.bareName = profileName;
|
|
347
386
|
this.connections.set(composite, conn);
|
|
348
387
|
await this.applyDefaultDownloadBehavior(conn, composite);
|
|
349
388
|
}
|
|
389
|
+
if (conn && !conn.bareName)
|
|
390
|
+
conn.bareName = profileName;
|
|
350
391
|
// Browsers launch with --no-startup-window (session-cookie persistence,
|
|
351
392
|
// see launchBrowser), so a bare `start` with no --url would otherwise
|
|
352
393
|
// leave the user staring at a process with zero windows. Recreate the
|
|
@@ -374,6 +415,7 @@ export class BrowserService {
|
|
|
374
415
|
const task = {
|
|
375
416
|
id: taskId,
|
|
376
417
|
name: taskName,
|
|
418
|
+
label: taskLabel,
|
|
377
419
|
profile: effectiveProfileName,
|
|
378
420
|
tabs: {},
|
|
379
421
|
currentTabId: undefined,
|
|
@@ -574,9 +616,18 @@ export class BrowserService {
|
|
|
574
616
|
return reapAbandonedTasks(this, opts);
|
|
575
617
|
}
|
|
576
618
|
async stop(taskName) {
|
|
619
|
+
// Rehydrate from tasks.json when the daemon restarted and lost RAM state.
|
|
620
|
+
try {
|
|
621
|
+
await this.findTask(taskName);
|
|
622
|
+
}
|
|
623
|
+
catch {
|
|
624
|
+
// findTask throws when truly gone; fall through to the miss return.
|
|
625
|
+
}
|
|
577
626
|
for (const [profileName, conn] of this.connections) {
|
|
578
|
-
const task =
|
|
627
|
+
const task = this.lookupTaskOnConn(conn, taskName);
|
|
579
628
|
if (task) {
|
|
629
|
+
// Map key may be task.name, not the caller-supplied handle.
|
|
630
|
+
const mapKey = this.taskMapKey(conn, task) ?? taskName;
|
|
580
631
|
// Get domains from tabs before closing (for history)
|
|
581
632
|
const domains = new Set();
|
|
582
633
|
try {
|
|
@@ -608,16 +659,16 @@ export class BrowserService {
|
|
|
608
659
|
conn.sessionCache.delete(cdpId);
|
|
609
660
|
}
|
|
610
661
|
this.invalidateTargetCache(conn);
|
|
611
|
-
conn.tasks.delete(
|
|
662
|
+
conn.tasks.delete(mapKey);
|
|
612
663
|
await this.saveTaskState(profileName, conn.tasks);
|
|
613
|
-
emit('browser.close', { profile: profileName, task:
|
|
664
|
+
emit('browser.close', { profile: profileName, task: task.name });
|
|
614
665
|
void import('../analytics/usage-db.js').then(({ recordUsage }) => {
|
|
615
666
|
recordUsage({
|
|
616
667
|
kind: 'browser',
|
|
617
668
|
name: profileName,
|
|
618
669
|
event: 'close',
|
|
619
670
|
source: 'browser',
|
|
620
|
-
meta: { task:
|
|
671
|
+
meta: { task: task.name },
|
|
621
672
|
});
|
|
622
673
|
}).catch(() => { });
|
|
623
674
|
if (conn.forkedFrom && conn.tasks.size === 0) {
|
|
@@ -669,6 +720,7 @@ export class BrowserService {
|
|
|
669
720
|
}
|
|
670
721
|
async navigate(taskId, url, profileName) {
|
|
671
722
|
const { conn, task } = await this.findTask(taskId, profileName);
|
|
723
|
+
this.maybeUpdateLabelFromUrl(conn, task, url);
|
|
672
724
|
// If we have a current tab, navigate in it (reuse)
|
|
673
725
|
const currentShortId = task.currentTabId;
|
|
674
726
|
if (currentShortId && task.tabs[currentShortId]) {
|
|
@@ -1357,14 +1409,25 @@ export class BrowserService {
|
|
|
1357
1409
|
return { path: stageUploadFile(source) };
|
|
1358
1410
|
}
|
|
1359
1411
|
async status(profileName) {
|
|
1412
|
+
// Reconnect live browsers after a daemon restart so status is not empty
|
|
1413
|
+
// while tasks.json still holds open work.
|
|
1414
|
+
await this.rehydrateAllFromDisk();
|
|
1360
1415
|
const seen = new Set();
|
|
1361
1416
|
const statuses = [];
|
|
1362
|
-
|
|
1417
|
+
// Connections are keyed by composite `<profile>@<endpoint>`. A bare
|
|
1418
|
+
// `--profile comet-local` must still list those tasks (same rule as
|
|
1419
|
+
// stopProfile). Match exact key, bareName, and composite/fork prefixes.
|
|
1420
|
+
const candidates = profileName
|
|
1421
|
+
? [...this.connections.keys()].filter((k) => connectionKeyMatchesProfile(k, profileName) ||
|
|
1422
|
+
this.connections.get(k)?.bareName === profileName)
|
|
1423
|
+
: Array.from(this.connections.keys());
|
|
1363
1424
|
for (const name of candidates) {
|
|
1364
1425
|
const status = await this.getProfileStatus(name);
|
|
1365
1426
|
if (status) {
|
|
1366
1427
|
statuses.push(status);
|
|
1367
1428
|
seen.add(name);
|
|
1429
|
+
if (status.name)
|
|
1430
|
+
seen.add(status.name);
|
|
1368
1431
|
}
|
|
1369
1432
|
}
|
|
1370
1433
|
if (!profileName) {
|
|
@@ -1372,12 +1435,14 @@ export class BrowserService {
|
|
|
1372
1435
|
for (const profile of profiles) {
|
|
1373
1436
|
if (seen.has(profile.name))
|
|
1374
1437
|
continue;
|
|
1438
|
+
if ([...seen].some((s) => connectionKeyMatchesProfile(s, profile.name)))
|
|
1439
|
+
continue;
|
|
1375
1440
|
const reconciled = await this.reconcileFromDisk(profile.name);
|
|
1376
1441
|
if (reconciled)
|
|
1377
1442
|
statuses.push(reconciled);
|
|
1378
1443
|
}
|
|
1379
1444
|
}
|
|
1380
|
-
else if (
|
|
1445
|
+
else if (statuses.length === 0) {
|
|
1381
1446
|
const reconciled = await this.reconcileFromDisk(profileName);
|
|
1382
1447
|
if (reconciled)
|
|
1383
1448
|
statuses.push(reconciled);
|
|
@@ -1385,16 +1450,37 @@ export class BrowserService {
|
|
|
1385
1450
|
return statuses;
|
|
1386
1451
|
}
|
|
1387
1452
|
async reconcileFromDisk(profileName) {
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
const
|
|
1392
|
-
|
|
1453
|
+
// Runtime dirs live under the composite (`comet-local@endpoint-0`), not the
|
|
1454
|
+
// bare YAML name. Walk every matching cache dir so bare `status --profile`
|
|
1455
|
+
// sees the same tasks as unscoped `status`.
|
|
1456
|
+
const dirs = listProfileCacheDirs(profileName);
|
|
1457
|
+
if (dirs.length === 0) {
|
|
1458
|
+
// Legacy: non-composite dir named exactly the bare profile.
|
|
1459
|
+
const info = getRunningChromeInfo(profileName);
|
|
1460
|
+
if (!info)
|
|
1461
|
+
return null;
|
|
1462
|
+
return this.profileStatusFromDisk(profileName, profileName, info.port, info.pid);
|
|
1463
|
+
}
|
|
1464
|
+
// Prefer a dir that still has a live browser; fall back to the first.
|
|
1465
|
+
for (const dir of dirs) {
|
|
1466
|
+
const dirName = path.basename(dir);
|
|
1467
|
+
const info = getRunningChromeInfo(dirName);
|
|
1468
|
+
if (!info)
|
|
1469
|
+
continue;
|
|
1470
|
+
return this.profileStatusFromDisk(profileName, dirName, info.port, info.pid);
|
|
1471
|
+
}
|
|
1472
|
+
return null;
|
|
1473
|
+
}
|
|
1474
|
+
async profileStatusFromDisk(displayName, runtimeKey, port, pid) {
|
|
1475
|
+
const bare = displayName.includes('@') ? displayName.split('@')[0] : displayName;
|
|
1476
|
+
const profile = await getProfile(bare);
|
|
1477
|
+
const tasks = this.loadTaskState(runtimeKey);
|
|
1393
1478
|
const taskStatuses = [];
|
|
1394
1479
|
for (const [, task] of tasks) {
|
|
1395
1480
|
taskStatuses.push({
|
|
1396
1481
|
id: task.id,
|
|
1397
1482
|
name: task.name,
|
|
1483
|
+
label: task.label ?? task.name,
|
|
1398
1484
|
tabCount: Object.keys(task.tabs).length,
|
|
1399
1485
|
currentTabId: task.currentTabId,
|
|
1400
1486
|
createdAt: task.createdAt,
|
|
@@ -1402,11 +1488,11 @@ export class BrowserService {
|
|
|
1402
1488
|
}
|
|
1403
1489
|
const configuredPort = profile ? extractConfiguredPort(profile) : undefined;
|
|
1404
1490
|
return {
|
|
1405
|
-
name:
|
|
1491
|
+
name: runtimeKey,
|
|
1406
1492
|
running: true,
|
|
1407
|
-
port
|
|
1408
|
-
pid
|
|
1409
|
-
configuredPort: configuredPort !==
|
|
1493
|
+
port,
|
|
1494
|
+
pid,
|
|
1495
|
+
configuredPort: configuredPort !== port ? configuredPort : undefined,
|
|
1410
1496
|
tasks: taskStatuses,
|
|
1411
1497
|
};
|
|
1412
1498
|
}
|
|
@@ -2009,18 +2095,143 @@ export class BrowserService {
|
|
|
2009
2095
|
}
|
|
2010
2096
|
hasTaskNamed(name) {
|
|
2011
2097
|
for (const conn of this.connections.values()) {
|
|
2012
|
-
if (
|
|
2098
|
+
if (this.lookupTaskOnConn(conn, name))
|
|
2013
2099
|
return true;
|
|
2014
2100
|
}
|
|
2015
2101
|
return false;
|
|
2016
2102
|
}
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
if (
|
|
2021
|
-
return
|
|
2103
|
+
/** Map key for a task on a connection (tasks are keyed by `name`). */
|
|
2104
|
+
taskMapKey(conn, task) {
|
|
2105
|
+
for (const [key, t] of conn.tasks) {
|
|
2106
|
+
if (t === task || t.id === task.id || t.name === task.name)
|
|
2107
|
+
return key;
|
|
2022
2108
|
}
|
|
2023
|
-
|
|
2109
|
+
return undefined;
|
|
2110
|
+
}
|
|
2111
|
+
/** Find a task by map key, id, or name on one connection. */
|
|
2112
|
+
lookupTaskOnConn(conn, handle) {
|
|
2113
|
+
const direct = conn.tasks.get(handle);
|
|
2114
|
+
if (direct)
|
|
2115
|
+
return direct;
|
|
2116
|
+
for (const task of conn.tasks.values()) {
|
|
2117
|
+
if (task.id === handle || task.name === handle)
|
|
2118
|
+
return task;
|
|
2119
|
+
}
|
|
2120
|
+
return undefined;
|
|
2121
|
+
}
|
|
2122
|
+
maybeUpdateLabelFromUrl(conn, task, url) {
|
|
2123
|
+
if (task.label && task.label !== 'untitled')
|
|
2124
|
+
return;
|
|
2125
|
+
const next = deriveTaskLabel({ url, existing: task.label });
|
|
2126
|
+
if (next === task.label)
|
|
2127
|
+
return;
|
|
2128
|
+
task.label = next;
|
|
2129
|
+
void this.saveTaskState(task.profile, conn.tasks).catch(() => { });
|
|
2130
|
+
}
|
|
2131
|
+
/**
|
|
2132
|
+
* Resolve a task for a page/close verb.
|
|
2133
|
+
*
|
|
2134
|
+
* - explicit `task` handle → look up (with disk rehydrate on RAM miss)
|
|
2135
|
+
* - no handle, exactly one live task for this caller → use it
|
|
2136
|
+
* - no handle, multiple for this caller → error listing them + `--task`
|
|
2137
|
+
* - no handle, zero for this caller, createIfMissing → start a new task
|
|
2138
|
+
* - no handle, zero, !createIfMissing → return null (caller reports "nothing to close")
|
|
2139
|
+
*/
|
|
2140
|
+
async resolveOrCreateTask(opts) {
|
|
2141
|
+
if (opts.task) {
|
|
2142
|
+
const found = await this.findTask(opts.task, opts.profile);
|
|
2143
|
+
return { ...found, created: false };
|
|
2144
|
+
}
|
|
2145
|
+
// After a daemon restart the connections map is empty while tasks.json still
|
|
2146
|
+
// holds live tasks. Rehydrate before identity matching so done/screenshot
|
|
2147
|
+
// without --task still find the caller's task.
|
|
2148
|
+
await this.rehydrateAllFromDisk();
|
|
2149
|
+
const caller = {
|
|
2150
|
+
sessionId: opts.sessionId,
|
|
2151
|
+
launchId: opts.launchId,
|
|
2152
|
+
};
|
|
2153
|
+
const matches = this.listTasksForCaller(caller);
|
|
2154
|
+
if (matches.length === 1) {
|
|
2155
|
+
const m = matches[0];
|
|
2156
|
+
await this.touchTask(m.conn, m.task);
|
|
2157
|
+
return { conn: m.conn, task: m.task, profileName: m.profileName, created: false };
|
|
2158
|
+
}
|
|
2159
|
+
if (matches.length > 1) {
|
|
2160
|
+
const lines = matches.map((m) => {
|
|
2161
|
+
const ageMs = Date.now() - m.task.createdAt;
|
|
2162
|
+
const age = ageMs < 60_000
|
|
2163
|
+
? `${Math.round(ageMs / 1000)}s`
|
|
2164
|
+
: `${Math.round(ageMs / 60_000)}m`;
|
|
2165
|
+
const label = m.task.label ?? m.task.name;
|
|
2166
|
+
const url = this.currentUrlHint(m.conn, m.task) ?? '-';
|
|
2167
|
+
return ` ${label} id=${m.task.name} url=${url} age=${age}`;
|
|
2168
|
+
});
|
|
2169
|
+
throw new Error(actionable(`Multiple browser tasks for this session — pass --task <name>:`, ...lines, `Next: agents browser status`));
|
|
2170
|
+
}
|
|
2171
|
+
// Zero matches for this caller.
|
|
2172
|
+
if (!opts.createIfMissing)
|
|
2173
|
+
return null;
|
|
2174
|
+
// Implicit start on the default / named profile.
|
|
2175
|
+
let profileName = opts.profile;
|
|
2176
|
+
if (!profileName) {
|
|
2177
|
+
const { ensureDefaultBrowserProfile } = await import('./profiles.js');
|
|
2178
|
+
const detected = await ensureDefaultBrowserProfile();
|
|
2179
|
+
profileName = detected.name;
|
|
2180
|
+
}
|
|
2181
|
+
const started = await this.start(profileName, {
|
|
2182
|
+
url: opts.url,
|
|
2183
|
+
actor: opts.actor,
|
|
2184
|
+
launchId: opts.launchId,
|
|
2185
|
+
sessionId: opts.sessionId,
|
|
2186
|
+
title: opts.title,
|
|
2187
|
+
});
|
|
2188
|
+
return { ...(await this.findTask(started.name, started.profile)), created: true };
|
|
2189
|
+
}
|
|
2190
|
+
listTasksForCaller(caller) {
|
|
2191
|
+
const out = [];
|
|
2192
|
+
const hasIdentity = !!(caller.sessionId || caller.launchId);
|
|
2193
|
+
for (const [key, conn] of this.connections) {
|
|
2194
|
+
for (const task of conn.tasks.values()) {
|
|
2195
|
+
if (hasIdentity) {
|
|
2196
|
+
if (taskMatchesCaller(task, caller)) {
|
|
2197
|
+
out.push({ conn, task, profileName: conn.profileName ?? key });
|
|
2198
|
+
}
|
|
2199
|
+
}
|
|
2200
|
+
else {
|
|
2201
|
+
// No caller identity: only match tasks that also lack identity
|
|
2202
|
+
// (a human-driven shell). If there is exactly one live task total,
|
|
2203
|
+
// the single-task path still needs that listed — fall through below.
|
|
2204
|
+
if (!task.sessionId && !task.launchId) {
|
|
2205
|
+
out.push({ conn, task, profileName: conn.profileName ?? key });
|
|
2206
|
+
}
|
|
2207
|
+
}
|
|
2208
|
+
}
|
|
2209
|
+
}
|
|
2210
|
+
// No-identity caller with zero unscoped matches: if the daemon holds
|
|
2211
|
+
// exactly one live task that ALSO has no identity (a human-started one),
|
|
2212
|
+
// use it. Never steal a task stamped with another caller's session/launch.
|
|
2213
|
+
if (!hasIdentity && out.length === 0) {
|
|
2214
|
+
const unowned = this.listTasks().filter((t) => !t.task.sessionId && !t.task.launchId);
|
|
2215
|
+
if (unowned.length === 1) {
|
|
2216
|
+
const only = unowned[0];
|
|
2217
|
+
const conn = this.connections.get(only.profile) ??
|
|
2218
|
+
[...this.connections.entries()].find(([, c]) => c.tasks.has(only.task.name) || [...c.tasks.values()].includes(only.task))?.[1];
|
|
2219
|
+
if (conn) {
|
|
2220
|
+
out.push({ conn, task: only.task, profileName: only.profile });
|
|
2221
|
+
}
|
|
2222
|
+
}
|
|
2223
|
+
}
|
|
2224
|
+
return out;
|
|
2225
|
+
}
|
|
2226
|
+
currentUrlHint(conn, task) {
|
|
2227
|
+
const shortId = task.currentTabId;
|
|
2228
|
+
if (!shortId)
|
|
2229
|
+
return undefined;
|
|
2230
|
+
const cdpId = task.tabs[shortId];
|
|
2231
|
+
if (!cdpId)
|
|
2232
|
+
return undefined;
|
|
2233
|
+
const cached = conn.targetCache?.targets.find((t) => t.targetId === cdpId);
|
|
2234
|
+
return cached?.url;
|
|
2024
2235
|
}
|
|
2025
2236
|
/**
|
|
2026
2237
|
* Mark a task as active, so the reaper's idle window measures time since the
|
|
@@ -2059,25 +2270,214 @@ export class BrowserService {
|
|
|
2059
2270
|
}
|
|
2060
2271
|
async findTask(taskId, profileName) {
|
|
2061
2272
|
if (profileName) {
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2273
|
+
// Accept bare profile names against composite connection keys.
|
|
2274
|
+
const keys = [...this.connections.keys()].filter((k) => k === profileName ||
|
|
2275
|
+
connectionKeyMatchesProfile(k, profileName) ||
|
|
2276
|
+
this.connections.get(k)?.bareName === profileName);
|
|
2277
|
+
for (const key of keys) {
|
|
2278
|
+
const conn = this.connections.get(key);
|
|
2279
|
+
const task = this.lookupTaskOnConn(conn, taskId);
|
|
2280
|
+
if (task) {
|
|
2281
|
+
await this.touchTask(conn, task);
|
|
2282
|
+
return { conn, task, profileName: conn.profileName ?? key };
|
|
2283
|
+
}
|
|
2065
2284
|
}
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2285
|
+
// RAM miss → rehydrate from disk for this profile.
|
|
2286
|
+
const rehydrated = await this.rehydrateTaskFromDisk(taskId, profileName);
|
|
2287
|
+
if (rehydrated) {
|
|
2288
|
+
await this.touchTask(rehydrated.conn, rehydrated.task);
|
|
2289
|
+
return rehydrated;
|
|
2069
2290
|
}
|
|
2070
|
-
|
|
2071
|
-
return { conn, task, profileName };
|
|
2291
|
+
throw new Error(actionable(`Task "${taskId}" not found on profile "${profileName}".`, `Next: agents browser status --profile ${profileName}`));
|
|
2072
2292
|
}
|
|
2073
2293
|
for (const [key, conn] of this.connections) {
|
|
2074
|
-
const task =
|
|
2294
|
+
const task = this.lookupTaskOnConn(conn, taskId);
|
|
2075
2295
|
if (task) {
|
|
2076
2296
|
await this.touchTask(conn, task);
|
|
2077
2297
|
return { conn, task, profileName: conn.profileName ?? key };
|
|
2078
2298
|
}
|
|
2079
2299
|
}
|
|
2080
|
-
|
|
2300
|
+
// RAM miss after a daemon restart: tasks.json still has the task and
|
|
2301
|
+
// meta.json still has the browser pid/port. Reconnect and adopt.
|
|
2302
|
+
const rehydrated = await this.rehydrateTaskFromDisk(taskId);
|
|
2303
|
+
if (rehydrated) {
|
|
2304
|
+
await this.touchTask(rehydrated.conn, rehydrated.task);
|
|
2305
|
+
return rehydrated;
|
|
2306
|
+
}
|
|
2307
|
+
const live = this.listTasks()
|
|
2308
|
+
.map(({ task }) => task.label ?? task.name)
|
|
2309
|
+
.join(', ');
|
|
2310
|
+
throw new Error(actionable(`Task "${taskId}" not found.`, `Active tasks: ${live || 'none'}`, `It may have been closed by the idle reaper (30 min).`, `Next: agents browser status | agents browser navigate <url>`));
|
|
2311
|
+
}
|
|
2312
|
+
/**
|
|
2313
|
+
* Soft-attach to an already-running browser for a runtime dir.
|
|
2314
|
+
*
|
|
2315
|
+
* Never launches a browser and never clears pid/port files on failure —
|
|
2316
|
+
* `connectProfile` does both, which made `status()` wipe disk state when
|
|
2317
|
+
* CDP was unreachable (CI / post-reaper) and then return an empty list.
|
|
2318
|
+
* Returns null when nothing is speaking CDP so callers can fall through
|
|
2319
|
+
* to disk reconcile.
|
|
2320
|
+
*/
|
|
2321
|
+
async attachRunningProfile(dirName, diskTasks) {
|
|
2322
|
+
const bare = dirName.includes('@') ? dirName.split('@')[0] : dirName;
|
|
2323
|
+
const endpointFromKey = dirName.includes('@')
|
|
2324
|
+
? dirName.slice(dirName.indexOf('@') + 1).split('.')[0]
|
|
2325
|
+
: undefined;
|
|
2326
|
+
const profile = await getProfile(bare);
|
|
2327
|
+
if (!profile)
|
|
2328
|
+
return null;
|
|
2329
|
+
let resolved;
|
|
2330
|
+
try {
|
|
2331
|
+
resolved = resolveEndpoint(profile, endpointFromKey);
|
|
2332
|
+
}
|
|
2333
|
+
catch {
|
|
2334
|
+
return null;
|
|
2335
|
+
}
|
|
2336
|
+
const existingInfo = getRunningChromeInfo(dirName);
|
|
2337
|
+
const parsed = parseEndpointUrl(resolved.target);
|
|
2338
|
+
const port = existingInfo?.port ?? parsed?.port;
|
|
2339
|
+
if (port === undefined)
|
|
2340
|
+
return null;
|
|
2341
|
+
const host = parsed?.host && parsed.host !== 'localhost' ? parsed.host : 'localhost';
|
|
2342
|
+
// ssh:// endpoints need a tunnel; soft rehydrate only handles local CDP.
|
|
2343
|
+
if (resolved.target.startsWith('ssh:'))
|
|
2344
|
+
return null;
|
|
2345
|
+
try {
|
|
2346
|
+
const { wsUrl, browser } = await discoverBrowserWsUrl(port, host, dirName);
|
|
2347
|
+
verifyBrowserIdentity(browser, profile.browser, port, host);
|
|
2348
|
+
const cdp = new CDPClient();
|
|
2349
|
+
await cdp.connect(wsUrl);
|
|
2350
|
+
await this.enableDomains(cdp);
|
|
2351
|
+
const tasks = this.loadTaskState(dirName);
|
|
2352
|
+
for (const [k, t] of diskTasks) {
|
|
2353
|
+
if (!tasks.has(k))
|
|
2354
|
+
tasks.set(k, t);
|
|
2355
|
+
}
|
|
2356
|
+
const conn = {
|
|
2357
|
+
cdp,
|
|
2358
|
+
port,
|
|
2359
|
+
pid: existingInfo?.pid ?? 0,
|
|
2360
|
+
electron: profile.electron,
|
|
2361
|
+
targetFilter: resolved.targetFilter ?? profile.targetFilter,
|
|
2362
|
+
profileName: dirName,
|
|
2363
|
+
bareName: bare,
|
|
2364
|
+
tasks,
|
|
2365
|
+
sessionCache: new Map(),
|
|
2366
|
+
};
|
|
2367
|
+
return conn;
|
|
2368
|
+
}
|
|
2369
|
+
catch {
|
|
2370
|
+
return null;
|
|
2371
|
+
}
|
|
2372
|
+
}
|
|
2373
|
+
/**
|
|
2374
|
+
* Reconnect every profile runtime dir that still has a live browser and
|
|
2375
|
+
* tasks.json entries. Used before identity-based resolution so a daemon
|
|
2376
|
+
* restart does not make the caller's tasks invisible.
|
|
2377
|
+
*/
|
|
2378
|
+
async rehydrateAllFromDisk() {
|
|
2379
|
+
const runtimeRoot = getBrowserRuntimeDir();
|
|
2380
|
+
let dirNames = [];
|
|
2381
|
+
try {
|
|
2382
|
+
dirNames = fs.readdirSync(runtimeRoot).filter((d) => {
|
|
2383
|
+
try {
|
|
2384
|
+
return fs.statSync(path.join(runtimeRoot, d)).isDirectory();
|
|
2385
|
+
}
|
|
2386
|
+
catch {
|
|
2387
|
+
return false;
|
|
2388
|
+
}
|
|
2389
|
+
});
|
|
2390
|
+
}
|
|
2391
|
+
catch {
|
|
2392
|
+
return;
|
|
2393
|
+
}
|
|
2394
|
+
for (const dirName of dirNames) {
|
|
2395
|
+
if (this.connections.has(dirName))
|
|
2396
|
+
continue;
|
|
2397
|
+
// Skip non-runtime dirs (e.g. profiles/)
|
|
2398
|
+
if (dirName === 'profiles' || dirName === 'sessions' || dirName === 'exports')
|
|
2399
|
+
continue;
|
|
2400
|
+
const tasks = this.loadTaskState(dirName);
|
|
2401
|
+
if (tasks.size === 0)
|
|
2402
|
+
continue;
|
|
2403
|
+
// Soft attach only — never launch / never clear pid files.
|
|
2404
|
+
const conn = await this.attachRunningProfile(dirName, tasks);
|
|
2405
|
+
if (!conn)
|
|
2406
|
+
continue;
|
|
2407
|
+
this.connections.set(dirName, conn);
|
|
2408
|
+
try {
|
|
2409
|
+
await this.applyDefaultDownloadBehavior(conn, dirName);
|
|
2410
|
+
}
|
|
2411
|
+
catch {
|
|
2412
|
+
// Non-fatal for rehydrate.
|
|
2413
|
+
}
|
|
2414
|
+
}
|
|
2415
|
+
}
|
|
2416
|
+
/**
|
|
2417
|
+
* On a RAM miss, scan profile runtime dirs for a tasks.json entry matching
|
|
2418
|
+
* `taskId`, soft-attach when CDP is still up, and register the connection.
|
|
2419
|
+
* Does not launch browsers or clear runtime files.
|
|
2420
|
+
*/
|
|
2421
|
+
async rehydrateTaskFromDisk(taskId, profileHint) {
|
|
2422
|
+
const runtimeRoot = getBrowserRuntimeDir();
|
|
2423
|
+
let dirNames = [];
|
|
2424
|
+
try {
|
|
2425
|
+
dirNames = fs.readdirSync(runtimeRoot).filter((d) => {
|
|
2426
|
+
try {
|
|
2427
|
+
return fs.statSync(path.join(runtimeRoot, d)).isDirectory();
|
|
2428
|
+
}
|
|
2429
|
+
catch {
|
|
2430
|
+
return false;
|
|
2431
|
+
}
|
|
2432
|
+
});
|
|
2433
|
+
}
|
|
2434
|
+
catch {
|
|
2435
|
+
return null;
|
|
2436
|
+
}
|
|
2437
|
+
if (profileHint) {
|
|
2438
|
+
dirNames = dirNames.filter((d) => d === profileHint || connectionKeyMatchesProfile(d, profileHint));
|
|
2439
|
+
}
|
|
2440
|
+
for (const dirName of dirNames) {
|
|
2441
|
+
if (dirName === 'profiles' || dirName === 'sessions' || dirName === 'exports')
|
|
2442
|
+
continue;
|
|
2443
|
+
const tasks = this.loadTaskState(dirName);
|
|
2444
|
+
let found;
|
|
2445
|
+
for (const task of tasks.values()) {
|
|
2446
|
+
if (task.id === taskId || task.name === taskId) {
|
|
2447
|
+
found = task;
|
|
2448
|
+
break;
|
|
2449
|
+
}
|
|
2450
|
+
}
|
|
2451
|
+
// Also allow map-key match
|
|
2452
|
+
if (!found)
|
|
2453
|
+
found = tasks.get(taskId);
|
|
2454
|
+
if (!found)
|
|
2455
|
+
continue;
|
|
2456
|
+
// Already connected under this key?
|
|
2457
|
+
let conn = this.connections.get(dirName);
|
|
2458
|
+
if (!conn) {
|
|
2459
|
+
const attached = await this.attachRunningProfile(dirName, tasks);
|
|
2460
|
+
if (!attached) {
|
|
2461
|
+
// CDP down — cannot rehydrate a live connection; caller will error.
|
|
2462
|
+
continue;
|
|
2463
|
+
}
|
|
2464
|
+
conn = attached;
|
|
2465
|
+
this.connections.set(dirName, conn);
|
|
2466
|
+
try {
|
|
2467
|
+
await this.applyDefaultDownloadBehavior(conn, dirName);
|
|
2468
|
+
}
|
|
2469
|
+
catch {
|
|
2470
|
+
// Non-fatal.
|
|
2471
|
+
}
|
|
2472
|
+
}
|
|
2473
|
+
const task = this.lookupTaskOnConn(conn, taskId) ?? found;
|
|
2474
|
+
// Ensure the rehydrated task is on the connection map.
|
|
2475
|
+
if (!this.lookupTaskOnConn(conn, task.name)) {
|
|
2476
|
+
conn.tasks.set(task.name, task);
|
|
2477
|
+
}
|
|
2478
|
+
return { conn, task, profileName: conn.profileName ?? dirName };
|
|
2479
|
+
}
|
|
2480
|
+
return null;
|
|
2081
2481
|
}
|
|
2082
2482
|
async getTabsForTask(cdp, task) {
|
|
2083
2483
|
const targets = (await cdp.send('Target.getTargets'));
|
|
@@ -2096,7 +2496,19 @@ export class BrowserService {
|
|
|
2096
2496
|
return tabs;
|
|
2097
2497
|
}
|
|
2098
2498
|
async getProfileStatus(profileName) {
|
|
2099
|
-
|
|
2499
|
+
// Exact composite key first; bare name falls through connectionKeyMatches.
|
|
2500
|
+
let conn = this.connections.get(profileName);
|
|
2501
|
+
let resolvedKey = profileName;
|
|
2502
|
+
if (!conn) {
|
|
2503
|
+
for (const [key, c] of this.connections) {
|
|
2504
|
+
if (connectionKeyMatchesProfile(key, profileName) ||
|
|
2505
|
+
c.bareName === profileName) {
|
|
2506
|
+
conn = c;
|
|
2507
|
+
resolvedKey = key;
|
|
2508
|
+
break;
|
|
2509
|
+
}
|
|
2510
|
+
}
|
|
2511
|
+
}
|
|
2100
2512
|
if (!conn)
|
|
2101
2513
|
return null;
|
|
2102
2514
|
// Fetch all targets once for efficiency
|
|
@@ -2134,6 +2546,7 @@ export class BrowserService {
|
|
|
2134
2546
|
tasks.push({
|
|
2135
2547
|
id: task.id,
|
|
2136
2548
|
name: task.name,
|
|
2549
|
+
label: task.label ?? task.name,
|
|
2137
2550
|
tabCount: Object.keys(task.tabs).length,
|
|
2138
2551
|
currentTabId: task.currentTabId,
|
|
2139
2552
|
createdAt: task.createdAt,
|
|
@@ -2141,10 +2554,11 @@ export class BrowserService {
|
|
|
2141
2554
|
domains: domainSet.size > 0 ? Array.from(domainSet) : undefined,
|
|
2142
2555
|
});
|
|
2143
2556
|
}
|
|
2144
|
-
const
|
|
2557
|
+
const bare = conn.bareName ?? (resolvedKey.includes('@') ? resolvedKey.split('@')[0] : resolvedKey);
|
|
2558
|
+
const profile = await getProfile(bare);
|
|
2145
2559
|
const configuredPort = profile ? extractConfiguredPort(profile) : undefined;
|
|
2146
2560
|
return {
|
|
2147
|
-
name:
|
|
2561
|
+
name: resolvedKey,
|
|
2148
2562
|
running: true,
|
|
2149
2563
|
port: conn.port,
|
|
2150
2564
|
pid: conn.pid,
|