@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,17 +0,0 @@
1
- import type { Readable, Writable } from 'stream';
2
- interface BrowserIPCStreamOptions {
3
- input: Readable;
4
- output: Writable;
5
- task?: string;
6
- actor: string;
7
- launchId?: string;
8
- /** Calling agent session, forwarded onto `start` requests (RUSH-2549). */
9
- sessionId?: string;
10
- autoStartDaemon?: boolean;
11
- }
12
- /**
13
- * Read browser IPC requests as NDJSON and write one NDJSON response per line.
14
- * The Node process and daemon connection stay alive until input closes.
15
- */
16
- export declare function runBrowserIPCStream(options: BrowserIPCStreamOptions): Promise<void>;
17
- export {};
@@ -1,72 +0,0 @@
1
- import { createInterface } from 'readline';
2
- import { connectBrowserIPC } from './ipc.js';
3
- import { assertRemoteControlAllowed } from './remote-control.js';
4
- function parseRequest(line) {
5
- const parsed = JSON.parse(line);
6
- if (!parsed ||
7
- typeof parsed !== 'object' ||
8
- Array.isArray(parsed) ||
9
- typeof parsed.action !== 'string') {
10
- throw new Error('Each input line must be a JSON object with an action');
11
- }
12
- return parsed;
13
- }
14
- function writeResponse(output, response) {
15
- output.write(`${JSON.stringify(response)}\n`);
16
- }
17
- function writeErrorResponse(output, error) {
18
- writeResponse(output, {
19
- ok: false,
20
- error: error instanceof Error ? error.message : String(error),
21
- });
22
- }
23
- /**
24
- * Read browser IPC requests as NDJSON and write one NDJSON response per line.
25
- * The Node process and daemon connection stay alive until input closes.
26
- */
27
- export async function runBrowserIPCStream(options) {
28
- const client = await connectBrowserIPC({ autoStartDaemon: options.autoStartDaemon });
29
- const lines = createInterface({ input: options.input, crlfDelay: Infinity });
30
- let defaultTask = options.task;
31
- try {
32
- for await (const line of lines) {
33
- if (!line.trim())
34
- continue;
35
- let request;
36
- try {
37
- request = parseRequest(line);
38
- }
39
- catch (error) {
40
- writeErrorResponse(options.output, error);
41
- continue;
42
- }
43
- if (request.action === 'start') {
44
- try {
45
- assertRemoteControlAllowed();
46
- }
47
- catch (error) {
48
- writeErrorResponse(options.output, error);
49
- continue;
50
- }
51
- request = {
52
- ...request,
53
- taskName: request.taskName ?? defaultTask,
54
- actor: request.actor ?? options.actor,
55
- launchId: request.launchId ?? options.launchId,
56
- sessionId: request.sessionId ?? options.sessionId,
57
- };
58
- }
59
- else if (!request.task && defaultTask) {
60
- request = { ...request, task: defaultTask };
61
- }
62
- const response = await client.request(request);
63
- if (response.task)
64
- defaultTask = response.task;
65
- writeResponse(options.output, response);
66
- }
67
- }
68
- finally {
69
- lines.close();
70
- await client.close();
71
- }
72
- }
@@ -1,78 +0,0 @@
1
- interface TaskBinding {
2
- device: string;
3
- profile?: string;
4
- url?: string;
5
- sessionId?: string;
6
- launchId?: string;
7
- createdAt: number;
8
- }
9
- type TaskIndex = Record<string, TaskBinding>;
10
- export declare const REJECT_DEVICE_MESSAGE: string;
11
- type TaskRoute = {
12
- kind: 'proceed';
13
- task?: string;
14
- device: string;
15
- } | {
16
- kind: 'unknown';
17
- task: string;
18
- message: string;
19
- } | {
20
- kind: 'ambiguous';
21
- message: string;
22
- } | {
23
- kind: 'reject-device';
24
- message: string;
25
- };
26
- export declare function isTerminalBrowserVerb(verb: string): boolean;
27
- export declare function readTaskIndex(): TaskIndex;
28
- export declare function getTaskBinding(name: string): TaskBinding | undefined;
29
- export declare function listTaskBindings(): Array<{
30
- name: string;
31
- } & TaskBinding>;
32
- export declare function bindTask(name: string, binding: TaskBinding): void;
33
- export declare function updateTaskBinding(name: string, patch: Partial<TaskBinding>): void;
34
- export declare function unbindTask(name: string): void;
35
- /** Drop every binding recorded under this profile (after `stop --profile` / gc). */
36
- export declare function unbindTasksForProfile(profile: string): void;
37
- export declare function tasksForCaller(sessionId?: string, launchId?: string): Array<{
38
- name: string;
39
- } & TaskBinding>;
40
- export declare function formatOpenTaskList(entries: Array<{
41
- name: string;
42
- } & TaskBinding>, now?: number): string;
43
- export declare function unknownTaskMessage(name: string, entries?: ({
44
- name: string;
45
- } & TaskBinding)[]): string;
46
- export declare function ambiguousTasksMessage(entries: Array<{
47
- name: string;
48
- } & TaskBinding>): string;
49
- /**
50
- * Decide where a page verb should run.
51
- *
52
- * `--device` on a later verb is always rejected (bind it at start).
53
- * A named task that is not in the local index fails loud with the open-task
54
- * list. Two or more tasks for this caller, with no `--task`, fail the same way.
55
- * Zero matches with no `--task` follows the fleet browser hub when the caller
56
- * passed one (`opts.hub` — the box drives `browser.device` by default), so a
57
- * cold page verb reaches the logged-in hub browser exactly as a bare
58
- * `agents browser start` already does; with no hub it proceeds locally and the
59
- * daemon resolves from caller identity for the first implicit create. The caller
60
- * (the CLI's task-routing hook) supplies `opts.hub` from `defaultBrowserHub()`,
61
- * which is already undefined on the hub itself and on a fleet-remote re-exec, so
62
- * this can never forward to self or loop.
63
- */
64
- export declare function resolveTaskRoute(opts: {
65
- task?: string;
66
- device?: string;
67
- sessionId?: string;
68
- launchId?: string;
69
- self?: string;
70
- hub?: string;
71
- }): TaskRoute;
72
- /**
73
- * The daemon sandboxes screenshot writes to the browser runtime dir, so `-o`
74
- * outside that dir is ignored there. The CLI process honors `-o` by copying
75
- * the captured file to the requested path. Same path → no copy.
76
- */
77
- export declare function honorScreenshotOutput(requested: string | undefined, daemonPath: string): string;
78
- export {};
@@ -1,199 +0,0 @@
1
- /**
2
- * Local task → device index.
3
- *
4
- * `tasks.json` is per-profile and lives in that profile's cache dir on the
5
- * machine running the browser daemon, so a later verb issued from a different
6
- * box cannot see where task `post` lives. The machine that STARTS a task
7
- * records the binding here — a small JSON file in THIS box's browser cache.
8
- * No fleet state, no sync: the agent that opens a task is the agent that
9
- * drives it.
10
- *
11
- * Kind is not stored. A missing name fails loud and lists the open tasks;
12
- * it never guesses, never opens a new browser, never falls back to a default
13
- * profile.
14
- */
15
- import * as fs from 'node:fs';
16
- import * as path from 'node:path';
17
- import { getBrowserRuntimeDir } from '../state.js';
18
- import { machineId } from '../machine-id.js';
19
- export const REJECT_DEVICE_MESSAGE = '--device is only valid on `agents browser start`.\n' +
20
- 'The task is bound to a device at start; later verbs resolve it from --task.\n' +
21
- 'Next: agents browser start --task <name> --device <device>';
22
- /**
23
- * Verbs that CLOSE a browsing context. A cold (no bound task) close verb must
24
- * never follow the fleet hub: with no task it would target a browsing context
25
- * this session never opened, and on the hub could stop an unrelated task. They
26
- * stay local unless an explicit `--task` or a local binding routes them. The
27
- * driving/read verbs (navigate, screenshot, click, …) are safe to forward cold
28
- * because the hub creates the caller's OWN task, keyed to the identity the
29
- * dispatch now forwards.
30
- */
31
- const TERMINAL_BROWSER_VERBS = new Set(['done', 'stop']);
32
- export function isTerminalBrowserVerb(verb) {
33
- return TERMINAL_BROWSER_VERBS.has(verb);
34
- }
35
- function taskIndexPath() {
36
- return path.join(getBrowserRuntimeDir(), 'task-index.json');
37
- }
38
- export function readTaskIndex() {
39
- const file = taskIndexPath();
40
- if (!fs.existsSync(file))
41
- return {};
42
- let parsed;
43
- try {
44
- parsed = JSON.parse(fs.readFileSync(file, 'utf8'));
45
- }
46
- catch (error) {
47
- throw new Error(`Cannot read browser task index at ${file}: ${error instanceof Error ? error.message : String(error)}`);
48
- }
49
- if (parsed == null || typeof parsed !== 'object' || Array.isArray(parsed)) {
50
- throw new Error(`Cannot read browser task index at ${file}: document root must be a map.`);
51
- }
52
- return parsed;
53
- }
54
- function writeTaskIndex(index) {
55
- const file = taskIndexPath();
56
- fs.mkdirSync(path.dirname(file), { recursive: true });
57
- const tmp = `${file}.${process.pid}.tmp`;
58
- fs.writeFileSync(tmp, `${JSON.stringify(index, null, 2)}\n`);
59
- fs.renameSync(tmp, file);
60
- }
61
- export function getTaskBinding(name) {
62
- return readTaskIndex()[name];
63
- }
64
- export function listTaskBindings() {
65
- return Object.entries(readTaskIndex())
66
- .map(([name, binding]) => ({ name, ...binding }))
67
- .sort((a, b) => a.createdAt - b.createdAt);
68
- }
69
- export function bindTask(name, binding) {
70
- if (!name) {
71
- throw new Error('Cannot bind a browser task with an empty name.');
72
- }
73
- if (!binding.device) {
74
- throw new Error(`Cannot bind browser task "${name}" without a device.`);
75
- }
76
- const index = readTaskIndex();
77
- index[name] = { ...binding };
78
- writeTaskIndex(index);
79
- }
80
- export function updateTaskBinding(name, patch) {
81
- const index = readTaskIndex();
82
- const existing = index[name];
83
- if (!existing)
84
- return;
85
- index[name] = { ...existing, ...patch };
86
- writeTaskIndex(index);
87
- }
88
- export function unbindTask(name) {
89
- const index = readTaskIndex();
90
- if (!(name in index))
91
- return;
92
- delete index[name];
93
- writeTaskIndex(index);
94
- }
95
- /** Drop every binding recorded under this profile (after `stop --profile` / gc). */
96
- export function unbindTasksForProfile(profile) {
97
- const index = readTaskIndex();
98
- let changed = false;
99
- for (const [name, binding] of Object.entries(index)) {
100
- if (binding.profile === profile) {
101
- delete index[name];
102
- changed = true;
103
- }
104
- }
105
- if (changed)
106
- writeTaskIndex(index);
107
- }
108
- export function tasksForCaller(sessionId, launchId) {
109
- return listTaskBindings().filter((entry) => {
110
- if (sessionId && entry.sessionId === sessionId)
111
- return true;
112
- if (launchId && entry.launchId === launchId)
113
- return true;
114
- return false;
115
- });
116
- }
117
- function formatAge(createdAt, now = Date.now()) {
118
- const ageMs = Math.max(0, now - createdAt);
119
- if (ageMs < 60_000)
120
- return `${Math.round(ageMs / 1000)}s`;
121
- return `${Math.round(ageMs / 60_000)}m`;
122
- }
123
- export function formatOpenTaskList(entries, now = Date.now()) {
124
- if (entries.length === 0)
125
- return ' (no open tasks)';
126
- return entries
127
- .map((entry) => {
128
- const url = entry.url && entry.url.length > 0 ? entry.url : '-';
129
- return ` ${entry.name} url=${url} device=${entry.device} age=${formatAge(entry.createdAt, now)}`;
130
- })
131
- .join('\n');
132
- }
133
- export function unknownTaskMessage(name, entries = listTaskBindings()) {
134
- return [
135
- `Unknown browser task "${name}".`,
136
- 'Open tasks:',
137
- formatOpenTaskList(entries),
138
- 'Next: agents browser start --task <name> [--device <device>]',
139
- ].join('\n');
140
- }
141
- export function ambiguousTasksMessage(entries) {
142
- return [
143
- 'Multiple browser tasks for this session — pass --task <name>:',
144
- formatOpenTaskList(entries),
145
- 'Next: agents browser status',
146
- ].join('\n');
147
- }
148
- /**
149
- * Decide where a page verb should run.
150
- *
151
- * `--device` on a later verb is always rejected (bind it at start).
152
- * A named task that is not in the local index fails loud with the open-task
153
- * list. Two or more tasks for this caller, with no `--task`, fail the same way.
154
- * Zero matches with no `--task` follows the fleet browser hub when the caller
155
- * passed one (`opts.hub` — the box drives `browser.device` by default), so a
156
- * cold page verb reaches the logged-in hub browser exactly as a bare
157
- * `agents browser start` already does; with no hub it proceeds locally and the
158
- * daemon resolves from caller identity for the first implicit create. The caller
159
- * (the CLI's task-routing hook) supplies `opts.hub` from `defaultBrowserHub()`,
160
- * which is already undefined on the hub itself and on a fleet-remote re-exec, so
161
- * this can never forward to self or loop.
162
- */
163
- export function resolveTaskRoute(opts) {
164
- if (opts.device) {
165
- return { kind: 'reject-device', message: REJECT_DEVICE_MESSAGE };
166
- }
167
- const self = opts.self ?? machineId();
168
- if (opts.task) {
169
- const binding = getTaskBinding(opts.task);
170
- if (!binding) {
171
- return { kind: 'unknown', task: opts.task, message: unknownTaskMessage(opts.task) };
172
- }
173
- return { kind: 'proceed', task: opts.task, device: binding.device };
174
- }
175
- const matches = tasksForCaller(opts.sessionId, opts.launchId);
176
- if (matches.length > 1) {
177
- return { kind: 'ambiguous', message: ambiguousTasksMessage(matches) };
178
- }
179
- if (matches.length === 1) {
180
- const only = matches[0];
181
- return { kind: 'proceed', task: only.name, device: only.device };
182
- }
183
- return { kind: 'proceed', device: opts.hub ?? self };
184
- }
185
- /**
186
- * The daemon sandboxes screenshot writes to the browser runtime dir, so `-o`
187
- * outside that dir is ignored there. The CLI process honors `-o` by copying
188
- * the captured file to the requested path. Same path → no copy.
189
- */
190
- export function honorScreenshotOutput(requested, daemonPath) {
191
- if (!requested)
192
- return daemonPath;
193
- const dest = path.resolve(requested);
194
- if (path.resolve(daemonPath) === dest)
195
- return dest;
196
- fs.mkdirSync(path.dirname(dest), { recursive: true });
197
- fs.copyFileSync(daemonPath, dest);
198
- return dest;
199
- }