@phnx-labs/agents-cli 1.22.117 → 1.22.119

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.
Files changed (271) hide show
  1. package/CHANGELOG.md +301 -0
  2. package/dist/bootstrap.js +4 -11
  3. package/dist/commands/accounts.js +7 -4
  4. package/dist/commands/browser.d.ts +52 -70
  5. package/dist/commands/browser.js +192 -3562
  6. package/dist/commands/daemon.d.ts +39 -2
  7. package/dist/commands/daemon.js +72 -73
  8. package/dist/commands/events.js +1 -1
  9. package/dist/commands/exec.js +53 -1
  10. package/dist/commands/projects.js +92 -7
  11. package/dist/commands/repo.js +13 -49
  12. package/dist/commands/route.js +5 -5
  13. package/dist/commands/routines.js +39 -12
  14. package/dist/commands/sessions-render.d.ts +1 -1
  15. package/dist/commands/sessions-render.js +0 -1
  16. package/dist/commands/sessions.test-fixture.d.ts +0 -1
  17. package/dist/commands/sessions.test-fixture.js +0 -37
  18. package/dist/commands/setup-browser.d.ts +17 -12
  19. package/dist/commands/setup-browser.js +52 -87
  20. package/dist/commands/setup-preferences.d.ts +0 -17
  21. package/dist/commands/setup-preferences.js +18 -49
  22. package/dist/commands/setup.js +9 -9
  23. package/dist/commands/ssh.js +10 -1
  24. package/dist/commands/sync.js +6 -44
  25. package/dist/commands/teams.js +0 -1
  26. package/dist/commands/update.d.ts +19 -0
  27. package/dist/commands/update.js +34 -6
  28. package/dist/commands/view.d.ts +12 -1
  29. package/dist/commands/view.js +47 -2
  30. package/dist/index.js +107 -26
  31. package/dist/lib/account-capabilities.js +0 -2
  32. package/dist/lib/account-catalog.d.ts +38 -0
  33. package/dist/lib/account-catalog.js +110 -26
  34. package/dist/lib/account-provider-registry.js +1 -1
  35. package/dist/lib/account-registry.d.ts +1 -1
  36. package/dist/lib/account-registry.js +36 -2
  37. package/dist/lib/accounting/rotate.js +22 -8
  38. package/dist/lib/accounting/usage-ingest.d.ts +6 -1
  39. package/dist/lib/accounting/usage-ingest.js +115 -45
  40. package/dist/lib/accounting/usage-sync.d.ts +134 -31
  41. package/dist/lib/accounting/usage-sync.js +227 -66
  42. package/dist/lib/accounting/usage.d.ts +16 -3
  43. package/dist/lib/accounting/usage.js +26 -8
  44. package/dist/lib/accounts/add.d.ts +3 -1
  45. package/dist/lib/accounts/add.js +24 -13
  46. package/dist/lib/accounts/slots.d.ts +8 -0
  47. package/dist/lib/accounts/slots.js +17 -0
  48. package/dist/lib/add-dir.js +0 -1
  49. package/dist/lib/agent-cli-commands.js +0 -1
  50. package/dist/lib/agent-spec/agents.d.ts +6 -7
  51. package/dist/lib/agent-spec/agents.js +7 -60
  52. package/dist/lib/auth-health.d.ts +69 -1
  53. package/dist/lib/auth-health.js +133 -4
  54. package/dist/lib/browser/context.d.ts +82 -0
  55. package/dist/lib/browser/context.js +88 -0
  56. package/dist/lib/browser/paths.d.ts +25 -0
  57. package/dist/lib/browser/paths.js +51 -0
  58. package/dist/lib/browser/record.d.ts +38 -0
  59. package/dist/lib/browser/record.js +57 -0
  60. package/dist/lib/browser/sessions-list.js +1 -2
  61. package/dist/lib/browser-client.d.ts +158 -0
  62. package/dist/lib/browser-client.js +234 -0
  63. package/dist/lib/channels/owner-sink.d.ts +1 -18
  64. package/dist/lib/channels/owner-sink.js +29 -82
  65. package/dist/lib/channels/providers/rush.d.ts +7 -7
  66. package/dist/lib/channels/providers/rush.js +127 -57
  67. package/dist/lib/claude-account-token.d.ts +7 -1
  68. package/dist/lib/claude-account-token.js +26 -4
  69. package/dist/lib/claude-statusline.d.ts +1 -1
  70. package/dist/lib/claude-statusline.js +15 -3
  71. package/dist/lib/commands.js +1 -1
  72. package/dist/lib/crabbox/lease.js +1 -1
  73. package/dist/lib/crabbox/runtimes.js +1 -3
  74. package/dist/lib/daemon/account-state-daemon-service.js +1 -0
  75. package/dist/lib/daemon/auth-sync-service.d.ts +21 -18
  76. package/dist/lib/daemon/auth-sync-service.js +37 -43
  77. package/dist/lib/daemon/daemon.d.ts +25 -14
  78. package/dist/lib/daemon/daemon.js +92 -154
  79. package/dist/lib/daemon/runner.d.ts +2 -2
  80. package/dist/lib/daemon/runner.js +32 -9
  81. package/dist/lib/daemon/self-update-service.d.ts +35 -34
  82. package/dist/lib/daemon/self-update-service.js +40 -36
  83. package/dist/lib/daemon/service.d.ts +12 -8
  84. package/dist/lib/daemon/supervisor.d.ts +65 -54
  85. package/dist/lib/daemon/supervisor.js +136 -171
  86. package/dist/lib/daemon/testdata/process-view-start.js +5 -2
  87. package/dist/lib/daemon/usage-sync-service.d.ts +21 -30
  88. package/dist/lib/daemon/usage-sync-service.js +46 -78
  89. package/dist/lib/daemon-health.d.ts +22 -5
  90. package/dist/lib/daemon-health.js +47 -3
  91. package/dist/lib/daemon-services.d.ts +1 -1
  92. package/dist/lib/daemon-services.js +0 -10
  93. package/dist/lib/daemon-ticks.js +4 -2
  94. package/dist/lib/devices/doctor-findings.js +8 -11
  95. package/dist/lib/devices/harness-inventory.js +14 -5
  96. package/dist/lib/exec-account-home.js +5 -0
  97. package/dist/lib/exec.js +10 -13
  98. package/dist/lib/feed/events.js +0 -1
  99. package/dist/lib/feed/tool-activity.js +5 -2
  100. package/dist/lib/fleet-shared-state.d.ts +45 -0
  101. package/dist/lib/fleet-shared-state.js +78 -9
  102. package/dist/lib/fs-atomic.js +25 -0
  103. package/dist/lib/github/pr-mergeable.d.ts +4 -2
  104. package/dist/lib/github/pr-mergeable.js +5 -3
  105. package/dist/lib/github/project-prs.d.ts +58 -3
  106. package/dist/lib/github/project-prs.js +205 -7
  107. package/dist/lib/harness/adapters/cursor.js +14 -4
  108. package/dist/lib/harness-auth-capabilities.d.ts +4 -3
  109. package/dist/lib/harness-auth-capabilities.js +1 -2
  110. package/dist/lib/helper-versions.js +1 -1
  111. package/dist/lib/hooks/cache.d.ts +7 -0
  112. package/dist/lib/hooks/cache.js +27 -2
  113. package/dist/lib/hooks/install.js +7 -1
  114. package/dist/lib/hosts/ready.d.ts +38 -8
  115. package/dist/lib/hosts/ready.js +62 -14
  116. package/dist/lib/installations/active-check.d.ts +27 -0
  117. package/dist/lib/installations/active-check.js +61 -0
  118. package/dist/lib/installations/migrate.js +6 -3
  119. package/dist/lib/installations/shims.d.ts +1 -1
  120. package/dist/lib/installations/shims.js +32 -12
  121. package/dist/lib/installations/store.d.ts +9 -0
  122. package/dist/lib/installations/store.js +24 -0
  123. package/dist/lib/installations/update.js +21 -15
  124. package/dist/lib/installations/versions.js +15 -3
  125. package/dist/lib/manifest.js +1 -1
  126. package/dist/lib/models.js +0 -2
  127. package/dist/lib/open-url.js +28 -54
  128. package/dist/lib/profiles.js +0 -2
  129. package/dist/lib/projects.d.ts +16 -0
  130. package/dist/lib/projects.js +31 -0
  131. package/dist/lib/refresh.js +1 -1
  132. package/dist/lib/routine-readiness.d.ts +35 -0
  133. package/dist/lib/routine-readiness.js +74 -16
  134. package/dist/lib/rules/compile.js +6 -0
  135. package/dist/lib/sandbox.js +0 -2
  136. package/dist/lib/secrets-policy.d.ts +89 -11
  137. package/dist/lib/secrets-policy.js +175 -28
  138. package/dist/lib/session/active.d.ts +17 -6
  139. package/dist/lib/session/active.js +105 -49
  140. package/dist/lib/session/db.d.ts +1 -1
  141. package/dist/lib/session/db.js +65 -18
  142. package/dist/lib/session/discover.js +1 -283
  143. package/dist/lib/session/mirror.d.ts +4 -3
  144. package/dist/lib/session/mirror.js +4 -3
  145. package/dist/lib/session/parse.d.ts +0 -2
  146. package/dist/lib/session/parse.js +1 -138
  147. package/dist/lib/session/pid-registry.d.ts +6 -3
  148. package/dist/lib/session/pid-registry.js +18 -8
  149. package/dist/lib/session/remote/watch.d.ts +1 -1
  150. package/dist/lib/session/remote/watch.js +7 -3
  151. package/dist/lib/session/throughput.d.ts +1 -3
  152. package/dist/lib/session/throughput.js +0 -20
  153. package/dist/lib/session/types.d.ts +2 -2
  154. package/dist/lib/session/types.js +2 -2
  155. package/dist/lib/sessions-client.d.ts +60 -1
  156. package/dist/lib/sessions-client.js +227 -4
  157. package/dist/lib/setup-tool-status.js +5 -10
  158. package/dist/lib/signin-badge.d.ts +3 -2
  159. package/dist/lib/signin-badge.js +6 -3
  160. package/dist/lib/smart-launch.js +4 -1
  161. package/dist/lib/staleness/detectors/commands.d.ts +1 -1
  162. package/dist/lib/staleness/detectors/hooks.d.ts +1 -1
  163. package/dist/lib/staleness/detectors/mcp.d.ts +1 -1
  164. package/dist/lib/staleness/detectors/permissions.d.ts +1 -1
  165. package/dist/lib/staleness/detectors/plugins.d.ts +1 -1
  166. package/dist/lib/staleness/detectors/rules.d.ts +1 -1
  167. package/dist/lib/staleness/detectors/skills.d.ts +1 -1
  168. package/dist/lib/staleness/detectors/subagents.d.ts +1 -1
  169. package/dist/lib/staleness/detectors/workflows.d.ts +1 -1
  170. package/dist/lib/staleness/writers/commands.d.ts +1 -1
  171. package/dist/lib/staleness/writers/hooks.d.ts +1 -1
  172. package/dist/lib/staleness/writers/mcp.d.ts +1 -1
  173. package/dist/lib/staleness/writers/permissions.d.ts +1 -1
  174. package/dist/lib/staleness/writers/plugins.d.ts +1 -1
  175. package/dist/lib/staleness/writers/rules.d.ts +1 -1
  176. package/dist/lib/staleness/writers/skills.d.ts +1 -1
  177. package/dist/lib/staleness/writers/sources.js +5 -0
  178. package/dist/lib/staleness/writers/subagents.d.ts +1 -1
  179. package/dist/lib/staleness/writers/workflows.d.ts +1 -1
  180. package/dist/lib/state.d.ts +6 -4
  181. package/dist/lib/state.js +7 -17
  182. package/dist/lib/teams/parsers.d.ts +1 -1
  183. package/dist/lib/teams/parsers.js +1 -136
  184. package/dist/lib/teams/placement-probe.js +50 -15
  185. package/dist/lib/teams/scheduler.d.ts +7 -0
  186. package/dist/lib/types.d.ts +16 -1
  187. package/dist/lib/types.js +1 -1
  188. package/dist/lib/view-types.d.ts +32 -0
  189. package/dist/lib/watchdog/read.js +0 -1
  190. package/dist/lib/watchdog/rotate.js +1 -2
  191. package/dist/lib/watchdog/runner.js +2 -2
  192. package/dist/lib/watchdog/watchdog.d.ts +1 -1
  193. package/dist/lib/watchdog/watchdogTail.js +0 -31
  194. package/package.json +1 -1
  195. package/scripts/postinstall.js +8 -8
  196. package/dist/browser.d.ts +0 -2
  197. package/dist/browser.js +0 -17
  198. package/dist/commands/browser-picker.d.ts +0 -18
  199. package/dist/commands/browser-picker.js +0 -97
  200. package/dist/lib/browser/arc-discovery.d.ts +0 -52
  201. package/dist/lib/browser/arc-discovery.js +0 -197
  202. package/dist/lib/browser/arc-dom.d.ts +0 -14
  203. package/dist/lib/browser/arc-dom.js +0 -121
  204. package/dist/lib/browser/caller-identity.d.ts +0 -40
  205. package/dist/lib/browser/caller-identity.js +0 -175
  206. package/dist/lib/browser/cdp.d.ts +0 -51
  207. package/dist/lib/browser/cdp.js +0 -254
  208. package/dist/lib/browser/chrome.d.ts +0 -141
  209. package/dist/lib/browser/chrome.js +0 -704
  210. package/dist/lib/browser/chromium-discovery.d.ts +0 -28
  211. package/dist/lib/browser/chromium-discovery.js +0 -96
  212. package/dist/lib/browser/devices.d.ts +0 -34
  213. package/dist/lib/browser/devices.js +0 -61
  214. package/dist/lib/browser/domain-skills.d.ts +0 -71
  215. package/dist/lib/browser/domain-skills.js +0 -195
  216. package/dist/lib/browser/drivers/arc.d.ts +0 -40
  217. package/dist/lib/browser/drivers/arc.js +0 -276
  218. package/dist/lib/browser/drivers/firefox.d.ts +0 -99
  219. package/dist/lib/browser/drivers/firefox.js +0 -377
  220. package/dist/lib/browser/drivers/local.d.ts +0 -55
  221. package/dist/lib/browser/drivers/local.js +0 -266
  222. package/dist/lib/browser/drivers/ssh.d.ts +0 -120
  223. package/dist/lib/browser/drivers/ssh.js +0 -467
  224. package/dist/lib/browser/editor.d.ts +0 -3
  225. package/dist/lib/browser/editor.js +0 -50
  226. package/dist/lib/browser/ffmpeg.d.ts +0 -12
  227. package/dist/lib/browser/ffmpeg.js +0 -184
  228. package/dist/lib/browser/firefox-discovery.d.ts +0 -69
  229. package/dist/lib/browser/firefox-discovery.js +0 -162
  230. package/dist/lib/browser/har.d.ts +0 -85
  231. package/dist/lib/browser/har.js +0 -77
  232. package/dist/lib/browser/hygiene.d.ts +0 -97
  233. package/dist/lib/browser/hygiene.js +0 -153
  234. package/dist/lib/browser/index.d.ts +0 -5
  235. package/dist/lib/browser/index.js +0 -5
  236. package/dist/lib/browser/input.d.ts +0 -7
  237. package/dist/lib/browser/input.js +0 -92
  238. package/dist/lib/browser/ipc.d.ts +0 -204
  239. package/dist/lib/browser/ipc.js +0 -1453
  240. package/dist/lib/browser/login-detection.d.ts +0 -87
  241. package/dist/lib/browser/login-detection.js +0 -274
  242. package/dist/lib/browser/profiles.d.ts +0 -422
  243. package/dist/lib/browser/profiles.js +0 -1157
  244. package/dist/lib/browser/refs.d.ts +0 -89
  245. package/dist/lib/browser/refs.js +0 -191
  246. package/dist/lib/browser/registry.d.ts +0 -70
  247. package/dist/lib/browser/registry.js +0 -252
  248. package/dist/lib/browser/remote-control.d.ts +0 -59
  249. package/dist/lib/browser/remote-control.js +0 -85
  250. package/dist/lib/browser/resolve-target.d.ts +0 -66
  251. package/dist/lib/browser/resolve-target.js +0 -264
  252. package/dist/lib/browser/runtime-state.d.ts +0 -291
  253. package/dist/lib/browser/runtime-state.js +0 -584
  254. package/dist/lib/browser/secret-ref.d.ts +0 -10
  255. package/dist/lib/browser/secret-ref.js +0 -14
  256. package/dist/lib/browser/service.d.ts +0 -812
  257. package/dist/lib/browser/service.js +0 -4693
  258. package/dist/lib/browser/stream.d.ts +0 -17
  259. package/dist/lib/browser/stream.js +0 -72
  260. package/dist/lib/browser/task-index.d.ts +0 -78
  261. package/dist/lib/browser/task-index.js +0 -199
  262. package/dist/lib/browser/types.d.ts +0 -647
  263. package/dist/lib/browser/types.js +0 -61
  264. package/dist/lib/browser/upload.d.ts +0 -29
  265. package/dist/lib/browser/upload.js +0 -298
  266. package/dist/lib/daemon/browser-ipc-service.d.ts +0 -20
  267. package/dist/lib/daemon/browser-ipc-service.js +0 -46
  268. package/dist/lib/daemon/browser-task-reap-service.d.ts +0 -14
  269. package/dist/lib/daemon/browser-task-reap-service.js +0 -26
  270. package/dist/lib/fleet-shared-repo-sync.d.ts +0 -55
  271. package/dist/lib/fleet-shared-repo-sync.js +0 -490
@@ -1,85 +0,0 @@
1
- /**
2
- * Consent gate for driving THIS machine's browser from another fleet machine.
3
- *
4
- * `browser --device <device>` routes a browser command to `<device>` over SSH (the
5
- * fleet passthrough) and runs `agents browser ...` there. `agents ssh <device>
6
- * agents browser …` is the same remote drive through the ssh wrapper. Every such
7
- * remote invocation carries the {@link FLEET_REMOTE_ENV} marker, set at the fleet
8
- * dispatch site (`maybeRunOnHost` / `streamAgentsOnHost`) and on the ssh wrapper
9
- * (`buildSshInvocation` via `markFleetRemote`). A machine only accepts being driven
10
- * when its owner has opted in via `agents browser remote-control on` (the
11
- * device-scope `browser.remote-control` config key, never synced).
12
- *
13
- * Local invocations (no marker) are never gated — this only governs cross-machine
14
- * drives. Read-only queries are not gated.
15
- *
16
- * The authoritative gate is {@link assertRemoteControlAllowedForRequest}, called
17
- * inside the browser daemon at the top of `resolveOrCreateTask` — the one
18
- * chokepoint every task-scoped verb resolves through — plus `BrowserService.start`
19
- * for the task-less `browser start` command and `BrowserService.stopProfile` for
20
- * the task-less `browser stop --profile` command. It has to live there because ~18
21
- * page verbs (`navigate`, `click`, `screenshot`, `tab-add`, …) launch OR attach
22
- * to a browser implicitly, and gating only the `browser start` command left every
23
- * one of them ungated; gating only the create branch of `resolveOrCreateTask` left
24
- * the attach paths ungated (RUSH-3064); `stop --profile` is handled by `bindTask`'s
25
- * early return before `resolveOrCreateTask` ever runs, so it needed its own gate
26
- * call too (PHNX-3317). {@link assertRemoteControlAllowed} remains as a fast-fail
27
- * CLI-side check so a refused `start` never auto-creates a profile.
28
- */
29
- import { getConfigValue } from '../device-config.js';
30
- /** Env marker set on every remote `agents` invocation by `buildRemoteAgentsInvocation` and `markFleetRemote`. */
31
- export const FLEET_REMOTE_ENV = 'AGENTS_FLEET_REMOTE';
32
- /** True when this process was dispatched to this machine by a fleet `--device` or `agents ssh … agents browser` run. */
33
- export function isFleetRemoteInvocation(env = process.env) {
34
- return env[FLEET_REMOTE_ENV] === '1';
35
- }
36
- /**
37
- * Whether this machine allows other fleet machines to drive its browser. Reads
38
- * the device-scope `browser.remote-control` config key. Unset = off (deny).
39
- */
40
- function remoteControlEnabled() {
41
- return getConfigValue('browser.remote-control').value === true;
42
- }
43
- /**
44
- * Guard the browser-drive entry point. A fleet-remote invocation may only drive
45
- * this machine's browser when the owner opted in; otherwise throw a clear,
46
- * actionable error. A local invocation is never gated.
47
- *
48
- * `env` and `enabled` are injectable so the decision is unit-testable without a
49
- * real remote hop or a real config store.
50
- */
51
- export function assertRemoteControlAllowed(opts) {
52
- const env = opts?.env ?? process.env;
53
- if (!isFleetRemoteInvocation(env))
54
- return;
55
- const enabled = opts?.enabled ?? remoteControlEnabled();
56
- if (enabled)
57
- return;
58
- const who = env.AGENTS_ACTOR_HOST || env.AGENTS_ACTOR || 'A fleet machine';
59
- throw new Error(remoteControlRefusal(who));
60
- }
61
- /**
62
- * The daemon-side consent gate.
63
- *
64
- * Reads ONLY the per-request marker, never `process.env`: the browser daemon is
65
- * shared and long-lived, and it may have been auto-started by a fleet-remote CLI
66
- * that leaked `AGENTS_FLEET_REMOTE=1` into its environment permanently. Reading
67
- * the daemon's env would then refuse every subsequent LOCAL drive on this
68
- * machine until the daemon restarted.
69
- *
70
- * `actor` is likewise the caller's forwarded identity, not the daemon's.
71
- */
72
- export function assertRemoteControlAllowedForRequest(fleetRemote, opts = {}) {
73
- if (!fleetRemote)
74
- return;
75
- const enabled = opts.enabled ?? remoteControlEnabled();
76
- if (enabled)
77
- return;
78
- throw new Error(remoteControlRefusal(opts.actor || 'A fleet machine'));
79
- }
80
- /** The refusal text, shared by the CLI-side and daemon-side gates. */
81
- function remoteControlRefusal(who) {
82
- return (`${who} tried to drive this machine's browser over \`browser --device\`, but remote ` +
83
- `browser control is off here. To allow it, run on THIS machine:\n` +
84
- ` agents browser remote-control on`);
85
- }
@@ -1,66 +0,0 @@
1
- import { type BrowserProfile, type ConnectionKey } from './types.js';
2
- import type { BrowserProfileConfig } from '../types.js';
3
- export type DeviceProbeResult = {
4
- reachable: true;
5
- os?: string;
6
- } | {
7
- reachable: false;
8
- reason: string;
9
- };
10
- export type DeviceProbe = (device: string) => DeviceProbeResult;
11
- interface ResolvedBrowserTarget {
12
- name: string;
13
- kind: 'identity' | 'fungible';
14
- /** Device the daemon will talk to. */
15
- device: string;
16
- local: boolean;
17
- key: ConnectionKey;
18
- /** `cdp:` locally, `ssh:` when tunnelling to a declaring device. */
19
- target: string;
20
- profile: BrowserProfile;
21
- /**
22
- * Human line when the daemon picked a remote device. Command output
23
- * surfaces this so the caller knows WHERE the browser actually lives.
24
- */
25
- picked?: string;
26
- /** Native endpoints cannot be tunnelled; the CLI must re-exec on this device. */
27
- commandDispatch?: boolean;
28
- }
29
- /** Fork only fungible Electron profiles; identity-bearing ones share one connection. */
30
- export declare function shouldForkProfile(kind: 'identity' | 'fungible', conn: {
31
- electron?: boolean;
32
- tasks: {
33
- size: number;
34
- };
35
- }): boolean;
36
- /** Runtime key: profile name plus the resolved device. Drops `@endpoint-N`. */
37
- export declare function profileConnectionKey(profileName: string, device: string): ConnectionKey;
38
- export declare function undeclaredProfileError(name: string): Error;
39
- export declare function sshEndpointForDeclaration(device: string, config: BrowserProfileConfig, endpointName?: string, os?: string): string;
40
- /**
41
- * Decide where `name` connects. Never claims a central leftover. Never
42
- * falls back to a local browser when the name is declared elsewhere.
43
- */
44
- export declare function resolveBrowserTarget(name: string, opts?: {
45
- endpointName?: string;
46
- here?: string;
47
- probe?: DeviceProbe;
48
- }): ResolvedBrowserTarget;
49
- /**
50
- * Rename a leftover `<name>@endpoint-N` runtime dir to the new
51
- * `<name>@<device>` key so logins in chrome-data are not orphaned.
52
- *
53
- * Only when exactly one legacy dir exists and the destination does not —
54
- * several endpoint dirs stay put so a multi-endpoint profile is not merged.
55
- */
56
- /**
57
- * Adopt a leftover `@endpoint-N` runtime dir onto the new key ONLY for a
58
- * local connect. Doing this for a tunnel key would rename this machine's
59
- * pre-T2 logged-out chrome-data onto `comet-local@zion`, and connectProfile
60
- * would then attach localhost CDP instead of tunnelling.
61
- */
62
- export declare function adoptLegacyRuntimeIfLocal(local: boolean, profileName: string, newKey: ConnectionKey, runtimeRoot: string): void;
63
- export declare function migrateLegacyRuntimeDir(profileName: string, newKey: ConnectionKey, runtimeRoot: string): void;
64
- /** True when a live key is a leftover `@endpoint-N` key of `profileName`. */
65
- export declare function isLegacyEndpointKey(key: string, profileName: string): boolean;
66
- export {};
@@ -1,264 +0,0 @@
1
- /**
2
- * Registry-driven browser target resolution.
3
- *
4
- * The profile KIND is not a field: it falls out of how many devices declare
5
- * the name ({@link profileKind}). This module answers where a name should
6
- * connect, and never claims a leftover central profile (that race is T1).
7
- *
8
- * Three outcomes, no fourth:
9
- * 1. THIS device declares the name → connect locally.
10
- * 2. Only OTHER devices declare it → tunnel to a reachable declaring device.
11
- * 3. Nobody declares it → fail loud. Never auto-create a local browser
12
- * under a name that means a logged-in browser somewhere else.
13
- */
14
- import * as fs from 'node:fs';
15
- import * as path from 'node:path';
16
- import { machineId } from '../machine-id.js';
17
- import { centralBrowserProfiles, declaringDevices, profileKind, profileRegistry, } from './registry.js';
18
- import { parseEndpointUrl, resolveEndpoint } from './profiles.js';
19
- import { connectionKey, parseConnectionKey, } from './types.js';
20
- import { probeHost } from '../hosts/ready.js';
21
- /** Fork only fungible Electron profiles; identity-bearing ones share one connection. */
22
- export function shouldForkProfile(kind, conn) {
23
- if (kind === 'identity')
24
- return false;
25
- return Boolean(conn.electron && conn.tasks.size > 0);
26
- }
27
- function profileFromDeclaration(name, declaration) {
28
- const config = declaration.config;
29
- return {
30
- name,
31
- description: config.description,
32
- browser: config.browser,
33
- binary: config.binary,
34
- electron: config.electron,
35
- targetFilter: config.targetFilter,
36
- endpoints: config.endpoints,
37
- defaultEndpoint: config.defaultEndpoint,
38
- launchPolicy: config.launchPolicy,
39
- userDataDir: config.userDataDir,
40
- chrome: config.chrome,
41
- secrets: config.secrets,
42
- viewport: config.viewport,
43
- logDir: config.logDir,
44
- logHost: config.logHost,
45
- arc: config.arc,
46
- firefox: config.firefox,
47
- };
48
- }
49
- /** Runtime key: profile name plus the resolved device. Drops `@endpoint-N`. */
50
- export function profileConnectionKey(profileName, device) {
51
- return connectionKey(profileName, device);
52
- }
53
- function levenshtein(a, b) {
54
- if (a === b)
55
- return 0;
56
- if (a.length === 0)
57
- return b.length;
58
- if (b.length === 0)
59
- return a.length;
60
- const row = Array.from({ length: b.length + 1 }, (_, i) => i);
61
- for (let i = 0; i < a.length; i++) {
62
- let prev = row[0];
63
- row[0] = i + 1;
64
- for (let j = 0; j < b.length; j++) {
65
- const cur = row[j + 1];
66
- const cost = a[i] === b[j] ? 0 : 1;
67
- row[j + 1] = Math.min(row[j] + 1, row[j + 1] + 1, prev + cost);
68
- prev = cur;
69
- }
70
- }
71
- return row[b.length];
72
- }
73
- /** Declared names that look like `query` — used in the undeclared error. */
74
- function similarProfileNames(query, names) {
75
- const q = query.toLowerCase();
76
- const maxDist = Math.max(2, Math.floor(q.length / 4));
77
- return names
78
- .filter((name) => {
79
- const x = name.toLowerCase();
80
- if (x === q)
81
- return false;
82
- if (x.includes(q) || q.includes(x))
83
- return Math.min(x.length, q.length) >= 3;
84
- return levenshtein(x, q) <= maxDist;
85
- })
86
- .sort();
87
- }
88
- function lines(...parts) {
89
- return parts.filter((part) => part != null && part !== '').join('\n');
90
- }
91
- export function undeclaredProfileError(name) {
92
- const central = centralBrowserProfiles();
93
- if (central[name]) {
94
- return new Error(lines(`Browser profile "${name}" is not declared by any device.`, `It still lives in the central agents.yaml browser: map.`, `Claim it on the machine that hosts that browser with: agents browser profiles claim ${name}`));
95
- }
96
- const registry = profileRegistry();
97
- const similar = similarProfileNames(name, [...registry.keys()]);
98
- const similarLines = similar.map((candidate) => {
99
- const devices = (registry.get(candidate) ?? []).map((entry) => entry.device);
100
- return devices.length > 0
101
- ? ` ${candidate} (declared on ${devices.join(', ')})`
102
- : ` ${candidate}`;
103
- });
104
- return new Error(lines(`Browser profile "${name}" is not declared by any device.`, ...(similarLines.length > 0
105
- ? ['Similar names:', ...similarLines]
106
- : ['No device declares a similar name.']), `Create it on the machine that hosts the browser: agents browser profiles create ${name} --browser <chrome|comet|chromium|brave|edge|arc|custom>`, `Or, if it is a leftover central profile, claim it: agents browser profiles claim ${name}`));
107
- }
108
- function unreachableDeclaringDevicesError(name, devices, failures) {
109
- return new Error(lines(`Browser profile "${name}" is declared on ${devices.join(', ')}, but none of those devices are reachable.`, ...failures.map((line) => ` ${line}`), `A local browser will not be launched — that would be a logged-out copy of an identity-bearing profile.`));
110
- }
111
- /**
112
- * Build the SSH endpoint used to tunnel to a declaring device's own browser.
113
- *
114
- * A `cdp://localhost:N` declaration on that device is THIS machine's loopback
115
- * if used as-is — that is the original bug. Rewrite it to `ssh://<device>?port=N`.
116
- * An already-`ssh://` declaration is used as written.
117
- */
118
- function isWindowsOs(os) {
119
- if (!os)
120
- return false;
121
- const normalized = os.toLowerCase();
122
- return (normalized.includes('windows') ||
123
- normalized === 'win32' ||
124
- normalized === 'win64' ||
125
- normalized === 'windows_nt');
126
- }
127
- export function sshEndpointForDeclaration(device, config, endpointName, os) {
128
- const profile = profileFromDeclaration('_', { device, config });
129
- const resolved = resolveEndpoint(profile, endpointName);
130
- if (resolved.target.startsWith('ssh:'))
131
- return resolved.target;
132
- // Native Arc endpoints (arc-native:) must NOT be rewritten into SSH tunnels
133
- // (PHNX-2399). They dispatch the whole command to the owner device instead.
134
- if (resolved.target.startsWith('arc-native:'))
135
- return resolved.target;
136
- // Firefox BiDi endpoints (firefox-bidi:) are a local WebSocket bound to a
137
- // specific profile directory on the owner box; they dispatch the whole
138
- // command to that box rather than tunnelling (PHNX-4043).
139
- if (resolved.target.startsWith('firefox-bidi:'))
140
- return resolved.target;
141
- const parsed = parseEndpointUrl(resolved.target);
142
- const port = parsed?.port ?? 9222;
143
- const osQuery = isWindowsOs(os) ? '&os=windows' : '';
144
- return `ssh://${device}?port=${port}${osQuery}`;
145
- }
146
- function defaultProbe(device) {
147
- try {
148
- const result = probeHost(device);
149
- if (result.reachable)
150
- return { reachable: true, os: result.os };
151
- return { reachable: false, reason: `ssh to ${device} failed` };
152
- }
153
- catch (error) {
154
- return {
155
- reachable: false,
156
- reason: error instanceof Error ? error.message : String(error),
157
- };
158
- }
159
- }
160
- /**
161
- * Decide where `name` connects. Never claims a central leftover. Never
162
- * falls back to a local browser when the name is declared elsewhere.
163
- */
164
- export function resolveBrowserTarget(name, opts = {}) {
165
- const here = opts.here ?? machineId();
166
- const kind = profileKind(name);
167
- const declarations = profileRegistry().get(name) ?? [];
168
- if (kind === null || declarations.length === 0) {
169
- throw undeclaredProfileError(name);
170
- }
171
- const devices = declaringDevices(name);
172
- const local = declarations.find((entry) => entry.device === here);
173
- if (local) {
174
- const profile = profileFromDeclaration(name, local);
175
- const resolved = resolveEndpoint(profile, opts.endpointName);
176
- return {
177
- name,
178
- kind,
179
- device: here,
180
- local: true,
181
- key: profileConnectionKey(name, here),
182
- target: resolved.target,
183
- profile: {
184
- ...profile,
185
- binary: resolved.binary,
186
- targetFilter: resolved.targetFilter,
187
- },
188
- };
189
- }
190
- const probe = opts.probe ?? defaultProbe;
191
- const candidates = [...declarations].sort((a, b) => a.device.localeCompare(b.device));
192
- const failures = [];
193
- for (const declaration of candidates) {
194
- const result = probe(declaration.device);
195
- if (!result.reachable) {
196
- failures.push(`${declaration.device}: ${result.reason}`);
197
- continue;
198
- }
199
- const profile = profileFromDeclaration(name, declaration);
200
- const resolved = resolveEndpoint(profile, opts.endpointName);
201
- const target = sshEndpointForDeclaration(declaration.device, declaration.config, opts.endpointName, result.os);
202
- const picked = candidates.length > 1
203
- ? `Using ${name} on ${declaration.device} (declared on ${devices.join(', ')})`
204
- : `Using ${name} on ${declaration.device}`;
205
- return {
206
- name,
207
- kind,
208
- device: declaration.device,
209
- local: false,
210
- key: profileConnectionKey(name, declaration.device),
211
- target,
212
- profile: {
213
- ...profile,
214
- binary: resolved.binary,
215
- targetFilter: resolved.targetFilter,
216
- },
217
- picked,
218
- commandDispatch: resolved.target.startsWith('arc-native:') || resolved.target.startsWith('firefox-bidi:'),
219
- };
220
- }
221
- throw unreachableDeclaringDevicesError(name, devices, failures);
222
- }
223
- /**
224
- * Rename a leftover `<name>@endpoint-N` runtime dir to the new
225
- * `<name>@<device>` key so logins in chrome-data are not orphaned.
226
- *
227
- * Only when exactly one legacy dir exists and the destination does not —
228
- * several endpoint dirs stay put so a multi-endpoint profile is not merged.
229
- */
230
- /**
231
- * Adopt a leftover `@endpoint-N` runtime dir onto the new key ONLY for a
232
- * local connect. Doing this for a tunnel key would rename this machine's
233
- * pre-T2 logged-out chrome-data onto `comet-local@zion`, and connectProfile
234
- * would then attach localhost CDP instead of tunnelling.
235
- */
236
- export function adoptLegacyRuntimeIfLocal(local, profileName, newKey, runtimeRoot) {
237
- if (!local)
238
- return;
239
- migrateLegacyRuntimeDir(profileName, newKey, runtimeRoot);
240
- }
241
- export function migrateLegacyRuntimeDir(profileName, newKey, runtimeRoot) {
242
- const dest = path.join(runtimeRoot, newKey);
243
- if (fs.existsSync(dest))
244
- return;
245
- if (!fs.existsSync(runtimeRoot))
246
- return;
247
- const legacy = fs.readdirSync(runtimeRoot).filter((entry) => {
248
- const parsed = parseConnectionKey(entry);
249
- return (parsed.profile === profileName &&
250
- parsed.endpoint !== undefined &&
251
- /^endpoint-\d+$/.test(parsed.endpoint) &&
252
- parsed.fork === undefined);
253
- });
254
- if (legacy.length !== 1)
255
- return;
256
- fs.renameSync(path.join(runtimeRoot, legacy[0]), dest);
257
- }
258
- /** True when a live key is a leftover `@endpoint-N` key of `profileName`. */
259
- export function isLegacyEndpointKey(key, profileName) {
260
- const parsed = parseConnectionKey(key);
261
- return (parsed.profile === profileName &&
262
- parsed.endpoint !== undefined &&
263
- /^endpoint-\d+$/.test(parsed.endpoint));
264
- }