@phnx-labs/agents-cli 1.22.39 → 1.22.41
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +316 -0
- package/README.md +116 -70
- package/dist/bin/agents +0 -0
- package/dist/bootstrap.js +45 -27
- package/dist/browser.js +2 -2
- package/dist/cli/command-registry.d.ts +135 -0
- package/dist/cli/command-registry.js +259 -0
- package/dist/commands/accounts.d.ts +20 -0
- package/dist/commands/accounts.js +186 -25
- package/dist/commands/apply.d.ts +3 -3
- package/dist/commands/apply.js +20 -63
- package/dist/commands/browser.d.ts +15 -0
- package/dist/commands/browser.js +318 -131
- package/dist/commands/budget.d.ts +7 -6
- package/dist/commands/budget.js +8 -7
- package/dist/commands/cloud.js +17 -20
- package/dist/commands/commands.js +1 -1
- package/dist/commands/computer-actions.d.ts +1 -35
- package/dist/commands/computer-actions.js +13 -181
- package/dist/commands/computer.d.ts +28 -3
- package/dist/commands/computer.js +93 -65
- package/dist/commands/config.js +16 -3
- package/dist/commands/cost.d.ts +2 -1
- package/dist/commands/cost.js +7 -6
- package/dist/commands/daemon.js +151 -4
- package/dist/commands/detach.js +1 -1
- package/dist/commands/doctor.js +19 -11
- package/dist/commands/events.js +1 -1
- package/dist/commands/exec.d.ts +2 -50
- package/dist/commands/exec.js +160 -215
- package/dist/commands/factory.js +1 -1
- package/dist/commands/feed.d.ts +2 -2
- package/dist/commands/feed.js +12 -23
- package/dist/commands/fleet-capture.js +25 -27
- package/dist/commands/focus.d.ts +2 -3
- package/dist/commands/focus.js +34 -9
- package/dist/commands/go.js +15 -5
- package/dist/commands/harness.d.ts +3 -3
- package/dist/commands/harness.js +53 -5
- package/dist/commands/hooks.js +1 -1
- package/dist/commands/import.js +2 -2
- package/dist/commands/insights.d.ts +5 -5
- package/dist/commands/insights.js +15 -9
- package/dist/commands/inspect.d.ts +2 -3
- package/dist/commands/inspect.js +4 -16
- package/dist/commands/logs.d.ts +2 -2
- package/dist/commands/logs.js +9 -9
- package/dist/commands/mcp.js +2 -2
- package/dist/commands/memory.js +1 -1
- package/dist/commands/message.d.ts +1 -1
- package/dist/commands/message.js +7 -7
- package/dist/commands/mine.d.ts +11 -8
- package/dist/commands/mine.js +11 -10
- package/dist/commands/models.js +1 -1
- package/dist/commands/modes.js +1 -1
- package/dist/commands/monitors.js +23 -6
- package/dist/commands/output.d.ts +7 -6
- package/dist/commands/output.js +11 -10
- package/dist/commands/packages.js +4 -4
- package/dist/commands/perf.js +1 -1
- package/dist/commands/permissions.js +49 -32
- package/dist/commands/plugins.d.ts +1 -1
- package/dist/commands/plugins.js +3 -3
- package/dist/commands/profiles.d.ts +6 -9
- package/dist/commands/profiles.js +8 -321
- package/dist/commands/projects.d.ts +9 -0
- package/dist/commands/projects.js +140 -8
- package/dist/commands/prune.js +5 -13
- package/dist/commands/refresh-rules.js +1 -1
- package/dist/commands/repo.d.ts +5 -7
- package/dist/commands/repo.js +34 -18
- package/dist/commands/resource-view.js +4 -0
- package/dist/commands/resume.d.ts +11 -7
- package/dist/commands/resume.js +82 -98
- package/dist/commands/route.d.ts +12 -0
- package/dist/commands/route.js +261 -0
- package/dist/commands/routines.d.ts +2 -3
- package/dist/commands/routines.js +120 -145
- package/dist/commands/rules.js +1 -1
- package/dist/commands/run-account-picker.d.ts +1 -1
- package/dist/commands/run-account-picker.js +2 -2
- package/dist/commands/run-broadcast.d.ts +21 -0
- package/dist/commands/run-broadcast.js +89 -0
- package/dist/commands/run-cloud.js +1 -1
- package/dist/commands/secrets-vault.d.ts +10 -0
- package/dist/commands/{login.js → secrets-vault.js} +34 -14
- package/dist/commands/secrets.d.ts +4 -4
- package/dist/commands/secrets.js +158 -159
- package/dist/commands/sessions-backfill.js +1 -1
- package/dist/commands/sessions-browser.d.ts +1 -1
- package/dist/commands/sessions-browser.js +9 -5
- package/dist/commands/sessions-export.js +2 -2
- package/dist/commands/sessions-inject.js +4 -5
- package/dist/commands/sessions-migrate.js +16 -27
- package/dist/commands/sessions-picker.d.ts +14 -0
- package/dist/commands/sessions-picker.js +209 -13
- package/dist/commands/sessions-resume.d.ts +7 -3
- package/dist/commands/sessions-resume.js +81 -33
- package/dist/commands/sessions-share.d.ts +25 -0
- package/dist/commands/sessions-share.js +166 -0
- package/dist/commands/sessions-stats.js +2 -2
- package/dist/commands/sessions.d.ts +75 -68
- package/dist/commands/sessions.js +144 -180
- package/dist/commands/setup-browser.js +1 -1
- package/dist/commands/setup-computer.js +1 -1
- package/dist/commands/setup-fleet.js +3 -0
- package/dist/commands/setup-mine.d.ts +7 -3
- package/dist/commands/setup-mine.js +13 -8
- package/dist/commands/setup-preferences.js +2 -2
- package/dist/commands/setup-secrets.js +2 -2
- package/dist/commands/setup-watchdog.js +2 -2
- package/dist/commands/setup.js +7 -2
- package/dist/commands/share.d.ts +65 -0
- package/dist/commands/share.js +253 -20
- package/dist/commands/skills.js +3 -3
- package/dist/commands/snapshot.d.ts +2 -2
- package/dist/commands/snapshot.js +9 -9
- package/dist/commands/ssh.d.ts +1 -1
- package/dist/commands/ssh.js +186 -64
- package/dist/commands/subagents.js +1 -1
- package/dist/commands/sync.js +24 -9
- package/dist/commands/teams.d.ts +2 -2
- package/dist/commands/teams.js +9 -18
- package/dist/commands/tmux.js +5 -1
- package/dist/commands/trash.js +1 -1
- package/dist/commands/usage.d.ts +1 -1
- package/dist/commands/usage.js +2 -2
- package/dist/commands/utils.d.ts +4 -14
- package/dist/commands/utils.js +6 -27
- package/dist/commands/versions.js +2 -2
- package/dist/commands/view.d.ts +9 -77
- package/dist/commands/view.js +23 -8
- package/dist/commands/watchdog.js +1 -1
- package/dist/commands/webhook.js +25 -34
- package/dist/commands/workflows.js +1 -1
- package/dist/index.js +1 -1
- package/dist/lib/account-capabilities.d.ts +39 -0
- package/dist/lib/account-capabilities.js +59 -0
- package/dist/lib/account-catalog.js +1 -1
- package/dist/lib/account-registry.d.ts +66 -0
- package/dist/lib/account-registry.js +151 -5
- package/dist/lib/{rotate.d.ts → accounting/rotate.d.ts} +4 -4
- package/dist/lib/{rotate.js → accounting/rotate.js} +8 -8
- package/dist/lib/{usage.d.ts → accounting/usage.d.ts} +4 -4
- package/dist/lib/{usage.js → accounting/usage.js} +11 -11
- package/dist/lib/agent-spec/provider.js +1 -1
- package/dist/lib/agents.d.ts +72 -4
- package/dist/lib/agents.js +231 -199
- package/dist/lib/answer-router.d.ts +2 -2
- package/dist/lib/ask-classifier.d.ts +1 -1
- package/dist/lib/ask-classifier.js +1 -1
- package/dist/lib/audit/log.js +1 -1
- package/dist/lib/auth-health.d.ts +1 -1
- package/dist/lib/auth-health.js +2 -2
- package/dist/lib/brand.js +1 -1
- package/dist/lib/browser/caller-identity.d.ts +28 -0
- package/dist/lib/browser/caller-identity.js +156 -0
- package/dist/lib/browser/cdp.js +5 -1
- package/dist/lib/browser/chrome.js +12 -1
- package/dist/lib/browser/drivers/ssh.d.ts +1 -1
- package/dist/lib/browser/drivers/ssh.js +3 -2
- package/dist/lib/browser/hygiene.d.ts +10 -2
- package/dist/lib/browser/hygiene.js +7 -4
- package/dist/lib/browser/ipc.d.ts +22 -2
- package/dist/lib/browser/ipc.js +185 -21
- package/dist/lib/browser/profiles.d.ts +69 -5
- package/dist/lib/browser/profiles.js +135 -13
- package/dist/lib/browser/remote-control.d.ts +2 -2
- package/dist/lib/browser/remote-control.js +3 -3
- package/dist/lib/browser/runtime-state.d.ts +79 -0
- package/dist/lib/browser/runtime-state.js +119 -1
- package/dist/lib/browser/service.d.ts +137 -2
- package/dist/lib/browser/service.js +492 -54
- package/dist/lib/browser/sessions-list.d.ts +2 -1
- package/dist/lib/browser/sessions-list.js +4 -13
- package/dist/lib/browser/types.d.ts +19 -1
- package/dist/lib/budget/config.js +1 -1
- package/dist/lib/budget/ledger.js +1 -1
- package/dist/lib/byok-usage.js +1 -1
- package/dist/lib/capabilities.js +3 -15
- package/dist/lib/catchup.d.ts +1 -1
- package/dist/lib/catchup.js +1 -1
- package/dist/lib/cloud/dispatch.js +1 -1
- package/dist/lib/cloud/host.d.ts +3 -3
- package/dist/lib/cloud/host.js +5 -5
- package/dist/lib/cloud/rush.d.ts +9 -21
- package/dist/lib/cloud/rush.js +33 -120
- package/dist/lib/cloud/types.d.ts +2 -2
- package/dist/lib/commands.js +3 -13
- package/dist/lib/computer/actions.d.ts +36 -0
- package/dist/lib/computer/actions.js +162 -0
- package/dist/lib/computer/dispatch.d.ts +2 -2
- package/dist/lib/computer/dispatch.js +2 -2
- package/dist/lib/computer/sessions-list.d.ts +3 -3
- package/dist/lib/computer/sessions-list.js +2 -2
- package/dist/lib/config-keys.d.ts +2 -1
- package/dist/lib/config-keys.js +8 -4
- package/dist/lib/config-machine-keys.d.ts +4 -2
- package/dist/lib/config-machine-keys.js +5 -2
- package/dist/lib/crabbox/lease.js +9 -1
- package/dist/lib/crabbox/runtimes.d.ts +32 -7
- package/dist/lib/crabbox/runtimes.js +72 -30
- package/dist/lib/{daemon.d.ts → daemon/daemon.d.ts} +59 -1
- package/dist/lib/{daemon.js → daemon/daemon.js} +237 -49
- package/dist/lib/daemon-services.d.ts +1 -1
- package/dist/lib/daemon-services.js +5 -0
- package/dist/lib/daemon-ticks.d.ts +25 -0
- package/dist/lib/daemon-ticks.js +31 -1
- package/dist/lib/daemon-webhooks.d.ts +104 -0
- package/dist/lib/daemon-webhooks.js +215 -0
- package/dist/lib/device-config.d.ts +85 -75
- package/dist/lib/device-config.js +280 -142
- package/dist/lib/devices/config-migration.d.ts +29 -34
- package/dist/lib/devices/config-migration.js +209 -130
- package/dist/lib/devices/connect.d.ts +2 -2
- package/dist/lib/devices/connect.js +2 -2
- package/dist/lib/devices/discovery-policy.d.ts +30 -0
- package/dist/lib/devices/discovery-policy.js +101 -0
- package/dist/lib/devices/fleet-inventory.js +8 -3
- package/dist/lib/devices/harness-inventory.d.ts +1 -1
- package/dist/lib/devices/harness-inventory.js +12 -3
- package/dist/lib/devices/known-hosts.js +1 -1
- package/dist/lib/devices/pool.d.ts +10 -5
- package/dist/lib/devices/pool.js +5 -7
- package/dist/lib/devices/registry.d.ts +1 -1
- package/dist/lib/devices/registry.js +1 -1
- package/dist/lib/devices/resolve-profile.d.ts +2 -2
- package/dist/lib/devices/resolve-profile.js +2 -2
- package/dist/lib/devices/resolve-target.d.ts +1 -1
- package/dist/lib/devices/resolve-target.js +4 -4
- package/dist/lib/devices/self-host.d.ts +1 -1
- package/dist/lib/devices/self-host.js +3 -3
- package/dist/lib/devices/ssh-config.d.ts +1 -1
- package/dist/lib/devices/stats-cache.js +1 -1
- package/dist/lib/devices/sync.d.ts +3 -1
- package/dist/lib/devices/sync.js +3 -3
- package/dist/lib/doctor-diff.js +4 -4
- package/dist/lib/drift-sync.js +2 -2
- package/dist/lib/drift.js +2 -2
- package/dist/lib/event-families.d.ts +1 -1
- package/dist/lib/event-stream.d.ts +1 -1
- package/dist/lib/event-stream.js +2 -2
- package/dist/lib/events-ingest.js +2 -2
- package/dist/lib/exec.d.ts +4 -4
- package/dist/lib/exec.js +11 -11
- package/dist/lib/factory/snapshot.d.ts +1 -1
- package/dist/lib/factory/snapshot.js +1 -1
- package/dist/lib/{activity.d.ts → feed/activity.d.ts} +1 -1
- package/dist/lib/{activity.js → feed/activity.js} +7 -7
- package/dist/lib/{events.d.ts → feed/events.d.ts} +2 -2
- package/dist/lib/{events.js → feed/events.js} +5 -5
- package/dist/lib/{feed.js → feed/feed.js} +4 -4
- package/dist/lib/feed-outcome.d.ts +1 -1
- package/dist/lib/feed-policy.d.ts +1 -1
- package/dist/lib/feed-policy.js +1 -1
- package/dist/lib/feed-post.d.ts +1 -1
- package/dist/lib/feed-post.js +1 -1
- package/dist/lib/feed-ranking.d.ts +1 -1
- package/dist/lib/fleet/apply.d.ts +0 -6
- package/dist/lib/fleet/apply.js +18 -45
- package/dist/lib/fleet/auth-sync.d.ts +16 -35
- package/dist/lib/fleet/auth-sync.js +16 -75
- package/dist/lib/fleet/capture.js +8 -6
- package/dist/lib/fleet/manifest.js +23 -11
- package/dist/lib/fleet/types.d.ts +19 -7
- package/dist/lib/fleet-cache.d.ts +1 -1
- package/dist/lib/fleet-cache.js +1 -1
- package/dist/lib/format.d.ts +19 -0
- package/dist/lib/format.js +44 -1
- package/dist/lib/friction-heuristics.d.ts +1 -1
- package/dist/lib/git.d.ts +24 -1
- package/dist/lib/git.js +82 -38
- package/dist/lib/heal.js +3 -3
- package/dist/lib/hooks.d.ts +18 -0
- package/dist/lib/hooks.js +113 -1
- package/dist/lib/hosts/credentials.d.ts +30 -10
- package/dist/lib/hosts/credentials.js +38 -26
- package/dist/lib/hosts/dispatch.d.ts +3 -3
- package/dist/lib/hosts/dispatch.js +3 -3
- package/dist/lib/hosts/option.d.ts +3 -3
- package/dist/lib/hosts/option.js +7 -8
- package/dist/lib/hosts/passthrough.d.ts +17 -17
- package/dist/lib/hosts/passthrough.js +67 -50
- package/dist/lib/hosts/providers/devices.d.ts +1 -1
- package/dist/lib/hosts/providers/devices.js +1 -1
- package/dist/lib/hosts/reconcile.d.ts +1 -1
- package/dist/lib/hosts/reconcile.js +1 -1
- package/dist/lib/hosts/reconnect.d.ts +1 -1
- package/dist/lib/hosts/reconnect.js +2 -2
- package/dist/lib/hosts/registry.d.ts +5 -5
- package/dist/lib/hosts/registry.js +5 -5
- package/dist/lib/hosts/remote-cmd.d.ts +10 -10
- package/dist/lib/hosts/remote-cmd.js +25 -18
- package/dist/lib/hosts/remote-os.d.ts +4 -4
- package/dist/lib/hosts/remote-os.js +4 -4
- package/dist/lib/hosts/remote-session-id.d.ts +2 -2
- package/dist/lib/hosts/remote-session-id.js +2 -2
- package/dist/lib/hosts/routing-flag.d.ts +3 -3
- package/dist/lib/hosts/routing-flag.js +4 -5
- package/dist/lib/hosts/run-target.d.ts +4 -4
- package/dist/lib/hosts/run-target.js +4 -4
- package/dist/lib/hosts/session-index.d.ts +2 -2
- package/dist/lib/hosts/session-index.js +1 -1
- package/dist/lib/hosts/session-marker.d.ts +1 -1
- package/dist/lib/hosts/session-marker.js +1 -1
- package/dist/lib/hosts/types.d.ts +2 -2
- package/dist/lib/hosts/types.js +2 -2
- package/dist/lib/import.js +1 -1
- package/dist/lib/{migrate.d.ts → installations/migrate.d.ts} +13 -1
- package/dist/lib/{migrate.js → installations/migrate.js} +160 -40
- package/dist/lib/installations/resolve.js +1 -1
- package/dist/lib/installations/strategies.js +1 -1
- package/dist/lib/installations/update.js +2 -2
- package/dist/lib/{versions.d.ts → installations/versions.d.ts} +12 -3
- package/dist/lib/{versions.js → installations/versions.js} +51 -31
- package/dist/lib/isolation-boundary-report.js +1 -1
- package/dist/lib/linear-projects.d.ts +28 -0
- package/dist/lib/linear-projects.js +30 -0
- package/dist/lib/loop.js +1 -1
- package/dist/lib/mailbox-gc.js +1 -1
- package/dist/lib/mailbox.js +1 -1
- package/dist/lib/mcp-registry.d.ts +62 -0
- package/dist/lib/mcp-registry.js +177 -0
- package/dist/lib/mcp.d.ts +5 -0
- package/dist/lib/mcp.js +124 -307
- package/dist/lib/menubar/MenubarHelper.app/Contents/CodeResources +0 -0
- package/dist/lib/menubar/MenubarHelper.app/Contents/MacOS/MenubarHelper +0 -0
- package/dist/lib/menubar/install-menubar.d.ts +7 -0
- package/dist/lib/menubar/install-menubar.js +24 -6
- package/dist/lib/menubar/snapshot.d.ts +7 -0
- package/dist/lib/menubar/snapshot.js +8 -6
- package/dist/lib/models.js +1 -1
- package/dist/lib/monitors/config.d.ts +12 -0
- package/dist/lib/monitors/config.js +3 -0
- package/dist/lib/monitors/dispatch.js +26 -5
- package/dist/lib/monitors/engine.js +10 -0
- package/dist/lib/monitors/sources/webhook.d.ts +1 -1
- package/dist/lib/monitors/state.d.ts +42 -0
- package/dist/lib/monitors/state.js +30 -0
- package/dist/lib/notify.d.ts +1 -1
- package/dist/lib/observe-aliases.d.ts +3 -3
- package/dist/lib/observe-aliases.js +3 -21
- package/dist/lib/output/git-output.js +2 -2
- package/dist/lib/overdue.d.ts +1 -1
- package/dist/lib/overdue.js +1 -1
- package/dist/lib/permissions-registry.d.ts +65 -0
- package/dist/lib/permissions-registry.js +599 -0
- package/dist/lib/permissions.d.ts +24 -13
- package/dist/lib/permissions.js +52 -271
- package/dist/lib/picker.d.ts +16 -0
- package/dist/lib/picker.js +33 -3
- package/dist/lib/placement.d.ts +1 -1
- package/dist/lib/placement.js +4 -4
- package/dist/lib/platform/paths.d.ts +8 -0
- package/dist/lib/platform/paths.js +11 -0
- package/dist/lib/platform/posixpath.js +3 -3
- package/dist/lib/{plugin-marketplace.d.ts → plugins/plugin-marketplace.d.ts} +1 -1
- package/dist/lib/{plugin-marketplace.js → plugins/plugin-marketplace.js} +2 -2
- package/dist/lib/{plugins.d.ts → plugins/plugins.d.ts} +1 -1
- package/dist/lib/{plugins.js → plugins/plugins.js} +7 -7
- package/dist/lib/{skills.d.ts → plugins/skills.d.ts} +1 -1
- package/dist/lib/{skills.js → plugins/skills.js} +7 -7
- package/dist/lib/pricing/cost.d.ts +1 -1
- package/dist/lib/pricing/cost.js +1 -1
- package/dist/lib/profiles-presets.d.ts +1 -1
- package/dist/lib/profiles-presets.js +1 -1
- package/dist/lib/profiles.js +2 -2
- package/dist/lib/project-import.js +4 -1
- package/dist/lib/project-key.js +7 -0
- package/dist/lib/project-launch.d.ts +1 -1
- package/dist/lib/project-launch.js +3 -3
- package/dist/lib/project-pull.d.ts +128 -0
- package/dist/lib/project-pull.js +384 -0
- package/dist/lib/project-root.d.ts +2 -2
- package/dist/lib/project-root.js +2 -2
- package/dist/lib/project-status.js +1 -1
- package/dist/lib/projects.d.ts +23 -0
- package/dist/lib/projects.js +29 -6
- package/dist/lib/refresh.d.ts +12 -1
- package/dist/lib/refresh.js +16 -9
- package/dist/lib/registry.d.ts +9 -2
- package/dist/lib/registry.js +11 -4
- package/dist/lib/resources.d.ts +2 -2
- package/dist/lib/resources.js +2 -2
- package/dist/lib/routers.d.ts +81 -0
- package/dist/lib/routers.js +153 -0
- package/dist/lib/routine-context.d.ts +1 -0
- package/dist/lib/routine-context.js +7 -2
- package/dist/lib/routine-notify-owner.d.ts +1 -1
- package/dist/lib/routine-notify.d.ts +1 -1
- package/dist/lib/routine-notify.js +1 -1
- package/dist/lib/routine-process-cleanup.d.ts +1 -1
- package/dist/lib/routine-readiness.d.ts +1 -1
- package/dist/lib/routine-readiness.js +11 -4
- package/dist/lib/routines-placement.d.ts +12 -2
- package/dist/lib/routines-placement.js +20 -3
- package/dist/lib/routines-project.d.ts +1 -1
- package/dist/lib/routines-project.js +1 -1
- package/dist/lib/rules/compile.d.ts +1 -0
- package/dist/lib/rules/compile.js +10 -2
- package/dist/lib/rules/rules.js +1 -1
- package/dist/lib/run-notify.js +1 -1
- package/dist/lib/runner.d.ts +80 -2
- package/dist/lib/runner.js +261 -67
- package/dist/lib/sandbox.d.ts +1 -1
- package/dist/lib/scheduler.d.ts +1 -1
- package/dist/lib/scheduler.js +1 -1
- package/dist/lib/{routines.d.ts → scheduling/routines.d.ts} +54 -9
- package/dist/lib/{routines.js → scheduling/routines.js} +173 -14
- package/dist/lib/secrets/Agents CLI.app/Contents/CodeResources +0 -0
- package/dist/lib/secrets/Agents CLI.app/Contents/MacOS/Agents CLI +0 -0
- package/dist/lib/secrets/agent.js +2 -2
- package/dist/lib/secrets/audit.js +1 -1
- package/dist/lib/secrets/bundles.d.ts +4 -4
- package/dist/lib/secrets/bundles.js +8 -8
- package/dist/lib/secrets/headless.d.ts +16 -0
- package/dist/lib/secrets/headless.js +21 -0
- package/dist/lib/secrets/index.js +1 -1
- package/dist/lib/secrets/list-filter.d.ts +1 -1
- package/dist/lib/secrets/list-filter.js +2 -2
- package/dist/lib/secrets/push.d.ts +1 -1
- package/dist/lib/secrets/push.js +10 -12
- package/dist/lib/secrets/remote.d.ts +21 -17
- package/dist/lib/secrets/remote.js +39 -23
- package/dist/lib/secrets/vault.js +2 -2
- package/dist/lib/self-heal/checks/shadowing.js +1 -1
- package/dist/lib/self-heal/checks/shims.js +1 -1
- package/dist/lib/self-update.d.ts +39 -4
- package/dist/lib/self-update.js +78 -25
- package/dist/lib/serve/control.d.ts +1 -1
- package/dist/lib/serve/control.js +4 -4
- package/dist/lib/serve/data.d.ts +1 -1
- package/dist/lib/serve/data.js +1 -1
- package/dist/lib/serve/stream.js +1 -1
- package/dist/lib/service-manifest.d.ts +29 -0
- package/dist/lib/service-manifest.js +80 -0
- package/dist/lib/session/active.d.ts +59 -7
- package/dist/lib/session/active.js +158 -17
- package/dist/lib/session/bash-command.js +1 -1
- package/dist/lib/session/claude-accounts.d.ts +1 -1
- package/dist/lib/session/claude-accounts.js +1 -1
- package/dist/lib/session/db.d.ts +10 -2
- package/dist/lib/session/db.js +25 -6
- package/dist/lib/session/discover.d.ts +77 -0
- package/dist/lib/session/discover.js +130 -25
- package/dist/lib/session/fork.js +1 -1
- package/dist/lib/session/inject.d.ts +1 -1
- package/dist/lib/session/insights.d.ts +1 -1
- package/dist/lib/session/insights.js +1 -1
- package/dist/lib/session/live-metadata.d.ts +15 -0
- package/dist/lib/session/live-metadata.js +76 -0
- package/dist/lib/session/migrate-targets.d.ts +1 -1
- package/dist/lib/session/recovery.d.ts +1 -1
- package/dist/lib/session/recovery.js +2 -2
- package/dist/lib/session/remote-active.d.ts +10 -1
- package/dist/lib/session/remote-active.js +8 -3
- package/dist/lib/session/remote-bundle.d.ts +1 -1
- package/dist/lib/session/remote-bundle.js +2 -2
- package/dist/lib/session/remote-list.d.ts +15 -4
- package/dist/lib/session/remote-list.js +40 -3
- package/dist/lib/session/remote.d.ts +5 -5
- package/dist/lib/session/remote.js +13 -17
- package/dist/lib/session/resume-command.d.ts +3 -2
- package/dist/lib/session/resume-command.js +4 -3
- package/dist/lib/session/resume-owner.d.ts +3 -3
- package/dist/lib/session/resume-owner.js +3 -3
- package/dist/lib/session/session-cache.d.ts +12 -0
- package/dist/lib/session/session-cache.js +2 -0
- package/dist/lib/session/share-html.d.ts +55 -0
- package/dist/lib/session/share-html.js +319 -0
- package/dist/lib/session/shell-programs.js +1 -1
- package/dist/lib/session/state.js +5 -5
- package/dist/lib/session/team-filter.d.ts +1 -1
- package/dist/lib/session/team-filter.js +1 -1
- package/dist/lib/session/types.d.ts +3 -3
- package/dist/lib/session/width.d.ts +2 -2
- package/dist/lib/session/width.js +2 -2
- package/dist/lib/settings-manifest.d.ts +2 -0
- package/dist/lib/settings-manifest.js +81 -3
- package/dist/lib/share/delete.d.ts +21 -0
- package/dist/lib/share/delete.js +42 -0
- package/dist/lib/share/publish.d.ts +120 -0
- package/dist/lib/share/publish.js +243 -18
- package/dist/lib/share/worker-template.js +172 -9
- package/dist/lib/shims.js +35 -7
- package/dist/lib/smart-launch.d.ts +4 -4
- package/dist/lib/smart-launch.js +6 -4
- package/dist/lib/snapshot.d.ts +4 -4
- package/dist/lib/snapshot.js +2 -2
- package/dist/lib/ssh-exec.d.ts +1 -1
- package/dist/lib/ssh-exec.js +2 -2
- package/dist/lib/ssh-tunnel.d.ts +10 -10
- package/dist/lib/ssh-tunnel.js +12 -12
- package/dist/lib/staleness/checkers/hooks.d.ts +1 -1
- package/dist/lib/staleness/checkers/hooks.js +1 -1
- package/dist/lib/staleness/detectors/permissions.js +0 -23
- package/dist/lib/staleness/detectors/plugins.js +1 -1
- package/dist/lib/staleness/fingerprint.js +1 -1
- package/dist/lib/staleness/layers.d.ts +1 -1
- package/dist/lib/staleness/layers.js +1 -1
- package/dist/lib/staleness/writers/mcp.js +3 -1
- package/dist/lib/staleness/writers/plugins.js +1 -1
- package/dist/lib/staleness/writers/types.d.ts +8 -0
- package/dist/lib/startup/command-registry.d.ts +6 -147
- package/dist/lib/startup/command-registry.js +40 -303
- package/dist/lib/startup/dev-build.d.ts +0 -21
- package/dist/lib/startup/dev-build.js +29 -8
- package/dist/lib/state.d.ts +19 -1
- package/dist/lib/state.js +120 -51
- package/dist/lib/subagents.js +1 -1
- package/dist/lib/sync-status.js +1 -1
- package/dist/lib/sync-umbrella.d.ts +6 -1
- package/dist/lib/sync-umbrella.js +9 -4
- package/dist/lib/teams/agents.js +2 -2
- package/dist/lib/teams/registry.js +1 -1
- package/dist/lib/terminal/backends/vscodium-agent.js +1 -1
- package/dist/lib/terminal/engine.js +1 -1
- package/dist/lib/terminal/index.d.ts +4 -0
- package/dist/lib/terminal/index.js +3 -0
- package/dist/lib/terminal/inject.d.ts +2 -2
- package/dist/lib/terminal/inject.js +2 -2
- package/dist/lib/terminal/transport.js +1 -1
- package/dist/lib/tmux/index.d.ts +1 -1
- package/dist/lib/tmux/index.js +1 -1
- package/dist/lib/tmux/session.d.ts +21 -5
- package/dist/lib/tmux/session.js +76 -22
- package/dist/lib/triggers/handlers.d.ts +38 -3
- package/dist/lib/triggers/handlers.js +58 -7
- package/dist/lib/triggers/webhook.d.ts +113 -5
- package/dist/lib/triggers/webhook.js +223 -44
- package/dist/lib/types.d.ts +19 -7
- package/dist/lib/usage-fleet.d.ts +1 -1
- package/dist/lib/usage-fleet.js +1 -1
- package/dist/lib/usage-refresh.d.ts +1 -1
- package/dist/lib/usage-refresh.js +3 -3
- package/dist/lib/version-duplicates.js +2 -2
- package/dist/lib/view-types.d.ts +56 -0
- package/dist/lib/view-types.js +1 -0
- package/dist/lib/watchdog/rotate.d.ts +2 -2
- package/dist/lib/watchdog/rotate.js +3 -3
- package/dist/lib/watchdog/runner.d.ts +2 -3
- package/dist/lib/watchdog/runner.js +3 -4
- package/dist/lib/workflows.js +1 -1
- package/dist/lib/wrap.d.ts +33 -0
- package/dist/lib/wrap.js +70 -0
- package/package.json +2 -1
- package/scripts/postinstall.js +2 -2
- package/dist/commands/bench.d.ts +0 -2
- package/dist/commands/bench.js +0 -101
- package/dist/commands/cp.d.ts +0 -84
- package/dist/commands/cp.js +0 -231
- package/dist/commands/login.d.ts +0 -2
- package/dist/lib/resources/commands.d.ts +0 -46
- package/dist/lib/resources/commands.js +0 -211
- package/dist/lib/resources/hooks.d.ts +0 -16
- package/dist/lib/resources/hooks.js +0 -153
- package/dist/lib/resources/mcp.d.ts +0 -34
- package/dist/lib/resources/mcp.js +0 -250
- package/dist/lib/resources/memory.d.ts +0 -15
- package/dist/lib/resources/memory.js +0 -46
- package/dist/lib/resources/permissions.d.ts +0 -13
- package/dist/lib/resources/permissions.js +0 -202
- package/dist/lib/resources/rules.d.ts +0 -46
- package/dist/lib/resources/rules.js +0 -179
- package/dist/lib/resources/skills.d.ts +0 -37
- package/dist/lib/resources/skills.js +0 -247
- package/dist/lib/resources/subagents.d.ts +0 -46
- package/dist/lib/resources/subagents.js +0 -202
- /package/dist/lib/{feed.d.ts → feed/feed.d.ts} +0 -0
|
@@ -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,47 @@ 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
|
+
/**
|
|
247
|
+
* Arc answers `Browser.getVersion` (so a connection succeeds) but exposes zero
|
|
248
|
+
* CDP page targets via every discovery method and CRASHES when a new tab is
|
|
249
|
+
* requested via `Target.createTarget` (verified live, PR #2778). It is therefore
|
|
250
|
+
* not drivable. This is the single clear, actionable error every tab-creating
|
|
251
|
+
* path throws instead of crashing the user's Arc window.
|
|
252
|
+
*/
|
|
253
|
+
export function arcNotDrivableError(profileName) {
|
|
254
|
+
const scope = profileName ? ` (profile "${profileName}")` : '';
|
|
255
|
+
return new Error(actionable(`Browser "arc"${scope} is not drivable: Arc exposes no CDP page targets and`, 'crashes when a new tab is requested, so `agents browser` cannot control it.', 'Use a Chromium-family browser instead — Comet, Chrome, Chromium, or Brave:', ' agents browser profiles create <name> --browser comet'));
|
|
256
|
+
}
|
|
257
|
+
/** Derive a human label from an explicit title or a navigated URL. */
|
|
258
|
+
export function deriveTaskLabel(opts) {
|
|
259
|
+
if (opts.title?.trim())
|
|
260
|
+
return opts.title.trim();
|
|
261
|
+
if (opts.existing && opts.existing !== 'untitled')
|
|
262
|
+
return opts.existing;
|
|
263
|
+
if (opts.url) {
|
|
264
|
+
try {
|
|
265
|
+
const host = new URL(opts.url).hostname.replace(/^www\./, '');
|
|
266
|
+
if (host)
|
|
267
|
+
return host;
|
|
268
|
+
}
|
|
269
|
+
catch {
|
|
270
|
+
/* unparseable */
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
return opts.existing ?? 'untitled';
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* True when `key` is the bare profile name or one of its composite endpoint
|
|
277
|
+
* keys (`name@endpoint`). Used by status/stop so `comet-local` matches
|
|
278
|
+
* `comet-local@endpoint-0`.
|
|
279
|
+
*/
|
|
280
|
+
export function connectionKeyMatchesProfile(key, profileName) {
|
|
281
|
+
return key === profileName || key.startsWith(`${profileName}@`) || key.startsWith(`${profileName}.`);
|
|
282
|
+
}
|
|
241
283
|
/**
|
|
242
284
|
* Resolve the identity stamped on a task at start: WHO (`owner`) and WHICH run
|
|
243
285
|
* (`launchId`). The forwarded values come from the caller's own CLI process and
|
|
@@ -280,14 +322,20 @@ export class BrowserService {
|
|
|
280
322
|
// passed an unknown name. The composite identifier `<profile>@<endpoint>`
|
|
281
323
|
// is what the connection map + per-profile runtime dirs are keyed on, so
|
|
282
324
|
// a single YAML profile can run at multiple endpoints concurrently.
|
|
325
|
+
//
|
|
326
|
+
// The YAML profile's bare `name` stays on `profile.name` for status
|
|
327
|
+
// matching; only the connection/runtime key uses the composite.
|
|
283
328
|
const resolved = resolveEndpoint(profile, opts.endpointName);
|
|
284
329
|
const composite = `${profileName}@${resolved.name}`;
|
|
285
330
|
const effectiveProfile = {
|
|
286
331
|
...profile,
|
|
332
|
+
// Runtime/connect path key — NOT the bare YAML name. Connections and
|
|
333
|
+
// tasks.json live under this composite so multi-endpoint is safe.
|
|
287
334
|
name: composite,
|
|
288
335
|
binary: resolved.binary,
|
|
289
336
|
targetFilter: resolved.targetFilter,
|
|
290
337
|
};
|
|
338
|
+
const taskId = generateTaskId();
|
|
291
339
|
let taskName;
|
|
292
340
|
if (opts.taskName) {
|
|
293
341
|
if (this.hasTaskNamed(opts.taskName)) {
|
|
@@ -296,9 +344,10 @@ export class BrowserService {
|
|
|
296
344
|
taskName = opts.taskName;
|
|
297
345
|
}
|
|
298
346
|
else {
|
|
299
|
-
|
|
347
|
+
// Address by the short machine id — no word-salad. Status shows `label`.
|
|
348
|
+
taskName = taskId;
|
|
300
349
|
}
|
|
301
|
-
const
|
|
350
|
+
const taskLabel = deriveTaskLabel({ title: opts.title, url: opts.url });
|
|
302
351
|
let conn = this.connections.get(composite);
|
|
303
352
|
let effectiveProfileName = composite;
|
|
304
353
|
// If we have a cached connection, confirm it's still usable before any
|
|
@@ -344,9 +393,12 @@ export class BrowserService {
|
|
|
344
393
|
else if (!conn) {
|
|
345
394
|
conn = await this.connectProfile(effectiveProfile, resolved.target);
|
|
346
395
|
conn.profileName = composite;
|
|
396
|
+
conn.bareName = profileName;
|
|
347
397
|
this.connections.set(composite, conn);
|
|
348
398
|
await this.applyDefaultDownloadBehavior(conn, composite);
|
|
349
399
|
}
|
|
400
|
+
if (conn && !conn.bareName)
|
|
401
|
+
conn.bareName = profileName;
|
|
350
402
|
// Browsers launch with --no-startup-window (session-cookie persistence,
|
|
351
403
|
// see launchBrowser), so a bare `start` with no --url would otherwise
|
|
352
404
|
// leave the user staring at a process with zero windows. Recreate the
|
|
@@ -363,9 +415,9 @@ export class BrowserService {
|
|
|
363
415
|
if (!opts.url && !conn.electron) {
|
|
364
416
|
const { targetInfos } = (await conn.cdp.send('Target.getTargets'));
|
|
365
417
|
if (!targetInfos.some((t) => t.type === 'page')) {
|
|
366
|
-
const created =
|
|
418
|
+
const created = await this.createPageTarget(conn, {
|
|
367
419
|
url: 'about:blank',
|
|
368
|
-
})
|
|
420
|
+
});
|
|
369
421
|
startupBlankTargetId = created.targetId;
|
|
370
422
|
this.invalidateTargetCache(conn);
|
|
371
423
|
}
|
|
@@ -374,6 +426,7 @@ export class BrowserService {
|
|
|
374
426
|
const task = {
|
|
375
427
|
id: taskId,
|
|
376
428
|
name: taskName,
|
|
429
|
+
label: taskLabel,
|
|
377
430
|
profile: effectiveProfileName,
|
|
378
431
|
tabs: {},
|
|
379
432
|
currentTabId: undefined,
|
|
@@ -443,10 +496,7 @@ export class BrowserService {
|
|
|
443
496
|
let tabId;
|
|
444
497
|
if (opts.url && !conn.electron) {
|
|
445
498
|
const adopted = opts.fresh ? undefined : await this.adoptTabShowing(conn, opts.url);
|
|
446
|
-
const targetId = adopted ??
|
|
447
|
-
(await conn.cdp.send('Target.createTarget', {
|
|
448
|
-
url: opts.url,
|
|
449
|
-
})).targetId;
|
|
499
|
+
const targetId = adopted ?? (await this.createPageTarget(conn, { url: opts.url })).targetId;
|
|
450
500
|
const shortId = generateShortId();
|
|
451
501
|
task.tabs[shortId] = targetId;
|
|
452
502
|
task.currentTabId = shortId;
|
|
@@ -574,9 +624,18 @@ export class BrowserService {
|
|
|
574
624
|
return reapAbandonedTasks(this, opts);
|
|
575
625
|
}
|
|
576
626
|
async stop(taskName) {
|
|
627
|
+
// Rehydrate from tasks.json when the daemon restarted and lost RAM state.
|
|
628
|
+
try {
|
|
629
|
+
await this.findTask(taskName);
|
|
630
|
+
}
|
|
631
|
+
catch {
|
|
632
|
+
// findTask throws when truly gone; fall through to the miss return.
|
|
633
|
+
}
|
|
577
634
|
for (const [profileName, conn] of this.connections) {
|
|
578
|
-
const task =
|
|
635
|
+
const task = this.lookupTaskOnConn(conn, taskName);
|
|
579
636
|
if (task) {
|
|
637
|
+
// Map key may be task.name, not the caller-supplied handle.
|
|
638
|
+
const mapKey = this.taskMapKey(conn, task) ?? taskName;
|
|
580
639
|
// Get domains from tabs before closing (for history)
|
|
581
640
|
const domains = new Set();
|
|
582
641
|
try {
|
|
@@ -608,16 +667,16 @@ export class BrowserService {
|
|
|
608
667
|
conn.sessionCache.delete(cdpId);
|
|
609
668
|
}
|
|
610
669
|
this.invalidateTargetCache(conn);
|
|
611
|
-
conn.tasks.delete(
|
|
670
|
+
conn.tasks.delete(mapKey);
|
|
612
671
|
await this.saveTaskState(profileName, conn.tasks);
|
|
613
|
-
emit('browser.close', { profile: profileName, task:
|
|
672
|
+
emit('browser.close', { profile: profileName, task: task.name });
|
|
614
673
|
void import('../analytics/usage-db.js').then(({ recordUsage }) => {
|
|
615
674
|
recordUsage({
|
|
616
675
|
kind: 'browser',
|
|
617
676
|
name: profileName,
|
|
618
677
|
event: 'close',
|
|
619
678
|
source: 'browser',
|
|
620
|
-
meta: { task:
|
|
679
|
+
meta: { task: task.name },
|
|
621
680
|
});
|
|
622
681
|
}).catch(() => { });
|
|
623
682
|
if (conn.forkedFrom && conn.tasks.size === 0) {
|
|
@@ -669,6 +728,7 @@ export class BrowserService {
|
|
|
669
728
|
}
|
|
670
729
|
async navigate(taskId, url, profileName) {
|
|
671
730
|
const { conn, task } = await this.findTask(taskId, profileName);
|
|
731
|
+
this.maybeUpdateLabelFromUrl(conn, task, url);
|
|
672
732
|
// If we have a current tab, navigate in it (reuse)
|
|
673
733
|
const currentShortId = task.currentTabId;
|
|
674
734
|
if (currentShortId && task.tabs[currentShortId]) {
|
|
@@ -695,9 +755,7 @@ export class BrowserService {
|
|
|
695
755
|
return { tabId: shortId, url, created: true };
|
|
696
756
|
}
|
|
697
757
|
// Chrome: create new tab
|
|
698
|
-
const result =
|
|
699
|
-
url,
|
|
700
|
-
}));
|
|
758
|
+
const result = await this.createPageTarget(conn, { url });
|
|
701
759
|
const shortId = generateShortId();
|
|
702
760
|
task.tabs[shortId] = result.targetId;
|
|
703
761
|
task.currentTabId = shortId;
|
|
@@ -711,9 +769,7 @@ export class BrowserService {
|
|
|
711
769
|
if (conn.electron) {
|
|
712
770
|
throw new Error('Electron apps do not support opening additional tabs');
|
|
713
771
|
}
|
|
714
|
-
const result =
|
|
715
|
-
url,
|
|
716
|
-
}));
|
|
772
|
+
const result = await this.createPageTarget(conn, { url });
|
|
717
773
|
const shortId = generateShortId();
|
|
718
774
|
task.tabs[shortId] = result.targetId;
|
|
719
775
|
task.currentTabId = shortId; // new tab becomes current
|
|
@@ -1357,14 +1413,25 @@ export class BrowserService {
|
|
|
1357
1413
|
return { path: stageUploadFile(source) };
|
|
1358
1414
|
}
|
|
1359
1415
|
async status(profileName) {
|
|
1416
|
+
// Reconnect live browsers after a daemon restart so status is not empty
|
|
1417
|
+
// while tasks.json still holds open work.
|
|
1418
|
+
await this.rehydrateAllFromDisk();
|
|
1360
1419
|
const seen = new Set();
|
|
1361
1420
|
const statuses = [];
|
|
1362
|
-
|
|
1421
|
+
// Connections are keyed by composite `<profile>@<endpoint>`. A bare
|
|
1422
|
+
// `--profile comet-local` must still list those tasks (same rule as
|
|
1423
|
+
// stopProfile). Match exact key, bareName, and composite/fork prefixes.
|
|
1424
|
+
const candidates = profileName
|
|
1425
|
+
? [...this.connections.keys()].filter((k) => connectionKeyMatchesProfile(k, profileName) ||
|
|
1426
|
+
this.connections.get(k)?.bareName === profileName)
|
|
1427
|
+
: Array.from(this.connections.keys());
|
|
1363
1428
|
for (const name of candidates) {
|
|
1364
1429
|
const status = await this.getProfileStatus(name);
|
|
1365
1430
|
if (status) {
|
|
1366
1431
|
statuses.push(status);
|
|
1367
1432
|
seen.add(name);
|
|
1433
|
+
if (status.name)
|
|
1434
|
+
seen.add(status.name);
|
|
1368
1435
|
}
|
|
1369
1436
|
}
|
|
1370
1437
|
if (!profileName) {
|
|
@@ -1372,12 +1439,14 @@ export class BrowserService {
|
|
|
1372
1439
|
for (const profile of profiles) {
|
|
1373
1440
|
if (seen.has(profile.name))
|
|
1374
1441
|
continue;
|
|
1442
|
+
if ([...seen].some((s) => connectionKeyMatchesProfile(s, profile.name)))
|
|
1443
|
+
continue;
|
|
1375
1444
|
const reconciled = await this.reconcileFromDisk(profile.name);
|
|
1376
1445
|
if (reconciled)
|
|
1377
1446
|
statuses.push(reconciled);
|
|
1378
1447
|
}
|
|
1379
1448
|
}
|
|
1380
|
-
else if (
|
|
1449
|
+
else if (statuses.length === 0) {
|
|
1381
1450
|
const reconciled = await this.reconcileFromDisk(profileName);
|
|
1382
1451
|
if (reconciled)
|
|
1383
1452
|
statuses.push(reconciled);
|
|
@@ -1385,16 +1454,37 @@ export class BrowserService {
|
|
|
1385
1454
|
return statuses;
|
|
1386
1455
|
}
|
|
1387
1456
|
async reconcileFromDisk(profileName) {
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
const
|
|
1392
|
-
|
|
1457
|
+
// Runtime dirs live under the composite (`comet-local@endpoint-0`), not the
|
|
1458
|
+
// bare YAML name. Walk every matching cache dir so bare `status --profile`
|
|
1459
|
+
// sees the same tasks as unscoped `status`.
|
|
1460
|
+
const dirs = listProfileCacheDirs(profileName);
|
|
1461
|
+
if (dirs.length === 0) {
|
|
1462
|
+
// Legacy: non-composite dir named exactly the bare profile.
|
|
1463
|
+
const info = getRunningChromeInfo(profileName);
|
|
1464
|
+
if (!info)
|
|
1465
|
+
return null;
|
|
1466
|
+
return this.profileStatusFromDisk(profileName, profileName, info.port, info.pid);
|
|
1467
|
+
}
|
|
1468
|
+
// Prefer a dir that still has a live browser; fall back to the first.
|
|
1469
|
+
for (const dir of dirs) {
|
|
1470
|
+
const dirName = path.basename(dir);
|
|
1471
|
+
const info = getRunningChromeInfo(dirName);
|
|
1472
|
+
if (!info)
|
|
1473
|
+
continue;
|
|
1474
|
+
return this.profileStatusFromDisk(profileName, dirName, info.port, info.pid);
|
|
1475
|
+
}
|
|
1476
|
+
return null;
|
|
1477
|
+
}
|
|
1478
|
+
async profileStatusFromDisk(displayName, runtimeKey, port, pid) {
|
|
1479
|
+
const bare = displayName.includes('@') ? displayName.split('@')[0] : displayName;
|
|
1480
|
+
const profile = await getProfile(bare);
|
|
1481
|
+
const tasks = this.loadTaskState(runtimeKey);
|
|
1393
1482
|
const taskStatuses = [];
|
|
1394
1483
|
for (const [, task] of tasks) {
|
|
1395
1484
|
taskStatuses.push({
|
|
1396
1485
|
id: task.id,
|
|
1397
1486
|
name: task.name,
|
|
1487
|
+
label: task.label ?? task.name,
|
|
1398
1488
|
tabCount: Object.keys(task.tabs).length,
|
|
1399
1489
|
currentTabId: task.currentTabId,
|
|
1400
1490
|
createdAt: task.createdAt,
|
|
@@ -1402,11 +1492,11 @@ export class BrowserService {
|
|
|
1402
1492
|
}
|
|
1403
1493
|
const configuredPort = profile ? extractConfiguredPort(profile) : undefined;
|
|
1404
1494
|
return {
|
|
1405
|
-
name:
|
|
1495
|
+
name: runtimeKey,
|
|
1406
1496
|
running: true,
|
|
1407
|
-
port
|
|
1408
|
-
pid
|
|
1409
|
-
configuredPort: configuredPort !==
|
|
1497
|
+
port,
|
|
1498
|
+
pid,
|
|
1499
|
+
configuredPort: configuredPort !== port ? configuredPort : undefined,
|
|
1410
1500
|
tasks: taskStatuses,
|
|
1411
1501
|
};
|
|
1412
1502
|
}
|
|
@@ -1843,6 +1933,7 @@ export class BrowserService {
|
|
|
1843
1933
|
port,
|
|
1844
1934
|
pid,
|
|
1845
1935
|
electron: true,
|
|
1936
|
+
browserType: profile.browser,
|
|
1846
1937
|
targetFilter: profile.targetFilter,
|
|
1847
1938
|
profileName: forkName,
|
|
1848
1939
|
forkedFrom: profile.name,
|
|
@@ -1877,6 +1968,7 @@ export class BrowserService {
|
|
|
1877
1968
|
port: existingInfo.port,
|
|
1878
1969
|
pid: existingInfo.pid,
|
|
1879
1970
|
electron: effectiveProfile.electron,
|
|
1971
|
+
browserType: effectiveProfile.browser,
|
|
1880
1972
|
targetFilter: effectiveProfile.targetFilter,
|
|
1881
1973
|
tasks,
|
|
1882
1974
|
sessionCache: new Map(),
|
|
@@ -1908,6 +2000,7 @@ export class BrowserService {
|
|
|
1908
2000
|
port: conn.port,
|
|
1909
2001
|
pid: conn.pid,
|
|
1910
2002
|
electron: profile.electron,
|
|
2003
|
+
browserType: profile.browser,
|
|
1911
2004
|
targetFilter: profile.targetFilter,
|
|
1912
2005
|
tasks: conn.pid === 0 ? this.loadTaskState(profile.name) : new Map(),
|
|
1913
2006
|
sessionCache: new Map(),
|
|
@@ -1921,6 +2014,7 @@ export class BrowserService {
|
|
|
1921
2014
|
port: conn.port,
|
|
1922
2015
|
pid: conn.pid,
|
|
1923
2016
|
electron: profile.electron,
|
|
2017
|
+
browserType: profile.browser,
|
|
1924
2018
|
targetFilter: profile.targetFilter,
|
|
1925
2019
|
tasks: new Map(),
|
|
1926
2020
|
sessionCache: new Map(),
|
|
@@ -1942,6 +2036,7 @@ export class BrowserService {
|
|
|
1942
2036
|
port: 0,
|
|
1943
2037
|
pid: 0,
|
|
1944
2038
|
electron: profile.electron,
|
|
2039
|
+
browserType: profile.browser,
|
|
1945
2040
|
targetFilter: profile.targetFilter,
|
|
1946
2041
|
tasks: this.loadTaskState(profile.name),
|
|
1947
2042
|
sessionCache: new Map(),
|
|
@@ -1959,6 +2054,7 @@ export class BrowserService {
|
|
|
1959
2054
|
port,
|
|
1960
2055
|
pid: 0,
|
|
1961
2056
|
electron: profile.electron,
|
|
2057
|
+
browserType: profile.browser,
|
|
1962
2058
|
targetFilter: profile.targetFilter,
|
|
1963
2059
|
tasks: this.loadTaskState(profile.name),
|
|
1964
2060
|
sessionCache: new Map(),
|
|
@@ -1969,6 +2065,19 @@ export class BrowserService {
|
|
|
1969
2065
|
async enableDomains(cdp) {
|
|
1970
2066
|
await cdp.send('Target.setDiscoverTargets', { discover: true });
|
|
1971
2067
|
}
|
|
2068
|
+
/**
|
|
2069
|
+
* The single path that opens a new CDP page/window target. It exists so the
|
|
2070
|
+
* Arc guard lives in exactly one place: `Target.createTarget` crashes Arc
|
|
2071
|
+
* (verified, PR #2778), so an Arc connection throws a clear, actionable error
|
|
2072
|
+
* here instead of every call site re-checking. Every drivable Chromium-family
|
|
2073
|
+
* browser goes straight through.
|
|
2074
|
+
*/
|
|
2075
|
+
async createPageTarget(conn, params) {
|
|
2076
|
+
if (conn.browserType === 'arc') {
|
|
2077
|
+
throw arcNotDrivableError(conn.bareName ?? conn.profileName);
|
|
2078
|
+
}
|
|
2079
|
+
return (await conn.cdp.send('Target.createTarget', params));
|
|
2080
|
+
}
|
|
1972
2081
|
async getOrCreateWindow(conn) {
|
|
1973
2082
|
// Already have a window for this profile?
|
|
1974
2083
|
if (conn.windowId) {
|
|
@@ -2000,27 +2109,152 @@ export class BrowserService {
|
|
|
2000
2109
|
`Available page targets:\n${candidates || ' (none)'}`);
|
|
2001
2110
|
}
|
|
2002
2111
|
// First ever use - create window
|
|
2003
|
-
const result =
|
|
2112
|
+
const result = await this.createPageTarget(conn, {
|
|
2004
2113
|
url: 'about:blank',
|
|
2005
2114
|
newWindow: true,
|
|
2006
|
-
})
|
|
2115
|
+
});
|
|
2007
2116
|
conn.windowId = result.targetId;
|
|
2008
2117
|
return result.targetId;
|
|
2009
2118
|
}
|
|
2010
2119
|
hasTaskNamed(name) {
|
|
2011
2120
|
for (const conn of this.connections.values()) {
|
|
2012
|
-
if (
|
|
2121
|
+
if (this.lookupTaskOnConn(conn, name))
|
|
2013
2122
|
return true;
|
|
2014
2123
|
}
|
|
2015
2124
|
return false;
|
|
2016
2125
|
}
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
if (
|
|
2021
|
-
return
|
|
2126
|
+
/** Map key for a task on a connection (tasks are keyed by `name`). */
|
|
2127
|
+
taskMapKey(conn, task) {
|
|
2128
|
+
for (const [key, t] of conn.tasks) {
|
|
2129
|
+
if (t === task || t.id === task.id || t.name === task.name)
|
|
2130
|
+
return key;
|
|
2022
2131
|
}
|
|
2023
|
-
|
|
2132
|
+
return undefined;
|
|
2133
|
+
}
|
|
2134
|
+
/** Find a task by map key, id, or name on one connection. */
|
|
2135
|
+
lookupTaskOnConn(conn, handle) {
|
|
2136
|
+
const direct = conn.tasks.get(handle);
|
|
2137
|
+
if (direct)
|
|
2138
|
+
return direct;
|
|
2139
|
+
for (const task of conn.tasks.values()) {
|
|
2140
|
+
if (task.id === handle || task.name === handle)
|
|
2141
|
+
return task;
|
|
2142
|
+
}
|
|
2143
|
+
return undefined;
|
|
2144
|
+
}
|
|
2145
|
+
maybeUpdateLabelFromUrl(conn, task, url) {
|
|
2146
|
+
if (task.label && task.label !== 'untitled')
|
|
2147
|
+
return;
|
|
2148
|
+
const next = deriveTaskLabel({ url, existing: task.label });
|
|
2149
|
+
if (next === task.label)
|
|
2150
|
+
return;
|
|
2151
|
+
task.label = next;
|
|
2152
|
+
void this.saveTaskState(task.profile, conn.tasks).catch(() => { });
|
|
2153
|
+
}
|
|
2154
|
+
/**
|
|
2155
|
+
* Resolve a task for a page/close verb.
|
|
2156
|
+
*
|
|
2157
|
+
* - explicit `task` handle → look up (with disk rehydrate on RAM miss)
|
|
2158
|
+
* - no handle, exactly one live task for this caller → use it
|
|
2159
|
+
* - no handle, multiple for this caller → error listing them + `--task`
|
|
2160
|
+
* - no handle, zero for this caller, createIfMissing → start a new task
|
|
2161
|
+
* - no handle, zero, !createIfMissing → return null (caller reports "nothing to close")
|
|
2162
|
+
*/
|
|
2163
|
+
async resolveOrCreateTask(opts) {
|
|
2164
|
+
if (opts.task) {
|
|
2165
|
+
const found = await this.findTask(opts.task, opts.profile);
|
|
2166
|
+
return { ...found, created: false };
|
|
2167
|
+
}
|
|
2168
|
+
// After a daemon restart the connections map is empty while tasks.json still
|
|
2169
|
+
// holds live tasks. Rehydrate before identity matching so done/screenshot
|
|
2170
|
+
// without --task still find the caller's task.
|
|
2171
|
+
await this.rehydrateAllFromDisk();
|
|
2172
|
+
const caller = {
|
|
2173
|
+
sessionId: opts.sessionId,
|
|
2174
|
+
launchId: opts.launchId,
|
|
2175
|
+
};
|
|
2176
|
+
const matches = this.listTasksForCaller(caller);
|
|
2177
|
+
if (matches.length === 1) {
|
|
2178
|
+
const m = matches[0];
|
|
2179
|
+
await this.touchTask(m.conn, m.task);
|
|
2180
|
+
return { conn: m.conn, task: m.task, profileName: m.profileName, created: false };
|
|
2181
|
+
}
|
|
2182
|
+
if (matches.length > 1) {
|
|
2183
|
+
const lines = matches.map((m) => {
|
|
2184
|
+
const ageMs = Date.now() - m.task.createdAt;
|
|
2185
|
+
const age = ageMs < 60_000
|
|
2186
|
+
? `${Math.round(ageMs / 1000)}s`
|
|
2187
|
+
: `${Math.round(ageMs / 60_000)}m`;
|
|
2188
|
+
const label = m.task.label ?? m.task.name;
|
|
2189
|
+
const url = this.currentUrlHint(m.conn, m.task) ?? '-';
|
|
2190
|
+
return ` ${label} id=${m.task.name} url=${url} age=${age}`;
|
|
2191
|
+
});
|
|
2192
|
+
throw new Error(actionable(`Multiple browser tasks for this session — pass --task <name>:`, ...lines, `Next: agents browser status`));
|
|
2193
|
+
}
|
|
2194
|
+
// Zero matches for this caller.
|
|
2195
|
+
if (!opts.createIfMissing)
|
|
2196
|
+
return null;
|
|
2197
|
+
// Implicit start on the default / named profile.
|
|
2198
|
+
let profileName = opts.profile;
|
|
2199
|
+
if (!profileName) {
|
|
2200
|
+
const { ensureDefaultBrowserProfile } = await import('./profiles.js');
|
|
2201
|
+
const detected = await ensureDefaultBrowserProfile();
|
|
2202
|
+
profileName = detected.name;
|
|
2203
|
+
}
|
|
2204
|
+
const started = await this.start(profileName, {
|
|
2205
|
+
url: opts.url,
|
|
2206
|
+
actor: opts.actor,
|
|
2207
|
+
launchId: opts.launchId,
|
|
2208
|
+
sessionId: opts.sessionId,
|
|
2209
|
+
title: opts.title,
|
|
2210
|
+
});
|
|
2211
|
+
return { ...(await this.findTask(started.name, started.profile)), created: true };
|
|
2212
|
+
}
|
|
2213
|
+
listTasksForCaller(caller) {
|
|
2214
|
+
const out = [];
|
|
2215
|
+
const hasIdentity = !!(caller.sessionId || caller.launchId);
|
|
2216
|
+
for (const [key, conn] of this.connections) {
|
|
2217
|
+
for (const task of conn.tasks.values()) {
|
|
2218
|
+
if (hasIdentity) {
|
|
2219
|
+
if (taskMatchesCaller(task, caller)) {
|
|
2220
|
+
out.push({ conn, task, profileName: conn.profileName ?? key });
|
|
2221
|
+
}
|
|
2222
|
+
}
|
|
2223
|
+
else {
|
|
2224
|
+
// No caller identity: only match tasks that also lack identity
|
|
2225
|
+
// (a human-driven shell). If there is exactly one live task total,
|
|
2226
|
+
// the single-task path still needs that listed — fall through below.
|
|
2227
|
+
if (!task.sessionId && !task.launchId) {
|
|
2228
|
+
out.push({ conn, task, profileName: conn.profileName ?? key });
|
|
2229
|
+
}
|
|
2230
|
+
}
|
|
2231
|
+
}
|
|
2232
|
+
}
|
|
2233
|
+
// No-identity caller with zero unscoped matches: if the daemon holds
|
|
2234
|
+
// exactly one live task that ALSO has no identity (a human-started one),
|
|
2235
|
+
// use it. Never steal a task stamped with another caller's session/launch.
|
|
2236
|
+
if (!hasIdentity && out.length === 0) {
|
|
2237
|
+
const unowned = this.listTasks().filter((t) => !t.task.sessionId && !t.task.launchId);
|
|
2238
|
+
if (unowned.length === 1) {
|
|
2239
|
+
const only = unowned[0];
|
|
2240
|
+
const conn = this.connections.get(only.profile) ??
|
|
2241
|
+
[...this.connections.entries()].find(([, c]) => c.tasks.has(only.task.name) || [...c.tasks.values()].includes(only.task))?.[1];
|
|
2242
|
+
if (conn) {
|
|
2243
|
+
out.push({ conn, task: only.task, profileName: only.profile });
|
|
2244
|
+
}
|
|
2245
|
+
}
|
|
2246
|
+
}
|
|
2247
|
+
return out;
|
|
2248
|
+
}
|
|
2249
|
+
currentUrlHint(conn, task) {
|
|
2250
|
+
const shortId = task.currentTabId;
|
|
2251
|
+
if (!shortId)
|
|
2252
|
+
return undefined;
|
|
2253
|
+
const cdpId = task.tabs[shortId];
|
|
2254
|
+
if (!cdpId)
|
|
2255
|
+
return undefined;
|
|
2256
|
+
const cached = conn.targetCache?.targets.find((t) => t.targetId === cdpId);
|
|
2257
|
+
return cached?.url;
|
|
2024
2258
|
}
|
|
2025
2259
|
/**
|
|
2026
2260
|
* Mark a task as active, so the reaper's idle window measures time since the
|
|
@@ -2059,25 +2293,215 @@ export class BrowserService {
|
|
|
2059
2293
|
}
|
|
2060
2294
|
async findTask(taskId, profileName) {
|
|
2061
2295
|
if (profileName) {
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2296
|
+
// Accept bare profile names against composite connection keys.
|
|
2297
|
+
const keys = [...this.connections.keys()].filter((k) => k === profileName ||
|
|
2298
|
+
connectionKeyMatchesProfile(k, profileName) ||
|
|
2299
|
+
this.connections.get(k)?.bareName === profileName);
|
|
2300
|
+
for (const key of keys) {
|
|
2301
|
+
const conn = this.connections.get(key);
|
|
2302
|
+
const task = this.lookupTaskOnConn(conn, taskId);
|
|
2303
|
+
if (task) {
|
|
2304
|
+
await this.touchTask(conn, task);
|
|
2305
|
+
return { conn, task, profileName: conn.profileName ?? key };
|
|
2306
|
+
}
|
|
2065
2307
|
}
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2308
|
+
// RAM miss → rehydrate from disk for this profile.
|
|
2309
|
+
const rehydrated = await this.rehydrateTaskFromDisk(taskId, profileName);
|
|
2310
|
+
if (rehydrated) {
|
|
2311
|
+
await this.touchTask(rehydrated.conn, rehydrated.task);
|
|
2312
|
+
return rehydrated;
|
|
2069
2313
|
}
|
|
2070
|
-
|
|
2071
|
-
return { conn, task, profileName };
|
|
2314
|
+
throw new Error(actionable(`Task "${taskId}" not found on profile "${profileName}".`, `Next: agents browser status --profile ${profileName}`));
|
|
2072
2315
|
}
|
|
2073
2316
|
for (const [key, conn] of this.connections) {
|
|
2074
|
-
const task =
|
|
2317
|
+
const task = this.lookupTaskOnConn(conn, taskId);
|
|
2075
2318
|
if (task) {
|
|
2076
2319
|
await this.touchTask(conn, task);
|
|
2077
2320
|
return { conn, task, profileName: conn.profileName ?? key };
|
|
2078
2321
|
}
|
|
2079
2322
|
}
|
|
2080
|
-
|
|
2323
|
+
// RAM miss after a daemon restart: tasks.json still has the task and
|
|
2324
|
+
// meta.json still has the browser pid/port. Reconnect and adopt.
|
|
2325
|
+
const rehydrated = await this.rehydrateTaskFromDisk(taskId);
|
|
2326
|
+
if (rehydrated) {
|
|
2327
|
+
await this.touchTask(rehydrated.conn, rehydrated.task);
|
|
2328
|
+
return rehydrated;
|
|
2329
|
+
}
|
|
2330
|
+
const live = this.listTasks()
|
|
2331
|
+
.map(({ task }) => task.label ?? task.name)
|
|
2332
|
+
.join(', ');
|
|
2333
|
+
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>`));
|
|
2334
|
+
}
|
|
2335
|
+
/**
|
|
2336
|
+
* Soft-attach to an already-running browser for a runtime dir.
|
|
2337
|
+
*
|
|
2338
|
+
* Never launches a browser and never clears pid/port files on failure —
|
|
2339
|
+
* `connectProfile` does both, which made `status()` wipe disk state when
|
|
2340
|
+
* CDP was unreachable (CI / post-reaper) and then return an empty list.
|
|
2341
|
+
* Returns null when nothing is speaking CDP so callers can fall through
|
|
2342
|
+
* to disk reconcile.
|
|
2343
|
+
*/
|
|
2344
|
+
async attachRunningProfile(dirName, diskTasks) {
|
|
2345
|
+
const bare = dirName.includes('@') ? dirName.split('@')[0] : dirName;
|
|
2346
|
+
const endpointFromKey = dirName.includes('@')
|
|
2347
|
+
? dirName.slice(dirName.indexOf('@') + 1).split('.')[0]
|
|
2348
|
+
: undefined;
|
|
2349
|
+
const profile = await getProfile(bare);
|
|
2350
|
+
if (!profile)
|
|
2351
|
+
return null;
|
|
2352
|
+
let resolved;
|
|
2353
|
+
try {
|
|
2354
|
+
resolved = resolveEndpoint(profile, endpointFromKey);
|
|
2355
|
+
}
|
|
2356
|
+
catch {
|
|
2357
|
+
return null;
|
|
2358
|
+
}
|
|
2359
|
+
const existingInfo = getRunningChromeInfo(dirName);
|
|
2360
|
+
const parsed = parseEndpointUrl(resolved.target);
|
|
2361
|
+
const port = existingInfo?.port ?? parsed?.port;
|
|
2362
|
+
if (port === undefined)
|
|
2363
|
+
return null;
|
|
2364
|
+
const host = parsed?.host && parsed.host !== 'localhost' ? parsed.host : 'localhost';
|
|
2365
|
+
// ssh:// endpoints need a tunnel; soft rehydrate only handles local CDP.
|
|
2366
|
+
if (resolved.target.startsWith('ssh:'))
|
|
2367
|
+
return null;
|
|
2368
|
+
try {
|
|
2369
|
+
const { wsUrl, browser } = await discoverBrowserWsUrl(port, host, dirName);
|
|
2370
|
+
verifyBrowserIdentity(browser, profile.browser, port, host);
|
|
2371
|
+
const cdp = new CDPClient();
|
|
2372
|
+
await cdp.connect(wsUrl);
|
|
2373
|
+
await this.enableDomains(cdp);
|
|
2374
|
+
const tasks = this.loadTaskState(dirName);
|
|
2375
|
+
for (const [k, t] of diskTasks) {
|
|
2376
|
+
if (!tasks.has(k))
|
|
2377
|
+
tasks.set(k, t);
|
|
2378
|
+
}
|
|
2379
|
+
const conn = {
|
|
2380
|
+
cdp,
|
|
2381
|
+
port,
|
|
2382
|
+
pid: existingInfo?.pid ?? 0,
|
|
2383
|
+
electron: profile.electron,
|
|
2384
|
+
browserType: profile.browser,
|
|
2385
|
+
targetFilter: resolved.targetFilter ?? profile.targetFilter,
|
|
2386
|
+
profileName: dirName,
|
|
2387
|
+
bareName: bare,
|
|
2388
|
+
tasks,
|
|
2389
|
+
sessionCache: new Map(),
|
|
2390
|
+
};
|
|
2391
|
+
return conn;
|
|
2392
|
+
}
|
|
2393
|
+
catch {
|
|
2394
|
+
return null;
|
|
2395
|
+
}
|
|
2396
|
+
}
|
|
2397
|
+
/**
|
|
2398
|
+
* Reconnect every profile runtime dir that still has a live browser and
|
|
2399
|
+
* tasks.json entries. Used before identity-based resolution so a daemon
|
|
2400
|
+
* restart does not make the caller's tasks invisible.
|
|
2401
|
+
*/
|
|
2402
|
+
async rehydrateAllFromDisk() {
|
|
2403
|
+
const runtimeRoot = getBrowserRuntimeDir();
|
|
2404
|
+
let dirNames = [];
|
|
2405
|
+
try {
|
|
2406
|
+
dirNames = fs.readdirSync(runtimeRoot).filter((d) => {
|
|
2407
|
+
try {
|
|
2408
|
+
return fs.statSync(path.join(runtimeRoot, d)).isDirectory();
|
|
2409
|
+
}
|
|
2410
|
+
catch {
|
|
2411
|
+
return false;
|
|
2412
|
+
}
|
|
2413
|
+
});
|
|
2414
|
+
}
|
|
2415
|
+
catch {
|
|
2416
|
+
return;
|
|
2417
|
+
}
|
|
2418
|
+
for (const dirName of dirNames) {
|
|
2419
|
+
if (this.connections.has(dirName))
|
|
2420
|
+
continue;
|
|
2421
|
+
// Skip non-runtime dirs (e.g. profiles/)
|
|
2422
|
+
if (dirName === 'profiles' || dirName === 'sessions' || dirName === 'exports')
|
|
2423
|
+
continue;
|
|
2424
|
+
const tasks = this.loadTaskState(dirName);
|
|
2425
|
+
if (tasks.size === 0)
|
|
2426
|
+
continue;
|
|
2427
|
+
// Soft attach only — never launch / never clear pid files.
|
|
2428
|
+
const conn = await this.attachRunningProfile(dirName, tasks);
|
|
2429
|
+
if (!conn)
|
|
2430
|
+
continue;
|
|
2431
|
+
this.connections.set(dirName, conn);
|
|
2432
|
+
try {
|
|
2433
|
+
await this.applyDefaultDownloadBehavior(conn, dirName);
|
|
2434
|
+
}
|
|
2435
|
+
catch {
|
|
2436
|
+
// Non-fatal for rehydrate.
|
|
2437
|
+
}
|
|
2438
|
+
}
|
|
2439
|
+
}
|
|
2440
|
+
/**
|
|
2441
|
+
* On a RAM miss, scan profile runtime dirs for a tasks.json entry matching
|
|
2442
|
+
* `taskId`, soft-attach when CDP is still up, and register the connection.
|
|
2443
|
+
* Does not launch browsers or clear runtime files.
|
|
2444
|
+
*/
|
|
2445
|
+
async rehydrateTaskFromDisk(taskId, profileHint) {
|
|
2446
|
+
const runtimeRoot = getBrowserRuntimeDir();
|
|
2447
|
+
let dirNames = [];
|
|
2448
|
+
try {
|
|
2449
|
+
dirNames = fs.readdirSync(runtimeRoot).filter((d) => {
|
|
2450
|
+
try {
|
|
2451
|
+
return fs.statSync(path.join(runtimeRoot, d)).isDirectory();
|
|
2452
|
+
}
|
|
2453
|
+
catch {
|
|
2454
|
+
return false;
|
|
2455
|
+
}
|
|
2456
|
+
});
|
|
2457
|
+
}
|
|
2458
|
+
catch {
|
|
2459
|
+
return null;
|
|
2460
|
+
}
|
|
2461
|
+
if (profileHint) {
|
|
2462
|
+
dirNames = dirNames.filter((d) => d === profileHint || connectionKeyMatchesProfile(d, profileHint));
|
|
2463
|
+
}
|
|
2464
|
+
for (const dirName of dirNames) {
|
|
2465
|
+
if (dirName === 'profiles' || dirName === 'sessions' || dirName === 'exports')
|
|
2466
|
+
continue;
|
|
2467
|
+
const tasks = this.loadTaskState(dirName);
|
|
2468
|
+
let found;
|
|
2469
|
+
for (const task of tasks.values()) {
|
|
2470
|
+
if (task.id === taskId || task.name === taskId) {
|
|
2471
|
+
found = task;
|
|
2472
|
+
break;
|
|
2473
|
+
}
|
|
2474
|
+
}
|
|
2475
|
+
// Also allow map-key match
|
|
2476
|
+
if (!found)
|
|
2477
|
+
found = tasks.get(taskId);
|
|
2478
|
+
if (!found)
|
|
2479
|
+
continue;
|
|
2480
|
+
// Already connected under this key?
|
|
2481
|
+
let conn = this.connections.get(dirName);
|
|
2482
|
+
if (!conn) {
|
|
2483
|
+
const attached = await this.attachRunningProfile(dirName, tasks);
|
|
2484
|
+
if (!attached) {
|
|
2485
|
+
// CDP down — cannot rehydrate a live connection; caller will error.
|
|
2486
|
+
continue;
|
|
2487
|
+
}
|
|
2488
|
+
conn = attached;
|
|
2489
|
+
this.connections.set(dirName, conn);
|
|
2490
|
+
try {
|
|
2491
|
+
await this.applyDefaultDownloadBehavior(conn, dirName);
|
|
2492
|
+
}
|
|
2493
|
+
catch {
|
|
2494
|
+
// Non-fatal.
|
|
2495
|
+
}
|
|
2496
|
+
}
|
|
2497
|
+
const task = this.lookupTaskOnConn(conn, taskId) ?? found;
|
|
2498
|
+
// Ensure the rehydrated task is on the connection map.
|
|
2499
|
+
if (!this.lookupTaskOnConn(conn, task.name)) {
|
|
2500
|
+
conn.tasks.set(task.name, task);
|
|
2501
|
+
}
|
|
2502
|
+
return { conn, task, profileName: conn.profileName ?? dirName };
|
|
2503
|
+
}
|
|
2504
|
+
return null;
|
|
2081
2505
|
}
|
|
2082
2506
|
async getTabsForTask(cdp, task) {
|
|
2083
2507
|
const targets = (await cdp.send('Target.getTargets'));
|
|
@@ -2096,7 +2520,19 @@ export class BrowserService {
|
|
|
2096
2520
|
return tabs;
|
|
2097
2521
|
}
|
|
2098
2522
|
async getProfileStatus(profileName) {
|
|
2099
|
-
|
|
2523
|
+
// Exact composite key first; bare name falls through connectionKeyMatches.
|
|
2524
|
+
let conn = this.connections.get(profileName);
|
|
2525
|
+
let resolvedKey = profileName;
|
|
2526
|
+
if (!conn) {
|
|
2527
|
+
for (const [key, c] of this.connections) {
|
|
2528
|
+
if (connectionKeyMatchesProfile(key, profileName) ||
|
|
2529
|
+
c.bareName === profileName) {
|
|
2530
|
+
conn = c;
|
|
2531
|
+
resolvedKey = key;
|
|
2532
|
+
break;
|
|
2533
|
+
}
|
|
2534
|
+
}
|
|
2535
|
+
}
|
|
2100
2536
|
if (!conn)
|
|
2101
2537
|
return null;
|
|
2102
2538
|
// Fetch all targets once for efficiency
|
|
@@ -2134,6 +2570,7 @@ export class BrowserService {
|
|
|
2134
2570
|
tasks.push({
|
|
2135
2571
|
id: task.id,
|
|
2136
2572
|
name: task.name,
|
|
2573
|
+
label: task.label ?? task.name,
|
|
2137
2574
|
tabCount: Object.keys(task.tabs).length,
|
|
2138
2575
|
currentTabId: task.currentTabId,
|
|
2139
2576
|
createdAt: task.createdAt,
|
|
@@ -2141,10 +2578,11 @@ export class BrowserService {
|
|
|
2141
2578
|
domains: domainSet.size > 0 ? Array.from(domainSet) : undefined,
|
|
2142
2579
|
});
|
|
2143
2580
|
}
|
|
2144
|
-
const
|
|
2581
|
+
const bare = conn.bareName ?? (resolvedKey.includes('@') ? resolvedKey.split('@')[0] : resolvedKey);
|
|
2582
|
+
const profile = await getProfile(bare);
|
|
2145
2583
|
const configuredPort = profile ? extractConfiguredPort(profile) : undefined;
|
|
2146
2584
|
return {
|
|
2147
|
-
name:
|
|
2585
|
+
name: resolvedKey,
|
|
2148
2586
|
running: true,
|
|
2149
2587
|
port: conn.port,
|
|
2150
2588
|
pid: conn.pid,
|