@monoes/monobrowse 1.0.26 → 1.0.28

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 (101) hide show
  1. package/dist/src/__tests__/disconnect-session.test.d.ts +13 -0
  2. package/dist/src/__tests__/disconnect-session.test.d.ts.map +1 -0
  3. package/dist/src/__tests__/disconnect-session.test.js +37 -0
  4. package/dist/src/__tests__/disconnect-session.test.js.map +1 -0
  5. package/dist/src/__tests__/profile-cleanup-close.test.d.ts +16 -0
  6. package/dist/src/__tests__/profile-cleanup-close.test.d.ts.map +1 -0
  7. package/dist/src/__tests__/profile-cleanup-close.test.js +250 -0
  8. package/dist/src/__tests__/profile-cleanup-close.test.js.map +1 -0
  9. package/dist/src/__tests__/profile-dir.test.d.ts +10 -0
  10. package/dist/src/__tests__/profile-dir.test.d.ts.map +1 -0
  11. package/dist/src/__tests__/profile-dir.test.js +160 -0
  12. package/dist/src/__tests__/profile-dir.test.js.map +1 -0
  13. package/dist/src/__tests__/setup/isolated-tmpdir.global.d.ts +2 -0
  14. package/dist/src/__tests__/setup/isolated-tmpdir.global.d.ts.map +1 -0
  15. package/dist/src/__tests__/setup/isolated-tmpdir.global.js +30 -0
  16. package/dist/src/__tests__/setup/isolated-tmpdir.global.js.map +1 -0
  17. package/dist/src/browser/actions-eval.d.ts +8 -0
  18. package/dist/src/browser/actions-eval.d.ts.map +1 -0
  19. package/dist/src/browser/actions-eval.js +72 -0
  20. package/dist/src/browser/actions-eval.js.map +1 -0
  21. package/dist/src/browser/actions-form.d.ts +10 -0
  22. package/dist/src/browser/actions-form.d.ts.map +1 -0
  23. package/dist/src/browser/actions-form.js +133 -0
  24. package/dist/src/browser/actions-form.js.map +1 -0
  25. package/dist/src/browser/actions-keyboard.d.ts +7 -0
  26. package/dist/src/browser/actions-keyboard.d.ts.map +1 -0
  27. package/dist/src/browser/actions-keyboard.js +131 -0
  28. package/dist/src/browser/actions-keyboard.js.map +1 -0
  29. package/dist/src/browser/actions-mouse.d.ts +11 -0
  30. package/dist/src/browser/actions-mouse.d.ts.map +1 -0
  31. package/dist/src/browser/actions-mouse.js +105 -0
  32. package/dist/src/browser/actions-mouse.js.map +1 -0
  33. package/dist/src/browser/actions.d.ts +12 -28
  34. package/dist/src/browser/actions.d.ts.map +1 -1
  35. package/dist/src/browser/actions.js +12 -434
  36. package/dist/src/browser/actions.js.map +1 -1
  37. package/dist/src/browser/browser-discovery.d.ts +26 -0
  38. package/dist/src/browser/browser-discovery.d.ts.map +1 -0
  39. package/dist/src/browser/browser-discovery.js +113 -0
  40. package/dist/src/browser/browser-discovery.js.map +1 -0
  41. package/dist/src/browser/browser-launch.d.ts +3 -0
  42. package/dist/src/browser/browser-launch.d.ts.map +1 -0
  43. package/dist/src/browser/browser-launch.js +341 -0
  44. package/dist/src/browser/browser-launch.js.map +1 -0
  45. package/dist/src/browser/browser-lifecycle.d.ts +57 -0
  46. package/dist/src/browser/browser-lifecycle.d.ts.map +1 -0
  47. package/dist/src/browser/browser-lifecycle.js +245 -0
  48. package/dist/src/browser/browser-lifecycle.js.map +1 -0
  49. package/dist/src/browser/browser-page.d.ts +13 -0
  50. package/dist/src/browser/browser-page.d.ts.map +1 -0
  51. package/dist/src/browser/browser-page.js +179 -0
  52. package/dist/src/browser/browser-page.js.map +1 -0
  53. package/dist/src/browser/browser-state.d.ts +11 -0
  54. package/dist/src/browser/browser-state.d.ts.map +1 -0
  55. package/dist/src/browser/browser-state.js +27 -0
  56. package/dist/src/browser/browser-state.js.map +1 -0
  57. package/dist/src/browser/browser.d.ts +5 -68
  58. package/dist/src/browser/browser.d.ts.map +1 -1
  59. package/dist/src/browser/browser.js +9 -849
  60. package/dist/src/browser/browser.js.map +1 -1
  61. package/dist/src/browser/profile-dir.d.ts +88 -0
  62. package/dist/src/browser/profile-dir.d.ts.map +1 -0
  63. package/dist/src/browser/profile-dir.js +242 -0
  64. package/dist/src/browser/profile-dir.js.map +1 -0
  65. package/dist/src/browser/ref-cache.d.ts +4 -0
  66. package/dist/src/browser/ref-cache.d.ts.map +1 -1
  67. package/dist/src/browser/ref-cache.js +2 -0
  68. package/dist/src/browser/ref-cache.js.map +1 -1
  69. package/dist/src/browser/types.d.ts +6 -0
  70. package/dist/src/browser/types.d.ts.map +1 -1
  71. package/dist/src/browser/types.js.map +1 -1
  72. package/dist/src/cli/commands.d.ts +1 -0
  73. package/dist/src/cli/commands.d.ts.map +1 -1
  74. package/dist/src/cli/commands.js +2 -0
  75. package/dist/src/cli/commands.js.map +1 -1
  76. package/dist/src/cli/session.d.ts +9 -0
  77. package/dist/src/cli/session.d.ts.map +1 -1
  78. package/dist/src/cli/session.js +21 -4
  79. package/dist/src/cli/session.js.map +1 -1
  80. package/dist/tsconfig.tsbuildinfo +1 -1
  81. package/package.json +1 -1
  82. package/src/__tests__/disconnect-session.test.ts +42 -0
  83. package/src/__tests__/profile-cleanup-close.test.ts +285 -0
  84. package/src/__tests__/profile-dir.test.ts +196 -0
  85. package/src/__tests__/setup/isolated-tmpdir.global.ts +28 -0
  86. package/src/browser/actions-eval.ts +118 -0
  87. package/src/browser/actions-form.ts +218 -0
  88. package/src/browser/actions-keyboard.ts +171 -0
  89. package/src/browser/actions-mouse.ts +212 -0
  90. package/src/browser/actions.ts +37 -712
  91. package/src/browser/browser-discovery.ts +116 -0
  92. package/src/browser/browser-launch.ts +375 -0
  93. package/src/browser/browser-lifecycle.ts +248 -0
  94. package/src/browser/browser-page.ts +213 -0
  95. package/src/browser/browser-state.ts +30 -0
  96. package/src/browser/browser.ts +21 -917
  97. package/src/browser/profile-dir.ts +249 -0
  98. package/src/browser/ref-cache.ts +7 -0
  99. package/src/browser/types.ts +6 -0
  100. package/src/cli/commands.ts +2 -0
  101. package/src/cli/session.ts +22 -4
@@ -0,0 +1,341 @@
1
+ // Split out of browser.ts (file-size sweep). Pure move: no behaviour change.
2
+ import { spawn } from 'node:child_process';
3
+ import { randomUUID } from 'node:crypto';
4
+ import { rmSync } from 'node:fs';
5
+ import { join } from 'node:path';
6
+ import { browserIdOf, chromeIdentity, fetchBrowserWebSocketUrl, findChrome, isChromeIdentity, isPortOpen, isTcpPortOpen, readDevToolsActivePort, } from './browser-discovery.js';
7
+ import { reapIdleLaunchedBrowser } from './browser-lifecycle.js';
8
+ import { launchedPids, launchedUserDataDirs, ownedUserDataDirPorts } from './browser-state.js';
9
+ import { launchProfileDirPath, removeOwnedProfileDir, sweepStaleProfileDirs, } from './profile-dir.js';
10
+ const DEFAULT_PORT = 9222;
11
+ const LAUNCH_TIMEOUT = 10_000;
12
+ const POLL_INTERVAL = 200;
13
+ /** Ports scanned when the requested one is occupied by a non-Chrome process
14
+ * (e.g. another local tool that happens to reuse Chrome's conventional CDP
15
+ * default, 9222 — mirrors the auto-increment convention this monorepo's own
16
+ * dashboard server uses in bindServer/server.mjs). Only occupied-by-a-
17
+ * DIFFERENT-process is worked around; an already-attachable Chrome on the
18
+ * requested port is still returned as-is (existing "attach, don't relaunch"
19
+ * behavior). */
20
+ const LAUNCH_PORT_SCAN_TRIES = 10;
21
+ export async function launchBrowser(config = {}) {
22
+ const rawPort = config.port ?? DEFAULT_PORT;
23
+ // Port 0 means "let Chrome bind a free port and tell us which" — see
24
+ // launchOnFreePort. Otherwise validate port is in a safe range for
25
+ // localhost CDP debugging.
26
+ if (rawPort === 0) {
27
+ if (!config.userDataDir) {
28
+ throw new Error('port 0 requires a dedicated userDataDir (Chrome reports the port it bound inside it).');
29
+ }
30
+ }
31
+ else if (!Number.isInteger(rawPort) || rawPort < 1024 || rawPort > 65535) {
32
+ throw new Error(`Invalid port: ${rawPort}. Must be 0 or an integer between 1024 and 65535.`);
33
+ }
34
+ // Every launch/attach is this tool's only chance to notice that a previous
35
+ // session abandoned a browser on a CDP port and never gave it back — there
36
+ // is no daemon to do it on a timer. Bounded and non-throwing; a stale
37
+ // instance on the port we are about to use is freed before we probe it.
38
+ await reapIdleLaunchedBrowser();
39
+ // Same reasoning for temp profile dirs (#395): a process that crashed or
40
+ // was killed never deleted the one its browser used. Only dead, idle,
41
+ // unused dirs of our own naming go — see sweepStaleProfileDirs.
42
+ await sweepStaleProfileDirs();
43
+ if (rawPort === 0)
44
+ return launchOnFreePort(config, 0);
45
+ // strictPort: fail fast on the exact requested port, matching the old
46
+ // behavior (Vite has the same escape hatch for the same reason) — for
47
+ // callers that treat the error as a signal ("this port is taken by
48
+ // something else, bail") rather than consuming the returned port.
49
+ if (config.strictPort) {
50
+ if (await isTcpPortOpen(rawPort)) {
51
+ if (await isChromeIdentity(rawPort))
52
+ return rawPort;
53
+ throw new Error(`Port ${rawPort} is occupied by a process that does not identify as Chrome/Chromium. ` +
54
+ `Refusing to attach — pass a different port or free port ${rawPort}.`);
55
+ }
56
+ return launchOnFreePort(config, rawPort);
57
+ }
58
+ const candidates = [];
59
+ for (let i = 0; i < LAUNCH_PORT_SCAN_TRIES && rawPort + i <= 65535; i++)
60
+ candidates.push(rawPort + i);
61
+ for (const candidate of candidates) {
62
+ // TCP-level check for "is anything at all listening" — isPortOpen()
63
+ // does a full CDP /json fetch, which returns false BOTH for a genuinely
64
+ // free port and for one occupied by a non-CDP process (that ambiguity is
65
+ // exactly what the post-spawn isTcpPortOpen fallback below exists to
66
+ // resolve, the hard way, after a 10s launch timeout). Checking the raw
67
+ // socket first tells free and occupied apart up front, so the scan can
68
+ // skip an occupied candidate instead of trying to spawn Chrome on top of
69
+ // it and only discovering the conflict after a timeout.
70
+ if (await isTcpPortOpen(candidate)) {
71
+ // Attach-if-already-Chrome only applies to the EXACT requested port —
72
+ // the original, deliberate, single-port risk ("don't silently take
73
+ // over an unrelated real browser that happens to be on this port").
74
+ // Scanning past an occupied default must not let that same shortcut
75
+ // attach to a DIFFERENT Chrome instance the caller never named;
76
+ // forward candidates are launch-only (skip if anything is there,
77
+ // Chrome or not).
78
+ if (candidate === rawPort && (await isChromeIdentity(candidate)))
79
+ return candidate;
80
+ // Occupied (by anything — not just non-Chrome, per the note above) —
81
+ // try the next candidate instead of failing outright, same as a
82
+ // normal EADDRINUSE retry would.
83
+ continue;
84
+ }
85
+ try {
86
+ return await launchOnFreePort(config, candidate);
87
+ }
88
+ catch (err) {
89
+ const isLastCandidate = candidate === candidates[candidates.length - 1];
90
+ // isTcpPortOpen() above is a connect() probe, not an atomic claim: two
91
+ // concurrent launchBrowser() calls with no explicit --port can both
92
+ // observe the same candidate as free and both spawn Chrome on it
93
+ // before either binds. One wins; the other's Chrome exits before its
94
+ // CDP endpoint opens ("Chrome exited before the CDP endpoint opened
95
+ // ... code=21"). Something is listening on the candidate NOW that
96
+ // was not a moment ago — a losing race, not a broken Chrome install —
97
+ // so try the next candidate exactly like an already-occupied one,
98
+ // instead of failing the whole launch outright. A candidate that
99
+ // fails with nothing now listening (a real launch failure — bad
100
+ // executable, sandbox refusal, etc.) is not a race: retrying the
101
+ // next candidate would only fail the same way, so surface it as-is.
102
+ if (!isLastCandidate && (await isTcpPortOpen(candidate)))
103
+ continue;
104
+ throw err;
105
+ }
106
+ }
107
+ throw new Error(`Ports ${candidates[0]}-${candidates[candidates.length - 1]} are all occupied and port ${candidates[0]} ` +
108
+ `isn't a Chrome/Chromium instance to attach to. Pass a different --port.`);
109
+ }
110
+ /**
111
+ * Spawn Chrome and wait for its CDP endpoint.
112
+ *
113
+ * With a fixed `port`, "our Chrome is up" is inferred from *some* Chrome
114
+ * answering on 127.0.0.1:<port> — which is not necessarily ours. When the port
115
+ * is already bound on 127.0.0.1 (a concurrent launch picked the same "free"
116
+ * port between its probe and Chrome's bind), Chrome does not fail: it logs
117
+ * `bind() failed: Address already in use` and listens on [::1]:<port>
118
+ * instead. The poll then accepts the OTHER launcher's browser, both callers
119
+ * drive one Chrome, and whichever closes first kills the other's connections
120
+ * ("CDP connection closed") while this launch's own Chrome is orphaned.
121
+ *
122
+ * `port: 0` removes the race instead of narrowing it: Chrome asks the kernel
123
+ * for a free port at bind time — atomic, nothing to collide on — and writes
124
+ * the port it got to DevToolsActivePort in its own (caller-dedicated) profile
125
+ * directory, which is proof the endpoint is the process we spawned.
126
+ *
127
+ * A fixed port needs its own proof, since Chrome writes DevToolsActivePort
128
+ * only for port 0. Once listening, Chrome prints `DevTools listening on
129
+ * ws://…/devtools/browser/<id>` to stderr, and `<id>` is unique to that
130
+ * browser process, the same one `/json/version` reports. An endpoint is
131
+ * adopted only when it reports the id our own child printed. Without that
132
+ * check, a launch whose Chrome was still booting found a concurrent
133
+ * launch's Chrome on the shared port and returned it, recording its own
134
+ * doomed pid there. It also covers Chrome's [::1] fallback described above.
135
+ */
136
+ async function launchOnFreePort(config, port) {
137
+ const chromePath = findChrome(config.executablePath);
138
+ // Unique per launch, not just per port and pid: two concurrent launches
139
+ // that both probe the same candidate port as free (the race this function
140
+ // exists to survive — see the retry-on-collision caller above) would
141
+ // otherwise share one profile directory (lock files, preferences, the
142
+ // DevToolsActivePort file itself) — including two launches from the same
143
+ // process, where a pid suffix alone is identical.
144
+ const userDataDir = config.userDataDir ?? launchProfileDirPath(port, randomUUID().slice(0, 8));
145
+ // A dir we created is deleted once its browser is gone (#395); a
146
+ // caller-supplied one only when the caller says it made it for us.
147
+ const ownsUserDataDir = config.userDataDir === undefined || config.ownsUserDataDir === true;
148
+ const activePortFile = join(userDataDir, 'DevToolsActivePort');
149
+ // A reused profile dir may hold a previous run's file naming a port some
150
+ // other process now owns.
151
+ if (port === 0)
152
+ rmSync(activePortFile, { force: true });
153
+ const defaultArgs = [
154
+ `--remote-debugging-port=${port}`,
155
+ `--user-data-dir=${userDataDir}`,
156
+ '--no-first-run',
157
+ '--no-default-browser-check',
158
+ '--disable-background-networking',
159
+ '--disable-client-side-phishing-detection',
160
+ '--disable-default-apps',
161
+ '--disable-extensions',
162
+ '--disable-hang-monitor',
163
+ '--disable-popup-blocking',
164
+ '--disable-prompt-on-repost',
165
+ '--disable-sync',
166
+ '--disable-translate',
167
+ '--metrics-recording-only',
168
+ '--safebrowsing-disable-auto-update',
169
+ '--password-store=basic',
170
+ '--use-mock-keychain',
171
+ ];
172
+ if (config.headless !== false) {
173
+ defaultArgs.push('--headless=new');
174
+ }
175
+ // Chrome's setuid sandbox cannot initialise on most CI runners and
176
+ // containers, so the process dies during startup and the CDP endpoint never
177
+ // opens. The launch below then burns its whole timeout before reporting a
178
+ // generic failure, which reads as a hang rather than "Chrome could not
179
+ // start". Disabling the sandbox is the standard remedy and is scoped to CI
180
+ // so a real user's browser keeps it. --disable-dev-shm-usage goes with it:
181
+ // a container's default /dev/shm is 64MB, which Chrome exhausts and then
182
+ // crashes the same way.
183
+ if (process.env.CI) {
184
+ defaultArgs.push('--no-sandbox', '--disable-dev-shm-usage');
185
+ }
186
+ // Cap caller-supplied args to prevent memory exhaustion via huge argument arrays
187
+ const callerArgs = (config.args ?? []).slice(0, 50);
188
+ const args = [...defaultArgs, ...callerArgs];
189
+ const child = spawn(chromePath, args, {
190
+ detached: true,
191
+ // stderr carries the fixed-port ownership proof (see above). The pipe is
192
+ // closed as soon as this launch settles, so a Chrome that outlives us
193
+ // never blocks writing to it.
194
+ stdio: port === 0 ? 'ignore' : ['ignore', 'ignore', 'pipe'],
195
+ });
196
+ let announcedBrowserId = null;
197
+ if (child.stderr) {
198
+ let stderrTail = '';
199
+ child.stderr.setEncoding('utf8');
200
+ child.stderr.on('data', (chunk) => {
201
+ if (announcedBrowserId)
202
+ return;
203
+ stderrTail = (stderrTail + chunk).slice(-4096);
204
+ announcedBrowserId = browserIdOf(/DevTools listening on (ws:\/\/\S+)\r?\n/.exec(stderrTail)?.[1]);
205
+ });
206
+ child.stderr.on('error', () => {
207
+ /* closed under us — nothing to read */
208
+ });
209
+ }
210
+ // Without this, a spawn failure (e.g. EACCES/ENOENT — chromePath exists per
211
+ // findChrome()'s existsSync check but isn't actually executable, or is
212
+ // removed between the check and exec) fires Node's 'error' event on the
213
+ // next tick. With no listener, Node throws it as an uncaught exception and
214
+ // the whole process goes down — which strands this function's promise
215
+ // forever pending rather than rejecting it (issue #314: node:test reports
216
+ // that as "cancelledByParent" / "still pending" because nothing here ever
217
+ // got the chance to settle it). Recording the failure and having the poll
218
+ // loop below notice it turns that crash into a normal rejection. 'exit'
219
+ // covers the twin case: Chrome execs fine but the process itself dies
220
+ // immediately (missing shared libraries, a container's sandbox refusing
221
+ // it, etc.) — that fires 'exit', not 'error', and without this the poll
222
+ // loop would just burn its whole timeout probing a port nothing will ever
223
+ // open on.
224
+ let earlyFailure = null;
225
+ child.on('error', (err) => {
226
+ earlyFailure ??= new Error(`Chrome failed to start on port ${port}: ${err.message}`);
227
+ });
228
+ child.on('exit', (code, signal) => {
229
+ earlyFailure ??= new Error(`Chrome exited before the CDP endpoint opened on port ${port} ` +
230
+ `(code=${code ?? 'null'}, signal=${signal ?? 'null'})`);
231
+ });
232
+ child.unref();
233
+ const track = (boundPort) => {
234
+ if (!child.pid)
235
+ return;
236
+ launchedPids.set(boundPort, child.pid);
237
+ launchedUserDataDirs.set(boundPort, userDataDir);
238
+ if (ownsUserDataDir)
239
+ ownedUserDataDirPorts.add(boundPort);
240
+ else
241
+ ownedUserDataDirPorts.delete(boundPort);
242
+ };
243
+ // Tracked only once Chrome is CONFIRMED up on `boundPort`, not right after
244
+ // spawn(). For port !== 0 this used to track(port) unconditionally the
245
+ // moment the child was spawned, before knowing whether it would actually
246
+ // win the port — when a racing launchBrowser() call retries a candidate
247
+ // that a concurrent process also spawned Chrome on (see the retry loop in
248
+ // launchBrowser), BOTH child processes called track() for the SAME
249
+ // candidate, and whichever call's spawn happened to run last clobbered the
250
+ // map entry with its own pid — including the LOSING, about-to-exit
251
+ // process's pid overwriting the real winner's, corrupting the very map
252
+ // closeBrowser()'s kill fallback depends on.
253
+ // A refused or timed-out launch is not tracked under any port, so no
254
+ // closeBrowser() could ever reach it — stop it rather than leave it
255
+ // running.
256
+ const killUntracked = () => {
257
+ try {
258
+ if (child.pid)
259
+ process.kill(child.pid, 'SIGKILL');
260
+ }
261
+ catch {
262
+ /* already gone */
263
+ }
264
+ };
265
+ // Our own Chrome, not just some Chrome, is serving `boundPort`. For port 0
266
+ // the port came from our own profile's DevToolsActivePort, so it is.
267
+ const isOwnEndpoint = async (boundPort) => {
268
+ if (port === 0)
269
+ return true;
270
+ if (!announcedBrowserId)
271
+ return false;
272
+ const wsUrl = await fetchBrowserWebSocketUrl(boundPort).catch(() => null);
273
+ return browserIdOf(wsUrl ?? undefined) === announcedBrowserId;
274
+ };
275
+ const launchTimeout = config.launchTimeoutMs ?? LAUNCH_TIMEOUT;
276
+ const deadline = Date.now() + launchTimeout;
277
+ let launched = false;
278
+ try {
279
+ while (Date.now() < deadline) {
280
+ if (earlyFailure)
281
+ throw earlyFailure;
282
+ await sleep(POLL_INTERVAL);
283
+ if (earlyFailure)
284
+ throw earlyFailure;
285
+ const boundPort = port === 0 ? readDevToolsActivePort(activePortFile) : port;
286
+ if (boundPort !== null && (await isPortOpen(boundPort))) {
287
+ const identity = await chromeIdentity(boundPort);
288
+ if (identity === 'chrome') {
289
+ if (await isOwnEndpoint(boundPort)) {
290
+ track(boundPort);
291
+ launched = true;
292
+ return boundPort;
293
+ }
294
+ // Some Chrome answers on the port, but not the one we started: a
295
+ // concurrent launch's, or ours before its stderr line arrived. Our
296
+ // child exiting (it lost the bind) or the deadline decides.
297
+ continue;
298
+ }
299
+ // A Chrome that has only just opened its endpoint, on a loaded
300
+ // machine, can answer /json/list (no timeout) yet miss the identity
301
+ // check's 1 s timeout on /json/version. That says nothing about who is
302
+ // listening — keep polling rather than refuse our own Chrome.
303
+ if (identity === 'unknown')
304
+ continue;
305
+ killUntracked();
306
+ throw new Error(`Port ${boundPort} is occupied by a CDP-speaking process that does not identify as Chrome/Chromium. ` +
307
+ `Refusing to attach — pass a different port or free port ${boundPort}.`);
308
+ }
309
+ }
310
+ if (earlyFailure)
311
+ throw earlyFailure;
312
+ killUntracked();
313
+ if (port === 0) {
314
+ throw new Error(`Chrome did not report a CDP port in ${activePortFile} within ${launchTimeout}ms`);
315
+ }
316
+ // Timed out waiting for our Chrome to come up on the port. Distinguish
317
+ // "another Chrome holds it", "nothing is listening" (real launch failure)
318
+ // and "something non-CDP is squatting the port" (confusing generic
319
+ // timeout otherwise).
320
+ if (await isChromeIdentity(port)) {
321
+ throw new Error(`Port ${port} is held by a Chrome/Chromium this launch did not start (a concurrent launch took it).`);
322
+ }
323
+ if (await isTcpPortOpen(port)) {
324
+ throw new Error(`Port ${port} is occupied by a non-Chrome process (TCP connection succeeds but no CDP response within ${launchTimeout}ms). ` +
325
+ `Free the port or pass a different one.`);
326
+ }
327
+ throw new Error(`Chrome failed to start on port ${port} within ${launchTimeout}ms`);
328
+ }
329
+ finally {
330
+ child.stderr?.destroy();
331
+ // A failed launch (lost port race, refusal, timeout) is never tracked,
332
+ // so no close will ever clean its profile — and the port-scan retry in
333
+ // launchBrowser can make several of these per call.
334
+ if (!launched && ownsUserDataDir)
335
+ await removeOwnedProfileDir(userDataDir, { pid: child.pid });
336
+ }
337
+ }
338
+ function sleep(ms) {
339
+ return new Promise((r) => setTimeout(r, ms));
340
+ }
341
+ //# sourceMappingURL=browser-launch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser-launch.js","sourceRoot":"","sources":["../../../src/browser/browser-launch.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,57 @@
1
+ import { CdpClient } from './cdp.js';
2
+ /**
3
+ * Cleanly terminate a Chrome/Chromium instance we launched on `port`.
4
+ * Sends the `Browser.close` CDP command (the correct graceful shutdown —
5
+ * closes all tabs and exits the process cleanly) over `client`'s connection.
6
+ * If that command fails, times out, or the connection is already gone,
7
+ * falls back to killing the tracked PID directly so a headed/interactive
8
+ * browser window (e.g. one spawned for a login/CAPTCHA flow) never lingers
9
+ * as a visible, authenticated, still-debuggable orphan process.
10
+ *
11
+ * On the graceful path this resolves only once the process is actually gone,
12
+ * or after force-killing it when it outlasts PROCESS_EXIT_TIMEOUT_MS. Chrome
13
+ * acknowledges `Browser.close` well before it exits, and this used to return
14
+ * on the ack with an unref'd 1s timer as the only force-kill — so it resolved
15
+ * while Chrome was still running and holding its port and its profile's
16
+ * singleton lock, and if the caller's process exited first the kill never
17
+ * happened at all. Callers reasonably read a resolved close() as "that
18
+ * browser is gone"; on the graceful path it now means that.
19
+ *
20
+ * A profile directory monobrowse created for that browser (the default temp
21
+ * profile, or a CLI session's — never a caller-supplied one) is deleted once
22
+ * the process is gone, on both paths (#395). In a fresh process the dir and
23
+ * its ownership come from the persisted session record.
24
+ */
25
+ export declare function closeBrowser(client: CdpClient, port: number): Promise<void>;
26
+ /**
27
+ * Give back the CDP port of a browser we launched that nothing is using any
28
+ * more. A launched Chrome is deliberately detached and unref'd so it outlives
29
+ * the command that spawned it and later commands can reattach — but nothing
30
+ * reaped it afterwards, so an abandoned session kept listening on its port
31
+ * (9222 by convention, which other local tools want) indefinitely. A foreign
32
+ * CDP server *answers* there rather than refusing the connection, so those
33
+ * tools retry the wrong port and report misleading errors instead of moving on.
34
+ *
35
+ * Nothing long-lived exists to run this on a timer — every CLI command is its
36
+ * own short-lived process — so it runs at the next launch/attach instead
37
+ * (see launchBrowser). A browser is reaped only when ALL of these hold:
38
+ * - the persisted session says WE launched it — `connect` records
39
+ * launched:false for a browser that belongs to the user, never touched;
40
+ * - that session is older than IDLE_REAP_AFTER_MS;
41
+ * - the CDP endpoint still answers and reports ZERO targets. An open tab,
42
+ * or a client attached to one (Chrome drops webSocketDebuggerUrl from an
43
+ * attached target but still lists it), means the session is live and
44
+ * reattach-across-commands must keep working.
45
+ *
46
+ * Shutdown goes through closeBrowser() on the browser-level websocket — the
47
+ * only connection left once every target is gone — so a recycled PID can
48
+ * never be signaled by mistake: graceful `Browser.close` reaches whatever is
49
+ * actually listening on that port, at any age, and the persisted PID is only
50
+ * ever used for closeBrowser's own age-bounded force-kill.
51
+ *
52
+ * Never throws: a failed reap must not stop a launch. Returns the reaped
53
+ * port, or null when there was nothing to reap. Sessions are per-port
54
+ * (#318), so every recorded one is considered, not just the newest.
55
+ */
56
+ export declare function reapIdleLaunchedBrowser(): Promise<number | null>;
57
+ //# sourceMappingURL=browser-lifecycle.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser-lifecycle.d.ts","sourceRoot":"","sources":["../../../src/browser/browser-lifecycle.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,SAAS,EAAgB,MAAM,UAAU,CAAC;AAuBnD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAsB,YAAY,CAAC,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAkFjF;AAgCD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAsB,uBAAuB,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAOtE"}