bb-browser 0.1.2 → 0.2.2

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,8 +1,20 @@
1
- const DAEMON_PORT = 19824;
2
- const DAEMON_HOST = "localhost";
3
- const DAEMON_BASE_URL = `http://${DAEMON_HOST}:${DAEMON_PORT}`;
1
+ const DEFAULT_DAEMON_PORT = 19824;
2
+ const DEFAULT_DAEMON_HOST = "localhost";
3
+ const DEFAULT_DAEMON_BASE_URL = `http://${DEFAULT_DAEMON_HOST}:${DEFAULT_DAEMON_PORT}`;
4
4
  const SSE_RECONNECT_DELAY = 3e3;
5
5
  const SSE_MAX_RECONNECT_ATTEMPTS = 5;
6
+ const STORAGE_KEY = "upstreamUrl";
7
+ async function getUpstreamUrl() {
8
+ try {
9
+ const result = await chrome.storage.sync.get(STORAGE_KEY);
10
+ const url = result[STORAGE_KEY];
11
+ if (url && typeof url === "string" && url.trim()) {
12
+ return url.trim().replace(/\/+$/, "");
13
+ }
14
+ } catch {
15
+ }
16
+ return DEFAULT_DAEMON_BASE_URL;
17
+ }
6
18
 
7
19
  class SSEClient {
8
20
  constructor() {
@@ -19,7 +31,8 @@ class SSEClient {
19
31
  console.warn("[SSEClient] Already connected");
20
32
  return;
21
33
  }
22
- const sseUrl = `${DAEMON_BASE_URL}/sse`;
34
+ const baseUrl = await getUpstreamUrl();
35
+ const sseUrl = `${baseUrl}/sse`;
23
36
  console.log("[SSEClient] Connecting to:", sseUrl);
24
37
  this.abortController = new AbortController();
25
38
  try {
@@ -81,7 +94,9 @@ class SSEClient {
81
94
  data = trimmedLine.substring(5).trim();
82
95
  } else if (trimmedLine === "") {
83
96
  if (event && data) {
84
- await this.handleMessage(event, data);
97
+ this.handleMessage(event, data).catch(
98
+ (err) => console.error("[SSEClient] handleMessage error:", err)
99
+ );
85
100
  event = "";
86
101
  data = "";
87
102
  }
@@ -172,7 +187,8 @@ class SSEClient {
172
187
  }
173
188
 
174
189
  async function sendResult(result) {
175
- const url = `${DAEMON_BASE_URL}/result`;
190
+ const baseUrl = await getUpstreamUrl();
191
+ const url = `${baseUrl}/result`;
176
192
  console.log("[APIClient] Sending result:", result.id, result.success);
177
193
  try {
178
194
  const response = await fetch(url, {
@@ -424,15 +440,15 @@ function initEventListeners() {
424
440
  });
425
441
  chrome.debugger.onDetach.addListener((source) => {
426
442
  if (source.tabId) {
427
- cleanupTab(source.tabId);
443
+ cleanupTab$2(source.tabId);
428
444
  console.log("[CDPService] Debugger detached from tab:", source.tabId);
429
445
  }
430
446
  });
431
447
  chrome.tabs.onRemoved.addListener((tabId) => {
432
- cleanupTab(tabId);
448
+ cleanupTab$2(tabId);
433
449
  });
434
450
  }
435
- function cleanupTab(tabId) {
451
+ function cleanupTab$2(tabId) {
436
452
  attachedTabs.delete(tabId);
437
453
  pendingDialogs.delete(tabId);
438
454
  networkRequests.delete(tabId);
@@ -584,13 +600,19 @@ function handleException(tabId, params) {
584
600
  jsErrors.set(tabId, errors);
585
601
  }
586
602
 
603
+ const tabSnapshotRefs$1 = /* @__PURE__ */ new Map();
604
+ const tabActiveFrameId$2 = /* @__PURE__ */ new Map();
605
+ function cleanupTab$1(tabId) {
606
+ tabSnapshotRefs$1.delete(tabId);
607
+ tabActiveFrameId$2.delete(tabId);
608
+ }
587
609
  async function getSnapshot$1(tabId, options = {}) {
588
610
  const { interactive = false } = options;
589
611
  console.log("[DOMService] Getting snapshot for tab:", tabId, { interactive });
590
612
  await injectBuildDomTreeScript(tabId);
591
613
  const domTreeResult = await executeBuildDomTree(tabId);
592
614
  const snapshotResult = interactive ? convertToAccessibilityTree(domTreeResult) : convertToFullTree(domTreeResult);
593
- snapshotResult.refs;
615
+ tabSnapshotRefs$1.set(tabId, snapshotResult.refs);
594
616
  console.log("[DOMService] Snapshot complete:", {
595
617
  mode: interactive ? "interactive" : "full",
596
618
  linesCount: snapshotResult.snapshot.split("\n").length,
@@ -599,6 +621,10 @@ async function getSnapshot$1(tabId, options = {}) {
599
621
  return snapshotResult;
600
622
  }
601
623
  function getFrameTarget(tabId) {
624
+ const frameId = tabActiveFrameId$2.get(tabId) ?? null;
625
+ if (frameId !== null) {
626
+ return { tabId, frameIds: [frameId] };
627
+ }
602
628
  return { tabId };
603
629
  }
604
630
  async function injectBuildDomTreeScript(tabId) {
@@ -883,22 +909,29 @@ function convertToFullTree(result) {
883
909
  return { snapshot: lines.join("\n"), refs };
884
910
  }
885
911
 
886
- let lastSnapshotRefs = {};
912
+ const tabSnapshotRefs = /* @__PURE__ */ new Map();
913
+ const tabActiveFrameId$1 = /* @__PURE__ */ new Map();
887
914
  async function loadRefsFromStorage() {
888
915
  try {
889
- const result = await chrome.storage.session.get("snapshotRefs");
890
- if (result.snapshotRefs) {
891
- lastSnapshotRefs = result.snapshotRefs;
892
- console.log("[CDPDOMService] Loaded refs from storage:", Object.keys(lastSnapshotRefs).length);
916
+ const result = await chrome.storage.session.get("tabSnapshotRefs");
917
+ if (result.tabSnapshotRefs) {
918
+ const stored = result.tabSnapshotRefs;
919
+ for (const [tabIdStr, refs] of Object.entries(stored)) {
920
+ tabSnapshotRefs.set(Number(tabIdStr), refs);
921
+ }
922
+ console.log("[CDPDOMService] Loaded refs from storage:", tabSnapshotRefs.size, "tabs");
893
923
  }
894
924
  } catch (e) {
895
925
  console.warn("[CDPDOMService] Failed to load refs from storage:", e);
896
926
  }
897
927
  }
898
- async function saveRefsToStorage(refs) {
928
+ async function saveRefsToStorage(tabId, refs) {
899
929
  try {
900
- await chrome.storage.session.set({ snapshotRefs: refs });
901
- console.log("[CDPDOMService] Saved refs to storage:", Object.keys(refs).length);
930
+ const result = await chrome.storage.session.get("tabSnapshotRefs");
931
+ const stored = result.tabSnapshotRefs || {};
932
+ stored[String(tabId)] = refs;
933
+ await chrome.storage.session.set({ tabSnapshotRefs: stored });
934
+ console.log("[CDPDOMService] Saved refs to storage for tab:", tabId, Object.keys(refs).length);
902
935
  } catch (e) {
903
936
  console.warn("[CDPDOMService] Failed to save refs to storage:", e);
904
937
  }
@@ -916,8 +949,8 @@ async function getSnapshot(tabId, options = {}) {
916
949
  tagName: refInfo.tagName
917
950
  };
918
951
  }
919
- lastSnapshotRefs = convertedRefs;
920
- await saveRefsToStorage(convertedRefs);
952
+ tabSnapshotRefs.set(tabId, convertedRefs);
953
+ await saveRefsToStorage(tabId, convertedRefs);
921
954
  console.log("[CDPDOMService] Snapshot complete:", {
922
955
  linesCount: result.snapshot.split("\n").length,
923
956
  refsCount: Object.keys(convertedRefs).length
@@ -927,15 +960,24 @@ async function getSnapshot(tabId, options = {}) {
927
960
  refs: convertedRefs
928
961
  };
929
962
  }
930
- async function getRefInfo(ref) {
963
+ async function getRefInfo(tabId, ref) {
931
964
  const refId = ref.startsWith("@") ? ref.slice(1) : ref;
932
- if (lastSnapshotRefs[refId]) {
933
- return lastSnapshotRefs[refId];
965
+ const refs = tabSnapshotRefs.get(tabId);
966
+ if (refs?.[refId]) {
967
+ return refs[refId];
934
968
  }
935
- if (Object.keys(lastSnapshotRefs).length === 0) {
969
+ if (!tabSnapshotRefs.has(tabId)) {
936
970
  await loadRefsFromStorage();
971
+ const loaded = tabSnapshotRefs.get(tabId);
972
+ if (loaded?.[refId]) {
973
+ return loaded[refId];
974
+ }
937
975
  }
938
- return lastSnapshotRefs[refId] || null;
976
+ return null;
977
+ }
978
+ function cleanupTab(tabId) {
979
+ tabSnapshotRefs.delete(tabId);
980
+ tabActiveFrameId$1.delete(tabId);
939
981
  }
940
982
  async function getElementCenterByXPath(tabId, xpath) {
941
983
  const result = await evaluate(tabId, `
@@ -966,7 +1008,7 @@ async function getElementCenterByXPath(tabId, xpath) {
966
1008
  return result;
967
1009
  }
968
1010
  async function clickElement(tabId, ref) {
969
- const refInfo = await getRefInfo(ref);
1011
+ const refInfo = await getRefInfo(tabId, ref);
970
1012
  if (!refInfo) {
971
1013
  throw new Error(`Ref "${ref}" not found. Run snapshot first to get available refs.`);
972
1014
  }
@@ -977,7 +1019,7 @@ async function clickElement(tabId, ref) {
977
1019
  return { role, name };
978
1020
  }
979
1021
  async function hoverElement(tabId, ref) {
980
- const refInfo = await getRefInfo(ref);
1022
+ const refInfo = await getRefInfo(tabId, ref);
981
1023
  if (!refInfo) {
982
1024
  throw new Error(`Ref "${ref}" not found. Run snapshot first to get available refs.`);
983
1025
  }
@@ -988,7 +1030,7 @@ async function hoverElement(tabId, ref) {
988
1030
  return { role, name };
989
1031
  }
990
1032
  async function fillElement(tabId, ref, text) {
991
- const refInfo = await getRefInfo(ref);
1033
+ const refInfo = await getRefInfo(tabId, ref);
992
1034
  if (!refInfo) {
993
1035
  throw new Error(`Ref "${ref}" not found. Run snapshot first to get available refs.`);
994
1036
  }
@@ -1021,7 +1063,7 @@ async function fillElement(tabId, ref, text) {
1021
1063
  return { role, name };
1022
1064
  }
1023
1065
  async function typeElement(tabId, ref, text) {
1024
- const refInfo = await getRefInfo(ref);
1066
+ const refInfo = await getRefInfo(tabId, ref);
1025
1067
  if (!refInfo) {
1026
1068
  throw new Error(`Ref "${ref}" not found. Run snapshot first to get available refs.`);
1027
1069
  }
@@ -1049,7 +1091,7 @@ async function typeElement(tabId, ref, text) {
1049
1091
  return { role, name };
1050
1092
  }
1051
1093
  async function getElementText(tabId, ref) {
1052
- const refInfo = await getRefInfo(ref);
1094
+ const refInfo = await getRefInfo(tabId, ref);
1053
1095
  if (!refInfo) {
1054
1096
  throw new Error(`Ref "${ref}" not found. Run snapshot first to get available refs.`);
1055
1097
  }
@@ -1072,7 +1114,7 @@ async function getElementText(tabId, ref) {
1072
1114
  return text;
1073
1115
  }
1074
1116
  async function checkElement(tabId, ref) {
1075
- const refInfo = await getRefInfo(ref);
1117
+ const refInfo = await getRefInfo(tabId, ref);
1076
1118
  if (!refInfo) {
1077
1119
  throw new Error(`Ref "${ref}" not found. Run snapshot first to get available refs.`);
1078
1120
  }
@@ -1103,7 +1145,7 @@ async function checkElement(tabId, ref) {
1103
1145
  return { role, name, wasAlreadyChecked: result };
1104
1146
  }
1105
1147
  async function uncheckElement(tabId, ref) {
1106
- const refInfo = await getRefInfo(ref);
1148
+ const refInfo = await getRefInfo(tabId, ref);
1107
1149
  if (!refInfo) {
1108
1150
  throw new Error(`Ref "${ref}" not found. Run snapshot first to get available refs.`);
1109
1151
  }
@@ -1134,7 +1176,7 @@ async function uncheckElement(tabId, ref) {
1134
1176
  return { role, name, wasAlreadyUnchecked: result };
1135
1177
  }
1136
1178
  async function selectOption(tabId, ref, value) {
1137
- const refInfo = await getRefInfo(ref);
1179
+ const refInfo = await getRefInfo(tabId, ref);
1138
1180
  if (!refInfo) {
1139
1181
  throw new Error(`Ref "${ref}" not found. Run snapshot first to get available refs.`);
1140
1182
  }
@@ -1191,7 +1233,7 @@ async function selectOption(tabId, ref, value) {
1191
1233
  return { role, name, selectedValue, selectedLabel };
1192
1234
  }
1193
1235
  async function waitForElement(tabId, ref, maxWait = 1e4, interval = 200) {
1194
- const refInfo = await getRefInfo(ref);
1236
+ const refInfo = await getRefInfo(tabId, ref);
1195
1237
  if (!refInfo) {
1196
1238
  throw new Error(`Ref "${ref}" not found. Run snapshot first to get available refs.`);
1197
1239
  }
@@ -1219,8 +1261,9 @@ async function waitForElement(tabId, ref, maxWait = 1e4, interval = 200) {
1219
1261
  }
1220
1262
  throw new Error(`Timeout waiting for element @${ref} after ${maxWait}ms`);
1221
1263
  }
1222
- function setActiveFrameId(frameId) {
1223
- console.log("[CDPDOMService] Active frame changed:", frameId ?? "main");
1264
+ function setActiveFrameId(tabId, frameId) {
1265
+ tabActiveFrameId$1.set(tabId, frameId);
1266
+ console.log("[CDPDOMService] Active frame changed:", { tabId, frameId: frameId ?? "main" });
1224
1267
  }
1225
1268
  async function pressKey(tabId, key, modifiers = []) {
1226
1269
  let modifierFlags = 0;
@@ -1365,7 +1408,17 @@ chrome.tabs.onUpdated.addListener(async (tabId, changeInfo, _tab) => {
1365
1408
  console.log("[TraceService] Initialized");
1366
1409
 
1367
1410
  initEventListeners();
1368
- let activeFrameId = null;
1411
+ const tabActiveFrameId = /* @__PURE__ */ new Map();
1412
+ async function resolveTab(command) {
1413
+ if (command.tabId !== void 0 && typeof command.tabId === "number") {
1414
+ return chrome.tabs.get(command.tabId);
1415
+ }
1416
+ const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
1417
+ if (!tab?.id) {
1418
+ throw new Error("No active tab found");
1419
+ }
1420
+ return tab;
1421
+ }
1369
1422
  async function handleCommand(command) {
1370
1423
  console.log("[CommandHandler] Processing command:", command.id, command.action);
1371
1424
  let result;
@@ -1530,8 +1583,8 @@ async function handleOpen(command) {
1530
1583
  };
1531
1584
  }
1532
1585
  async function handleSnapshot(command) {
1533
- const [activeTab] = await chrome.tabs.query({ active: true, currentWindow: true });
1534
- if (!activeTab || !activeTab.id) {
1586
+ const activeTab = await resolveTab(command);
1587
+ if (!activeTab.id) {
1535
1588
  return {
1536
1589
  id: command.id,
1537
1590
  success: false,
@@ -1577,8 +1630,8 @@ async function handleClick(command) {
1577
1630
  error: "Missing ref parameter"
1578
1631
  };
1579
1632
  }
1580
- const [activeTab] = await chrome.tabs.query({ active: true, currentWindow: true });
1581
- if (!activeTab || !activeTab.id) {
1633
+ const activeTab = await resolveTab(command);
1634
+ if (!activeTab.id) {
1582
1635
  return {
1583
1636
  id: command.id,
1584
1637
  success: false,
@@ -1614,8 +1667,8 @@ async function handleHover(command) {
1614
1667
  error: "Missing ref parameter"
1615
1668
  };
1616
1669
  }
1617
- const [activeTab] = await chrome.tabs.query({ active: true, currentWindow: true });
1618
- if (!activeTab || !activeTab.id) {
1670
+ const activeTab = await resolveTab(command);
1671
+ if (!activeTab.id) {
1619
1672
  return {
1620
1673
  id: command.id,
1621
1674
  success: false,
@@ -1659,8 +1712,8 @@ async function handleFill(command) {
1659
1712
  error: "Missing text parameter"
1660
1713
  };
1661
1714
  }
1662
- const [activeTab] = await chrome.tabs.query({ active: true, currentWindow: true });
1663
- if (!activeTab || !activeTab.id) {
1715
+ const activeTab = await resolveTab(command);
1716
+ if (!activeTab.id) {
1664
1717
  return {
1665
1718
  id: command.id,
1666
1719
  success: false,
@@ -1705,8 +1758,8 @@ async function handleType(command) {
1705
1758
  error: "Missing text parameter"
1706
1759
  };
1707
1760
  }
1708
- const [activeTab] = await chrome.tabs.query({ active: true, currentWindow: true });
1709
- if (!activeTab || !activeTab.id) {
1761
+ const activeTab = await resolveTab(command);
1762
+ if (!activeTab.id) {
1710
1763
  return {
1711
1764
  id: command.id,
1712
1765
  success: false,
@@ -1743,8 +1796,8 @@ async function handleCheck(command) {
1743
1796
  error: "Missing ref parameter"
1744
1797
  };
1745
1798
  }
1746
- const [activeTab] = await chrome.tabs.query({ active: true, currentWindow: true });
1747
- if (!activeTab || !activeTab.id) {
1799
+ const activeTab = await resolveTab(command);
1800
+ if (!activeTab.id) {
1748
1801
  return {
1749
1802
  id: command.id,
1750
1803
  success: false,
@@ -1781,8 +1834,8 @@ async function handleUncheck(command) {
1781
1834
  error: "Missing ref parameter"
1782
1835
  };
1783
1836
  }
1784
- const [activeTab] = await chrome.tabs.query({ active: true, currentWindow: true });
1785
- if (!activeTab || !activeTab.id) {
1837
+ const activeTab = await resolveTab(command);
1838
+ if (!activeTab.id) {
1786
1839
  return {
1787
1840
  id: command.id,
1788
1841
  success: false,
@@ -1827,8 +1880,8 @@ async function handleSelect(command) {
1827
1880
  error: "Missing value parameter"
1828
1881
  };
1829
1882
  }
1830
- const [activeTab] = await chrome.tabs.query({ active: true, currentWindow: true });
1831
- if (!activeTab || !activeTab.id) {
1883
+ const activeTab = await resolveTab(command);
1884
+ if (!activeTab.id) {
1832
1885
  return {
1833
1886
  id: command.id,
1834
1887
  success: false,
@@ -1858,8 +1911,8 @@ async function handleSelect(command) {
1858
1911
  }
1859
1912
  }
1860
1913
  async function handleClose(command) {
1861
- const [activeTab] = await chrome.tabs.query({ active: true, currentWindow: true });
1862
- if (!activeTab || !activeTab.id) {
1914
+ const activeTab = await resolveTab(command);
1915
+ if (!activeTab.id) {
1863
1916
  return {
1864
1917
  id: command.id,
1865
1918
  success: false,
@@ -1872,6 +1925,9 @@ async function handleClose(command) {
1872
1925
  console.log("[CommandHandler] Closing tab:", tabId, url);
1873
1926
  try {
1874
1927
  await chrome.tabs.remove(tabId);
1928
+ cleanupTab$1(tabId);
1929
+ cleanupTab(tabId);
1930
+ tabActiveFrameId.delete(tabId);
1875
1931
  return {
1876
1932
  id: command.id,
1877
1933
  success: true,
@@ -1899,8 +1955,8 @@ async function handleGet(command) {
1899
1955
  error: "Missing attribute parameter"
1900
1956
  };
1901
1957
  }
1902
- const [activeTab] = await chrome.tabs.query({ active: true, currentWindow: true });
1903
- if (!activeTab || !activeTab.id) {
1958
+ const activeTab = await resolveTab(command);
1959
+ if (!activeTab.id) {
1904
1960
  return {
1905
1961
  id: command.id,
1906
1962
  success: false,
@@ -1953,8 +2009,8 @@ async function handleGet(command) {
1953
2009
  }
1954
2010
  }
1955
2011
  async function handleScreenshot(command) {
1956
- const [activeTab] = await chrome.tabs.query({ active: true, currentWindow: true });
1957
- if (!activeTab || !activeTab.id || !activeTab.windowId) {
2012
+ const activeTab = await resolveTab(command);
2013
+ if (!activeTab.id || !activeTab.windowId) {
1958
2014
  return {
1959
2015
  id: command.id,
1960
2016
  success: false,
@@ -2009,8 +2065,8 @@ async function handleWait(command) {
2009
2065
  error: "Missing ref parameter"
2010
2066
  };
2011
2067
  }
2012
- const [activeTab] = await chrome.tabs.query({ active: true, currentWindow: true });
2013
- if (!activeTab || !activeTab.id) {
2068
+ const activeTab = await resolveTab(command);
2069
+ if (!activeTab.id) {
2014
2070
  return {
2015
2071
  id: command.id,
2016
2072
  success: false,
@@ -2051,8 +2107,8 @@ async function handlePress(command) {
2051
2107
  error: "Missing key parameter"
2052
2108
  };
2053
2109
  }
2054
- const [activeTab] = await chrome.tabs.query({ active: true, currentWindow: true });
2055
- if (!activeTab || !activeTab.id) {
2110
+ const activeTab = await resolveTab(command);
2111
+ if (!activeTab.id) {
2056
2112
  return {
2057
2113
  id: command.id,
2058
2114
  success: false,
@@ -2104,8 +2160,8 @@ async function handleScroll(command) {
2104
2160
  error: `Invalid direction: ${direction}`
2105
2161
  };
2106
2162
  }
2107
- const [activeTab] = await chrome.tabs.query({ active: true, currentWindow: true });
2108
- if (!activeTab || !activeTab.id) {
2163
+ const activeTab = await resolveTab(command);
2164
+ if (!activeTab.id) {
2109
2165
  return {
2110
2166
  id: command.id,
2111
2167
  success: false,
@@ -2133,8 +2189,8 @@ async function handleScroll(command) {
2133
2189
  }
2134
2190
  }
2135
2191
  async function handleBack(command) {
2136
- const [activeTab] = await chrome.tabs.query({ active: true, currentWindow: true });
2137
- if (!activeTab || !activeTab.id) {
2192
+ const activeTab = await resolveTab(command);
2193
+ if (!activeTab.id) {
2138
2194
  return {
2139
2195
  id: command.id,
2140
2196
  success: false,
@@ -2173,8 +2229,8 @@ async function handleBack(command) {
2173
2229
  }
2174
2230
  }
2175
2231
  async function handleForward(command) {
2176
- const [activeTab] = await chrome.tabs.query({ active: true, currentWindow: true });
2177
- if (!activeTab || !activeTab.id) {
2232
+ const activeTab = await resolveTab(command);
2233
+ if (!activeTab.id) {
2178
2234
  return {
2179
2235
  id: command.id,
2180
2236
  success: false,
@@ -2205,8 +2261,8 @@ async function handleForward(command) {
2205
2261
  }
2206
2262
  }
2207
2263
  async function handleRefresh(command) {
2208
- const [activeTab] = await chrome.tabs.query({ active: true, currentWindow: true });
2209
- if (!activeTab || !activeTab.id) {
2264
+ const activeTab = await resolveTab(command);
2265
+ if (!activeTab.id) {
2210
2266
  return {
2211
2267
  id: command.id,
2212
2268
  success: false,
@@ -2244,8 +2300,8 @@ async function handleEval(command) {
2244
2300
  error: "Missing script parameter"
2245
2301
  };
2246
2302
  }
2247
- const [activeTab] = await chrome.tabs.query({ active: true, currentWindow: true });
2248
- if (!activeTab || !activeTab.id) {
2303
+ const activeTab = await resolveTab(command);
2304
+ if (!activeTab.id) {
2249
2305
  return {
2250
2306
  id: command.id,
2251
2307
  success: false,
@@ -2422,6 +2478,9 @@ async function handleTabClose(command) {
2422
2478
  const title = targetTab.title || "";
2423
2479
  const url = targetTab.url || "";
2424
2480
  await chrome.tabs.remove(tabId);
2481
+ cleanupTab$1(tabId);
2482
+ cleanupTab(tabId);
2483
+ tabActiveFrameId.delete(tabId);
2425
2484
  return {
2426
2485
  id: command.id,
2427
2486
  success: true,
@@ -2449,8 +2508,8 @@ async function handleFrame(command) {
2449
2508
  error: "Missing selector parameter"
2450
2509
  };
2451
2510
  }
2452
- const [activeTab] = await chrome.tabs.query({ active: true, currentWindow: true });
2453
- if (!activeTab || !activeTab.id) {
2511
+ const activeTab = await resolveTab(command);
2512
+ if (!activeTab.id) {
2454
2513
  return {
2455
2514
  id: command.id,
2456
2515
  success: false,
@@ -2461,7 +2520,7 @@ async function handleFrame(command) {
2461
2520
  console.log("[CommandHandler] Switching to frame:", selector);
2462
2521
  try {
2463
2522
  const iframeInfoResults = await chrome.scripting.executeScript({
2464
- target: { tabId, frameIds: activeFrameId !== null ? [activeFrameId] : [0] },
2523
+ target: { tabId, frameIds: tabActiveFrameId.get(tabId) !== null && tabActiveFrameId.get(tabId) !== void 0 ? [tabActiveFrameId.get(tabId)] : [0] },
2465
2524
  func: (sel) => {
2466
2525
  const iframe = document.querySelector(sel);
2467
2526
  if (!iframe) {
@@ -2540,8 +2599,8 @@ async function handleFrame(command) {
2540
2599
  error: `无法访问 frame (frameId: ${targetFrameId}),可能是跨域 iframe`
2541
2600
  };
2542
2601
  }
2543
- activeFrameId = targetFrameId;
2544
- setActiveFrameId(String(targetFrameId));
2602
+ tabActiveFrameId.set(tabId, targetFrameId);
2603
+ setActiveFrameId(tabId, String(targetFrameId));
2545
2604
  const matchedFrameInfo = frames.find((f) => f.frameId === targetFrameId);
2546
2605
  return {
2547
2606
  id: command.id,
@@ -2566,8 +2625,17 @@ async function handleFrame(command) {
2566
2625
  }
2567
2626
  async function handleFrameMain(command) {
2568
2627
  console.log("[CommandHandler] Switching to main frame");
2569
- activeFrameId = null;
2570
- setActiveFrameId(null);
2628
+ const activeTab = await resolveTab(command);
2629
+ if (!activeTab.id) {
2630
+ return {
2631
+ id: command.id,
2632
+ success: false,
2633
+ error: "No active tab found"
2634
+ };
2635
+ }
2636
+ const tabId = activeTab.id;
2637
+ tabActiveFrameId.set(tabId, null);
2638
+ setActiveFrameId(tabId, null);
2571
2639
  return {
2572
2640
  id: command.id,
2573
2641
  success: true,
@@ -2588,8 +2656,8 @@ async function handleDialog(command) {
2588
2656
  error: "Missing or invalid dialogResponse parameter (accept/dismiss)"
2589
2657
  };
2590
2658
  }
2591
- const [activeTab] = await chrome.tabs.query({ active: true, currentWindow: true });
2592
- if (!activeTab || !activeTab.id) {
2659
+ const activeTab = await resolveTab(command);
2660
+ if (!activeTab.id) {
2593
2661
  return {
2594
2662
  id: command.id,
2595
2663
  success: false,
@@ -2650,8 +2718,8 @@ function waitForTabLoad(tabId, timeout = 3e4) {
2650
2718
  });
2651
2719
  }
2652
2720
  async function handleNetwork(command) {
2653
- const [activeTab] = await chrome.tabs.query({ active: true, currentWindow: true });
2654
- if (!activeTab || !activeTab.id) {
2721
+ const activeTab = await resolveTab(command);
2722
+ if (!activeTab.id) {
2655
2723
  return {
2656
2724
  id: command.id,
2657
2725
  success: false,
@@ -2742,8 +2810,8 @@ async function handleNetwork(command) {
2742
2810
  }
2743
2811
  }
2744
2812
  async function handleConsole(command) {
2745
- const [activeTab] = await chrome.tabs.query({ active: true, currentWindow: true });
2746
- if (!activeTab || !activeTab.id) {
2813
+ const activeTab = await resolveTab(command);
2814
+ if (!activeTab.id) {
2747
2815
  return {
2748
2816
  id: command.id,
2749
2817
  success: false,
@@ -2791,8 +2859,8 @@ async function handleConsole(command) {
2791
2859
  }
2792
2860
  }
2793
2861
  async function handleErrors(command) {
2794
- const [activeTab] = await chrome.tabs.query({ active: true, currentWindow: true });
2795
- if (!activeTab || !activeTab.id) {
2862
+ const activeTab = await resolveTab(command);
2863
+ if (!activeTab.id) {
2796
2864
  return {
2797
2865
  id: command.id,
2798
2866
  success: false,
@@ -2845,8 +2913,8 @@ async function handleTrace(command) {
2845
2913
  try {
2846
2914
  switch (subCommand) {
2847
2915
  case "start": {
2848
- const [activeTab] = await chrome.tabs.query({ active: true, currentWindow: true });
2849
- if (!activeTab || !activeTab.id) {
2916
+ const activeTab = await resolveTab(command);
2917
+ if (!activeTab.id) {
2850
2918
  return {
2851
2919
  id: command.id,
2852
2920
  success: false,
@@ -2915,6 +2983,14 @@ async function handleTrace(command) {
2915
2983
  const KEEPALIVE_ALARM = "bb-browser-keepalive";
2916
2984
  const sseClient = new SSEClient();
2917
2985
  sseClient.onCommand(handleCommand);
2986
+ chrome.storage.onChanged.addListener((changes, area) => {
2987
+ if (area === "sync" && changes.upstreamUrl) {
2988
+ const newUrl = changes.upstreamUrl.newValue || "default";
2989
+ console.log("[bb-browser] Upstream URL changed to:", newUrl, "— reconnecting...");
2990
+ sseClient.disconnect();
2991
+ sseClient.connect();
2992
+ }
2993
+ });
2918
2994
  chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
2919
2995
  console.log("[bb-browser] Message from content script:", message, "sender:", sender.tab?.id);
2920
2996
  sendResponse({ received: true });