browserclaw 0.3.9 → 0.3.10

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/dist/index.cjs CHANGED
@@ -382,6 +382,51 @@ async function getChromeWebSocketUrl(cdpUrl, timeoutMs = 500, authToken) {
382
382
  clearTimeout(t);
383
383
  }
384
384
  }
385
+ async function isChromeCdpReady(cdpUrl, timeoutMs = 500, handshakeTimeoutMs = 800) {
386
+ const wsUrl = await getChromeWebSocketUrl(cdpUrl, timeoutMs);
387
+ if (!wsUrl) return false;
388
+ return await canRunCdpHealthCommand(wsUrl, handshakeTimeoutMs);
389
+ }
390
+ async function canRunCdpHealthCommand(wsUrl, timeoutMs = 800) {
391
+ return new Promise((resolve2) => {
392
+ let settled = false;
393
+ const finish = (value) => {
394
+ if (settled) return;
395
+ settled = true;
396
+ clearTimeout(timer);
397
+ try {
398
+ ws.close();
399
+ } catch {
400
+ }
401
+ resolve2(value);
402
+ };
403
+ const timer = setTimeout(() => finish(false), Math.max(50, timeoutMs + 25));
404
+ let ws;
405
+ try {
406
+ ws = new WebSocket(wsUrl);
407
+ } catch {
408
+ finish(false);
409
+ return;
410
+ }
411
+ ws.onopen = () => {
412
+ try {
413
+ ws.send(JSON.stringify({ id: 1, method: "Browser.getVersion" }));
414
+ } catch {
415
+ finish(false);
416
+ }
417
+ };
418
+ ws.onmessage = (event) => {
419
+ try {
420
+ const parsed = JSON.parse(String(event.data));
421
+ if (parsed?.id !== 1) return;
422
+ finish(Boolean(parsed.result && typeof parsed.result === "object"));
423
+ } catch {
424
+ }
425
+ };
426
+ ws.onerror = () => finish(false);
427
+ ws.onclose = () => finish(false);
428
+ });
429
+ }
385
430
  async function launchChrome(opts = {}) {
386
431
  const cdpPort = opts.cdpPort ?? DEFAULT_CDP_PORT;
387
432
  await ensurePortAvailable(cdpPort);
@@ -456,18 +501,30 @@ async function launchChrome(opts = {}) {
456
501
  }
457
502
  const proc = spawnChrome();
458
503
  const cdpUrl = `http://127.0.0.1:${cdpPort}`;
504
+ const stderrChunks = [];
505
+ const onStderr = (chunk) => {
506
+ stderrChunks.push(chunk);
507
+ };
508
+ proc.stderr?.on("data", onStderr);
459
509
  const readyDeadline = Date.now() + 15e3;
460
510
  while (Date.now() < readyDeadline) {
461
511
  if (await isChromeReachable(cdpUrl, 500)) break;
462
512
  await new Promise((r) => setTimeout(r, 200));
463
513
  }
464
514
  if (!await isChromeReachable(cdpUrl, 500)) {
515
+ const stderrOutput = Buffer.concat(stderrChunks).toString("utf8").trim();
516
+ const stderrHint = stderrOutput ? `
517
+ Chrome stderr:
518
+ ${stderrOutput.slice(0, 2e3)}` : "";
519
+ const sandboxHint = process.platform === "linux" && !opts.noSandbox ? "\nHint: If running in a container or as root, try setting noSandbox: true." : "";
465
520
  try {
466
521
  proc.kill("SIGKILL");
467
522
  } catch {
468
523
  }
469
- throw new Error(`Failed to start Chrome CDP on port ${cdpPort}. Chrome may not have started correctly.`);
524
+ throw new Error(`Failed to start Chrome CDP on port ${cdpPort}.${sandboxHint}${stderrHint}`);
470
525
  }
526
+ proc.stderr?.off("data", onStderr);
527
+ stderrChunks.length = 0;
471
528
  return {
472
529
  pid: proc.pid ?? -1,
473
530
  exe,
@@ -3195,6 +3252,7 @@ exports.BrowserClaw = BrowserClaw;
3195
3252
  exports.CrawlPage = CrawlPage;
3196
3253
  exports.InvalidBrowserNavigationUrlError = InvalidBrowserNavigationUrlError;
3197
3254
  exports.assertBrowserNavigationAllowed = assertBrowserNavigationAllowed;
3255
+ exports.isChromeCdpReady = isChromeCdpReady;
3198
3256
  exports.withBrowserNavigationPolicy = withBrowserNavigationPolicy;
3199
3257
  //# sourceMappingURL=index.cjs.map
3200
3258
  //# sourceMappingURL=index.cjs.map