@phnx-labs/agents-cli 1.22.116 → 1.22.118

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 (254) hide show
  1. package/CHANGELOG.md +247 -0
  2. package/dist/bootstrap.js +4 -11
  3. package/dist/commands/browser.d.ts +52 -70
  4. package/dist/commands/browser.js +192 -3562
  5. package/dist/commands/daemon.d.ts +39 -2
  6. package/dist/commands/daemon.js +72 -73
  7. package/dist/commands/events.js +1 -1
  8. package/dist/commands/exec.d.ts +18 -0
  9. package/dist/commands/exec.js +104 -12
  10. package/dist/commands/repo.js +13 -49
  11. package/dist/commands/route.js +5 -5
  12. package/dist/commands/routines.js +39 -12
  13. package/dist/commands/sessions-render.d.ts +1 -1
  14. package/dist/commands/sessions-render.js +0 -1
  15. package/dist/commands/sessions.test-fixture.d.ts +0 -1
  16. package/dist/commands/sessions.test-fixture.js +0 -37
  17. package/dist/commands/setup-browser.d.ts +17 -12
  18. package/dist/commands/setup-browser.js +52 -87
  19. package/dist/commands/setup-preferences.d.ts +0 -17
  20. package/dist/commands/setup-preferences.js +18 -49
  21. package/dist/commands/setup-term.d.ts +4 -3
  22. package/dist/commands/setup-term.js +5 -4
  23. package/dist/commands/setup.js +11 -11
  24. package/dist/commands/ssh.js +10 -1
  25. package/dist/commands/sync.js +6 -44
  26. package/dist/commands/teams.js +0 -1
  27. package/dist/commands/update.d.ts +19 -0
  28. package/dist/commands/update.js +34 -6
  29. package/dist/commands/view.d.ts +12 -1
  30. package/dist/commands/view.js +47 -2
  31. package/dist/index.js +107 -26
  32. package/dist/lib/account-capabilities.js +0 -2
  33. package/dist/lib/account-catalog.d.ts +38 -0
  34. package/dist/lib/account-catalog.js +110 -26
  35. package/dist/lib/account-provider-registry.js +1 -1
  36. package/dist/lib/account-registry.d.ts +1 -1
  37. package/dist/lib/account-registry.js +36 -2
  38. package/dist/lib/accounting/rotate.js +22 -8
  39. package/dist/lib/accounting/usage-ingest.d.ts +6 -1
  40. package/dist/lib/accounting/usage-ingest.js +115 -45
  41. package/dist/lib/accounting/usage-sync.d.ts +134 -31
  42. package/dist/lib/accounting/usage-sync.js +227 -66
  43. package/dist/lib/accounting/usage.d.ts +16 -3
  44. package/dist/lib/accounting/usage.js +26 -8
  45. package/dist/lib/accounts/slots.d.ts +8 -0
  46. package/dist/lib/accounts/slots.js +17 -0
  47. package/dist/lib/add-dir.js +0 -1
  48. package/dist/lib/agent-cli-commands.js +0 -1
  49. package/dist/lib/agent-spec/agents.d.ts +6 -7
  50. package/dist/lib/agent-spec/agents.js +6 -60
  51. package/dist/lib/auth-health.d.ts +69 -1
  52. package/dist/lib/auth-health.js +133 -4
  53. package/dist/lib/browser/context.d.ts +82 -0
  54. package/dist/lib/browser/context.js +88 -0
  55. package/dist/lib/browser/paths.d.ts +25 -0
  56. package/dist/lib/browser/paths.js +51 -0
  57. package/dist/lib/browser/record.d.ts +38 -0
  58. package/dist/lib/browser/record.js +57 -0
  59. package/dist/lib/browser/sessions-list.js +1 -2
  60. package/dist/lib/browser-client.d.ts +158 -0
  61. package/dist/lib/browser-client.js +234 -0
  62. package/dist/lib/channels/owner-sink.d.ts +1 -18
  63. package/dist/lib/channels/owner-sink.js +29 -82
  64. package/dist/lib/channels/providers/rush.d.ts +7 -7
  65. package/dist/lib/channels/providers/rush.js +127 -57
  66. package/dist/lib/claude-statusline.d.ts +1 -1
  67. package/dist/lib/claude-statusline.js +15 -3
  68. package/dist/lib/commands.js +1 -1
  69. package/dist/lib/crabbox/lease.js +1 -1
  70. package/dist/lib/crabbox/runtimes.js +1 -3
  71. package/dist/lib/daemon/account-state-daemon-service.js +1 -0
  72. package/dist/lib/daemon/auth-sync-service.d.ts +21 -18
  73. package/dist/lib/daemon/auth-sync-service.js +37 -43
  74. package/dist/lib/daemon/daemon.d.ts +25 -14
  75. package/dist/lib/daemon/daemon.js +92 -154
  76. package/dist/lib/daemon/runner.d.ts +2 -2
  77. package/dist/lib/daemon/runner.js +32 -9
  78. package/dist/lib/daemon/self-update-service.d.ts +35 -34
  79. package/dist/lib/daemon/self-update-service.js +40 -36
  80. package/dist/lib/daemon/service.d.ts +12 -8
  81. package/dist/lib/daemon/supervisor.d.ts +65 -54
  82. package/dist/lib/daemon/supervisor.js +136 -171
  83. package/dist/lib/daemon/testdata/process-view-start.js +5 -2
  84. package/dist/lib/daemon/usage-sync-service.d.ts +21 -30
  85. package/dist/lib/daemon/usage-sync-service.js +46 -78
  86. package/dist/lib/daemon-health.d.ts +22 -5
  87. package/dist/lib/daemon-health.js +47 -3
  88. package/dist/lib/daemon-services.d.ts +1 -1
  89. package/dist/lib/daemon-services.js +0 -10
  90. package/dist/lib/daemon-ticks.js +4 -2
  91. package/dist/lib/devices/doctor-findings.js +8 -11
  92. package/dist/lib/devices/harness-inventory.js +14 -5
  93. package/dist/lib/exec-account-home.js +5 -0
  94. package/dist/lib/exec.js +0 -11
  95. package/dist/lib/feed/events.js +0 -1
  96. package/dist/lib/feed/tool-activity.js +5 -2
  97. package/dist/lib/fleet-shared-state.d.ts +45 -0
  98. package/dist/lib/fleet-shared-state.js +78 -9
  99. package/dist/lib/fs-atomic.js +25 -0
  100. package/dist/lib/harness-auth-capabilities.js +0 -1
  101. package/dist/lib/helper-versions.js +1 -1
  102. package/dist/lib/hooks/cache.d.ts +7 -0
  103. package/dist/lib/hooks/cache.js +27 -2
  104. package/dist/lib/hooks/install.js +7 -1
  105. package/dist/lib/hosts/ready.d.ts +38 -8
  106. package/dist/lib/hosts/ready.js +62 -14
  107. package/dist/lib/hosts/remote-cmd.js +1 -0
  108. package/dist/lib/installations/active-check.d.ts +27 -0
  109. package/dist/lib/installations/active-check.js +61 -0
  110. package/dist/lib/installations/migrate.js +6 -3
  111. package/dist/lib/installations/shims.js +9 -7
  112. package/dist/lib/installations/update.js +21 -15
  113. package/dist/lib/installations/versions.js +12 -1
  114. package/dist/lib/manifest.js +1 -1
  115. package/dist/lib/models.js +0 -2
  116. package/dist/lib/open-url.js +28 -54
  117. package/dist/lib/placement.d.ts +2 -0
  118. package/dist/lib/placement.js +5 -0
  119. package/dist/lib/profiles.js +0 -2
  120. package/dist/lib/refresh.js +1 -1
  121. package/dist/lib/routine-readiness.d.ts +35 -0
  122. package/dist/lib/routine-readiness.js +74 -16
  123. package/dist/lib/rules/compile.js +6 -0
  124. package/dist/lib/sandbox.js +0 -2
  125. package/dist/lib/secrets-policy.d.ts +89 -11
  126. package/dist/lib/secrets-policy.js +175 -28
  127. package/dist/lib/session/db.d.ts +1 -1
  128. package/dist/lib/session/db.js +65 -18
  129. package/dist/lib/session/discover.js +1 -283
  130. package/dist/lib/session/mirror.d.ts +4 -3
  131. package/dist/lib/session/mirror.js +4 -3
  132. package/dist/lib/session/parse.d.ts +0 -2
  133. package/dist/lib/session/parse.js +1 -138
  134. package/dist/lib/session/throughput.d.ts +1 -3
  135. package/dist/lib/session/throughput.js +0 -20
  136. package/dist/lib/session/types.d.ts +2 -2
  137. package/dist/lib/session/types.js +2 -2
  138. package/dist/lib/sessions-client.d.ts +60 -1
  139. package/dist/lib/sessions-client.js +227 -4
  140. package/dist/lib/setup-tool-status.js +8 -12
  141. package/dist/lib/signin-badge.d.ts +3 -2
  142. package/dist/lib/signin-badge.js +6 -3
  143. package/dist/lib/smart-launch.js +4 -1
  144. package/dist/lib/staleness/detectors/commands.d.ts +1 -1
  145. package/dist/lib/staleness/detectors/hooks.d.ts +1 -1
  146. package/dist/lib/staleness/detectors/mcp.d.ts +1 -1
  147. package/dist/lib/staleness/detectors/permissions.d.ts +1 -1
  148. package/dist/lib/staleness/detectors/plugins.d.ts +1 -1
  149. package/dist/lib/staleness/detectors/rules.d.ts +1 -1
  150. package/dist/lib/staleness/detectors/skills.d.ts +1 -1
  151. package/dist/lib/staleness/detectors/subagents.d.ts +1 -1
  152. package/dist/lib/staleness/detectors/workflows.d.ts +1 -1
  153. package/dist/lib/staleness/writers/commands.d.ts +1 -1
  154. package/dist/lib/staleness/writers/hooks.d.ts +1 -1
  155. package/dist/lib/staleness/writers/mcp.d.ts +1 -1
  156. package/dist/lib/staleness/writers/permissions.d.ts +1 -1
  157. package/dist/lib/staleness/writers/plugins.d.ts +1 -1
  158. package/dist/lib/staleness/writers/rules.d.ts +1 -1
  159. package/dist/lib/staleness/writers/skills.d.ts +1 -1
  160. package/dist/lib/staleness/writers/sources.js +5 -0
  161. package/dist/lib/staleness/writers/subagents.d.ts +1 -1
  162. package/dist/lib/staleness/writers/workflows.d.ts +1 -1
  163. package/dist/lib/state.d.ts +6 -4
  164. package/dist/lib/state.js +7 -17
  165. package/dist/lib/teams/parsers.d.ts +1 -1
  166. package/dist/lib/teams/parsers.js +1 -136
  167. package/dist/lib/teams/placement-probe.js +50 -15
  168. package/dist/lib/teams/scheduler.d.ts +7 -0
  169. package/dist/lib/types.d.ts +10 -1
  170. package/dist/lib/types.js +1 -1
  171. package/dist/lib/view-types.d.ts +32 -0
  172. package/dist/lib/watchdog/read.js +0 -1
  173. package/dist/lib/watchdog/rotate.js +1 -2
  174. package/dist/lib/watchdog/runner.js +2 -2
  175. package/dist/lib/watchdog/watchdog.d.ts +1 -1
  176. package/dist/lib/watchdog/watchdogTail.js +0 -31
  177. package/package.json +1 -1
  178. package/scripts/postinstall.js +8 -8
  179. package/dist/browser.d.ts +0 -2
  180. package/dist/browser.js +0 -17
  181. package/dist/commands/browser-picker.d.ts +0 -18
  182. package/dist/commands/browser-picker.js +0 -97
  183. package/dist/lib/browser/arc-discovery.d.ts +0 -52
  184. package/dist/lib/browser/arc-discovery.js +0 -197
  185. package/dist/lib/browser/arc-dom.d.ts +0 -14
  186. package/dist/lib/browser/arc-dom.js +0 -121
  187. package/dist/lib/browser/caller-identity.d.ts +0 -40
  188. package/dist/lib/browser/caller-identity.js +0 -175
  189. package/dist/lib/browser/cdp.d.ts +0 -51
  190. package/dist/lib/browser/cdp.js +0 -254
  191. package/dist/lib/browser/chrome.d.ts +0 -141
  192. package/dist/lib/browser/chrome.js +0 -704
  193. package/dist/lib/browser/chromium-discovery.d.ts +0 -28
  194. package/dist/lib/browser/chromium-discovery.js +0 -96
  195. package/dist/lib/browser/devices.d.ts +0 -34
  196. package/dist/lib/browser/devices.js +0 -61
  197. package/dist/lib/browser/domain-skills.d.ts +0 -71
  198. package/dist/lib/browser/domain-skills.js +0 -195
  199. package/dist/lib/browser/drivers/arc.d.ts +0 -40
  200. package/dist/lib/browser/drivers/arc.js +0 -276
  201. package/dist/lib/browser/drivers/firefox.d.ts +0 -99
  202. package/dist/lib/browser/drivers/firefox.js +0 -377
  203. package/dist/lib/browser/drivers/local.d.ts +0 -55
  204. package/dist/lib/browser/drivers/local.js +0 -266
  205. package/dist/lib/browser/drivers/ssh.d.ts +0 -120
  206. package/dist/lib/browser/drivers/ssh.js +0 -467
  207. package/dist/lib/browser/editor.d.ts +0 -3
  208. package/dist/lib/browser/editor.js +0 -50
  209. package/dist/lib/browser/ffmpeg.d.ts +0 -12
  210. package/dist/lib/browser/ffmpeg.js +0 -184
  211. package/dist/lib/browser/firefox-discovery.d.ts +0 -69
  212. package/dist/lib/browser/firefox-discovery.js +0 -162
  213. package/dist/lib/browser/har.d.ts +0 -85
  214. package/dist/lib/browser/har.js +0 -77
  215. package/dist/lib/browser/hygiene.d.ts +0 -97
  216. package/dist/lib/browser/hygiene.js +0 -153
  217. package/dist/lib/browser/index.d.ts +0 -5
  218. package/dist/lib/browser/index.js +0 -5
  219. package/dist/lib/browser/input.d.ts +0 -7
  220. package/dist/lib/browser/input.js +0 -92
  221. package/dist/lib/browser/ipc.d.ts +0 -204
  222. package/dist/lib/browser/ipc.js +0 -1453
  223. package/dist/lib/browser/login-detection.d.ts +0 -87
  224. package/dist/lib/browser/login-detection.js +0 -274
  225. package/dist/lib/browser/profiles.d.ts +0 -422
  226. package/dist/lib/browser/profiles.js +0 -1157
  227. package/dist/lib/browser/refs.d.ts +0 -89
  228. package/dist/lib/browser/refs.js +0 -191
  229. package/dist/lib/browser/registry.d.ts +0 -70
  230. package/dist/lib/browser/registry.js +0 -252
  231. package/dist/lib/browser/remote-control.d.ts +0 -59
  232. package/dist/lib/browser/remote-control.js +0 -85
  233. package/dist/lib/browser/resolve-target.d.ts +0 -66
  234. package/dist/lib/browser/resolve-target.js +0 -264
  235. package/dist/lib/browser/runtime-state.d.ts +0 -291
  236. package/dist/lib/browser/runtime-state.js +0 -584
  237. package/dist/lib/browser/secret-ref.d.ts +0 -10
  238. package/dist/lib/browser/secret-ref.js +0 -14
  239. package/dist/lib/browser/service.d.ts +0 -812
  240. package/dist/lib/browser/service.js +0 -4693
  241. package/dist/lib/browser/stream.d.ts +0 -17
  242. package/dist/lib/browser/stream.js +0 -72
  243. package/dist/lib/browser/task-index.d.ts +0 -78
  244. package/dist/lib/browser/task-index.js +0 -199
  245. package/dist/lib/browser/types.d.ts +0 -647
  246. package/dist/lib/browser/types.js +0 -61
  247. package/dist/lib/browser/upload.d.ts +0 -29
  248. package/dist/lib/browser/upload.js +0 -298
  249. package/dist/lib/daemon/browser-ipc-service.d.ts +0 -20
  250. package/dist/lib/daemon/browser-ipc-service.js +0 -46
  251. package/dist/lib/daemon/browser-task-reap-service.d.ts +0 -14
  252. package/dist/lib/daemon/browser-task-reap-service.js +0 -26
  253. package/dist/lib/fleet-shared-repo-sync.d.ts +0 -55
  254. package/dist/lib/fleet-shared-repo-sync.js +0 -490
@@ -1,175 +0,0 @@
1
- /**
2
- * Resolve the agent identity of whoever invoked `agents browser`.
3
- *
4
- * The browser daemon is shared and long-lived, so identity must be captured in
5
- * the CLI process and forwarded over IPC. Order:
6
- *
7
- * 1. Env already set by `agents run` (`AGENT_SESSION_ID` / `AGENT_LAUNCH_ID`)
8
- * 2. Session-tracker state file under `~/.agents/.cache/terminals/sessions/`
9
- * (and the deployed fleet path under `state/sessions/`), keyed by
10
- * process.ppid and walked up the ancestor chain
11
- * 3. Process-table anchor — nearest ancestor whose comm maps to a known
12
- * harness via `agentKindFromComm` (derived from SESSION_AGENTS)
13
- *
14
- * No per-harness env-var table: the state-file reader and the process-table
15
- * classifier cover every harness by construction.
16
- */
17
- import { execFileSync } from 'child_process';
18
- import { resolveActor } from '../actor.js';
19
- import { agentKindFromComm } from '../session/active.js';
20
- import { loadHookSessionIndex, readStateSessionRecord, } from '../session/hook-sessions.js';
21
- const MAX_ANCESTOR_WALK = 16;
22
- /**
23
- * Read pid→ppid→comm for one process. Returns null when the process is gone
24
- * or `ps` fails.
25
- */
26
- function readProc(pid) {
27
- if (!pid || pid <= 1)
28
- return null;
29
- try {
30
- const out = execFileSync('ps', ['-p', String(pid), '-o', 'pid=,ppid=,comm='], {
31
- encoding: 'utf8',
32
- timeout: 1000,
33
- }).trim();
34
- const m = out.match(/^(\d+)\s+(\d+)\s+(.+)$/);
35
- if (!m)
36
- return null;
37
- return {
38
- pid: parseInt(m[1], 10),
39
- ppid: parseInt(m[2], 10),
40
- comm: m[3].trim(),
41
- };
42
- }
43
- catch {
44
- return null;
45
- }
46
- }
47
- /**
48
- * Walk ancestors starting at `startPid` (inclusive). Yields each pid once.
49
- */
50
- function* ancestorPids(startPid) {
51
- let cur = startPid;
52
- const seen = new Set();
53
- for (let i = 0; i < MAX_ANCESTOR_WALK && cur > 1 && !seen.has(cur); i++) {
54
- seen.add(cur);
55
- yield cur;
56
- const proc = readProc(cur);
57
- if (!proc)
58
- return;
59
- cur = proc.ppid;
60
- }
61
- }
62
- /**
63
- * Look up a session-tracker / deployed-hook state record for `pid`.
64
- * Prefers the richer terminals/sessions index; falls back to the fleet
65
- * state/sessions path (RUSH-2007).
66
- */
67
- function stateForPid(pid, index) {
68
- const fromIndex = index.byPid.get(pid);
69
- if (fromIndex?.session_id)
70
- return fromIndex;
71
- return readStateSessionRecord(pid);
72
- }
73
- /**
74
- * Resolve caller identity once per CLI process for browser IPC stamping.
75
- *
76
- * Starts from `process.ppid` — the shell or harness that launched this
77
- * `agents browser` invocation — and walks up until a state file or known
78
- * agent comm is found.
79
- */
80
- export function resolveCallerIdentity(env = process.env) {
81
- const actor = resolveActor().id;
82
- let sessionId = env.AGENT_SESSION_ID || env.AGENTS_SESSION_ID || undefined;
83
- let launchId = env.AGENT_LAUNCH_ID || undefined;
84
- let agentPid;
85
- if (sessionId && launchId) {
86
- return { actor, sessionId, launchId };
87
- }
88
- // Start at ppid: when an agent runs a Bash tool call, the browser CLI's
89
- // parent is that shell (or the agent itself when launched directly).
90
- const startPid = typeof process.ppid === 'number' && process.ppid > 0 ? process.ppid : 0;
91
- if (startPid > 0) {
92
- let index;
93
- try {
94
- index = loadHookSessionIndex();
95
- }
96
- catch {
97
- index = undefined;
98
- }
99
- for (const pid of ancestorPids(startPid)) {
100
- if (index) {
101
- const rec = stateForPid(pid, index);
102
- if (rec?.session_id) {
103
- sessionId = sessionId || rec.session_id;
104
- launchId = launchId || rec.launch_id;
105
- agentPid = pid;
106
- break;
107
- }
108
- }
109
- else {
110
- const rec = readStateSessionRecord(pid);
111
- if (rec?.session_id) {
112
- sessionId = sessionId || rec.session_id;
113
- launchId = launchId || rec.launch_id;
114
- agentPid = pid;
115
- break;
116
- }
117
- }
118
- }
119
- // Process-table anchor: nearest ancestor whose comm is a known harness.
120
- // Covers harnesses with no SessionStart hook (and sessions the hook missed).
121
- //
122
- // IMPORTANT: stamp as launchId, NEVER as sessionId. A synthetic
123
- // `agent-pid:<n>` is not a real agent session UUID — if it lands in
124
- // sessionId, hygiene.taskOwnerIsGone treats it as a dead session (the
125
- // process-table session probe rejects non-UUID shapes) and reaps the
126
- // task mid-run. launchId-only tasks are never session-reaped
127
- // (hygiene.ts: launchId alone is not proof of death).
128
- if (!sessionId && !launchId) {
129
- for (const pid of ancestorPids(startPid)) {
130
- const proc = readProc(pid);
131
- if (!proc)
132
- continue;
133
- const kind = agentKindFromComm(proc.comm);
134
- if (kind) {
135
- launchId = `agent-pid:${pid}`;
136
- agentPid = pid;
137
- break;
138
- }
139
- }
140
- }
141
- }
142
- return { actor, sessionId, launchId, agentPid };
143
- }
144
- /**
145
- * The env a `browser --device` dispatch forwards so the HUB resolves the SAME
146
- * caller identity the worker would stamp locally. Without it a forwarded verb
147
- * lands on the hub with BLANK identity, and the daemon's no-identity task bucket
148
- * matches any other no-identity task — so a cold verb could attach to (or a
149
- * `done`/`stop` could close) an unrelated agent's or a human's browser. This is
150
- * the same surface-parity fix the `run --device` path already carries via
151
- * `--env AGENT_LAUNCH_ID` (remote-session-id.ts). Absent fields are omitted so a
152
- * genuinely-unidentifiable caller forwards nothing rather than an empty string
153
- * that would read as a real (blank) identity on the far side.
154
- */
155
- export function callerIdentityEnv(caller) {
156
- const env = {};
157
- if (caller.sessionId)
158
- env.AGENT_SESSION_ID = caller.sessionId;
159
- if (caller.launchId)
160
- env.AGENT_LAUNCH_ID = caller.launchId;
161
- return env;
162
- }
163
- /**
164
- * True when a live task belongs to this caller. Match on sessionId or
165
- * launchId — either is enough. A task with no recorded identity never
166
- * matches a caller that has one (and vice versa for the no-identity case
167
- * handled separately by the resolver).
168
- */
169
- export function taskMatchesCaller(task, caller) {
170
- if (caller.sessionId && task.sessionId && caller.sessionId === task.sessionId)
171
- return true;
172
- if (caller.launchId && task.launchId && caller.launchId === task.launchId)
173
- return true;
174
- return false;
175
- }
@@ -1,51 +0,0 @@
1
- import type { Readable, Writable } from 'stream';
2
- interface CDPPipeTransport {
3
- read: Readable;
4
- write: Writable;
5
- }
6
- type EventHandler = (params: Record<string, unknown>, sessionId?: string) => void;
7
- export declare function registerPipeTransport(transport: CDPPipeTransport): string;
8
- export declare class CDPClient {
9
- private ws;
10
- private pipe;
11
- private pipeBuffer;
12
- private transport;
13
- private messageId;
14
- private pending;
15
- private eventHandlers;
16
- private pipeDataHandler;
17
- private pipeErrorHandler;
18
- private pipeCloseHandler;
19
- connect(endpoint: string): Promise<void>;
20
- connectPipe(transport: CDPPipeTransport): void;
21
- send<T = unknown>(method: string, params?: Record<string, unknown>, sessionId?: string): Promise<T>;
22
- on(event: string, handler: EventHandler): void;
23
- off(event: string, handler: EventHandler): void;
24
- close(): void;
25
- get connected(): boolean;
26
- get isOpen(): boolean;
27
- private handlePipeData;
28
- private handleMessage;
29
- private handleClose;
30
- }
31
- export interface BrowserDiscovery {
32
- wsUrl: string;
33
- browser: string;
34
- }
35
- export declare class BrowserCdpConnectionError extends Error {
36
- readonly port: number;
37
- readonly profileName: string;
38
- readonly host: string;
39
- constructor(port: number, profileName?: string, host?: string);
40
- }
41
- export declare function formatBrowserCdpConnectionError(port: number, profileName?: string, host?: string): string;
42
- export declare function discoverBrowserWsUrl(port: number, host?: string, profileName?: string): Promise<BrowserDiscovery>;
43
- export declare function normalizeBrowserName(s: string): string;
44
- export declare function verifyBrowserIdentity(reported: string, expected: string, port: number, host?: string): void;
45
- export declare function listTargets(port: number, host?: string): Promise<Array<{
46
- id: string;
47
- type: string;
48
- title: string;
49
- url: string;
50
- }>>;
51
- export {};
@@ -1,254 +0,0 @@
1
- // The `ws` client, NOT the platform (undici) WebSocket: undici enforces a
2
- // non-configurable max decompressed message size, and a CDP response carrying
3
- // a base64 screenshot of a content-rich page blows past it — the socket dies
4
- // with code 1006 ("Max decompressed message size exceeded") while the command
5
- // is pending. `ws` offers no permessage-deflate by default and takes an
6
- // explicit payload cap. (Reproduced live against a remote Edge over
7
- // `browser --device`: fresh blank newtab passed, reused content-rich newtab
8
- // failed on every Page.captureScreenshot.)
9
- import WSWebSocket from 'ws';
10
- const pipeTransports = new Map();
11
- export function registerPipeTransport(transport) {
12
- const id = `pipe://${process.pid}/${pipeTransports.size + 1}`;
13
- pipeTransports.set(id, transport);
14
- return id;
15
- }
16
- export class CDPClient {
17
- ws = null;
18
- pipe = null;
19
- pipeBuffer = Buffer.alloc(0);
20
- transport = null;
21
- messageId = 0;
22
- pending = new Map();
23
- eventHandlers = new Map();
24
- pipeDataHandler = null;
25
- pipeErrorHandler = null;
26
- pipeCloseHandler = null;
27
- async connect(endpoint) {
28
- if (endpoint.startsWith('pipe://')) {
29
- const transport = pipeTransports.get(endpoint);
30
- if (!transport) {
31
- throw new Error('CDP pipe transport is not available in this process');
32
- }
33
- pipeTransports.delete(endpoint);
34
- this.connectPipe(transport);
35
- return;
36
- }
37
- return new Promise((resolve, reject) => {
38
- this.ws = new WSWebSocket(endpoint, { maxPayload: 256 * 1024 * 1024 });
39
- this.transport = 'websocket';
40
- this.ws.onopen = () => resolve();
41
- this.ws.onerror = () => reject(new Error('WebSocket error'));
42
- this.ws.onclose = () => this.handleClose();
43
- this.ws.onmessage = (ev) => this.handleMessage(String(ev.data));
44
- });
45
- }
46
- connectPipe(transport) {
47
- this.pipe = transport;
48
- this.transport = 'pipe';
49
- this.pipeBuffer = Buffer.alloc(0);
50
- this.pipeDataHandler = (chunk) => this.handlePipeData(chunk);
51
- this.pipeErrorHandler = (err) => this.handleClose(err);
52
- this.pipeCloseHandler = () => this.handleClose();
53
- transport.read.on('data', this.pipeDataHandler);
54
- transport.read.on('error', this.pipeErrorHandler);
55
- transport.read.on('close', this.pipeCloseHandler);
56
- transport.write.on('error', this.pipeErrorHandler);
57
- transport.write.on('close', this.pipeCloseHandler);
58
- }
59
- async send(method, params, sessionId) {
60
- if (!this.isOpen) {
61
- // Reached when the underlying browser was killed externally between
62
- // the daemon establishing the connection and a CDP call going out.
63
- // The service-layer healthcheck normally catches this on the next
64
- // `start`, so seeing this in the wild means a request landed against
65
- // an in-flight conn that just died — tell the user how to recover.
66
- throw new Error('CDP connection not open — the browser was likely closed externally. ' +
67
- 'Run `agents browser stop --profile <name>` (or restart the daemon) and try again.');
68
- }
69
- const id = ++this.messageId;
70
- const message = sessionId
71
- ? JSON.stringify({ id, method, params, sessionId })
72
- : JSON.stringify({ id, method, params });
73
- return new Promise((resolve, reject) => {
74
- this.pending.set(id, { resolve: resolve, reject });
75
- if (this.transport === 'pipe') {
76
- this.pipe.write.write(message + '\0');
77
- }
78
- else {
79
- this.ws.send(message);
80
- }
81
- });
82
- }
83
- on(event, handler) {
84
- if (!this.eventHandlers.has(event)) {
85
- this.eventHandlers.set(event, new Set());
86
- }
87
- this.eventHandlers.get(event).add(handler);
88
- }
89
- off(event, handler) {
90
- this.eventHandlers.get(event)?.delete(handler);
91
- }
92
- close() {
93
- if (this.ws) {
94
- this.ws.close();
95
- this.ws = null;
96
- }
97
- if (this.pipe) {
98
- if (this.pipeDataHandler)
99
- this.pipe.read.off('data', this.pipeDataHandler);
100
- if (this.pipeErrorHandler) {
101
- this.pipe.read.off('error', this.pipeErrorHandler);
102
- this.pipe.write.off('error', this.pipeErrorHandler);
103
- }
104
- if (this.pipeCloseHandler) {
105
- this.pipe.read.off('close', this.pipeCloseHandler);
106
- this.pipe.write.off('close', this.pipeCloseHandler);
107
- }
108
- this.pipe.write.end();
109
- this.pipe = null;
110
- }
111
- this.transport = null;
112
- }
113
- get connected() {
114
- return ((this.ws !== null && this.ws.readyState === WSWebSocket.OPEN) ||
115
- (this.pipe !== null && !this.pipe.write.destroyed));
116
- }
117
- get isOpen() {
118
- return this.connected;
119
- }
120
- handlePipeData(chunk) {
121
- this.pipeBuffer = Buffer.concat([this.pipeBuffer, chunk]);
122
- let idx = this.pipeBuffer.indexOf(0);
123
- while (idx !== -1) {
124
- const frame = this.pipeBuffer.subarray(0, idx).toString('utf8');
125
- this.pipeBuffer = this.pipeBuffer.subarray(idx + 1);
126
- if (frame.length > 0)
127
- this.handleMessage(frame);
128
- idx = this.pipeBuffer.indexOf(0);
129
- }
130
- }
131
- handleMessage(data) {
132
- const msg = JSON.parse(data);
133
- if ('id' in msg) {
134
- const pending = this.pending.get(msg.id);
135
- if (pending) {
136
- this.pending.delete(msg.id);
137
- if ('error' in msg) {
138
- pending.reject(new Error(msg.error.message || 'CDP error'));
139
- }
140
- else {
141
- pending.resolve(msg.result);
142
- }
143
- }
144
- }
145
- else if ('method' in msg) {
146
- const handlers = this.eventHandlers.get(msg.method);
147
- if (handlers) {
148
- for (const handler of handlers) {
149
- handler(msg.params || {}, msg.sessionId);
150
- }
151
- }
152
- }
153
- }
154
- handleClose(err) {
155
- for (const pending of this.pending.values()) {
156
- pending.reject(err ?? new Error('CDP connection closed'));
157
- }
158
- this.pending.clear();
159
- this.ws = null;
160
- this.pipe = null;
161
- this.transport = null;
162
- }
163
- }
164
- export class BrowserCdpConnectionError extends Error {
165
- port;
166
- profileName;
167
- host;
168
- constructor(port, profileName = '<name>', host = 'localhost') {
169
- super(formatBrowserCdpConnectionError(port, profileName, host));
170
- this.port = port;
171
- this.profileName = profileName;
172
- this.host = host;
173
- this.name = 'BrowserCdpConnectionError';
174
- }
175
- }
176
- export function formatBrowserCdpConnectionError(port, profileName = '<name>', host = 'localhost') {
177
- const target = host === 'localhost' || host === '127.0.0.1'
178
- ? `port ${port}`
179
- : `${host}:${port}`;
180
- return [
181
- `Could not connect to Chrome on ${target}.`,
182
- `- Is Chrome running with --remote-debugging-port=${port}?`,
183
- `- Try: agents browser start --profile ${profileName}`,
184
- ].join('\n');
185
- }
186
- export async function discoverBrowserWsUrl(port, host = 'localhost', profileName = '<name>') {
187
- // Node's fetch has no default timeout — a port that ACKs the TCP connect
188
- // but never sends an HTTP response will hang here indefinitely. Bound the
189
- // discovery probe so the caller can surface an actionable error in seconds,
190
- // not minutes.
191
- const controller = new AbortController();
192
- const timer = setTimeout(() => controller.abort(), 3000);
193
- let response;
194
- try {
195
- response = await fetch(`http://${host}:${port}/json/version`, { signal: controller.signal });
196
- }
197
- catch {
198
- throw new BrowserCdpConnectionError(port, profileName, host);
199
- }
200
- finally {
201
- clearTimeout(timer);
202
- }
203
- if (!response.ok) {
204
- throw new BrowserCdpConnectionError(port, profileName, host);
205
- }
206
- const data = (await response.json());
207
- const browserField = data.Browser || data.Product || '';
208
- return {
209
- wsUrl: data.webSocketDebuggerUrl,
210
- browser: normalizeBrowserName(browserField),
211
- };
212
- }
213
- export function normalizeBrowserName(s) {
214
- if (!s)
215
- return 'unknown';
216
- return s.split('/')[0].trim().toLowerCase().replace(/\s+/g, '-');
217
- }
218
- export function verifyBrowserIdentity(reported, expected, port, host = 'localhost') {
219
- if (expected === 'custom')
220
- return;
221
- if (reported === 'unknown')
222
- return;
223
- const matches = {
224
- chrome: ['chrome', 'google-chrome', 'headlesschrome'],
225
- chromium: ['chromium', 'headlesschrome', 'chrome'],
226
- // Comet reports itself as plain "Chrome/<version>" in /json/version — it
227
- // doesn't override the Chromium branding. Accept chrome here so attaching
228
- // to a Comet instance doesn't trip a false "identity mismatch".
229
- comet: ['comet', 'chrome'],
230
- // Arc reports itself as plain "Chrome/<version>" in /json/version (verified
231
- // live: Arc 1.15x → "Chrome/151"), so accept chrome to avoid a false
232
- // identity mismatch when attaching to a real Arc.
233
- arc: ['arc', 'chrome'],
234
- brave: ['brave', 'brave-browser', 'chrome'],
235
- // Windows/Chromium Edge reports its /json/version "Browser" field as
236
- // "Edg/<version>" (the `Edg` token, not `Edge`), which normalizeBrowserName
237
- // reduces to "edg". Accept it so attaching to a real Edge doesn't trip a
238
- // false identity mismatch (the profile enum only allows "edge").
239
- edge: ['edge', 'edg', 'microsoft-edge', 'msedge', 'chrome'],
240
- };
241
- const accepted = matches[expected] || [expected];
242
- if (accepted.includes(reported))
243
- return;
244
- const target = host === 'localhost' || host === '127.0.0.1' ? `port ${port}` : `${host}:${port}`;
245
- throw new Error(`Browser identity mismatch: profile expects "${expected}" but ${target} is serving "${reported}". ` +
246
- `Stop the running browser (e.g. \`pkill -f ${reported}\`) or update the profile to browser=${reported}, then retry.`);
247
- }
248
- export async function listTargets(port, host = 'localhost') {
249
- const response = await fetch(`http://${host}:${port}/json`);
250
- if (!response.ok) {
251
- throw new Error(`Failed to list targets: ${response.status}`);
252
- }
253
- return response.json();
254
- }
@@ -1,141 +0,0 @@
1
- import type { ChromeOptions } from './types.js';
2
- import type { BrowserType } from './types.js';
3
- /**
4
- * True when `binaryPath` is a shebang script rather than a native browser
5
- * executable. The Linux distro launchers (`/usr/bin/brave-browser`, …) are such
6
- * scripts; `launchBrowser` can't drive one over `--remote-debugging-pipe` (see
7
- * resolveBrowserBinary). `profiles doctor` uses this to flag a profile whose
8
- * binary resolves to a wrapper we couldn't unwrap. Shebang scripts are a
9
- * Linux/Unix concept — returns false on Windows/macOS app bundles.
10
- */
11
- export declare function isLauncherScript(binaryPath: string): boolean;
12
- export declare function resolveBrowserBinary(binaryPath: string): string;
13
- export declare function findBrowserPath(browserType: BrowserType, customBinary?: string): string;
14
- /**
15
- * Walk the per-platform priority list and return the first browser that's
16
- * actually installed on disk. Returns null if none of them are present.
17
- *
18
- * This is the auto-pick the `agents browser start` command uses when the user
19
- * doesn't pass `--profile`. The intent matches "use whatever's preinstalled,"
20
- * but constrained to Chromium-family binaries so CDP works without a new
21
- * driver layer.
22
- */
23
- export declare function findFirstInstalledBrowser(platform?: string): {
24
- browserType: BrowserType;
25
- binary: string;
26
- } | null;
27
- /**
28
- * List every installed Chromium-family browser on this machine, in the platform
29
- * priority order. Used by `agents setup browser` to offer a pick when more than
30
- * one is present. Returns [] if none are installed.
31
- */
32
- export declare function listInstalledBrowsers(platform?: string): {
33
- browserType: BrowserType;
34
- binary: string;
35
- }[];
36
- export interface LaunchResult {
37
- pid: number;
38
- port: number;
39
- wsUrl: string;
40
- }
41
- /**
42
- * The process that holds a Chromium user-data dir, read from the `SingletonLock`
43
- * symlink Chromium keeps inside the dir on macOS and Linux (`<host>-<pid>`,
44
- * PHNX-4042). A live pid means a browser already owns that store: a launch on it
45
- * would only hand its arguments to the running instance and the requested debug
46
- * port would never bind. Windows keeps no lock file, so this reports null there
47
- * and `launchBrowser` catches the hand-off by the child's early exit instead.
48
- */
49
- export declare function storeOccupant(userDataDir: string): {
50
- pid: number;
51
- } | null;
52
- /**
53
- * The relaunch that makes a browser holding `userDataDir` attachable: the
54
- * ownership guard reads `--user-data-dir` off the running process, so the
55
- * owner's normal launch (no flag) can never be verified.
56
- */
57
- export declare function storeRelaunchCommand(browserType: BrowserType, port: number, userDataDir: string, profileDirectory?: string): string;
58
- /**
59
- * Resolve a browser-profile secrets bundle into an env map for the child, or an
60
- * EMPTY map when the bundle is absent, locked, or otherwise unreadable — never a
61
- * throw and never a prompt. Injecting profile secrets on launch is a BACKGROUND
62
- * read (the agent is spawning the browser, not a human at a Touch ID sheet), so
63
- * the read is `agentOnly` (SEC-13: never pop biometry on its own): a `never`/no-ACL
64
- * or broker-held bundle resolves silently; a locked `hold`/`always` bundle throws,
65
- * and we swallow it so the launch proceeds without those secrets (an agent that
66
- * needs them can `agents secrets unlock <bundle>`). Exported so the no-prompt
67
- * behavior is testable on the real keychain path without spawning a browser.
68
- */
69
- export declare function resolveProfileSecretsEnv(secrets?: string): Promise<Record<string, string>>;
70
- export declare function launchBrowser(profileName: string, browserType: BrowserType, port: number, options?: ChromeOptions, secrets?: string, customBinary?: string, isElectron?: boolean): Promise<LaunchResult>;
71
- export declare function killChrome(pid: number): void;
72
- export declare function getRunningChromeInfo(profileName: string): {
73
- pid: number;
74
- port: number;
75
- } | null;
76
- /**
77
- * Prepare `<userDataDir>/Default/Preferences` before launch.
78
- *
79
- * Two concerns, one write:
80
- * - First launch (file absent): stamp the agents-cli profile name so
81
- * Chromium's UI shows "<profile>" instead of its default "Person 1".
82
- * Cosmetic; existing files keep whatever Chrome wrote in the meantime.
83
- * - Every launch (when `persistSessionCookies`): pin
84
- * `session.restore_on_startup: 1` ("continue where you left off").
85
- * Chromium purges memory-only session cookies at startup UNLESS this
86
- * preference says the session will be restored — it keys the purge off
87
- * the pref, not off tabs actually reopening. Sites like idealista issue
88
- * login cookies with `expires=-1`, so without this every browser restart
89
- * silently logs the profile out. The visible tab-restore side effect is
90
- * suppressed separately via `--no-startup-window` (see launchBrowser).
91
- *
92
- * Runs only while the browser is down (called before spawn), so Chromium
93
- * can't overwrite the patch on exit. Best-effort: a malformed existing file
94
- * is left untouched (Chromium recovers its own state better than we can),
95
- * and any I/O hiccup is silently ignored.
96
- */
97
- export declare function ensureProfilePreferences(userDataDir: string, profileName: string, persistSessionCookies: boolean): void;
98
- /**
99
- * Is a TCP port currently bound? `lsof` on POSIX, `netstat -ano` on Windows
100
- * (lsof doesn't exist there). Returns false on any tooling error so port
101
- * allocation degrades to "assume free" rather than throwing.
102
- *
103
- * Exported as the canonical cross-platform probe — `findFreeProfilePort`
104
- * (profiles.ts) must use this rather than shelling out to lsof directly,
105
- * or every port scans as free on Windows.
106
- */
107
- export declare function isPortInUse(port: number): boolean;
108
- export declare function allocatePort(): number;
109
- /**
110
- * Read the `--user-data-dir` a running browser process was launched with, by
111
- * inspecting its command line (PHNX-3967). This is the ownership signal the
112
- * attach-only guard uses to tell the credentialed canonical browser apart from a
113
- * foreign port-squatter serving CDP on the same port (e.g. a logged-out
114
- * `/tmp/...` Comet). Returns null when the pid is gone, exposes no
115
- * `--user-data-dir`, or the platform probe fails.
116
- *
117
- * POSIX: `ps -ww -o command=` (full, un-truncated argv). Windows: the
118
- * `CommandLine` from `Win32_Process` via PowerShell. The value is captured up to
119
- * the next ` --<flag>` (or end of line) so a data dir that itself contains a
120
- * space is not truncated at the space.
121
- */
122
- export declare function getProcessUserDataDir(pid: number): string | null;
123
- /**
124
- * Extract the `--user-data-dir` value from a browser command line. Handles both
125
- * `--user-data-dir=<path>` and `--user-data-dir <path>` and stops the value at
126
- * the next ` --<flag>` so a path containing spaces survives. Exported for unit
127
- * testing without a live process.
128
- */
129
- export declare function parseUserDataDirFromCommandLine(cmdline: string): string | null;
130
- interface PortOccupant {
131
- pid: number;
132
- command: string;
133
- }
134
- /**
135
- * Identify the process listening on a TCP port. Returns null when nothing is bound.
136
- * Used for clearer error messages when a profile's configured port is taken by a
137
- * non-debug process (e.g. Comet running without --remote-debugging-port).
138
- * `lsof` on POSIX; `netstat -ano` + `tasklist` on Windows.
139
- */
140
- export declare function getPortOccupant(port: number): PortOccupant | null;
141
- export {};