@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.
- package/CHANGELOG.md +14 -0
- package/dist/api.js +6 -2
- package/dist/api.js.map +2 -2
- package/dist/index.js +27 -13
- package/dist/index.js.map +2 -2
- package/extension/dist/background.js +104 -23
- package/extension/dist/background.js.map +2 -2
- package/extension/manifest.json +5 -17
- package/package.json +1 -1
- package/skills/browser-bridge/skill.json +1 -1
|
@@ -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(
|
|
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
|
|
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
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
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
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
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
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2461
|
+
respondError(
|
|
2462
|
+
mapScreenshotCaptureError(
|
|
2463
|
+
error,
|
|
2464
|
+
"Failed to capture full page screenshot."
|
|
2465
|
+
)
|
|
2466
|
+
);
|
|
2386
2467
|
}
|
|
2387
2468
|
return;
|
|
2388
2469
|
}
|