@btraut/browser-bridge 0.12.1 → 0.13.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.
@@ -1,3 +1,57 @@
1
+ var __create = Object.create;
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __getProtoOf = Object.getPrototypeOf;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __commonJS = (cb, mod) => function __require() {
8
+ return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
19
+ // If the importer is in node compatibility mode or this is not an ESM
20
+ // file that has been converted to a CommonJS file using a Babel-
21
+ // compatible transform (i.e. "__esModule" has not been set), then set
22
+ // "default" to the CommonJS "module.exports" for node compatibility.
23
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
24
+ mod
25
+ ));
26
+
27
+ // packages/shared/dist/contract-version.js
28
+ var require_contract_version = __commonJS({
29
+ "packages/shared/dist/contract-version.js"(exports) {
30
+ "use strict";
31
+ Object.defineProperty(exports, "__esModule", { value: true });
32
+ exports.resolveContractVersionMismatch = exports.DRIVE_WS_PROTOCOL_VERSION = exports.HTTP_CONTRACT_VERSION = exports.HTTP_CONTRACT_VERSION_HEADER = void 0;
33
+ exports.HTTP_CONTRACT_VERSION_HEADER = "x-browser-bridge-contract-version";
34
+ exports.HTTP_CONTRACT_VERSION = "2026-02-17.1";
35
+ exports.DRIVE_WS_PROTOCOL_VERSION = "2026-02-17.1";
36
+ var resolveContractVersionMismatch = (receivedVersion) => {
37
+ if (!receivedVersion || receivedVersion.trim().length === 0) {
38
+ return void 0;
39
+ }
40
+ if (receivedVersion === exports.HTTP_CONTRACT_VERSION) {
41
+ return void 0;
42
+ }
43
+ return {
44
+ expected: exports.HTTP_CONTRACT_VERSION,
45
+ received: receivedVersion
46
+ };
47
+ };
48
+ exports.resolveContractVersionMismatch = resolveContractVersionMismatch;
49
+ }
50
+ });
51
+
52
+ // packages/extension/src/background.ts
53
+ var import_contract_version = __toESM(require_contract_version());
54
+
1
55
  // packages/extension/src/error-sanitizer.ts
2
56
  var TRAILING_PUNCTUATION_RE = /[.,;:!?]+$/;
3
57
  var stripTrailingPunctuation = (value) => {
@@ -97,7 +151,9 @@ var SITE_ALLOWLIST_KEY = "siteAllowlist";
97
151
  var PERMISSION_PROMPT_WAIT_MS_KEY = "permissionPromptWaitMs";
98
152
  var DEFAULT_PERMISSION_PROMPT_WAIT_MS = 3e4;
99
153
  var SITE_PERMISSIONS_MODE_KEY = "sitePermissionsMode";
154
+ var DEBUGGER_CAPABILITY_ENABLED_KEY = "debuggerCapabilityEnabled";
100
155
  var DEFAULT_SITE_PERMISSIONS_MODE = "granular";
156
+ var DEFAULT_DEBUGGER_CAPABILITY_ENABLED = false;
101
157
  var siteKeyFromUrl = (rawUrl) => {
102
158
  if (!rawUrl || typeof rawUrl !== "string") {
103
159
  return null;
@@ -199,6 +255,27 @@ var readPermissionPromptWaitMs = async () => {
199
255
  );
200
256
  });
201
257
  };
258
+ var readDebuggerCapabilityEnabled = async () => {
259
+ return await new Promise((resolve) => {
260
+ chrome.storage.local.get(
261
+ [DEBUGGER_CAPABILITY_ENABLED_KEY],
262
+ (result) => {
263
+ const raw = result?.[DEBUGGER_CAPABILITY_ENABLED_KEY];
264
+ if (typeof raw === "boolean") {
265
+ resolve(raw);
266
+ return;
267
+ }
268
+ try {
269
+ chrome.storage.local.set({
270
+ [DEBUGGER_CAPABILITY_ENABLED_KEY]: DEFAULT_DEBUGGER_CAPABILITY_ENABLED
271
+ });
272
+ } catch {
273
+ }
274
+ resolve(DEFAULT_DEBUGGER_CAPABILITY_ENABLED);
275
+ }
276
+ );
277
+ });
278
+ };
202
279
  var isSiteAllowed = async (siteKey) => {
203
280
  const key = normalizeSiteKey(siteKey);
204
281
  const allowlist = await readAllowlistRaw();
@@ -501,9 +578,62 @@ var HISTORY_POST_NAV_DOM_GRACE_TIMEOUT_MS = 2e3;
501
578
  var AGENT_TAB_ID_KEY = "agentTabId";
502
579
  var AGENT_TAB_GROUP_TITLE = "Browser Bridge";
503
580
  var AGENT_TAB_BOOTSTRAP_PATH = "agent-tab.html";
581
+ var AGENT_TAB_FAVICON_ASSET_PATH = "assets/icons/icon-32.png";
582
+ var AGENT_TAB_BRANDING_ACTION = "drive.agent_tab_branding";
583
+ var AGENT_TAB_GROUP_RETRY_DELAYS_MS = [0, 120, 300];
584
+ var AGENT_TAB_BRANDING_TIMEOUT_MS = 1500;
585
+ var BASE_NEGOTIATED_CAPABILITIES = Object.freeze({
586
+ "drive.navigate": true,
587
+ "drive.go_back": true,
588
+ "drive.go_forward": true,
589
+ "drive.click": true,
590
+ "drive.hover": true,
591
+ "drive.select": true,
592
+ "drive.type": true,
593
+ "drive.fill_form": true,
594
+ "drive.drag": true,
595
+ "drive.handle_dialog": true,
596
+ "drive.key": true,
597
+ "drive.key_press": true,
598
+ "drive.scroll": true,
599
+ "drive.screenshot": true,
600
+ "drive.wait_for": true,
601
+ "drive.tab_list": true,
602
+ "drive.tab_activate": true,
603
+ "drive.tab_close": true,
604
+ "drive.ping": true
605
+ });
606
+ var DEBUGGER_CAPABILITY_ACTIONS = [
607
+ "debugger.attach",
608
+ "debugger.detach",
609
+ "debugger.command"
610
+ ];
611
+ var buildNegotiatedCapabilities = (debuggerCapabilityEnabled) => {
612
+ const capabilities = {
613
+ ...BASE_NEGOTIATED_CAPABILITIES
614
+ };
615
+ for (const action of DEBUGGER_CAPABILITY_ACTIONS) {
616
+ capabilities[action] = debuggerCapabilityEnabled;
617
+ }
618
+ return capabilities;
619
+ };
620
+ var debuggerCapabilityDisabledError = () => {
621
+ return {
622
+ code: "ATTACH_DENIED",
623
+ message: "Debugger capability is disabled. Enable debugger-based inspect in extension options and retry.",
624
+ retryable: false,
625
+ details: {
626
+ reason: "debugger_capability_disabled",
627
+ next_step: "Open Browser Bridge extension options, enable debugger-based inspect, then retry."
628
+ }
629
+ };
630
+ };
504
631
  var getAgentTabBootstrapUrl = () => {
505
632
  return typeof chrome.runtime?.getURL === "function" ? chrome.runtime.getURL(AGENT_TAB_BOOTSTRAP_PATH) : AGENT_TAB_BOOTSTRAP_PATH;
506
633
  };
634
+ var getAgentTabFaviconUrl = () => {
635
+ return typeof chrome.runtime?.getURL === "function" ? chrome.runtime.getURL(AGENT_TAB_FAVICON_ASSET_PATH) : AGENT_TAB_FAVICON_ASSET_PATH;
636
+ };
507
637
  var nowIso = () => (/* @__PURE__ */ new Date()).toISOString();
508
638
  var makeEventId = /* @__PURE__ */ (() => {
509
639
  let counter = 0;
@@ -872,23 +1002,38 @@ var ensureAgentTabGroup = async (tabId, windowId) => {
872
1002
  if (!chrome.tabGroups || typeof chrome.tabGroups.update !== "function") {
873
1003
  return;
874
1004
  }
875
- try {
876
- const groupId = await wrapChromeCallback(
877
- (callback) => chrome.tabs.group(
878
- { tabIds: tabId, createProperties: { windowId } },
879
- callback
880
- )
881
- );
882
- await wrapChromeVoid(
883
- (callback) => chrome.tabGroups.update(
884
- groupId,
885
- { title: AGENT_TAB_GROUP_TITLE },
886
- () => callback()
887
- )
888
- );
889
- } catch (error) {
890
- console.debug("Failed to create/update agent tab group.", error);
1005
+ let lastError;
1006
+ for (const retryDelayMs of AGENT_TAB_GROUP_RETRY_DELAYS_MS) {
1007
+ if (retryDelayMs > 0) {
1008
+ await delayMs(retryDelayMs);
1009
+ }
1010
+ try {
1011
+ const groupId = await wrapChromeCallback(
1012
+ (callback) => chrome.tabs.group(
1013
+ { tabIds: tabId, createProperties: { windowId } },
1014
+ callback
1015
+ )
1016
+ );
1017
+ await wrapChromeVoid(
1018
+ (callback) => chrome.tabGroups.update(
1019
+ groupId,
1020
+ { title: AGENT_TAB_GROUP_TITLE },
1021
+ () => callback()
1022
+ )
1023
+ );
1024
+ return;
1025
+ } catch (error) {
1026
+ lastError = error;
1027
+ }
1028
+ }
1029
+ console.debug("Failed to create/update agent tab group.", lastError);
1030
+ };
1031
+ var ensureAgentTabGroupForTab = async (tabId, tab) => {
1032
+ const windowId = tab.windowId;
1033
+ if (typeof windowId !== "number") {
1034
+ return;
891
1035
  }
1036
+ await ensureAgentTabGroup(tabId, windowId);
892
1037
  };
893
1038
  var createAgentWindow = async () => {
894
1039
  const created = await wrapChromeCallback(
@@ -943,6 +1088,8 @@ var getOrCreateAgentTabId = async () => {
943
1088
  if (typeof url === "string" && isRestrictedUrl(url)) {
944
1089
  throw new Error(`Agent tab points at restricted URL: ${url}`);
945
1090
  }
1091
+ await ensureAgentTabGroupForTab(agentTabId, tab);
1092
+ void refreshAgentTabBranding(agentTabId);
946
1093
  return agentTabId;
947
1094
  } catch {
948
1095
  clearAgentTarget();
@@ -959,6 +1106,8 @@ var getOrCreateAgentTabId = async () => {
959
1106
  agentTabId = stored;
960
1107
  ensureLastActiveAt(stored);
961
1108
  markTabActive(stored);
1109
+ await ensureAgentTabGroupForTab(stored, tab);
1110
+ void refreshAgentTabBranding(stored);
962
1111
  return stored;
963
1112
  } catch {
964
1113
  await writeAgentTabId(null);
@@ -1064,6 +1213,17 @@ var sendToTab = async (tabId, action, params, options) => {
1064
1213
  }
1065
1214
  };
1066
1215
  };
1216
+ var refreshAgentTabBranding = async (tabId) => {
1217
+ const result = await sendToTab(
1218
+ tabId,
1219
+ AGENT_TAB_BRANDING_ACTION,
1220
+ { favicon_url: getAgentTabFaviconUrl() },
1221
+ { timeoutMs: AGENT_TAB_BRANDING_TIMEOUT_MS }
1222
+ );
1223
+ if (!result.ok) {
1224
+ return;
1225
+ }
1226
+ };
1067
1227
  var waitForHistoryNavigationSignal = async (tabId, timeoutMs) => {
1068
1228
  return await new Promise((resolve, reject) => {
1069
1229
  let timeout;
@@ -1290,6 +1450,11 @@ var DriveSocket = class {
1290
1450
  getConnectionStatus() {
1291
1451
  return this.connection.getStatus();
1292
1452
  }
1453
+ refreshCapabilities() {
1454
+ void this.sendHello().catch((error) => {
1455
+ console.error("DriveSocket refreshCapabilities failed:", error);
1456
+ });
1457
+ }
1293
1458
  handleSocketUnavailable(socket2, reason) {
1294
1459
  if (this.socket !== socket2) {
1295
1460
  return;
@@ -1319,6 +1484,7 @@ var DriveSocket = class {
1319
1484
  async sendHello() {
1320
1485
  const manifest = chrome.runtime.getManifest();
1321
1486
  const endpoint = await readCoreEndpointConfig();
1487
+ const debuggerCapabilityEnabled = await readDebuggerCapabilityEnabled();
1322
1488
  let tabs = [];
1323
1489
  try {
1324
1490
  tabs = await queryTabs();
@@ -1328,6 +1494,8 @@ var DriveSocket = class {
1328
1494
  }
1329
1495
  const params = {
1330
1496
  version: manifest.version,
1497
+ protocol_version: import_contract_version.DRIVE_WS_PROTOCOL_VERSION,
1498
+ capabilities: buildNegotiatedCapabilities(debuggerCapabilityEnabled),
1331
1499
  core_host: endpoint.host,
1332
1500
  core_port: endpoint.port,
1333
1501
  core_port_source: endpoint.portSource,
@@ -1402,6 +1570,19 @@ var DriveSocket = class {
1402
1570
  });
1403
1571
  }
1404
1572
  }
1573
+ async refreshDebuggerCapabilityState() {
1574
+ const enabled = await readDebuggerCapabilityEnabled();
1575
+ if (!enabled) {
1576
+ await this.detachAllDebuggerSessions();
1577
+ }
1578
+ this.refreshCapabilities();
1579
+ }
1580
+ async handleDebuggerCapabilityChange(enabled) {
1581
+ if (!enabled) {
1582
+ await this.detachAllDebuggerSessions();
1583
+ }
1584
+ this.refreshCapabilities();
1585
+ }
1405
1586
  async handleRequest(message) {
1406
1587
  let driveMessage = null;
1407
1588
  let gatedSiteKey = null;
@@ -1440,6 +1621,15 @@ var DriveSocket = class {
1440
1621
  return;
1441
1622
  }
1442
1623
  if (message.action.startsWith("debugger.")) {
1624
+ if (!await readDebuggerCapabilityEnabled()) {
1625
+ this.sendMessage({
1626
+ id: message.id,
1627
+ action: message.action,
1628
+ status: "error",
1629
+ error: sanitizeDriveErrorInfo(debuggerCapabilityDisabledError())
1630
+ });
1631
+ return;
1632
+ }
1443
1633
  await this.handleDebuggerRequest(message);
1444
1634
  return;
1445
1635
  }
@@ -1451,8 +1641,6 @@ var DriveSocket = class {
1451
1641
  "drive.navigate",
1452
1642
  "drive.go_back",
1453
1643
  "drive.go_forward",
1454
- "drive.back",
1455
- "drive.forward",
1456
1644
  "drive.click",
1457
1645
  "drive.hover",
1458
1646
  "drive.select",
@@ -1645,13 +1833,14 @@ var DriveSocket = class {
1645
1833
  return;
1646
1834
  }
1647
1835
  }
1836
+ if (tabId === agentTabId) {
1837
+ void refreshAgentTabBranding(tabId);
1838
+ }
1648
1839
  respondOk({ ok: true });
1649
1840
  return;
1650
1841
  }
1651
1842
  case "drive.go_back":
1652
- case "drive.back":
1653
- case "drive.go_forward":
1654
- case "drive.forward": {
1843
+ case "drive.go_forward": {
1655
1844
  const params = message.params ?? {};
1656
1845
  let tabId = params.tab_id;
1657
1846
  if (tabId !== void 0 && typeof tabId !== "number") {
@@ -1697,6 +1886,9 @@ var DriveSocket = class {
1697
1886
  return;
1698
1887
  }
1699
1888
  }
1889
+ if (tabId === agentTabId) {
1890
+ void refreshAgentTabBranding(tabId);
1891
+ }
1700
1892
  respondOk({ ok: true });
1701
1893
  return;
1702
1894
  }
@@ -3082,6 +3274,9 @@ var DriveSocket = class {
3082
3274
  }
3083
3275
  }
3084
3276
  async handleDebuggerEvent(source, method, params) {
3277
+ if (!await readDebuggerCapabilityEnabled()) {
3278
+ return;
3279
+ }
3085
3280
  const tabId = source.tabId;
3086
3281
  if (typeof tabId !== "number") {
3087
3282
  return;
@@ -3100,6 +3295,9 @@ var DriveSocket = class {
3100
3295
  return;
3101
3296
  }
3102
3297
  this.clearDebuggerSession(tabId);
3298
+ if (!await readDebuggerCapabilityEnabled()) {
3299
+ return;
3300
+ }
3103
3301
  this.sendDebuggerEvent({
3104
3302
  tab_id: tabId,
3105
3303
  method: "Debugger.detached",
@@ -3238,6 +3436,15 @@ var DriveSocket = class {
3238
3436
  }
3239
3437
  return null;
3240
3438
  }
3439
+ async detachAllDebuggerSessions() {
3440
+ const tabIds = Array.from(this.debuggerSessions.keys());
3441
+ for (const tabId of tabIds) {
3442
+ const error = await this.detachDebugger(tabId);
3443
+ if (error) {
3444
+ console.warn("DriveSocket detachDebugger failed:", tabId, error);
3445
+ }
3446
+ }
3447
+ }
3241
3448
  async sendDebuggerCommand(tabId, method, params, timeoutMs) {
3242
3449
  return await new Promise((resolve, reject) => {
3243
3450
  let finished = false;
@@ -3368,14 +3575,53 @@ chrome.debugger.onDetach.addListener(
3368
3575
  );
3369
3576
  chrome.runtime.onMessage.addListener(
3370
3577
  (message, _sender, sendResponse) => {
3371
- if (!message || typeof message !== "object" || message.action !== "drive.connection_status") {
3578
+ if (!message || typeof message !== "object") {
3372
3579
  return void 0;
3373
3580
  }
3374
- sendResponse({
3375
- ok: true,
3376
- result: socket.getConnectionStatus()
3581
+ const action = message.action;
3582
+ if (action === "drive.connection_status") {
3583
+ sendResponse({
3584
+ ok: true,
3585
+ result: socket.getConnectionStatus()
3586
+ });
3587
+ return true;
3588
+ }
3589
+ if (action === "drive.refresh_capabilities") {
3590
+ void socket.refreshDebuggerCapabilityState().then(() => {
3591
+ sendResponse({ ok: true, result: { refreshed: true } });
3592
+ }).catch((error) => {
3593
+ const message2 = error instanceof Error ? error.message : "Failed to refresh capabilities.";
3594
+ sendResponse({ ok: false, error: { message: message2 } });
3595
+ });
3596
+ return true;
3597
+ }
3598
+ return void 0;
3599
+ }
3600
+ );
3601
+ chrome.storage.onChanged.addListener(
3602
+ (changes, areaName) => {
3603
+ if (areaName !== "local") {
3604
+ return;
3605
+ }
3606
+ const debuggerChange = changes[DEBUGGER_CAPABILITY_ENABLED_KEY];
3607
+ if (!debuggerChange) {
3608
+ return;
3609
+ }
3610
+ if (typeof debuggerChange.newValue === "boolean") {
3611
+ void socket.handleDebuggerCapabilityChange(debuggerChange.newValue).catch((error) => {
3612
+ console.error(
3613
+ "DriveSocket handleDebuggerCapabilityChange failed:",
3614
+ error
3615
+ );
3616
+ });
3617
+ return;
3618
+ }
3619
+ void socket.refreshDebuggerCapabilityState().catch((error) => {
3620
+ console.error(
3621
+ "DriveSocket refreshDebuggerCapabilityState failed:",
3622
+ error
3623
+ );
3377
3624
  });
3378
- return true;
3379
3625
  }
3380
3626
  );
3381
3627
  socket.start();