@opentabs-dev/browser-extension 0.0.35 → 0.0.36

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,6 +1,7 @@
1
- // dist/background.js
1
+ // dist/confirmation-badge.js
2
2
  var pendingConfirmationCount = 0;
3
3
  var confirmationTimeouts = /* @__PURE__ */ new Map();
4
+ var clearedConfirmationIds = /* @__PURE__ */ new Set();
4
5
  var CONFIRMATION_BACKGROUND_TIMEOUT_BUFFER_MS = 2e3;
5
6
  var CONFIRMATION_FALLBACK_TIMEOUT_MS = 3e4;
6
7
  var updateConfirmationBadge = () => {
@@ -23,6 +24,7 @@ var notifyConfirmationRequest = (params) => {
23
24
  if (existingTimeoutId !== void 0) {
24
25
  clearTimeout(existingTimeoutId);
25
26
  } else {
27
+ clearedConfirmationIds.delete(id);
26
28
  pendingConfirmationCount++;
27
29
  updateConfirmationBadge();
28
30
  }
@@ -30,6 +32,7 @@ var notifyConfirmationRequest = (params) => {
30
32
  const bgTimeoutId = setTimeout(() => {
31
33
  confirmationTimeouts.delete(id);
32
34
  clearConfirmationBadge(id);
35
+ clearedConfirmationIds.delete(id);
33
36
  }, effectiveTimeoutMs + CONFIRMATION_BACKGROUND_TIMEOUT_BUFFER_MS);
34
37
  confirmationTimeouts.set(id, bgTimeoutId);
35
38
  chrome.notifications.create(`opentabs-confirm-${id}`, {
@@ -44,17 +47,25 @@ var notifyConfirmationRequest = (params) => {
44
47
  };
45
48
  var clearConfirmationBadge = (id) => {
46
49
  if (id !== void 0) {
50
+ if (clearedConfirmationIds.has(id)) {
51
+ return;
52
+ }
53
+ clearedConfirmationIds.add(id);
47
54
  chrome.notifications.clear(`opentabs-confirm-${id}`).catch(() => {
48
55
  });
49
56
  }
50
57
  pendingConfirmationCount = Math.max(0, pendingConfirmationCount - 1);
51
58
  updateConfirmationBadge();
59
+ if (id !== void 0 && !confirmationTimeouts.has(id)) {
60
+ clearedConfirmationIds.delete(id);
61
+ }
52
62
  };
53
63
  var clearConfirmationBackgroundTimeout = (id) => {
54
64
  const timeoutId = confirmationTimeouts.get(id);
55
65
  if (timeoutId !== void 0) {
56
66
  clearTimeout(timeoutId);
57
67
  confirmationTimeouts.delete(id);
68
+ clearedConfirmationIds.delete(id);
58
69
  }
59
70
  };
60
71
  var clearAllConfirmationBadges = () => {
@@ -64,6 +75,7 @@ var clearAllConfirmationBadges = () => {
64
75
  });
65
76
  }
66
77
  confirmationTimeouts.clear();
78
+ clearedConfirmationIds.clear();
67
79
  pendingConfirmationCount = 0;
68
80
  updateConfirmationBadge();
69
81
  };
@@ -82,6 +94,8 @@ var initConfirmationBadge = () => {
82
94
  }
83
95
  });
84
96
  };
97
+
98
+ // dist/constants.js
85
99
  var KEEPALIVE_ALARM = "opentabs-keepalive";
86
100
  var KEEPALIVE_INTERVAL_MINUTES = 0.5;
87
101
  var PLUGINS_META_KEY = "plugins_meta";
@@ -109,11 +123,15 @@ var DEFAULT_LOG_LIMIT = 100;
109
123
  var VALID_PLUGIN_NAME = /^[a-z0-9]+(-[a-z0-9]+)*$/;
110
124
  var isValidPluginName = (name) => VALID_PLUGIN_NAME.test(name);
111
125
  var buildWsUrl = (port) => `ws://localhost:${port}/ws`;
126
+
127
+ // dist/json-rpc-errors.js
112
128
  var JSONRPC_METHOD_NOT_FOUND = -32601;
113
129
  var JSONRPC_INVALID_PARAMS = -32602;
114
130
  var JSONRPC_INTERNAL_ERROR = -32603;
115
131
  var JSONRPC_NO_USABLE_TAB = -32001;
116
132
  var JSONRPC_ADAPTER_NOT_READY = -32002;
133
+
134
+ // dist/messaging.js
117
135
  var sendToServer = (data) => {
118
136
  const method = data.method ?? "unknown";
119
137
  chrome.runtime.sendMessage({ type: "ws:send", data }).catch((err2) => {
@@ -147,6 +165,8 @@ var sendTabStateNotification = (pluginName, stateInfo) => {
147
165
  }
148
166
  });
149
167
  };
168
+
169
+ // dist/sanitize-error.js
150
170
  var MAX_LENGTH = 500;
151
171
  var sanitizeErrorMessage = (message) => {
152
172
  let sanitized = message.replace(/[a-z]:[/\\][^\s,;)}\]]+/gi, "[PATH]").replace(/\/[a-z0-9._-]+(?:\/[a-z0-9._-]+)+/gi, "[PATH]").replace(/https?:\/\/[^\s,;)}\]]+/gi, "[URL]").replace(/localhost:\d+/gi, "[LOCALHOST]").replace(/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/g, "[IP]");
@@ -155,7 +175,11 @@ var sanitizeErrorMessage = (message) => {
155
175
  }
156
176
  return sanitized;
157
177
  };
178
+
179
+ // ../shared/dist/error.js
158
180
  var toErrorMessage = (err2) => err2 instanceof Error ? err2.message : String(err2);
181
+
182
+ // ../shared/dist/index.js
159
183
  var BLOCKED_URL_SCHEMES = [
160
184
  "javascript:",
161
185
  "data:",
@@ -172,6 +196,8 @@ var isBlockedUrlScheme = (url) => {
172
196
  return true;
173
197
  }
174
198
  };
199
+
200
+ // dist/browser-commands/helpers.js
175
201
  var requireTabId = (params, id) => {
176
202
  const tabId = params.tabId;
177
203
  if (typeof tabId !== "number") {
@@ -264,6 +290,8 @@ var sendValidationError = (id, message) => {
264
290
  var sendSuccessResult = (id, result) => {
265
291
  sendToServer({ jsonrpc: "2.0", result, id });
266
292
  };
293
+
294
+ // dist/browser-commands/content-commands.js
267
295
  var handleBrowserGetTabContent = async (params, id) => {
268
296
  try {
269
297
  const tabId = requireTabId(params, id);
@@ -402,6 +430,8 @@ var handleBrowserScreenshotTab = async (params, id) => {
402
430
  sendErrorResult(id, err2);
403
431
  }
404
432
  };
433
+
434
+ // dist/browser-commands/cookie-commands.js
405
435
  var handleBrowserGetCookies = async (params, id) => {
406
436
  try {
407
437
  const url = requireUrl(params, id);
@@ -486,6 +516,8 @@ var handleBrowserDeleteCookies = async (params, id) => {
486
516
  sendErrorResult(id, err2);
487
517
  }
488
518
  };
519
+
520
+ // dist/log-collector.js
489
521
  var MAX_MESSAGE_LENGTH = 2e3;
490
522
  var DEFAULT_MAX_ENTRIES = 500;
491
523
  var formatArg = (arg) => {
@@ -572,10 +604,15 @@ var installLogCollector = (source, maxEntries) => {
572
604
  }
573
605
  return collector;
574
606
  };
607
+
608
+ // dist/background-log-state.js
575
609
  var bgLogCollector = installLogCollector("background");
610
+
611
+ // dist/network-capture.js
576
612
  var MAX_BODY_LENGTH = 102400;
577
613
  var PENDING_REQUEST_TTL_MS = 6e4;
578
614
  var PRUNE_INTERVAL_MS = 3e4;
615
+ var WS_TTL_MS = 5 * 6e4;
579
616
  var SENSITIVE_HEADERS = /* @__PURE__ */ new Set([
580
617
  "authorization",
581
618
  "cookie",
@@ -607,6 +644,7 @@ var scrubHeaders = (headers) => {
607
644
  return scrubbed;
608
645
  };
609
646
  var captures = /* @__PURE__ */ new Map();
647
+ var pendingCaptures = /* @__PURE__ */ new Map();
610
648
  var headersToRecord = (raw) => {
611
649
  if (!raw)
612
650
  return void 0;
@@ -674,6 +712,12 @@ chrome.debugger.onEvent.addListener((source, method, params) => {
674
712
  state.requestIdToRequest.delete(id);
675
713
  }
676
714
  }
715
+ for (const [id, createdAt] of state.wsCreatedAt) {
716
+ if (now - createdAt > WS_TTL_MS) {
717
+ state.wsFramesByRequestId.delete(id);
718
+ state.wsCreatedAt.delete(id);
719
+ }
720
+ }
677
721
  const url = stringProp(request, "url", "");
678
722
  if (state.urlFilter && !url.includes(state.urlFilter))
679
723
  return;
@@ -763,6 +807,7 @@ chrome.debugger.onEvent.addListener((source, method, params) => {
763
807
  return;
764
808
  if (requestId) {
765
809
  state.wsFramesByRequestId.set(requestId, url);
810
+ state.wsCreatedAt.set(requestId, Date.now());
766
811
  }
767
812
  const completed = {
768
813
  url,
@@ -796,6 +841,7 @@ chrome.debugger.onEvent.addListener((source, method, params) => {
796
841
  const requestId = paramsRecord?.requestId;
797
842
  if (requestId) {
798
843
  state.wsFramesByRequestId.delete(requestId);
844
+ state.wsCreatedAt.delete(requestId);
799
845
  }
800
846
  } else if (method === "Runtime.consoleAPICalled") {
801
847
  const type = paramsRecord?.type;
@@ -841,48 +887,67 @@ chrome.debugger.onDetach.addListener((source, _reason) => {
841
887
  }
842
888
  });
843
889
  var startCapture = async (tabId, maxRequests = 100, urlFilter, maxConsoleLogs = 500, maxWsFrames = 200) => {
890
+ const inFlightCapture = pendingCaptures.get(tabId);
891
+ if (inFlightCapture) {
892
+ return inFlightCapture;
893
+ }
844
894
  if (captures.has(tabId)) {
845
895
  throw new Error(`Network capture already active for tab ${tabId}. Call stopCapture first.`);
846
896
  }
847
- try {
848
- await chrome.debugger.attach({ tabId }, CDP_VERSION);
849
- } catch (err2) {
850
- throw new Error(`Failed to attach debugger to tab ${tabId}: ${toErrorMessage(err2)}. Another debugger (e.g., DevTools) may already be attached.`);
851
- }
852
- try {
853
- await chrome.debugger.sendCommand({ tabId }, "Network.enable");
854
- await chrome.debugger.sendCommand({ tabId }, "Runtime.enable");
855
- } catch (err2) {
856
- await chrome.debugger.detach({ tabId }).catch(() => {
857
- });
858
- throw err2;
859
- }
860
- const captureState = {
861
- requests: [],
862
- consoleLogs: [],
863
- wsFrames: [],
864
- maxRequests,
865
- maxConsoleLogs,
866
- maxWsFrames,
867
- urlFilter,
868
- pendingRequests: /* @__PURE__ */ new Map(),
869
- requestIdToRequest: /* @__PURE__ */ new Map(),
870
- wsFramesByRequestId: /* @__PURE__ */ new Map()
871
- };
872
- captureState.pruneIntervalId = setInterval(() => {
873
- const now = Date.now();
874
- for (const [id, pending] of captureState.pendingRequests) {
875
- if (pending.timestamp !== void 0 && now - pending.timestamp > PENDING_REQUEST_TTL_MS) {
876
- captureState.pendingRequests.delete(id);
877
- }
897
+ const capturePromise = (async () => {
898
+ try {
899
+ await chrome.debugger.attach({ tabId }, CDP_VERSION);
900
+ } catch (err2) {
901
+ throw new Error(`Failed to attach debugger to tab ${tabId}: ${toErrorMessage(err2)}. Another debugger (e.g., DevTools) may already be attached.`);
878
902
  }
879
- for (const [id, req] of captureState.requestIdToRequest) {
880
- if (now - req.timestamp > PENDING_REQUEST_TTL_MS) {
881
- captureState.requestIdToRequest.delete(id);
903
+ try {
904
+ await chrome.debugger.sendCommand({ tabId }, "Network.enable");
905
+ await chrome.debugger.sendCommand({ tabId }, "Runtime.enable");
906
+ } catch (err2) {
907
+ await chrome.debugger.detach({ tabId }).catch(() => {
908
+ });
909
+ throw err2;
910
+ }
911
+ const captureState = {
912
+ requests: [],
913
+ consoleLogs: [],
914
+ wsFrames: [],
915
+ maxRequests,
916
+ maxConsoleLogs,
917
+ maxWsFrames,
918
+ urlFilter,
919
+ pendingRequests: /* @__PURE__ */ new Map(),
920
+ requestIdToRequest: /* @__PURE__ */ new Map(),
921
+ wsFramesByRequestId: /* @__PURE__ */ new Map(),
922
+ wsCreatedAt: /* @__PURE__ */ new Map()
923
+ };
924
+ captureState.pruneIntervalId = setInterval(() => {
925
+ const now = Date.now();
926
+ for (const [id, pendingReq] of captureState.pendingRequests) {
927
+ if (pendingReq.timestamp !== void 0 && now - pendingReq.timestamp > PENDING_REQUEST_TTL_MS) {
928
+ captureState.pendingRequests.delete(id);
929
+ }
882
930
  }
883
- }
884
- }, PRUNE_INTERVAL_MS);
885
- captures.set(tabId, captureState);
931
+ for (const [id, req] of captureState.requestIdToRequest) {
932
+ if (now - req.timestamp > PENDING_REQUEST_TTL_MS) {
933
+ captureState.requestIdToRequest.delete(id);
934
+ }
935
+ }
936
+ for (const [id, createdAt] of captureState.wsCreatedAt) {
937
+ if (now - createdAt > WS_TTL_MS) {
938
+ captureState.wsFramesByRequestId.delete(id);
939
+ captureState.wsCreatedAt.delete(id);
940
+ }
941
+ }
942
+ }, PRUNE_INTERVAL_MS);
943
+ captures.set(tabId, captureState);
944
+ })();
945
+ pendingCaptures.set(tabId, capturePromise);
946
+ try {
947
+ await capturePromise;
948
+ } finally {
949
+ pendingCaptures.delete(tabId);
950
+ }
886
951
  };
887
952
  var stopCapture = (tabId) => {
888
953
  const state = captures.get(tabId);
@@ -890,6 +955,7 @@ var stopCapture = (tabId) => {
890
955
  return;
891
956
  clearInterval(state.pruneIntervalId);
892
957
  state.wsFramesByRequestId.clear();
958
+ state.wsCreatedAt.clear();
893
959
  void chrome.debugger.detach({ tabId }).catch(() => {
894
960
  });
895
961
  captures.delete(tabId);
@@ -906,7 +972,6 @@ var getRequests = (tabId, clear = false) => {
906
972
  if (clear) {
907
973
  state.requests = [];
908
974
  state.requestIdToRequest.clear();
909
- state.pendingRequests.clear();
910
975
  }
911
976
  return requests;
912
977
  };
@@ -937,7 +1002,6 @@ var getWsFrames = (tabId, clear = false) => {
937
1002
  const frames = [...state.wsFrames];
938
1003
  if (clear) {
939
1004
  state.wsFrames = [];
940
- state.wsFramesByRequestId.clear();
941
1005
  }
942
1006
  return frames;
943
1007
  };
@@ -946,6 +1010,8 @@ var getActiveCapturesSummary = () => Array.from(captures.entries()).map(([tabId,
946
1010
  requestCount: state.requests.length,
947
1011
  isCapturing: true
948
1012
  }));
1013
+
1014
+ // dist/plugin-storage.js
949
1015
  var metaCache = null;
950
1016
  var writeMutex = Promise.resolve();
951
1017
  var serialize = (fn) => {
@@ -1022,6 +1088,8 @@ var getPluginMeta = async (pluginName) => {
1022
1088
  var invalidatePluginCache = () => {
1023
1089
  metaCache = null;
1024
1090
  };
1091
+
1092
+ // dist/tab-matching.js
1025
1093
  var urlMatchesPatterns = (url, patterns) => {
1026
1094
  for (const pattern of patterns) {
1027
1095
  if (matchPattern(url, pattern))
@@ -1112,6 +1180,8 @@ var findAllMatchingTabs = async (plugin) => {
1112
1180
  };
1113
1181
  return allMatches.slice().sort((a, b) => rank(b) - rank(a));
1114
1182
  };
1183
+
1184
+ // dist/tab-state.js
1115
1185
  var lastKnownState = /* @__PURE__ */ new Map();
1116
1186
  var pluginLocks = /* @__PURE__ */ new Map();
1117
1187
  var withPluginLock = (pluginName, fn) => {
@@ -1292,6 +1362,8 @@ var checkTabChanged = async (changedTabId, changeInfo) => {
1292
1362
  return;
1293
1363
  await notifyAffectedPlugins(affectedPlugins);
1294
1364
  };
1365
+
1366
+ // dist/browser-commands/extension-commands.js
1295
1367
  var handleExtensionGetState = async (id) => {
1296
1368
  try {
1297
1369
  const sessionData = await chrome.storage.session.get(WS_CONNECTED_KEY).catch(() => ({}));
@@ -1551,7 +1623,11 @@ var handleBrowserExecuteScript = async (params, id) => {
1551
1623
  sendValidationError(id, "Invalid execFile format");
1552
1624
  return;
1553
1625
  }
1626
+ const execUuid = execFile.replace(/^__exec-/, "").replace(/\.js$/, "");
1627
+ const resultKey = `__execResult_${execUuid}`;
1628
+ const asyncKey = `__execAsync_${execUuid}`;
1554
1629
  let timeoutId;
1630
+ const cancelled = { value: false };
1555
1631
  const injectPromise = (async () => {
1556
1632
  await chrome.scripting.executeScript({
1557
1633
  target: { tabId },
@@ -1560,15 +1636,17 @@ var handleBrowserExecuteScript = async (params, id) => {
1560
1636
  });
1561
1637
  let elapsed = 0;
1562
1638
  while (elapsed <= EXEC_MAX_ASYNC_WAIT_MS) {
1639
+ if (cancelled.value)
1640
+ return;
1563
1641
  const results = await chrome.scripting.executeScript({
1564
1642
  target: { tabId },
1565
1643
  world: "MAIN",
1566
- func: (truncLimit) => {
1644
+ func: (truncLimit, rKey, aKey) => {
1567
1645
  const ot = globalThis.__openTabs;
1568
1646
  if (!ot)
1569
1647
  return { pending: false, result: { error: "__openTabs not found" } };
1570
- const result2 = ot.__lastExecResult;
1571
- const isAsync = ot.__lastExecAsync === true;
1648
+ const result2 = ot[rKey];
1649
+ const isAsync = ot[aKey] === true;
1572
1650
  if (result2 && ("value" in result2 || "error" in result2)) {
1573
1651
  const captured = { ...result2 };
1574
1652
  if (captured.value === void 0)
@@ -1581,15 +1659,15 @@ var handleBrowserExecuteScript = async (params, id) => {
1581
1659
  captured.value = String(captured.value);
1582
1660
  }
1583
1661
  }
1584
- delete ot.__lastExecResult;
1585
- delete ot.__lastExecAsync;
1662
+ Reflect.deleteProperty(ot, rKey);
1663
+ Reflect.deleteProperty(ot, aKey);
1586
1664
  return { pending: false, result: captured };
1587
1665
  }
1588
1666
  if (isAsync)
1589
1667
  return { pending: true };
1590
1668
  return { pending: false, result: { error: "No result captured" } };
1591
1669
  },
1592
- args: [EXEC_RESULT_TRUNCATION_LIMIT]
1670
+ args: [EXEC_RESULT_TRUNCATION_LIMIT, resultKey, asyncKey]
1593
1671
  });
1594
1672
  const first = results[0];
1595
1673
  const data = first?.result;
@@ -1599,18 +1677,21 @@ var handleBrowserExecuteScript = async (params, id) => {
1599
1677
  await new Promise((resolve) => setTimeout(resolve, EXEC_POLL_INTERVAL_MS));
1600
1678
  elapsed += EXEC_POLL_INTERVAL_MS;
1601
1679
  }
1602
- await chrome.scripting.executeScript({
1603
- target: { tabId },
1604
- world: "MAIN",
1605
- func: () => {
1606
- const ot = globalThis.__openTabs;
1607
- if (ot) {
1608
- delete ot.__lastExecResult;
1609
- delete ot.__lastExecAsync;
1610
- }
1611
- }
1612
- }).catch(() => {
1613
- });
1680
+ if (!cancelled.value) {
1681
+ await chrome.scripting.executeScript({
1682
+ target: { tabId },
1683
+ world: "MAIN",
1684
+ func: (rKey, aKey) => {
1685
+ const ot = globalThis.__openTabs;
1686
+ if (ot) {
1687
+ Reflect.deleteProperty(ot, rKey);
1688
+ Reflect.deleteProperty(ot, aKey);
1689
+ }
1690
+ },
1691
+ args: [resultKey, asyncKey]
1692
+ }).catch(() => {
1693
+ });
1694
+ }
1614
1695
  return { value: { error: `Async code did not resolve within ${EXEC_MAX_ASYNC_WAIT_MS}ms` } };
1615
1696
  })();
1616
1697
  const timeoutPromise = new Promise((_resolve, reject) => {
@@ -1623,12 +1704,15 @@ var handleBrowserExecuteScript = async (params, id) => {
1623
1704
  result = await Promise.race([injectPromise, timeoutPromise]);
1624
1705
  } finally {
1625
1706
  clearTimeout(timeoutId);
1707
+ cancelled.value = true;
1626
1708
  }
1627
1709
  sendSuccessResult(id, result);
1628
1710
  } catch (err2) {
1629
1711
  sendErrorResult(id, err2);
1630
1712
  }
1631
1713
  };
1714
+
1715
+ // dist/browser-commands/resource-commands.js
1632
1716
  var TEXT_MIME_PREFIXES = ["text/"];
1633
1717
  var TEXT_MIME_EXACT = /* @__PURE__ */ new Set([
1634
1718
  "application/javascript",
@@ -1754,6 +1838,8 @@ var handleBrowserGetResourceContent = async (params, id) => {
1754
1838
  sendErrorResult(id, err2);
1755
1839
  }
1756
1840
  };
1841
+
1842
+ // dist/browser-commands/interaction-commands.js
1757
1843
  var handleBrowserClickElement = async (params, id) => {
1758
1844
  try {
1759
1845
  const tabId = requireTabId(params, id);
@@ -1925,25 +2011,30 @@ var handleBrowserWaitForElement = async (params, id) => {
1925
2011
  func: (sel, tmo, vis, maxPreview, pollMs) => new Promise((resolve) => {
1926
2012
  let elapsed = 0;
1927
2013
  const poll = setInterval(() => {
1928
- const el = document.querySelector(sel);
1929
- if (el) {
1930
- const htmlEl = el;
1931
- const style = getComputedStyle(htmlEl);
1932
- const isVisible = !vis || style.display !== "none" && style.visibility !== "hidden" && (htmlEl.offsetParent !== null || style.position === "fixed" || style.position === "sticky");
1933
- if (isVisible) {
2014
+ try {
2015
+ const el = document.querySelector(sel);
2016
+ if (el) {
2017
+ const htmlEl = el;
2018
+ const style = getComputedStyle(htmlEl);
2019
+ const isVisible = !vis || style.display !== "none" && style.visibility !== "hidden" && (htmlEl.offsetParent !== null || style.position === "fixed" || style.position === "sticky");
2020
+ if (isVisible) {
2021
+ clearInterval(poll);
2022
+ resolve({
2023
+ found: true,
2024
+ tagName: el.tagName.toLowerCase(),
2025
+ text: (el.textContent || "").trim().slice(0, maxPreview)
2026
+ });
2027
+ return;
2028
+ }
2029
+ }
2030
+ elapsed += pollMs;
2031
+ if (elapsed >= tmo) {
1934
2032
  clearInterval(poll);
1935
- resolve({
1936
- found: true,
1937
- tagName: el.tagName.toLowerCase(),
1938
- text: (el.textContent || "").trim().slice(0, maxPreview)
1939
- });
1940
- return;
2033
+ resolve({ error: `Timeout waiting for element: ${sel} (${tmo}ms)` });
1941
2034
  }
1942
- }
1943
- elapsed += pollMs;
1944
- if (elapsed >= tmo) {
2035
+ } catch (err2) {
1945
2036
  clearInterval(poll);
1946
- resolve({ error: `Timeout waiting for element: ${sel} (${tmo}ms)` });
2037
+ resolve({ error: `Error checking element: ${err2 instanceof Error ? err2.message : String(err2)}` });
1947
2038
  }
1948
2039
  }, pollMs);
1949
2040
  }),
@@ -2074,6 +2165,8 @@ var handleBrowserHandleDialog = async (params, id) => {
2074
2165
  sendErrorResult(id, err2);
2075
2166
  }
2076
2167
  };
2168
+
2169
+ // dist/browser-commands/key-press-command.js
2077
2170
  var handleBrowserPressKey = async (params, id) => {
2078
2171
  try {
2079
2172
  const tabId = requireTabId(params, id);
@@ -2205,6 +2298,8 @@ var handleBrowserPressKey = async (params, id) => {
2205
2298
  sendErrorResult(id, err2);
2206
2299
  }
2207
2300
  };
2301
+
2302
+ // dist/browser-commands/scroll-command.js
2208
2303
  var handleBrowserScroll = async (params, id) => {
2209
2304
  try {
2210
2305
  const tabId = requireTabId(params, id);
@@ -2318,6 +2413,8 @@ var handleBrowserScroll = async (params, id) => {
2318
2413
  sendErrorResult(id, err2);
2319
2414
  }
2320
2415
  };
2416
+
2417
+ // dist/browser-commands/network-commands.js
2321
2418
  var handleBrowserEnableNetworkCapture = async (params, id) => {
2322
2419
  try {
2323
2420
  const tabId = requireTabId(params, id);
@@ -2392,6 +2489,8 @@ var handleBrowserGetWebSocketFrames = (params, id) => {
2392
2489
  sendErrorResult(id, err2);
2393
2490
  }
2394
2491
  };
2492
+
2493
+ // dist/browser-commands/tab-commands.js
2395
2494
  var handleBrowserListTabs = async (id) => {
2396
2495
  try {
2397
2496
  const tabs = await chrome.tabs.query({});
@@ -2479,6 +2578,8 @@ var handleBrowserGetTabInfo = async (params, id) => {
2479
2578
  sendErrorResult(id, err2);
2480
2579
  }
2481
2580
  };
2581
+
2582
+ // dist/iife-injection.js
2482
2583
  var RESERVED_NAMES = /* @__PURE__ */ new Set(["system", "browser", "opentabs", "extension", "config", "plugin", "tool", "mcp"]);
2483
2584
  var isSafePluginName = (name) => isValidPluginName(name) && !RESERVED_NAMES.has(name);
2484
2585
  var isAdapterPresent = async (tabId, pluginName) => {
@@ -2790,6 +2891,8 @@ var reinjectStoredPlugins = async () => {
2790
2891
  }
2791
2892
  }
2792
2893
  };
2894
+
2895
+ // dist/rate-limiter.js
2793
2896
  var METHOD_LIMITS = /* @__PURE__ */ new Map([
2794
2897
  // Expensive operations — tight limits
2795
2898
  ["browser.screenshotTab", { maxRequests: 2, windowMs: 1e3 }],
@@ -2818,6 +2921,8 @@ var checkRateLimit = (method, now = Date.now()) => {
2818
2921
  methodTimestamps.set(method, timestamps);
2819
2922
  return true;
2820
2923
  };
2924
+
2925
+ // dist/dispatch-helpers.js
2821
2926
  var isAdapterNotReady = (result) => result.type === "error" && result.code === JSONRPC_ADAPTER_NOT_READY;
2822
2927
  var resolvePlugin = async (pluginName, id) => {
2823
2928
  const plugin = await getPluginMeta(pluginName);
@@ -2924,6 +3029,8 @@ var dispatchWithTabFallback = async (config) => {
2924
3029
  });
2925
3030
  }
2926
3031
  };
3032
+
3033
+ // dist/resource-prompt-dispatch.js
2927
3034
  var executeResourceReadOnTab = async (tabId, pluginName, resourceUri) => {
2928
3035
  const scriptPromise = chrome.scripting.executeScript({
2929
3036
  target: { tabId },
@@ -3090,6 +3197,8 @@ var handlePromptGet = async (params, id) => {
3090
3197
  executeOnTab: (tabId) => executePromptGetOnTab(tabId, pluginName, promptName, promptArgs)
3091
3198
  });
3092
3199
  };
3200
+
3201
+ // dist/tool-dispatch.js
3093
3202
  var progressCallbacks = /* @__PURE__ */ new Map();
3094
3203
  var notifyDispatchProgress = (dispatchId) => {
3095
3204
  const cb = progressCallbacks.get(dispatchId);
@@ -3347,6 +3456,8 @@ var handleToolDispatch = async (params, id) => {
3347
3456
  }
3348
3457
  });
3349
3458
  };
3459
+
3460
+ // dist/message-router.js
3350
3461
  var wrapAsync = (method, fn) => (params, id) => {
3351
3462
  if (id !== void 0) {
3352
3463
  fn(params, id).catch((err2) => console.warn(`[opentabs] ${method} handler failed:`, err2));
@@ -3613,6 +3724,8 @@ var handleServerMessage = (message) => {
3613
3724
  }
3614
3725
  };
3615
3726
  var methodHandlerNames = Array.from(methodHandlers.keys());
3727
+
3728
+ // dist/background-message-handlers.js
3616
3729
  var wsConnected = false;
3617
3730
  var lastDisconnectReason;
3618
3731
  var restoreWsConnectedState = () => {
@@ -3639,7 +3752,6 @@ var handleOffscreenGetUrl = (_message, sendResponse) => {
3639
3752
  });
3640
3753
  };
3641
3754
  var handleWsState = (message, sendResponse) => {
3642
- const wasConnected = wsConnected;
3643
3755
  const nowConnected = message.connected;
3644
3756
  persistWsConnected(nowConnected);
3645
3757
  lastDisconnectReason = nowConnected ? void 0 : message.disconnectReason;
@@ -3650,7 +3762,7 @@ var handleWsState = (message, sendResponse) => {
3650
3762
  disconnectReason: lastDisconnectReason
3651
3763
  }
3652
3764
  });
3653
- if (!nowConnected && wasConnected) {
3765
+ if (!nowConnected) {
3654
3766
  clearTabStateCache();
3655
3767
  clearAllConfirmationBadges();
3656
3768
  }
@@ -3761,7 +3873,10 @@ var EXTENSION_ONLY_TYPES = /* @__PURE__ */ new Set([
3761
3873
  "ws:message",
3762
3874
  "bg:send",
3763
3875
  "bg:getConnectionState",
3764
- "offscreen:getLogs"
3876
+ "offscreen:getLogs",
3877
+ "sp:confirmationResponse",
3878
+ "sp:confirmationTimeout",
3879
+ "port-changed"
3765
3880
  ]);
3766
3881
  var initBackgroundMessageHandlers = () => {
3767
3882
  chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
@@ -3778,6 +3893,8 @@ var initBackgroundMessageHandlers = () => {
3778
3893
  });
3779
3894
  };
3780
3895
  var backgroundHandlerNames = [...backgroundHandlers.keys()];
3896
+
3897
+ // dist/side-panel-toggle.js
3781
3898
  var openWindows = /* @__PURE__ */ new Set();
3782
3899
  var initSidePanelToggle = () => {
3783
3900
  chrome.sidePanel.setPanelBehavior({ openPanelOnActionClick: false }).catch(() => {
@@ -3814,6 +3931,8 @@ var initSidePanelToggle = () => {
3814
3931
  })();
3815
3932
  });
3816
3933
  };
3934
+
3935
+ // dist/background.js
3817
3936
  initSidePanelToggle();
3818
3937
  restoreWsConnectedState();
3819
3938
  var creatingOffscreen = null;
@@ -5,6 +5,8 @@ export declare const handleExtensionCheckAdapter: (params: Record<string, unknow
5
5
  export declare const handleExtensionForceReconnect: (id: string | number) => Promise<void>;
6
6
  /**
7
7
  * Executes a pre-written JavaScript file in a tab's MAIN world, supporting both sync and async code.
8
+ * Each execution uses namespaced keys (`__execResult_<uuid>`, `__execAsync_<uuid>`) on
9
+ * `globalThis.__openTabs` so concurrent executions on the same tab do not collide.
8
10
  * @param params - Expects `{ tabId: number, execFile: string }` where execFile matches the `__exec-<uuid>.js` pattern.
9
11
  * @returns The script's return value, serialized as JSON. Async scripts are polled until resolved or timed out.
10
12
  */
@@ -1 +1 @@
1
- {"version":3,"file":"extension-commands.d.ts","sourceRoot":"","sources":["../../src/browser-commands/extension-commands.ts"],"names":[],"mappings":"AA6BA,eAAO,MAAM,uBAAuB,GAAU,IAAI,MAAM,GAAG,MAAM,KAAG,OAAO,CAAC,IAAI,CAqD/E,CAAC;AAEF,eAAO,MAAM,sBAAsB,GAAU,QAAQ,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,MAAM,GAAG,MAAM,KAAG,OAAO,CAAC,IAAI,CA8D/G,CAAC;AAEF,eAAO,MAAM,2BAA2B,GAAU,IAAI,MAAM,GAAG,MAAM,KAAG,OAAO,CAAC,IAAI,CA0BnF,CAAC;AAEF,eAAO,MAAM,2BAA2B,GACtC,QAAQ,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,IAAI,MAAM,GAAG,MAAM,KAClB,OAAO,CAAC,IAAI,CAkJd,CAAC;AAEF,eAAO,MAAM,6BAA6B,GAAU,IAAI,MAAM,GAAG,MAAM,KAAG,OAAO,CAAC,IAAI,CAiBrF,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,0BAA0B,GACrC,QAAQ,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,IAAI,MAAM,GAAG,MAAM,KAClB,OAAO,CAAC,IAAI,CAwHd,CAAC"}
1
+ {"version":3,"file":"extension-commands.d.ts","sourceRoot":"","sources":["../../src/browser-commands/extension-commands.ts"],"names":[],"mappings":"AA6BA,eAAO,MAAM,uBAAuB,GAAU,IAAI,MAAM,GAAG,MAAM,KAAG,OAAO,CAAC,IAAI,CAqD/E,CAAC;AAEF,eAAO,MAAM,sBAAsB,GAAU,QAAQ,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,MAAM,GAAG,MAAM,KAAG,OAAO,CAAC,IAAI,CA8D/G,CAAC;AAEF,eAAO,MAAM,2BAA2B,GAAU,IAAI,MAAM,GAAG,MAAM,KAAG,OAAO,CAAC,IAAI,CA0BnF,CAAC;AAEF,eAAO,MAAM,2BAA2B,GACtC,QAAQ,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,IAAI,MAAM,GAAG,MAAM,KAClB,OAAO,CAAC,IAAI,CAkJd,CAAC;AAEF,eAAO,MAAM,6BAA6B,GAAU,IAAI,MAAM,GAAG,MAAM,KAAG,OAAO,CAAC,IAAI,CAiBrF,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,0BAA0B,GACrC,QAAQ,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,IAAI,MAAM,GAAG,MAAM,KAClB,OAAO,CAAC,IAAI,CA+Hd,CAAC"}