browser-pilot 0.0.15 → 0.0.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -3
- package/dist/actions.cjs +60 -45
- package/dist/actions.d.cts +2 -2
- package/dist/actions.d.ts +2 -2
- package/dist/actions.mjs +1 -1
- package/dist/{browser-MEWT75IB.mjs → browser-ZCR6AA4D.mjs} +2 -2
- package/dist/browser.cjs +455 -67
- package/dist/browser.d.cts +3 -3
- package/dist/browser.d.ts +3 -3
- package/dist/browser.mjs +3 -3
- package/dist/{chunk-USYSHCI3.mjs → chunk-6GBYX7C2.mjs} +87 -62
- package/dist/chunk-EZNZ72VA.mjs +563 -0
- package/dist/{chunk-ZAXQ5OTV.mjs → chunk-NNEHWWHL.mjs} +23 -9
- package/dist/{chunk-WPNW23CE.mjs → chunk-TJ5B56NV.mjs} +345 -7
- package/dist/{chunk-7YVCOL2W.mjs → chunk-V3VLBQAM.mjs} +60 -45
- package/dist/cli.mjs +530 -381
- package/dist/index.cjs +419 -55
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.mjs +19 -7
- package/dist/{page-XPS6IC6V.mjs → page-IUUTJ3SW.mjs} +1 -1
- package/dist/providers.cjs +637 -2
- package/dist/providers.d.cts +2 -2
- package/dist/providers.d.ts +2 -2
- package/dist/providers.mjs +17 -3
- package/dist/{types-C9ySEdOX.d.cts → types-BflRmiDz.d.cts} +1 -1
- package/dist/{types-Cvvf0oGu.d.ts → types-BzM-IfsL.d.ts} +1 -1
- package/dist/types-DeVSWhXj.d.cts +142 -0
- package/dist/types-DeVSWhXj.d.ts +142 -0
- package/package.json +1 -1
- package/dist/chunk-BRAFQUMG.mjs +0 -229
- package/dist/types--wXNHUwt.d.cts +0 -56
- package/dist/types--wXNHUwt.d.ts +0 -56
package/dist/providers.cjs
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
2
3
|
var __defProp = Object.defineProperty;
|
|
3
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
5
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
8
|
var __export = (target, all) => {
|
|
7
9
|
for (var name in all)
|
|
@@ -15,17 +17,31 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
15
17
|
}
|
|
16
18
|
return to;
|
|
17
19
|
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
18
28
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
29
|
|
|
20
30
|
// src/providers/index.ts
|
|
21
31
|
var providers_exports = {};
|
|
22
32
|
__export(providers_exports, {
|
|
23
33
|
BrowserBaseProvider: () => BrowserBaseProvider,
|
|
34
|
+
BrowserEndpointResolutionError: () => BrowserEndpointResolutionError,
|
|
24
35
|
BrowserlessProvider: () => BrowserlessProvider,
|
|
25
36
|
GenericProvider: () => GenericProvider,
|
|
37
|
+
buildLocalBrowserScanTargets: () => buildLocalBrowserScanTargets,
|
|
26
38
|
createProvider: () => createProvider,
|
|
39
|
+
discoverLocalBrowsers: () => discoverLocalBrowsers,
|
|
27
40
|
discoverTargets: () => discoverTargets,
|
|
28
|
-
getBrowserWebSocketUrl: () => getBrowserWebSocketUrl
|
|
41
|
+
getBrowserWebSocketUrl: () => getBrowserWebSocketUrl,
|
|
42
|
+
parseDevToolsActivePortFile: () => parseDevToolsActivePortFile,
|
|
43
|
+
resolveBrowserEndpoint: () => resolveBrowserEndpoint,
|
|
44
|
+
resolveChromeUserDataDirs: () => resolveChromeUserDataDirs
|
|
29
45
|
});
|
|
30
46
|
module.exports = __toCommonJS(providers_exports);
|
|
31
47
|
|
|
@@ -217,6 +233,619 @@ async function getBrowserWebSocketUrl(host = "localhost:9222") {
|
|
|
217
233
|
return info.webSocketDebuggerUrl;
|
|
218
234
|
}
|
|
219
235
|
|
|
236
|
+
// src/utils/json.ts
|
|
237
|
+
function isRecord(value) {
|
|
238
|
+
return typeof value === "object" && value !== null;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// src/cdp/protocol.ts
|
|
242
|
+
var CDPError = class extends Error {
|
|
243
|
+
code;
|
|
244
|
+
data;
|
|
245
|
+
constructor(error) {
|
|
246
|
+
super(error.message);
|
|
247
|
+
this.name = "CDPError";
|
|
248
|
+
this.code = error.code;
|
|
249
|
+
this.data = error.data;
|
|
250
|
+
}
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
// src/cdp/transport.ts
|
|
254
|
+
function createTransport(wsUrl, options = {}) {
|
|
255
|
+
const { timeout = 3e4 } = options;
|
|
256
|
+
return new Promise((resolve, reject) => {
|
|
257
|
+
const timeoutId = setTimeout(() => {
|
|
258
|
+
reject(new Error(`WebSocket connection timeout after ${timeout}ms`));
|
|
259
|
+
}, timeout);
|
|
260
|
+
const ws = new WebSocket(wsUrl);
|
|
261
|
+
const messageHandlers = [];
|
|
262
|
+
const closeHandlers = [];
|
|
263
|
+
const errorHandlers = [];
|
|
264
|
+
ws.addEventListener("open", () => {
|
|
265
|
+
clearTimeout(timeoutId);
|
|
266
|
+
const transport = {
|
|
267
|
+
send(message) {
|
|
268
|
+
if (ws.readyState === WebSocket.OPEN) {
|
|
269
|
+
ws.send(message);
|
|
270
|
+
} else {
|
|
271
|
+
throw new Error(
|
|
272
|
+
`Cannot send message, WebSocket is ${getReadyStateString(ws.readyState)}`
|
|
273
|
+
);
|
|
274
|
+
}
|
|
275
|
+
},
|
|
276
|
+
async close() {
|
|
277
|
+
return new Promise((resolveClose) => {
|
|
278
|
+
if (ws.readyState === WebSocket.CLOSED) {
|
|
279
|
+
resolveClose();
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
let settled = false;
|
|
283
|
+
let fallbackTimer;
|
|
284
|
+
const finish = () => {
|
|
285
|
+
if (settled) return;
|
|
286
|
+
settled = true;
|
|
287
|
+
if (fallbackTimer) clearTimeout(fallbackTimer);
|
|
288
|
+
ws.removeEventListener("close", onClose);
|
|
289
|
+
resolveClose();
|
|
290
|
+
};
|
|
291
|
+
const onClose = () => {
|
|
292
|
+
finish();
|
|
293
|
+
};
|
|
294
|
+
ws.addEventListener("close", onClose);
|
|
295
|
+
try {
|
|
296
|
+
if (ws.readyState === WebSocket.OPEN || ws.readyState === WebSocket.CONNECTING) {
|
|
297
|
+
ws.close();
|
|
298
|
+
}
|
|
299
|
+
} catch {
|
|
300
|
+
finish();
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
fallbackTimer = setTimeout(finish, 200);
|
|
304
|
+
});
|
|
305
|
+
},
|
|
306
|
+
onMessage(handler) {
|
|
307
|
+
messageHandlers.push(handler);
|
|
308
|
+
},
|
|
309
|
+
onClose(handler) {
|
|
310
|
+
closeHandlers.push(handler);
|
|
311
|
+
},
|
|
312
|
+
onError(handler) {
|
|
313
|
+
errorHandlers.push(handler);
|
|
314
|
+
}
|
|
315
|
+
};
|
|
316
|
+
resolve(transport);
|
|
317
|
+
});
|
|
318
|
+
ws.addEventListener("message", (event) => {
|
|
319
|
+
const data = typeof event.data === "string" ? event.data : String(event.data);
|
|
320
|
+
for (const handler of messageHandlers) {
|
|
321
|
+
handler(data);
|
|
322
|
+
}
|
|
323
|
+
});
|
|
324
|
+
ws.addEventListener("close", () => {
|
|
325
|
+
for (const handler of closeHandlers) {
|
|
326
|
+
handler();
|
|
327
|
+
}
|
|
328
|
+
});
|
|
329
|
+
ws.addEventListener("error", (_event) => {
|
|
330
|
+
clearTimeout(timeoutId);
|
|
331
|
+
const error = new Error("WebSocket connection error");
|
|
332
|
+
for (const handler of errorHandlers) {
|
|
333
|
+
handler(error);
|
|
334
|
+
}
|
|
335
|
+
reject(error);
|
|
336
|
+
});
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
function getReadyStateString(state) {
|
|
340
|
+
switch (state) {
|
|
341
|
+
case WebSocket.CONNECTING:
|
|
342
|
+
return "CONNECTING";
|
|
343
|
+
case WebSocket.OPEN:
|
|
344
|
+
return "OPEN";
|
|
345
|
+
case WebSocket.CLOSING:
|
|
346
|
+
return "CLOSING";
|
|
347
|
+
case WebSocket.CLOSED:
|
|
348
|
+
return "CLOSED";
|
|
349
|
+
default:
|
|
350
|
+
return "UNKNOWN";
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// src/cdp/client.ts
|
|
355
|
+
async function createCDPClient(wsUrl, options = {}) {
|
|
356
|
+
const { timeout = 3e4 } = options;
|
|
357
|
+
const transport = await createTransport(wsUrl, { timeout });
|
|
358
|
+
return buildCDPClient(transport, options);
|
|
359
|
+
}
|
|
360
|
+
function buildCDPClient(transport, options = {}) {
|
|
361
|
+
const { debug = false, timeout = 3e4 } = options;
|
|
362
|
+
let messageId = 0;
|
|
363
|
+
let currentSessionId;
|
|
364
|
+
let connected = true;
|
|
365
|
+
const pending = /* @__PURE__ */ new Map();
|
|
366
|
+
const eventHandlers = /* @__PURE__ */ new Map();
|
|
367
|
+
const anyEventHandlers = /* @__PURE__ */ new Set();
|
|
368
|
+
transport.onMessage((raw) => {
|
|
369
|
+
let msg;
|
|
370
|
+
try {
|
|
371
|
+
const parsed = JSON.parse(raw);
|
|
372
|
+
if (!isRecord(parsed)) {
|
|
373
|
+
if (debug) console.error("[CDP] Ignoring non-object message:", raw);
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
376
|
+
if ("id" in parsed && typeof parsed["id"] === "number") {
|
|
377
|
+
msg = parsed;
|
|
378
|
+
} else if ("method" in parsed && typeof parsed["method"] === "string") {
|
|
379
|
+
msg = parsed;
|
|
380
|
+
} else {
|
|
381
|
+
if (debug) console.error("[CDP] Ignoring invalid message shape:", raw);
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
} catch {
|
|
385
|
+
if (debug) console.error("[CDP] Failed to parse message:", raw);
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
if (debug) {
|
|
389
|
+
console.log("[CDP] <--", JSON.stringify(msg, null, 2).slice(0, 500));
|
|
390
|
+
}
|
|
391
|
+
if ("id" in msg && typeof msg.id === "number") {
|
|
392
|
+
const response = msg;
|
|
393
|
+
const request = pending.get(response.id);
|
|
394
|
+
if (request) {
|
|
395
|
+
pending.delete(response.id);
|
|
396
|
+
clearTimeout(request.timer);
|
|
397
|
+
if (response.error) {
|
|
398
|
+
const error = typeof response.error === "string" ? { code: -32e3, message: response.error } : response.error;
|
|
399
|
+
request.reject(new CDPError(error));
|
|
400
|
+
} else {
|
|
401
|
+
request.resolve(response.result);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
return;
|
|
405
|
+
}
|
|
406
|
+
if ("method" in msg) {
|
|
407
|
+
const event = msg;
|
|
408
|
+
const params = event.params ?? {};
|
|
409
|
+
for (const handler of anyEventHandlers) {
|
|
410
|
+
try {
|
|
411
|
+
handler(event.method, params);
|
|
412
|
+
} catch (e) {
|
|
413
|
+
if (debug) console.error("[CDP] Error in any-event handler:", e);
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
const handlers = eventHandlers.get(event.method);
|
|
417
|
+
if (handlers) {
|
|
418
|
+
for (const handler of handlers) {
|
|
419
|
+
try {
|
|
420
|
+
handler(params);
|
|
421
|
+
} catch (e) {
|
|
422
|
+
if (debug) console.error(`[CDP] Error in handler for ${event.method}:`, e);
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
});
|
|
428
|
+
transport.onClose(() => {
|
|
429
|
+
connected = false;
|
|
430
|
+
for (const [id, request] of pending) {
|
|
431
|
+
clearTimeout(request.timer);
|
|
432
|
+
request.reject(new Error("WebSocket connection closed"));
|
|
433
|
+
pending.delete(id);
|
|
434
|
+
}
|
|
435
|
+
});
|
|
436
|
+
transport.onError((error) => {
|
|
437
|
+
if (debug) console.error("[CDP] Transport error:", error);
|
|
438
|
+
});
|
|
439
|
+
const client = {
|
|
440
|
+
async send(method, params, sessionId) {
|
|
441
|
+
if (!connected) {
|
|
442
|
+
throw new Error("CDP client is not connected");
|
|
443
|
+
}
|
|
444
|
+
const id = ++messageId;
|
|
445
|
+
const effectiveSessionId = sessionId === null ? void 0 : sessionId ?? currentSessionId;
|
|
446
|
+
const request = { id, method };
|
|
447
|
+
if (params !== void 0) {
|
|
448
|
+
request.params = params;
|
|
449
|
+
}
|
|
450
|
+
if (effectiveSessionId !== void 0) {
|
|
451
|
+
request.sessionId = effectiveSessionId;
|
|
452
|
+
}
|
|
453
|
+
const message = JSON.stringify(request);
|
|
454
|
+
if (debug) {
|
|
455
|
+
console.log("[CDP] -->", message.slice(0, 500));
|
|
456
|
+
}
|
|
457
|
+
return new Promise((resolve, reject) => {
|
|
458
|
+
const timer = setTimeout(() => {
|
|
459
|
+
pending.delete(id);
|
|
460
|
+
reject(new Error(`CDP command ${method} timed out after ${timeout}ms`));
|
|
461
|
+
}, timeout);
|
|
462
|
+
pending.set(id, {
|
|
463
|
+
resolve,
|
|
464
|
+
reject,
|
|
465
|
+
method,
|
|
466
|
+
timer
|
|
467
|
+
});
|
|
468
|
+
try {
|
|
469
|
+
transport.send(message);
|
|
470
|
+
} catch (e) {
|
|
471
|
+
pending.delete(id);
|
|
472
|
+
clearTimeout(timer);
|
|
473
|
+
reject(e);
|
|
474
|
+
}
|
|
475
|
+
});
|
|
476
|
+
},
|
|
477
|
+
on(event, handler) {
|
|
478
|
+
let handlers = eventHandlers.get(event);
|
|
479
|
+
if (!handlers) {
|
|
480
|
+
handlers = /* @__PURE__ */ new Set();
|
|
481
|
+
eventHandlers.set(event, handlers);
|
|
482
|
+
}
|
|
483
|
+
handlers.add(handler);
|
|
484
|
+
},
|
|
485
|
+
off(event, handler) {
|
|
486
|
+
const handlers = eventHandlers.get(event);
|
|
487
|
+
if (handlers) {
|
|
488
|
+
handlers.delete(handler);
|
|
489
|
+
if (handlers.size === 0) {
|
|
490
|
+
eventHandlers.delete(event);
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
},
|
|
494
|
+
onAny(handler) {
|
|
495
|
+
anyEventHandlers.add(handler);
|
|
496
|
+
},
|
|
497
|
+
offAny(handler) {
|
|
498
|
+
anyEventHandlers.delete(handler);
|
|
499
|
+
},
|
|
500
|
+
async close() {
|
|
501
|
+
connected = false;
|
|
502
|
+
await transport.close();
|
|
503
|
+
},
|
|
504
|
+
async attachToTarget(targetId) {
|
|
505
|
+
const result = await this.send("Target.attachToTarget", {
|
|
506
|
+
targetId,
|
|
507
|
+
flatten: true
|
|
508
|
+
});
|
|
509
|
+
currentSessionId = result.sessionId;
|
|
510
|
+
return result.sessionId;
|
|
511
|
+
},
|
|
512
|
+
get sessionId() {
|
|
513
|
+
return currentSessionId;
|
|
514
|
+
},
|
|
515
|
+
setSessionId(sessionId) {
|
|
516
|
+
currentSessionId = sessionId;
|
|
517
|
+
},
|
|
518
|
+
get isConnected() {
|
|
519
|
+
return connected;
|
|
520
|
+
}
|
|
521
|
+
};
|
|
522
|
+
return client;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
// src/providers/local-discovery.ts
|
|
526
|
+
var CHANNEL_ORDER = ["stable", "beta", "dev", "canary"];
|
|
527
|
+
var DEFAULT_PROBE_TIMEOUT_MS = 1e3;
|
|
528
|
+
var DevToolsActivePortParseError = class extends Error {
|
|
529
|
+
constructor(message, reason) {
|
|
530
|
+
super(message);
|
|
531
|
+
this.reason = reason;
|
|
532
|
+
this.name = "DevToolsActivePortParseError";
|
|
533
|
+
}
|
|
534
|
+
};
|
|
535
|
+
function getRuntimeEnv() {
|
|
536
|
+
if (typeof process === "undefined") {
|
|
537
|
+
return {};
|
|
538
|
+
}
|
|
539
|
+
return process.env;
|
|
540
|
+
}
|
|
541
|
+
function getRuntimePlatform() {
|
|
542
|
+
if (typeof process === "undefined") {
|
|
543
|
+
return void 0;
|
|
544
|
+
}
|
|
545
|
+
return process.platform;
|
|
546
|
+
}
|
|
547
|
+
function normalizePlatform(platform) {
|
|
548
|
+
if (platform === "darwin" || platform === "linux" || platform === "win32") {
|
|
549
|
+
return platform;
|
|
550
|
+
}
|
|
551
|
+
throw new Error(`Unsupported platform: ${platform ?? "unknown"}`);
|
|
552
|
+
}
|
|
553
|
+
function trimTrailingSeparator(path) {
|
|
554
|
+
return path.replace(/[\\/]+$/, "");
|
|
555
|
+
}
|
|
556
|
+
function joinPath(platform, ...parts) {
|
|
557
|
+
const separator = platform === "win32" ? "\\" : "/";
|
|
558
|
+
const cleaned = parts.map((part, index) => {
|
|
559
|
+
if (index === 0) return trimTrailingSeparator(part);
|
|
560
|
+
return part.replace(/^[\\/]+/, "").replace(/[\\/]+$/, "");
|
|
561
|
+
}).filter((part) => part.length > 0);
|
|
562
|
+
return cleaned.join(separator);
|
|
563
|
+
}
|
|
564
|
+
function resolveHomeDir(platform, env, explicitHomeDir) {
|
|
565
|
+
if (explicitHomeDir) {
|
|
566
|
+
return explicitHomeDir;
|
|
567
|
+
}
|
|
568
|
+
if (platform === "win32") {
|
|
569
|
+
return env["USERPROFILE"] ?? env["HOME"] ?? "";
|
|
570
|
+
}
|
|
571
|
+
return env["HOME"] ?? env["USERPROFILE"] ?? "";
|
|
572
|
+
}
|
|
573
|
+
function toFileFailure(target, error) {
|
|
574
|
+
const errno = error?.code;
|
|
575
|
+
if (errno === "ENOENT") {
|
|
576
|
+
return {
|
|
577
|
+
...target,
|
|
578
|
+
reason: "missing-file",
|
|
579
|
+
message: `DevToolsActivePort not found at ${target.portFile}`
|
|
580
|
+
};
|
|
581
|
+
}
|
|
582
|
+
return {
|
|
583
|
+
...target,
|
|
584
|
+
reason: "unreadable-file",
|
|
585
|
+
message: error instanceof Error ? error.message : `Could not read DevToolsActivePort at ${target.portFile}`
|
|
586
|
+
};
|
|
587
|
+
}
|
|
588
|
+
function toProbeFailure(target, wsUrl, error) {
|
|
589
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
590
|
+
const lowerMessage = message.toLowerCase();
|
|
591
|
+
let reason = "connection-error";
|
|
592
|
+
if (lowerMessage.includes("refused") || lowerMessage.includes("econnrefused")) {
|
|
593
|
+
reason = "connection-refused";
|
|
594
|
+
} else if (lowerMessage.includes("timeout") || lowerMessage.includes("timed out")) {
|
|
595
|
+
reason = "connection-timeout";
|
|
596
|
+
} else if (lowerMessage.includes("closed")) {
|
|
597
|
+
reason = "unexpected-close";
|
|
598
|
+
} else if (lowerMessage.includes("browser.getversion") || lowerMessage.includes("cdp") || lowerMessage.includes("protocol")) {
|
|
599
|
+
reason = "cdp-error";
|
|
600
|
+
}
|
|
601
|
+
return {
|
|
602
|
+
...target,
|
|
603
|
+
wsUrl,
|
|
604
|
+
reason,
|
|
605
|
+
message
|
|
606
|
+
};
|
|
607
|
+
}
|
|
608
|
+
async function readTextFile(path) {
|
|
609
|
+
const fs = await import("fs/promises");
|
|
610
|
+
return fs.readFile(path, "utf-8");
|
|
611
|
+
}
|
|
612
|
+
async function probeBrowserWebSocket(wsUrl, timeoutMs) {
|
|
613
|
+
let client;
|
|
614
|
+
try {
|
|
615
|
+
client = await createCDPClient(wsUrl, { timeout: timeoutMs });
|
|
616
|
+
const version = await client.send("Browser.getVersion", void 0, null);
|
|
617
|
+
return { browserVersion: version.product };
|
|
618
|
+
} finally {
|
|
619
|
+
await client?.close().catch(() => {
|
|
620
|
+
});
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
var defaultDependencies = {
|
|
624
|
+
readTextFile,
|
|
625
|
+
probeBrowserWebSocket,
|
|
626
|
+
getLegacyBrowserWebSocketUrl: getBrowserWebSocketUrl
|
|
627
|
+
};
|
|
628
|
+
function resolveChromeUserDataDirs(options = {}) {
|
|
629
|
+
const env = options.env ?? getRuntimeEnv();
|
|
630
|
+
const platform = normalizePlatform(options.platform ?? getRuntimePlatform());
|
|
631
|
+
const homeDir = resolveHomeDir(platform, env, options.homeDir);
|
|
632
|
+
if (!homeDir) {
|
|
633
|
+
throw new Error("Could not determine home directory for local Chrome discovery");
|
|
634
|
+
}
|
|
635
|
+
switch (platform) {
|
|
636
|
+
case "darwin": {
|
|
637
|
+
const base = joinPath(platform, homeDir, "Library", "Application Support", "Google");
|
|
638
|
+
return {
|
|
639
|
+
stable: joinPath(platform, base, "Chrome"),
|
|
640
|
+
beta: joinPath(platform, base, "Chrome Beta"),
|
|
641
|
+
dev: joinPath(platform, base, "Chrome Dev"),
|
|
642
|
+
canary: joinPath(platform, base, "Chrome Canary")
|
|
643
|
+
};
|
|
644
|
+
}
|
|
645
|
+
case "linux": {
|
|
646
|
+
const configHome = env["CHROME_CONFIG_HOME"] ?? env["XDG_CONFIG_HOME"] ?? joinPath(platform, homeDir, ".config");
|
|
647
|
+
return {
|
|
648
|
+
stable: joinPath(platform, configHome, "google-chrome"),
|
|
649
|
+
beta: joinPath(platform, configHome, "google-chrome-beta"),
|
|
650
|
+
dev: joinPath(platform, configHome, "google-chrome-dev"),
|
|
651
|
+
canary: joinPath(platform, configHome, "google-chrome-canary")
|
|
652
|
+
};
|
|
653
|
+
}
|
|
654
|
+
case "win32": {
|
|
655
|
+
const localAppData = env["LOCALAPPDATA"] ?? joinPath(platform, homeDir, "AppData", "Local");
|
|
656
|
+
const base = joinPath(platform, localAppData, "Google");
|
|
657
|
+
return {
|
|
658
|
+
stable: joinPath(platform, base, "Chrome", "User Data"),
|
|
659
|
+
beta: joinPath(platform, base, "Chrome Beta", "User Data"),
|
|
660
|
+
dev: joinPath(platform, base, "Chrome Dev", "User Data"),
|
|
661
|
+
canary: joinPath(platform, base, "Chrome SxS", "User Data")
|
|
662
|
+
};
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
throw new Error(`Unsupported platform for local Chrome discovery: ${platform}`);
|
|
666
|
+
}
|
|
667
|
+
function buildLocalBrowserScanTargets(options = {}) {
|
|
668
|
+
const env = options.env ?? getRuntimeEnv();
|
|
669
|
+
const platform = normalizePlatform(options.platform ?? getRuntimePlatform());
|
|
670
|
+
if (options.userDataDir) {
|
|
671
|
+
return [
|
|
672
|
+
{
|
|
673
|
+
channel: options.channel ?? "custom",
|
|
674
|
+
userDataDir: options.userDataDir,
|
|
675
|
+
portFile: joinPath(platform, options.userDataDir, "DevToolsActivePort")
|
|
676
|
+
}
|
|
677
|
+
];
|
|
678
|
+
}
|
|
679
|
+
const dirs = resolveChromeUserDataDirs({
|
|
680
|
+
platform,
|
|
681
|
+
env,
|
|
682
|
+
homeDir: options.homeDir
|
|
683
|
+
});
|
|
684
|
+
const channels = options.channel ? [options.channel] : CHANNEL_ORDER;
|
|
685
|
+
return channels.map((channel) => ({
|
|
686
|
+
channel,
|
|
687
|
+
userDataDir: dirs[channel],
|
|
688
|
+
portFile: joinPath(platform, dirs[channel], "DevToolsActivePort")
|
|
689
|
+
}));
|
|
690
|
+
}
|
|
691
|
+
function parseDevToolsActivePortFile(content) {
|
|
692
|
+
const lines = content.split(/\r?\n/u).map((line) => line.trim()).filter((line) => line.length > 0);
|
|
693
|
+
if (lines.length !== 2) {
|
|
694
|
+
throw new DevToolsActivePortParseError(
|
|
695
|
+
`Expected exactly 2 non-empty lines in DevToolsActivePort, got ${lines.length}`,
|
|
696
|
+
"malformed-file"
|
|
697
|
+
);
|
|
698
|
+
}
|
|
699
|
+
const portText = lines[0];
|
|
700
|
+
const browserPath = lines[1];
|
|
701
|
+
const port = Number.parseInt(portText, 10);
|
|
702
|
+
if (!Number.isInteger(port) || port < 1 || port > 65535) {
|
|
703
|
+
throw new DevToolsActivePortParseError(
|
|
704
|
+
`Invalid DevToolsActivePort port: ${portText}`,
|
|
705
|
+
"invalid-port"
|
|
706
|
+
);
|
|
707
|
+
}
|
|
708
|
+
if (!browserPath.startsWith("/devtools/browser/") || browserPath.includes("..") || /[?#\s\\]/u.test(browserPath)) {
|
|
709
|
+
throw new DevToolsActivePortParseError(
|
|
710
|
+
`Invalid DevToolsActivePort browser path: ${browserPath}`,
|
|
711
|
+
"invalid-path"
|
|
712
|
+
);
|
|
713
|
+
}
|
|
714
|
+
return {
|
|
715
|
+
port,
|
|
716
|
+
browserPath,
|
|
717
|
+
wsUrl: `ws://127.0.0.1:${port}${browserPath}`
|
|
718
|
+
};
|
|
719
|
+
}
|
|
720
|
+
async function inspectScanTarget(target, options, deps) {
|
|
721
|
+
let content;
|
|
722
|
+
try {
|
|
723
|
+
content = await deps.readTextFile(target.portFile);
|
|
724
|
+
} catch (error) {
|
|
725
|
+
return { kind: "failure", failure: toFileFailure(target, error) };
|
|
726
|
+
}
|
|
727
|
+
let parsed;
|
|
728
|
+
try {
|
|
729
|
+
parsed = parseDevToolsActivePortFile(content);
|
|
730
|
+
} catch (error) {
|
|
731
|
+
if (error instanceof DevToolsActivePortParseError) {
|
|
732
|
+
return {
|
|
733
|
+
kind: "failure",
|
|
734
|
+
failure: {
|
|
735
|
+
...target,
|
|
736
|
+
reason: error.reason,
|
|
737
|
+
message: error.message
|
|
738
|
+
}
|
|
739
|
+
};
|
|
740
|
+
}
|
|
741
|
+
throw error;
|
|
742
|
+
}
|
|
743
|
+
try {
|
|
744
|
+
const probe = await deps.probeBrowserWebSocket(
|
|
745
|
+
parsed.wsUrl,
|
|
746
|
+
options.probeTimeoutMs ?? DEFAULT_PROBE_TIMEOUT_MS
|
|
747
|
+
);
|
|
748
|
+
return {
|
|
749
|
+
kind: "candidate",
|
|
750
|
+
candidate: {
|
|
751
|
+
...target,
|
|
752
|
+
port: parsed.port,
|
|
753
|
+
browserPath: parsed.browserPath,
|
|
754
|
+
wsUrl: parsed.wsUrl,
|
|
755
|
+
browserVersion: probe.browserVersion
|
|
756
|
+
}
|
|
757
|
+
};
|
|
758
|
+
} catch (error) {
|
|
759
|
+
return {
|
|
760
|
+
kind: "failure",
|
|
761
|
+
failure: toProbeFailure(target, parsed.wsUrl, error)
|
|
762
|
+
};
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
async function discoverLocalBrowsers(options = {}, deps = defaultDependencies) {
|
|
766
|
+
const scanTargets = buildLocalBrowserScanTargets(options);
|
|
767
|
+
const outcomes = await Promise.all(
|
|
768
|
+
scanTargets.map((target) => inspectScanTarget(target, options, deps))
|
|
769
|
+
);
|
|
770
|
+
const candidates = [];
|
|
771
|
+
const failures = [];
|
|
772
|
+
for (const outcome of outcomes) {
|
|
773
|
+
if (outcome.kind === "candidate") {
|
|
774
|
+
candidates.push(outcome.candidate);
|
|
775
|
+
} else {
|
|
776
|
+
failures.push(outcome.failure);
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
return { candidates, failures };
|
|
780
|
+
}
|
|
781
|
+
var BrowserEndpointResolutionError = class extends Error {
|
|
782
|
+
constructor(code, message, details = {}) {
|
|
783
|
+
super(message);
|
|
784
|
+
this.code = code;
|
|
785
|
+
this.details = details;
|
|
786
|
+
}
|
|
787
|
+
name = "BrowserEndpointResolutionError";
|
|
788
|
+
};
|
|
789
|
+
async function resolveBrowserEndpoint(options = {}, deps = defaultDependencies) {
|
|
790
|
+
if (options.explicitWsUrl) {
|
|
791
|
+
return {
|
|
792
|
+
wsUrl: options.explicitWsUrl,
|
|
793
|
+
source: "explicit-ws"
|
|
794
|
+
};
|
|
795
|
+
}
|
|
796
|
+
let localDiscovery;
|
|
797
|
+
if (options.allowLocalDiscovery ?? true) {
|
|
798
|
+
localDiscovery = await discoverLocalBrowsers(options, deps);
|
|
799
|
+
if (localDiscovery.candidates.length === 1) {
|
|
800
|
+
const candidate = localDiscovery.candidates[0];
|
|
801
|
+
return {
|
|
802
|
+
wsUrl: candidate.wsUrl,
|
|
803
|
+
source: "devtools-active-port",
|
|
804
|
+
channel: candidate.channel,
|
|
805
|
+
userDataDir: candidate.userDataDir
|
|
806
|
+
};
|
|
807
|
+
}
|
|
808
|
+
if (localDiscovery.candidates.length > 1) {
|
|
809
|
+
throw new BrowserEndpointResolutionError(
|
|
810
|
+
"multiple-local-browsers",
|
|
811
|
+
"Multiple local Chrome profiles are available for auto-discovery",
|
|
812
|
+
{
|
|
813
|
+
candidates: localDiscovery.candidates,
|
|
814
|
+
failures: localDiscovery.failures
|
|
815
|
+
}
|
|
816
|
+
);
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
if (options.allowLegacyHostFallback ?? true) {
|
|
820
|
+
const legacyHost = options.legacyHost ?? "localhost:9222";
|
|
821
|
+
try {
|
|
822
|
+
return {
|
|
823
|
+
wsUrl: await deps.getLegacyBrowserWebSocketUrl(legacyHost),
|
|
824
|
+
source: "json-version"
|
|
825
|
+
};
|
|
826
|
+
} catch (error) {
|
|
827
|
+
throw new BrowserEndpointResolutionError(
|
|
828
|
+
"browser-not-found",
|
|
829
|
+
"Could not resolve a browser endpoint",
|
|
830
|
+
{
|
|
831
|
+
candidates: localDiscovery?.candidates,
|
|
832
|
+
failures: localDiscovery?.failures,
|
|
833
|
+
legacyError: error instanceof Error ? error : new Error(String(error)),
|
|
834
|
+
legacyHost
|
|
835
|
+
}
|
|
836
|
+
);
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
throw new BrowserEndpointResolutionError(
|
|
840
|
+
"browser-not-found",
|
|
841
|
+
"Could not resolve a browser endpoint",
|
|
842
|
+
{
|
|
843
|
+
candidates: localDiscovery?.candidates,
|
|
844
|
+
failures: localDiscovery?.failures
|
|
845
|
+
}
|
|
846
|
+
);
|
|
847
|
+
}
|
|
848
|
+
|
|
220
849
|
// src/providers/index.ts
|
|
221
850
|
function createProvider(options) {
|
|
222
851
|
switch (options.provider) {
|
|
@@ -252,9 +881,15 @@ function createProvider(options) {
|
|
|
252
881
|
// Annotate the CommonJS export names for ESM import in node:
|
|
253
882
|
0 && (module.exports = {
|
|
254
883
|
BrowserBaseProvider,
|
|
884
|
+
BrowserEndpointResolutionError,
|
|
255
885
|
BrowserlessProvider,
|
|
256
886
|
GenericProvider,
|
|
887
|
+
buildLocalBrowserScanTargets,
|
|
257
888
|
createProvider,
|
|
889
|
+
discoverLocalBrowsers,
|
|
258
890
|
discoverTargets,
|
|
259
|
-
getBrowserWebSocketUrl
|
|
891
|
+
getBrowserWebSocketUrl,
|
|
892
|
+
parseDevToolsActivePortFile,
|
|
893
|
+
resolveBrowserEndpoint,
|
|
894
|
+
resolveChromeUserDataDirs
|
|
260
895
|
});
|
package/dist/providers.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { P as Provider, C as CreateSessionOptions, a as ProviderSession, b as ConnectOptions } from './types
|
|
2
|
-
export {
|
|
1
|
+
import { P as Provider, C as CreateSessionOptions, a as ProviderSession, b as ConnectOptions } from './types-DeVSWhXj.cjs';
|
|
2
|
+
export { B as BrowserEndpointResolutionError, d as ChromeChannel, e as ChromeUserDataDirOptions, D as DiscoverLocalBrowsersOptions, i as ProxyConfig, R as ResolvedBrowserEndpoint, g as ResolvedBrowserSource, c as buildLocalBrowserScanTargets, f as discoverLocalBrowsers, p as parseDevToolsActivePortFile, r as resolveBrowserEndpoint, h as resolveChromeUserDataDirs } from './types-DeVSWhXj.cjs';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* BrowserBase provider implementation
|
package/dist/providers.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { P as Provider, C as CreateSessionOptions, a as ProviderSession, b as ConnectOptions } from './types
|
|
2
|
-
export {
|
|
1
|
+
import { P as Provider, C as CreateSessionOptions, a as ProviderSession, b as ConnectOptions } from './types-DeVSWhXj.js';
|
|
2
|
+
export { B as BrowserEndpointResolutionError, d as ChromeChannel, e as ChromeUserDataDirOptions, D as DiscoverLocalBrowsersOptions, i as ProxyConfig, R as ResolvedBrowserEndpoint, g as ResolvedBrowserSource, c as buildLocalBrowserScanTargets, f as discoverLocalBrowsers, p as parseDevToolsActivePortFile, r as resolveBrowserEndpoint, h as resolveChromeUserDataDirs } from './types-DeVSWhXj.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* BrowserBase provider implementation
|