@btraut/browser-bridge 0.8.1 → 0.10.0

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.
@@ -424,6 +424,13 @@ var HISTORY_NAVIGATION_SIGNAL_TIMEOUT_MS = 8e3;
424
424
  var HISTORY_POST_NAV_DOM_GRACE_TIMEOUT_MS = 2e3;
425
425
  var AGENT_TAB_ID_KEY = "agentTabId";
426
426
  var AGENT_TAB_GROUP_TITLE = "\u{1F309} Browser Bridge";
427
+ var AGENT_TAB_FAVICON_ASSET_PATH = "assets/icons/icon-32.png";
428
+ var getAgentTabBootstrapUrl = () => {
429
+ const faviconUrl = typeof chrome.runtime?.getURL === "function" ? chrome.runtime.getURL(AGENT_TAB_FAVICON_ASSET_PATH) : AGENT_TAB_FAVICON_ASSET_PATH;
430
+ return `data:text/html;charset=UTF-8,${encodeURIComponent(
431
+ `<!doctype html><html><head><meta charset="utf-8"><title>${AGENT_TAB_GROUP_TITLE}</title><link rel="icon" type="image/png" href="${faviconUrl}"></head><body></body></html>`
432
+ )}`;
433
+ };
427
434
  var nowIso = () => (/* @__PURE__ */ new Date()).toISOString();
428
435
  var makeEventId = /* @__PURE__ */ (() => {
429
436
  let counter = 0;
@@ -475,6 +482,80 @@ var delayMs = async (ms) => {
475
482
  self.setTimeout(resolve, ms);
476
483
  });
477
484
  };
485
+ var CAPTURE_VISIBLE_TAB_MIN_INTERVAL_MS = 400;
486
+ var CAPTURE_VISIBLE_TAB_MAX_RETRIES = 3;
487
+ var CAPTURE_VISIBLE_TAB_RETRY_BASE_DELAY_MS = 500;
488
+ var captureVisibleTabQueue = Promise.resolve();
489
+ var captureVisibleTabLastCallAt = 0;
490
+ var isCaptureVisibleTabRateLimitedMessage = (message) => {
491
+ const normalized = message.toLowerCase();
492
+ const hasCaptureSignal = normalized.includes("capturevisibletab") || normalized.includes("max_capture_visible_tab_calls_per_second");
493
+ const hasRateSignal = normalized.includes("too often") || normalized.includes("rate limit") || normalized.includes("rate-limit");
494
+ return hasCaptureSignal && hasRateSignal;
495
+ };
496
+ var isCaptureVisibleTabRateLimitedError = (error) => {
497
+ return error instanceof Error && isCaptureVisibleTabRateLimitedMessage(error.message);
498
+ };
499
+ var randomJitterMs = (maxMs) => {
500
+ return Math.floor(Math.random() * Math.max(1, maxMs));
501
+ };
502
+ var runCaptureVisibleTabOperation = async (operation) => {
503
+ const previous = captureVisibleTabQueue;
504
+ let releaseQueue;
505
+ captureVisibleTabQueue = new Promise((resolve) => {
506
+ releaseQueue = resolve;
507
+ });
508
+ await previous.catch(() => void 0);
509
+ try {
510
+ return await operation();
511
+ } finally {
512
+ releaseQueue?.();
513
+ }
514
+ };
515
+ var captureVisibleTabWithThrottle = async (windowId) => {
516
+ return await runCaptureVisibleTabOperation(async () => {
517
+ for (let attempt = 0; attempt <= CAPTURE_VISIBLE_TAB_MAX_RETRIES; attempt += 1) {
518
+ const waitMs = captureVisibleTabLastCallAt + CAPTURE_VISIBLE_TAB_MIN_INTERVAL_MS - Date.now();
519
+ if (waitMs > 0) {
520
+ await delayMs(waitMs);
521
+ }
522
+ try {
523
+ const dataUrl = await wrapChromeCallback(
524
+ (callback) => chrome.tabs.captureVisibleTab(windowId, { format: "png" }, callback)
525
+ );
526
+ captureVisibleTabLastCallAt = Date.now();
527
+ return dataUrl;
528
+ } catch (error) {
529
+ captureVisibleTabLastCallAt = Date.now();
530
+ if (!isCaptureVisibleTabRateLimitedError(error) || attempt >= CAPTURE_VISIBLE_TAB_MAX_RETRIES) {
531
+ throw error;
532
+ }
533
+ const backoffMs = CAPTURE_VISIBLE_TAB_RETRY_BASE_DELAY_MS * (attempt + 1) + randomJitterMs(120);
534
+ await delayMs(backoffMs);
535
+ }
536
+ }
537
+ throw new Error("captureVisibleTab failed unexpectedly.");
538
+ });
539
+ };
540
+ var mapScreenshotCaptureError = (error, fallbackMessage) => {
541
+ const message = error instanceof Error && error.message ? error.message : fallbackMessage;
542
+ if (isCaptureVisibleTabRateLimitedError(error)) {
543
+ return {
544
+ code: "RATE_LIMITED",
545
+ message: "Screenshot capture hit Chrome capture rate limits. Please retry shortly.",
546
+ retryable: true,
547
+ details: {
548
+ reason: "capture_visible_tab_rate_limited",
549
+ original_message: message
550
+ }
551
+ };
552
+ }
553
+ return {
554
+ code: "ARTIFACT_IO_ERROR",
555
+ message,
556
+ retryable: false
557
+ };
558
+ };
478
559
  var parseDataUrl = (dataUrl) => {
479
560
  const match = /^data:([^;]+);base64,(.*)$/s.exec(dataUrl);
480
561
  if (!match) {
@@ -726,7 +807,10 @@ var ensureAgentTabGroup = async (tabId, windowId) => {
726
807
  };
727
808
  var createAgentWindow = async () => {
728
809
  const created = await wrapChromeCallback(
729
- (callback) => chrome.windows.create({ url: "about:blank", focused: true }, callback)
810
+ (callback) => chrome.windows.create(
811
+ { url: getAgentTabBootstrapUrl(), focused: true },
812
+ callback
813
+ )
730
814
  );
731
815
  const windowId = created.id;
732
816
  if (typeof windowId !== "number") {
@@ -2072,13 +2156,7 @@ var DriveSocket = class {
2072
2156
  return;
2073
2157
  }
2074
2158
  const captureVisible = async () => {
2075
- return await wrapChromeCallback(
2076
- (callback) => chrome.tabs.captureVisibleTab(
2077
- windowId,
2078
- { format: "png" },
2079
- callback
2080
- )
2081
- );
2159
+ return await captureVisibleTabWithThrottle(windowId);
2082
2160
  };
2083
2161
  const scrollTo = async (top, left) => {
2084
2162
  const result = await sendToTab(tabId, "drive.scroll", {
@@ -2224,11 +2302,12 @@ var DriveSocket = class {
2224
2302
  );
2225
2303
  respondOk(rendered);
2226
2304
  } catch (error) {
2227
- respondError({
2228
- code: "ARTIFACT_IO_ERROR",
2229
- message: error instanceof Error ? error.message : "Failed to capture screenshot.",
2230
- retryable: false
2231
- });
2305
+ respondError(
2306
+ mapScreenshotCaptureError(
2307
+ error,
2308
+ "Failed to capture screenshot."
2309
+ )
2310
+ );
2232
2311
  }
2233
2312
  return;
2234
2313
  }
@@ -2360,11 +2439,12 @@ var DriveSocket = class {
2360
2439
  );
2361
2440
  respondOk(await canvasToResult(cropCanvas));
2362
2441
  } catch (error) {
2363
- respondError({
2364
- code: "ARTIFACT_IO_ERROR",
2365
- message: error instanceof Error ? error.message : "Failed to capture element screenshot.",
2366
- retryable: false
2367
- });
2442
+ respondError(
2443
+ mapScreenshotCaptureError(
2444
+ error,
2445
+ "Failed to capture element screenshot."
2446
+ )
2447
+ );
2368
2448
  } finally {
2369
2449
  try {
2370
2450
  await scrollTo(metaInfo.scrollY, metaInfo.scrollX);
@@ -2378,11 +2458,12 @@ var DriveSocket = class {
2378
2458
  const canvas = await captureFullPageCanvas(metaInfo);
2379
2459
  respondOk(await canvasToResult(canvas));
2380
2460
  } catch (error) {
2381
- respondError({
2382
- code: "ARTIFACT_IO_ERROR",
2383
- message: error instanceof Error ? error.message : "Failed to capture full page screenshot.",
2384
- retryable: false
2385
- });
2461
+ respondError(
2462
+ mapScreenshotCaptureError(
2463
+ error,
2464
+ "Failed to capture full page screenshot."
2465
+ )
2466
+ );
2386
2467
  }
2387
2468
  return;
2388
2469
  }