@opentabs-dev/browser-extension 0.0.53 → 0.0.54

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.
Files changed (58) hide show
  1. package/dist/background-message-handlers.d.ts +10 -4
  2. package/dist/background-message-handlers.d.ts.map +1 -1
  3. package/dist/background-message-handlers.js +110 -37
  4. package/dist/background-message-handlers.js.map +1 -1
  5. package/dist/background.js +392 -374
  6. package/dist/browser-commands/extension-commands.d.ts.map +1 -1
  7. package/dist/browser-commands/extension-commands.js +5 -2
  8. package/dist/browser-commands/extension-commands.js.map +1 -1
  9. package/dist/browser-commands/key-press-command.d.ts +2 -0
  10. package/dist/browser-commands/key-press-command.d.ts.map +1 -1
  11. package/dist/browser-commands/key-press-command.js +42 -4
  12. package/dist/browser-commands/key-press-command.js.map +1 -1
  13. package/dist/confirmation-badge.d.ts +15 -1
  14. package/dist/confirmation-badge.d.ts.map +1 -1
  15. package/dist/confirmation-badge.js +13 -8
  16. package/dist/confirmation-badge.js.map +1 -1
  17. package/dist/dispatch-helpers.d.ts +1 -1
  18. package/dist/extension-messages.d.ts +1 -17
  19. package/dist/extension-messages.d.ts.map +1 -1
  20. package/dist/known-methods.d.ts +2 -2
  21. package/dist/known-methods.d.ts.map +1 -1
  22. package/dist/known-methods.js +0 -2
  23. package/dist/known-methods.js.map +1 -1
  24. package/dist/message-router.d.ts.map +1 -1
  25. package/dist/message-router.js +52 -21
  26. package/dist/message-router.js.map +1 -1
  27. package/dist/offscreen/index.js +6 -5
  28. package/dist/offscreen/index.js.map +1 -1
  29. package/dist/server-request.js +1 -1
  30. package/dist/server-request.js.map +1 -1
  31. package/dist/server-state-cache.d.ts +30 -3
  32. package/dist/server-state-cache.d.ts.map +1 -1
  33. package/dist/server-state-cache.js +139 -6
  34. package/dist/server-state-cache.js.map +1 -1
  35. package/dist/side-panel/App.d.ts.map +1 -1
  36. package/dist/side-panel/App.js +37 -4
  37. package/dist/side-panel/App.js.map +1 -1
  38. package/dist/side-panel/bridge.d.ts +12 -1
  39. package/dist/side-panel/bridge.d.ts.map +1 -1
  40. package/dist/side-panel/bridge.js.map +1 -1
  41. package/dist/side-panel/components/ConfirmationDialog.d.ts +7 -2
  42. package/dist/side-panel/components/ConfirmationDialog.d.ts.map +1 -1
  43. package/dist/side-panel/components/ConfirmationDialog.js.map +1 -1
  44. package/dist/side-panel/components/ToolIcon.js +1 -1
  45. package/dist/side-panel/hooks/useServerNotifications.d.ts.map +1 -1
  46. package/dist/side-panel/hooks/useServerNotifications.js +13 -3
  47. package/dist/side-panel/hooks/useServerNotifications.js.map +1 -1
  48. package/dist/side-panel/side-panel.js +73 -28
  49. package/dist/side-panel/styles.css +1 -1
  50. package/dist/tab-state.d.ts +8 -1
  51. package/dist/tab-state.d.ts.map +1 -1
  52. package/dist/tab-state.js +36 -9
  53. package/dist/tab-state.js.map +1 -1
  54. package/dist/tool-dispatch.d.ts.map +1 -1
  55. package/dist/tool-dispatch.js +3 -2
  56. package/dist/tool-dispatch.js.map +1 -1
  57. package/manifest.json +1 -1
  58. package/package.json +1 -1
@@ -93,7 +93,7 @@ var initSidePanelToggle = () => {
93
93
  // dist/confirmation-badge.js
94
94
  var pendingConfirmationCount = 0;
95
95
  var NOTIFICATION_ID = "opentabs-confirmation";
96
- var pendingConfirmationInfo = /* @__PURE__ */ new Map();
96
+ var pendingConfirmations = /* @__PURE__ */ new Map();
97
97
  var confirmationTimeouts = /* @__PURE__ */ new Map();
98
98
  var clearedConfirmationIds = /* @__PURE__ */ new Set();
99
99
  var CONFIRMATION_BACKGROUND_TIMEOUT_BUFFER_MS = 2e3;
@@ -116,8 +116,8 @@ var syncConfirmationNotification = () => {
116
116
  return;
117
117
  }
118
118
  let message;
119
- if (pendingConfirmationCount === 1 && pendingConfirmationInfo.size === 1) {
120
- const info = pendingConfirmationInfo.values().next().value;
119
+ if (pendingConfirmationCount === 1 && pendingConfirmations.size === 1) {
120
+ const info = pendingConfirmations.values().next().value;
121
121
  message = info.domain ? `${info.tool} on ${info.domain}` : info.tool;
122
122
  } else if (pendingConfirmationCount > 1) {
123
123
  message = `${pendingConfirmationCount} tools awaiting approval`;
@@ -138,7 +138,10 @@ var notifyConfirmationRequest = (params) => {
138
138
  const tool = typeof params.tool === "string" ? params.tool : "unknown tool";
139
139
  const domain = typeof params.domain === "string" ? params.domain : null;
140
140
  const id = typeof params.id === "string" ? params.id : String(Date.now());
141
+ const tabId = typeof params.tabId === "number" ? params.tabId : void 0;
142
+ const paramsPreview = typeof params.paramsPreview === "string" ? params.paramsPreview : "";
141
143
  const timeoutMs = typeof params.timeoutMs === "number" ? params.timeoutMs : 0;
144
+ const receivedAt = Date.now();
142
145
  const existingTimeoutId = confirmationTimeouts.get(id);
143
146
  if (existingTimeoutId !== void 0) {
144
147
  clearTimeout(existingTimeoutId);
@@ -147,7 +150,7 @@ var notifyConfirmationRequest = (params) => {
147
150
  pendingConfirmationCount++;
148
151
  updateConfirmationBadge();
149
152
  }
150
- pendingConfirmationInfo.set(id, { tool, domain });
153
+ pendingConfirmations.set(id, { id, tool, domain, tabId, paramsPreview, timeoutMs, receivedAt });
151
154
  const effectiveTimeoutMs = timeoutMs > 0 ? timeoutMs : CONFIRMATION_FALLBACK_TIMEOUT_MS;
152
155
  const bgTimeoutId = setTimeout(() => {
153
156
  confirmationTimeouts.delete(id);
@@ -163,7 +166,7 @@ var clearConfirmationBadge = (id) => {
163
166
  return;
164
167
  }
165
168
  clearedConfirmationIds.add(id);
166
- pendingConfirmationInfo.delete(id);
169
+ pendingConfirmations.delete(id);
167
170
  }
168
171
  pendingConfirmationCount = Math.max(0, pendingConfirmationCount - 1);
169
172
  updateConfirmationBadge();
@@ -186,7 +189,7 @@ var clearAllConfirmationBadges = () => {
186
189
  }
187
190
  confirmationTimeouts.clear();
188
191
  clearedConfirmationIds.clear();
189
- pendingConfirmationInfo.clear();
192
+ pendingConfirmations.clear();
190
193
  pendingConfirmationCount = 0;
191
194
  updateConfirmationBadge();
192
195
  chrome.notifications.clear(NOTIFICATION_ID).catch(() => {
@@ -207,6 +210,7 @@ var initConfirmationBadge = () => {
207
210
  }
208
211
  });
209
212
  };
213
+ var getPendingConfirmations = () => [...pendingConfirmations.values()];
210
214
 
211
215
  // dist/json-rpc-errors.js
212
216
  var JSONRPC_METHOD_NOT_FOUND = -32601;
@@ -1335,33 +1339,45 @@ var computePluginTabState = async (plugin) => {
1335
1339
  if (matchingTabs.length === 0) {
1336
1340
  return { state: "closed", tabs: [] };
1337
1341
  }
1338
- const tabInfos = [];
1339
- for (const tab of matchingTabs) {
1340
- if (tab.id === void 0)
1341
- continue;
1342
+ const validTabs = matchingTabs.filter((tab) => tab.id !== void 0);
1343
+ const results = await Promise.allSettled(validTabs.map(async (tab) => {
1342
1344
  let ready = false;
1343
1345
  try {
1344
1346
  ready = await probeTabReadiness(tab.id, plugin.name);
1345
1347
  } catch (err2) {
1346
1348
  console.warn(`[opentabs] computePluginTabState failed for plugin ${plugin.name} in tab ${tab.id}:`, err2);
1347
1349
  }
1348
- tabInfos.push({
1350
+ return {
1349
1351
  tabId: tab.id,
1350
1352
  url: tab.url ?? "",
1351
1353
  title: tab.title ?? "",
1352
1354
  ready
1353
- });
1355
+ };
1356
+ }));
1357
+ const tabInfos = [];
1358
+ for (const result of results) {
1359
+ if (result.status === "fulfilled") {
1360
+ tabInfos.push(result.value);
1361
+ }
1354
1362
  }
1355
1363
  const hasReady = tabInfos.some((t) => t.ready);
1356
1364
  const state = hasReady ? "ready" : "unavailable";
1357
1365
  return { state, tabs: tabInfos };
1358
1366
  };
1359
1367
  var sendTabSyncAll = async () => {
1368
+ const syncAllStart = Date.now();
1369
+ console.log("[opentabs:timing] sendTabSyncAll started");
1360
1370
  const index = await getAllPluginMeta();
1361
1371
  const plugins = Object.values(index);
1362
1372
  if (plugins.length === 0)
1363
1373
  return;
1364
- const settled = await Promise.allSettled(plugins.map(async (plugin) => [plugin.name, await computePluginTabState(plugin)]));
1374
+ const settled = await Promise.allSettled(plugins.map(async (plugin) => {
1375
+ const t0 = Date.now();
1376
+ const result = [plugin.name, await computePluginTabState(plugin)];
1377
+ console.log(`[opentabs:timing] computePluginTabState(${plugin.name}): ${Date.now() - t0}ms`);
1378
+ return result;
1379
+ }));
1380
+ console.log(`[opentabs:timing] all probes done: +${Date.now() - syncAllStart}ms`);
1365
1381
  const entries = [];
1366
1382
  for (const result of settled) {
1367
1383
  if (result.status === "fulfilled") {
@@ -1408,6 +1424,13 @@ var sendTabSyncAll = async () => {
1408
1424
  });
1409
1425
  }
1410
1426
  };
1427
+ var flushLastKnownStateToSession = () => {
1428
+ if (lastKnownStatePersistTimer !== void 0) {
1429
+ clearTimeout(lastKnownStatePersistTimer);
1430
+ lastKnownStatePersistTimer = void 0;
1431
+ }
1432
+ persistLastKnownStateToSession();
1433
+ };
1411
1434
  var clearTabStateCache = () => {
1412
1435
  lastKnownState.clear();
1413
1436
  pluginLocks.clear();
@@ -1432,6 +1455,7 @@ var updateLastKnownState = (pluginName, stateInfo) => withPluginLock(pluginName,
1432
1455
  });
1433
1456
  var getLastKnownStates = () => lastKnownState;
1434
1457
  var loadLastKnownStateFromSession = async () => {
1458
+ lastKnownState.clear();
1435
1459
  try {
1436
1460
  const data = await chrome.storage.session.get(LAST_KNOWN_STATE_SESSION_KEY);
1437
1461
  const stored = data[LAST_KNOWN_STATE_SESSION_KEY];
@@ -1448,7 +1472,8 @@ var loadLastKnownStateFromSession = async () => {
1448
1472
  var getAggregateState = (serialized) => {
1449
1473
  try {
1450
1474
  const parsed = JSON.parse(serialized);
1451
- return parsed.state;
1475
+ const validStates = /* @__PURE__ */ new Set(["closed", "unavailable", "ready"]);
1476
+ return validStates.has(parsed.state) ? parsed.state : "closed";
1452
1477
  } catch {
1453
1478
  return "closed";
1454
1479
  }
@@ -1617,7 +1642,8 @@ var handleExtensionGetLogs = async (params, id) => {
1617
1642
  if (typeof params.since === "number") {
1618
1643
  filterOptions.since = params.since;
1619
1644
  }
1620
- const bgEntries = bgLogCollector.getEntries(filterOptions);
1645
+ const { limit: _, ...sourceOptions } = filterOptions;
1646
+ const bgEntries = bgLogCollector.getEntries(sourceOptions);
1621
1647
  const bgStats = bgLogCollector.getStats();
1622
1648
  let offscreenEntries = [];
1623
1649
  let offscreenStats = {
@@ -1629,7 +1655,7 @@ var handleExtensionGetLogs = async (params, id) => {
1629
1655
  try {
1630
1656
  const raw = await chrome.runtime.sendMessage({
1631
1657
  type: "offscreen:getLogs",
1632
- options: Object.keys(filterOptions).length > 0 ? filterOptions : void 0
1658
+ options: Object.keys(sourceOptions).length > 0 ? sourceOptions : void 0
1633
1659
  });
1634
1660
  const response = raw;
1635
1661
  if (response && Array.isArray(response.entries)) {
@@ -2389,6 +2415,42 @@ var handleBrowserHandleDialog = async (params, id) => {
2389
2415
  };
2390
2416
 
2391
2417
  // dist/browser-commands/key-press-command.js
2418
+ var SHIFTED_PUNCTUATION_CODES = {
2419
+ "!": "Digit1",
2420
+ "@": "Digit2",
2421
+ "#": "Digit3",
2422
+ $: "Digit4",
2423
+ "%": "Digit5",
2424
+ "^": "Digit6",
2425
+ "&": "Digit7",
2426
+ "*": "Digit8",
2427
+ "(": "Digit9",
2428
+ ")": "Digit0",
2429
+ _: "Minus",
2430
+ "+": "Equal",
2431
+ "{": "BracketLeft",
2432
+ "}": "BracketRight",
2433
+ "|": "Backslash",
2434
+ ":": "Semicolon",
2435
+ '"': "Quote",
2436
+ "<": "Comma",
2437
+ ">": "Period",
2438
+ "?": "Slash",
2439
+ "~": "Backquote"
2440
+ };
2441
+ var UNSHIFTED_PUNCTUATION_CODES = {
2442
+ "-": "Minus",
2443
+ "=": "Equal",
2444
+ "[": "BracketLeft",
2445
+ "]": "BracketRight",
2446
+ "\\": "Backslash",
2447
+ ";": "Semicolon",
2448
+ "'": "Quote",
2449
+ ",": "Comma",
2450
+ ".": "Period",
2451
+ "/": "Slash",
2452
+ "`": "Backquote"
2453
+ };
2392
2454
  var handleBrowserPressKey = async (params, id) => {
2393
2455
  try {
2394
2456
  const tabId = requireTabId(params, id);
@@ -2406,7 +2468,7 @@ var handleBrowserPressKey = async (params, id) => {
2406
2468
  const results = await chrome.scripting.executeScript({
2407
2469
  target: { tabId },
2408
2470
  world: "MAIN",
2409
- func: (k, sel, shift, ctrl, alt, meta) => {
2471
+ func: (k, sel, shift, ctrl, alt, meta, shiftedPunct, unshiftedPunct) => {
2410
2472
  let target = null;
2411
2473
  if (sel) {
2412
2474
  target = document.querySelector(sel);
@@ -2425,7 +2487,7 @@ var handleBrowserPressKey = async (params, id) => {
2425
2487
  return `Digit${k2}`;
2426
2488
  if (k2 === " ")
2427
2489
  return "Space";
2428
- return k2;
2490
+ return shiftedPunct[k2] ?? unshiftedPunct[k2] ?? k2;
2429
2491
  }
2430
2492
  return k2;
2431
2493
  };
@@ -2510,7 +2572,7 @@ var handleBrowserPressKey = async (params, id) => {
2510
2572
  }
2511
2573
  };
2512
2574
  },
2513
- args: [key, selector, shiftKey, ctrlKey, altKey, metaKey]
2575
+ args: [key, selector, shiftKey, ctrlKey, altKey, metaKey, SHIFTED_PUNCTUATION_CODES, UNSHIFTED_PUNCTUATION_CODES]
2514
2576
  });
2515
2577
  const result = extractScriptResult(results, id);
2516
2578
  if (!result)
@@ -3121,6 +3183,208 @@ var checkRateLimit = (method, now = Date.now()) => {
3121
3183
  return true;
3122
3184
  };
3123
3185
 
3186
+ // dist/server-request.js
3187
+ var REQUEST_TIMEOUT_MS = 3e4;
3188
+ var pendingRequests = /* @__PURE__ */ new Map();
3189
+ var nextId = 1;
3190
+ var sendServerRequest = (method, params = {}) => {
3191
+ const id = nextId++;
3192
+ const data = { jsonrpc: "2.0", method, params, id };
3193
+ return new Promise((resolve, reject) => {
3194
+ const timerId = setTimeout(() => {
3195
+ if (pendingRequests.has(id)) {
3196
+ pendingRequests.delete(id);
3197
+ reject(new Error(`Request ${method} timed out after ${REQUEST_TIMEOUT_MS}ms`));
3198
+ }
3199
+ }, REQUEST_TIMEOUT_MS);
3200
+ pendingRequests.set(id, { resolve, reject, timerId });
3201
+ sendToServer(data);
3202
+ });
3203
+ };
3204
+ var consumeServerResponse = (data) => {
3205
+ if (data.method !== void 0)
3206
+ return false;
3207
+ const rawId = data.id;
3208
+ if (rawId === void 0 || rawId === null)
3209
+ return false;
3210
+ const id = typeof rawId === "number" ? rawId : void 0;
3211
+ if (id === void 0)
3212
+ return false;
3213
+ const pending = pendingRequests.get(id);
3214
+ if (!pending)
3215
+ return false;
3216
+ pendingRequests.delete(id);
3217
+ clearTimeout(pending.timerId);
3218
+ if (data.error !== void 0 && data.error !== null) {
3219
+ const err2 = data.error;
3220
+ pending.reject(new Error(err2.message ?? "Unknown server error"));
3221
+ } else {
3222
+ pending.resolve(data.result);
3223
+ }
3224
+ return true;
3225
+ };
3226
+ var rejectAllPendingServerRequests = () => {
3227
+ for (const [id, pending] of pendingRequests) {
3228
+ pendingRequests.delete(id);
3229
+ clearTimeout(pending.timerId);
3230
+ pending.reject(new Error("Server disconnected"));
3231
+ }
3232
+ };
3233
+
3234
+ // dist/server-state-cache.js
3235
+ var SESSION_KEY = "serverStateCache";
3236
+ var CACHES_INITIALIZED_KEY = "cachesInitialized";
3237
+ var EMPTY_CACHE = {
3238
+ plugins: [],
3239
+ failedPlugins: [],
3240
+ browserTools: [],
3241
+ serverVersion: void 0
3242
+ };
3243
+ var cache = { ...EMPTY_CACHE };
3244
+ var pendingPluginToolUpdates = /* @__PURE__ */ new Map();
3245
+ var pendingBrowserToolUpdates = /* @__PURE__ */ new Map();
3246
+ var reapplyPendingOptimisticUpdates = () => {
3247
+ if (pendingPluginToolUpdates.size > 0) {
3248
+ cache = {
3249
+ ...cache,
3250
+ plugins: cache.plugins.map((plugin) => {
3251
+ const toolOverrides = pendingPluginToolUpdates.get(plugin.name);
3252
+ if (!toolOverrides)
3253
+ return plugin;
3254
+ return {
3255
+ ...plugin,
3256
+ tools: plugin.tools.map((tool) => {
3257
+ const override = toolOverrides.get(tool.name);
3258
+ return override !== void 0 ? { ...tool, enabled: override } : tool;
3259
+ })
3260
+ };
3261
+ })
3262
+ };
3263
+ }
3264
+ if (pendingBrowserToolUpdates.size > 0) {
3265
+ cache = {
3266
+ ...cache,
3267
+ browserTools: cache.browserTools.map((bt) => {
3268
+ const override = pendingBrowserToolUpdates.get(bt.name);
3269
+ return override !== void 0 ? { ...bt, enabled: override } : bt;
3270
+ })
3271
+ };
3272
+ }
3273
+ };
3274
+ var addPendingPluginToolUpdate = (plugin, tool, enabled) => {
3275
+ let toolMap = pendingPluginToolUpdates.get(plugin);
3276
+ if (!toolMap) {
3277
+ toolMap = /* @__PURE__ */ new Map();
3278
+ pendingPluginToolUpdates.set(plugin, toolMap);
3279
+ }
3280
+ toolMap.set(tool, enabled);
3281
+ };
3282
+ var removePendingPluginToolUpdate = (plugin, tool) => {
3283
+ const toolMap = pendingPluginToolUpdates.get(plugin);
3284
+ if (!toolMap)
3285
+ return;
3286
+ toolMap.delete(tool);
3287
+ if (toolMap.size === 0)
3288
+ pendingPluginToolUpdates.delete(plugin);
3289
+ };
3290
+ var addPendingPluginAllToolsUpdate = (plugin, toolNames, enabled) => {
3291
+ let toolMap = pendingPluginToolUpdates.get(plugin);
3292
+ if (!toolMap) {
3293
+ toolMap = /* @__PURE__ */ new Map();
3294
+ pendingPluginToolUpdates.set(plugin, toolMap);
3295
+ }
3296
+ for (const name of toolNames) {
3297
+ toolMap.set(name, enabled);
3298
+ }
3299
+ };
3300
+ var removePendingPluginAllToolsUpdate = (plugin, toolNames) => {
3301
+ const toolMap = pendingPluginToolUpdates.get(plugin);
3302
+ if (!toolMap)
3303
+ return;
3304
+ for (const name of toolNames) {
3305
+ toolMap.delete(name);
3306
+ }
3307
+ if (toolMap.size === 0)
3308
+ pendingPluginToolUpdates.delete(plugin);
3309
+ };
3310
+ var addPendingBrowserToolUpdate = (tool, enabled) => {
3311
+ pendingBrowserToolUpdates.set(tool, enabled);
3312
+ };
3313
+ var removePendingBrowserToolUpdate = (tool) => {
3314
+ pendingBrowserToolUpdates.delete(tool);
3315
+ };
3316
+ var addPendingAllBrowserToolsUpdate = (toolNames, enabled) => {
3317
+ for (const name of toolNames) {
3318
+ pendingBrowserToolUpdates.set(name, enabled);
3319
+ }
3320
+ };
3321
+ var removePendingAllBrowserToolsUpdate = (toolNames) => {
3322
+ for (const name of toolNames) {
3323
+ pendingBrowserToolUpdates.delete(name);
3324
+ }
3325
+ };
3326
+ var cachesInitialized = false;
3327
+ var persistTimer;
3328
+ var persistToSession = () => {
3329
+ chrome.storage.session.set({ [SESSION_KEY]: cache, [CACHES_INITIALIZED_KEY]: cachesInitialized }).catch(() => {
3330
+ });
3331
+ };
3332
+ var schedulePersist = () => {
3333
+ if (persistTimer !== void 0)
3334
+ clearTimeout(persistTimer);
3335
+ persistTimer = setTimeout(() => {
3336
+ persistTimer = void 0;
3337
+ persistToSession();
3338
+ }, 500);
3339
+ };
3340
+ var getServerStateCache = () => structuredClone(cache);
3341
+ var updateServerStateCache = (partial) => {
3342
+ cache = { ...cache, ...partial };
3343
+ reapplyPendingOptimisticUpdates();
3344
+ schedulePersist();
3345
+ };
3346
+ var flushServerStateCacheToSession = () => {
3347
+ if (persistTimer !== void 0) {
3348
+ clearTimeout(persistTimer);
3349
+ persistTimer = void 0;
3350
+ }
3351
+ persistToSession();
3352
+ };
3353
+ var clearServerStateCache = () => {
3354
+ cache = { ...EMPTY_CACHE };
3355
+ cachesInitialized = false;
3356
+ pendingPluginToolUpdates.clear();
3357
+ pendingBrowserToolUpdates.clear();
3358
+ if (persistTimer !== void 0) {
3359
+ clearTimeout(persistTimer);
3360
+ persistTimer = void 0;
3361
+ }
3362
+ chrome.storage.session.remove([SESSION_KEY, CACHES_INITIALIZED_KEY]).catch(() => {
3363
+ });
3364
+ };
3365
+ var loadServerStateCacheFromSession = async () => {
3366
+ try {
3367
+ const data = await chrome.storage.session.get([SESSION_KEY, CACHES_INITIALIZED_KEY]);
3368
+ const stored = data[SESSION_KEY];
3369
+ if (stored && typeof stored === "object") {
3370
+ cache = {
3371
+ plugins: Array.isArray(stored.plugins) ? stored.plugins : [],
3372
+ failedPlugins: Array.isArray(stored.failedPlugins) ? stored.failedPlugins : [],
3373
+ browserTools: Array.isArray(stored.browserTools) ? stored.browserTools : [],
3374
+ serverVersion: typeof stored.serverVersion === "string" ? stored.serverVersion : void 0
3375
+ };
3376
+ }
3377
+ if (typeof data[CACHES_INITIALIZED_KEY] === "boolean") {
3378
+ cachesInitialized = data[CACHES_INITIALIZED_KEY];
3379
+ }
3380
+ } catch {
3381
+ }
3382
+ };
3383
+ var getCachesInitialized = () => cachesInitialized;
3384
+ var setCachesInitialized = (value) => {
3385
+ cachesInitialized = value;
3386
+ };
3387
+
3124
3388
  // dist/dispatch-helpers.js
3125
3389
  var isAdapterNotReady = (result) => result.type === "error" && result.code === JSONRPC_ADAPTER_NOT_READY;
3126
3390
  var resolvePlugin = async (pluginName, id) => {
@@ -3135,26 +3399,6 @@ var resolvePlugin = async (pluginName, id) => {
3135
3399
  }
3136
3400
  return plugin;
3137
3401
  };
3138
- var executeWithTimeout = async (scriptPromise, timeoutMs, fallbackMessage) => {
3139
- let timeoutId;
3140
- const timeoutPromise = new Promise((_resolve, reject) => {
3141
- timeoutId = setTimeout(() => {
3142
- reject(new Error(`Script execution timed out after ${timeoutMs}ms`));
3143
- }, timeoutMs);
3144
- });
3145
- let results;
3146
- try {
3147
- results = await Promise.race([scriptPromise, timeoutPromise]);
3148
- } finally {
3149
- clearTimeout(timeoutId);
3150
- }
3151
- const firstResult = results[0];
3152
- const result = firstResult?.result;
3153
- if (!result || typeof result !== "object" || !("type" in result)) {
3154
- return { type: "error", code: JSONRPC_INTERNAL_ERROR, message: fallbackMessage };
3155
- }
3156
- return result;
3157
- };
3158
3402
  var dispatchWithTabFallback = async (config) => {
3159
3403
  const { id, pluginName, plugin, operationName, executeOnTab } = config;
3160
3404
  const matchingTabs = await findAllMatchingTabs(plugin);
@@ -3280,302 +3524,6 @@ var dispatchToTargetedTab = async (config) => {
3280
3524
  }
3281
3525
  };
3282
3526
 
3283
- // dist/resource-prompt-dispatch.js
3284
- var executeResourceReadOnTab = async (tabId, pluginName, resourceUri) => {
3285
- const scriptPromise = chrome.scripting.executeScript({
3286
- target: { tabId },
3287
- world: "MAIN",
3288
- func: async (pName, uri) => {
3289
- const ot = globalThis.__openTabs;
3290
- const adapter = ot?.adapters?.[pName];
3291
- if (!adapter || typeof adapter !== "object") {
3292
- return { type: "error", code: -32002, message: `Adapter "${pName}" not injected or not ready` };
3293
- }
3294
- if (!Object.isFrozen(adapter)) {
3295
- return {
3296
- type: "error",
3297
- code: -32002,
3298
- message: `Adapter "${pName}" failed integrity check (not frozen)`
3299
- };
3300
- }
3301
- if (typeof adapter.isReady !== "function") {
3302
- return { type: "error", code: -32002, message: `Adapter "${pName}" has no isReady function` };
3303
- }
3304
- let ready;
3305
- try {
3306
- ready = await adapter.isReady();
3307
- } catch {
3308
- return { type: "error", code: -32002, message: `Adapter "${pName}" isReady() threw an error` };
3309
- }
3310
- if (!ready) {
3311
- return {
3312
- type: "error",
3313
- code: -32002,
3314
- message: `Plugin "${pName}" is not ready (state: unavailable)`
3315
- };
3316
- }
3317
- if (!Array.isArray(adapter.resources)) {
3318
- return { type: "error", code: -32603, message: `Adapter "${pName}" has no resources array` };
3319
- }
3320
- const resource = adapter.resources.find((r) => r.uri === uri);
3321
- if (!resource || typeof resource.read !== "function") {
3322
- return { type: "error", code: -32603, message: `Resource "${uri}" not found in adapter "${pName}"` };
3323
- }
3324
- try {
3325
- const output = await resource.read(uri);
3326
- return { type: "success", output };
3327
- } catch (err2) {
3328
- const caughtError = err2;
3329
- return {
3330
- type: "error",
3331
- code: -32603,
3332
- message: caughtError.message ?? "Resource read failed"
3333
- };
3334
- }
3335
- },
3336
- args: [pluginName, resourceUri]
3337
- });
3338
- return executeWithTimeout(scriptPromise, SCRIPT_TIMEOUT_MS, "No result from resource read");
3339
- };
3340
- var executePromptGetOnTab = async (tabId, pluginName, promptName, promptArgs) => {
3341
- const scriptPromise = chrome.scripting.executeScript({
3342
- target: { tabId },
3343
- world: "MAIN",
3344
- func: async (pName, pPromptName, pArgs) => {
3345
- const ot = globalThis.__openTabs;
3346
- const adapter = ot?.adapters?.[pName];
3347
- if (!adapter || typeof adapter !== "object") {
3348
- return { type: "error", code: -32002, message: `Adapter "${pName}" not injected or not ready` };
3349
- }
3350
- if (!Object.isFrozen(adapter)) {
3351
- return {
3352
- type: "error",
3353
- code: -32002,
3354
- message: `Adapter "${pName}" failed integrity check (not frozen)`
3355
- };
3356
- }
3357
- if (typeof adapter.isReady !== "function") {
3358
- return { type: "error", code: -32002, message: `Adapter "${pName}" has no isReady function` };
3359
- }
3360
- let ready;
3361
- try {
3362
- ready = await adapter.isReady();
3363
- } catch {
3364
- return { type: "error", code: -32002, message: `Adapter "${pName}" isReady() threw an error` };
3365
- }
3366
- if (!ready) {
3367
- return {
3368
- type: "error",
3369
- code: -32002,
3370
- message: `Plugin "${pName}" is not ready (state: unavailable)`
3371
- };
3372
- }
3373
- if (!Array.isArray(adapter.prompts)) {
3374
- return { type: "error", code: -32603, message: `Adapter "${pName}" has no prompts array` };
3375
- }
3376
- const prompt = adapter.prompts.find((p) => p.name === pPromptName);
3377
- if (!prompt || typeof prompt.render !== "function") {
3378
- return {
3379
- type: "error",
3380
- code: -32603,
3381
- message: `Prompt "${pPromptName}" not found in adapter "${pName}"`
3382
- };
3383
- }
3384
- try {
3385
- const output = await prompt.render(pArgs);
3386
- return { type: "success", output };
3387
- } catch (err2) {
3388
- const caughtError = err2;
3389
- return {
3390
- type: "error",
3391
- code: -32603,
3392
- message: caughtError.message ?? "Prompt render failed"
3393
- };
3394
- }
3395
- },
3396
- args: [pluginName, promptName, promptArgs]
3397
- });
3398
- return executeWithTimeout(scriptPromise, SCRIPT_TIMEOUT_MS, "No result from prompt render");
3399
- };
3400
- var handleResourceRead = async (params, id) => {
3401
- const pluginName = requireStringParam(params, "plugin", id);
3402
- if (!pluginName)
3403
- return;
3404
- const resourceUri = requireStringParam(params, "uri", id);
3405
- if (!resourceUri)
3406
- return;
3407
- const rawTabId = params.tabId;
3408
- const targetTabId = typeof rawTabId === "number" && Number.isInteger(rawTabId) && rawTabId > 0 ? rawTabId : void 0;
3409
- const plugin = await resolvePlugin(pluginName, id);
3410
- if (!plugin)
3411
- return;
3412
- const executeOnTab = (tid) => executeResourceReadOnTab(tid, pluginName, resourceUri);
3413
- if (targetTabId !== void 0) {
3414
- await dispatchToTargetedTab({
3415
- id,
3416
- pluginName,
3417
- plugin,
3418
- tabId: targetTabId,
3419
- operationName: "resource read",
3420
- executeOnTab
3421
- });
3422
- } else {
3423
- await dispatchWithTabFallback({
3424
- id,
3425
- pluginName,
3426
- plugin,
3427
- operationName: "resource read",
3428
- executeOnTab
3429
- });
3430
- }
3431
- };
3432
- var handlePromptGet = async (params, id) => {
3433
- const pluginName = requireStringParam(params, "plugin", id);
3434
- if (!pluginName)
3435
- return;
3436
- const promptName = requireStringParam(params, "prompt", id);
3437
- if (!promptName)
3438
- return;
3439
- const rawArgs = params.arguments;
3440
- if (rawArgs !== void 0 && rawArgs !== null && (typeof rawArgs !== "object" || Array.isArray(rawArgs))) {
3441
- sendToServer({
3442
- jsonrpc: "2.0",
3443
- error: { code: JSONRPC_INVALID_PARAMS, message: 'Invalid "arguments" param (expected object)' },
3444
- id
3445
- });
3446
- return;
3447
- }
3448
- const rawObj = rawArgs ?? {};
3449
- const promptArgs = {};
3450
- for (const [key, val] of Object.entries(rawObj)) {
3451
- promptArgs[key] = String(val);
3452
- }
3453
- const rawTabId = params.tabId;
3454
- const targetTabId = typeof rawTabId === "number" && Number.isInteger(rawTabId) && rawTabId > 0 ? rawTabId : void 0;
3455
- const plugin = await resolvePlugin(pluginName, id);
3456
- if (!plugin)
3457
- return;
3458
- const executeOnTab = (tid) => executePromptGetOnTab(tid, pluginName, promptName, promptArgs);
3459
- if (targetTabId !== void 0) {
3460
- await dispatchToTargetedTab({
3461
- id,
3462
- pluginName,
3463
- plugin,
3464
- tabId: targetTabId,
3465
- operationName: "prompt get",
3466
- executeOnTab
3467
- });
3468
- } else {
3469
- await dispatchWithTabFallback({
3470
- id,
3471
- pluginName,
3472
- plugin,
3473
- operationName: "prompt get",
3474
- executeOnTab
3475
- });
3476
- }
3477
- };
3478
-
3479
- // dist/server-request.js
3480
- var REQUEST_TIMEOUT_MS = 3e4;
3481
- var pendingRequests = /* @__PURE__ */ new Map();
3482
- var nextId = 1;
3483
- var sendServerRequest = (method, params = {}) => {
3484
- const id = nextId++;
3485
- const data = { jsonrpc: "2.0", method, params, id };
3486
- return new Promise((resolve, reject) => {
3487
- const timerId = setTimeout(() => {
3488
- if (pendingRequests.has(id)) {
3489
- pendingRequests.delete(id);
3490
- reject(new Error(`Request ${method} timed out after ${REQUEST_TIMEOUT_MS}ms`));
3491
- }
3492
- }, REQUEST_TIMEOUT_MS);
3493
- pendingRequests.set(id, { resolve, reject, timerId });
3494
- sendToServer(data);
3495
- });
3496
- };
3497
- var consumeServerResponse = (data) => {
3498
- if (data.method !== void 0)
3499
- return false;
3500
- const rawId = data.id;
3501
- if (rawId === void 0 || rawId === null)
3502
- return false;
3503
- const id = typeof rawId === "number" ? rawId : void 0;
3504
- if (id === void 0)
3505
- return false;
3506
- const pending = pendingRequests.get(id);
3507
- if (!pending)
3508
- return false;
3509
- pendingRequests.delete(id);
3510
- clearTimeout(pending.timerId);
3511
- if ("error" in data) {
3512
- const err2 = data.error;
3513
- pending.reject(new Error(err2.message ?? "Unknown server error"));
3514
- } else {
3515
- pending.resolve(data.result);
3516
- }
3517
- return true;
3518
- };
3519
- var rejectAllPendingServerRequests = () => {
3520
- for (const [id, pending] of pendingRequests) {
3521
- pendingRequests.delete(id);
3522
- clearTimeout(pending.timerId);
3523
- pending.reject(new Error("Server disconnected"));
3524
- }
3525
- };
3526
-
3527
- // dist/server-state-cache.js
3528
- var SESSION_KEY = "serverStateCache";
3529
- var EMPTY_CACHE = {
3530
- plugins: [],
3531
- failedPlugins: [],
3532
- browserTools: [],
3533
- serverVersion: void 0
3534
- };
3535
- var cache = { ...EMPTY_CACHE };
3536
- var persistTimer;
3537
- var persistToSession = () => {
3538
- chrome.storage.session.set({ [SESSION_KEY]: cache }).catch(() => {
3539
- });
3540
- };
3541
- var schedulePersist = () => {
3542
- if (persistTimer !== void 0)
3543
- clearTimeout(persistTimer);
3544
- persistTimer = setTimeout(() => {
3545
- persistTimer = void 0;
3546
- persistToSession();
3547
- }, 500);
3548
- };
3549
- var getServerStateCache = () => cache;
3550
- var updateServerStateCache = (partial) => {
3551
- cache = { ...cache, ...partial };
3552
- schedulePersist();
3553
- };
3554
- var clearServerStateCache = () => {
3555
- cache = { ...EMPTY_CACHE };
3556
- if (persistTimer !== void 0) {
3557
- clearTimeout(persistTimer);
3558
- persistTimer = void 0;
3559
- }
3560
- chrome.storage.session.remove(SESSION_KEY).catch(() => {
3561
- });
3562
- };
3563
- var loadServerStateCacheFromSession = async () => {
3564
- try {
3565
- const data = await chrome.storage.session.get(SESSION_KEY);
3566
- const stored = data[SESSION_KEY];
3567
- if (stored && typeof stored === "object") {
3568
- cache = {
3569
- plugins: Array.isArray(stored.plugins) ? stored.plugins : [],
3570
- failedPlugins: Array.isArray(stored.failedPlugins) ? stored.failedPlugins : [],
3571
- browserTools: Array.isArray(stored.browserTools) ? stored.browserTools : [],
3572
- serverVersion: typeof stored.serverVersion === "string" ? stored.serverVersion : void 0
3573
- };
3574
- }
3575
- } catch {
3576
- }
3577
- };
3578
-
3579
3527
  // dist/tool-dispatch.js
3580
3528
  var progressCallbacks = /* @__PURE__ */ new Map();
3581
3529
  var notifyDispatchProgress = (dispatchId) => {
@@ -3772,7 +3720,7 @@ var executeToolOnTab = async (tabId, pluginName, toolName, input, dispatchId) =>
3772
3720
  return result;
3773
3721
  };
3774
3722
  var handleToolDispatch = async (params, id) => {
3775
- const dispatchId = typeof params.dispatchId === "string" ? params.dispatchId : String(id);
3723
+ const dispatchId = typeof params.__opentabs_dispatchId === "string" ? params.__opentabs_dispatchId : String(id);
3776
3724
  const pluginName = requireStringParam(params, "plugin", id);
3777
3725
  if (!pluginName)
3778
3726
  return;
@@ -3868,7 +3816,6 @@ var wrapNotification = (method, fn) => (params) => {
3868
3816
  fn(params).catch((err2) => console.warn(`[opentabs] ${method} handler failed:`, err2));
3869
3817
  };
3870
3818
  var SIDE_PANEL_METHODS = /* @__PURE__ */ new Set([
3871
- "tab.stateChanged",
3872
3819
  "tool.invocationStart",
3873
3820
  "tool.invocationEnd",
3874
3821
  "plugins.changed",
@@ -3902,13 +3849,18 @@ var validatePluginPayload = (raw) => {
3902
3849
  return null;
3903
3850
  }
3904
3851
  const urlPatterns = Array.isArray(obj.urlPatterns) ? obj.urlPatterns.filter((p) => typeof p === "string") : [];
3905
- const tools = Array.isArray(obj.tools) ? obj.tools.filter((t) => typeof t === "object" && t !== null && typeof t.name === "string" && typeof t.description === "string" && typeof t.enabled === "boolean").map((t) => ({
3906
- name: t.name,
3907
- displayName: typeof t.displayName === "string" ? t.displayName : t.name,
3908
- description: t.description,
3909
- icon: typeof t.icon === "string" ? t.icon : "wrench",
3910
- enabled: t.enabled
3911
- })) : [];
3852
+ const tools = Array.isArray(obj.tools) ? obj.tools.filter((t) => typeof t === "object" && t !== null && typeof t.name === "string" && typeof t.description === "string").map((t) => {
3853
+ if (typeof t.enabled !== "boolean") {
3854
+ console.warn(`[opentabs] Tool "${t.name}" in plugin "${obj.name}" is missing the "enabled" field \u2014 defaulting to enabled=true. This is a server-side bug.`);
3855
+ }
3856
+ return {
3857
+ name: t.name,
3858
+ displayName: typeof t.displayName === "string" ? t.displayName : t.name,
3859
+ description: t.description,
3860
+ icon: typeof t.icon === "string" ? t.icon : "wrench",
3861
+ enabled: typeof t.enabled === "boolean" ? t.enabled : true
3862
+ };
3863
+ }) : [];
3912
3864
  return {
3913
3865
  name: obj.name,
3914
3866
  version: typeof obj.version === "string" ? obj.version : "0.0.0",
@@ -3935,6 +3887,8 @@ var handleExtensionReload = (_params, id) => {
3935
3887
  });
3936
3888
  };
3937
3889
  var handleSyncFull = async (params) => {
3890
+ const syncStart = Date.now();
3891
+ console.log("[opentabs:timing] handleSyncFull started");
3938
3892
  const rawPlugins = params.plugins;
3939
3893
  if (!Array.isArray(rawPlugins))
3940
3894
  return;
@@ -3968,12 +3922,14 @@ var handleSyncFull = async (params) => {
3968
3922
  }
3969
3923
  const metas = uniquePlugins.map(toPluginMeta);
3970
3924
  await storePluginsBatch(metas);
3925
+ console.log(`[opentabs:timing] storePluginsBatch done: +${Date.now() - syncStart}ms`);
3971
3926
  const injectionResults = await Promise.allSettled(metas.map((meta) => injectPluginIntoMatchingTabs(meta.name, meta.urlPatterns, true, meta.adapterHash, meta.adapterFile, meta.adapterHash)));
3972
3927
  for (const result of injectionResults) {
3973
3928
  if (result.status === "rejected") {
3974
3929
  console.warn("[opentabs] Plugin injection failed during sync.full:", result.reason);
3975
3930
  }
3976
3931
  }
3932
+ console.log(`[opentabs:timing] injection done: +${Date.now() - syncStart}ms`);
3977
3933
  const rawPluginMap = /* @__PURE__ */ new Map();
3978
3934
  for (const raw of rawPlugins) {
3979
3935
  if (typeof raw === "object" && raw !== null && typeof raw.name === "string") {
@@ -4006,11 +3962,16 @@ var handleSyncFull = async (params) => {
4006
3962
  ...rawBrowserTools ? { browserTools: rawBrowserTools } : {},
4007
3963
  ...rawServerVersion !== void 0 ? { serverVersion: rawServerVersion } : {}
4008
3964
  });
3965
+ setCachesInitialized(true);
3966
+ flushServerStateCacheToSession();
3967
+ console.log(`[opentabs:timing] cache + flush done: +${Date.now() - syncStart}ms`);
4009
3968
  forwardToSidePanel({
4010
3969
  type: "sp:serverMessage",
4011
3970
  data: { jsonrpc: "2.0", method: "plugins.changed" }
4012
3971
  });
3972
+ console.log(`[opentabs:timing] plugins.changed sent to side panel: +${Date.now() - syncStart}ms`);
4013
3973
  void sendTabSyncAll().then(() => {
3974
+ flushLastKnownStateToSession();
4014
3975
  startReadinessPoll();
4015
3976
  });
4016
3977
  };
@@ -4075,14 +4036,24 @@ var handlePluginUninstall = async (params, id) => {
4075
4036
  }
4076
4037
  await removePlugin(pluginName);
4077
4038
  clearPluginTabState(pluginName);
4039
+ const existingCache = getServerStateCache();
4040
+ updateServerStateCache({ plugins: existingCache.plugins.filter((p) => p.name !== pluginName) });
4041
+ forwardToSidePanel({
4042
+ type: "sp:serverMessage",
4043
+ data: { jsonrpc: "2.0", method: "plugins.changed" }
4044
+ });
4078
4045
  sendToServer({
4079
4046
  jsonrpc: "2.0",
4080
4047
  result: { success: true },
4081
4048
  id
4082
4049
  });
4083
4050
  };
4084
- var handleExtensionGetTabState = (_params, id) => {
4085
- const states = getLastKnownStates();
4051
+ var handleExtensionGetTabState = async (_params, id) => {
4052
+ let states = getLastKnownStates();
4053
+ if (states.size === 0) {
4054
+ await loadLastKnownStateFromSession();
4055
+ states = getLastKnownStates();
4056
+ }
4086
4057
  const tabStates = {};
4087
4058
  for (const [pluginName, serialized] of states) {
4088
4059
  try {
@@ -4099,8 +4070,6 @@ var methodHandlers = /* @__PURE__ */ new Map([
4099
4070
  ["plugin.update", wrapNotification("plugin.update", handlePluginUpdate)],
4100
4071
  ["plugin.uninstall", wrapAsync("plugin.uninstall", handlePluginUninstall)],
4101
4072
  ["tool.dispatch", wrapAsync("tool.dispatch", handleToolDispatch)],
4102
- ["resource.read", wrapAsync("resource.read", handleResourceRead)],
4103
- ["prompt.get", wrapAsync("prompt.get", handlePromptGet)],
4104
4073
  ["browser.listTabs", wrapAsync("browser.listTabs", (_params, id) => handleBrowserListTabs(id))],
4105
4074
  ["browser.openTab", wrapAsync("browser.openTab", handleBrowserOpenTab)],
4106
4075
  ["browser.closeTab", wrapAsync("browser.closeTab", handleBrowserCloseTab)],
@@ -4140,7 +4109,7 @@ var methodHandlers = /* @__PURE__ */ new Map([
4140
4109
  "extension.forceReconnect",
4141
4110
  wrapAsync("extension.forceReconnect", (_params, id) => handleExtensionForceReconnect(id))
4142
4111
  ],
4143
- ["extension.getTabState", wrapSync("extension.getTabState", handleExtensionGetTabState)]
4112
+ ["extension.getTabState", wrapAsync("extension.getTabState", handleExtensionGetTabState)]
4144
4113
  ]);
4145
4114
  var handleServerMessage = (message) => {
4146
4115
  const method = message.method;
@@ -4195,14 +4164,18 @@ var methodHandlerNames = Array.from(methodHandlers.keys());
4195
4164
  // dist/background-message-handlers.js
4196
4165
  var wsConnected = false;
4197
4166
  var lastDisconnectReason;
4167
+ var wsConnectedRestorePromise;
4198
4168
  var restoreWsConnectedState = () => {
4199
- chrome.storage.session.get(WS_CONNECTED_KEY).then((data) => {
4169
+ if (wsConnectedRestorePromise !== void 0)
4170
+ return;
4171
+ wsConnectedRestorePromise = chrome.storage.session.get(WS_CONNECTED_KEY).then((data) => {
4200
4172
  if (typeof data[WS_CONNECTED_KEY] === "boolean") {
4201
4173
  wsConnected = data[WS_CONNECTED_KEY];
4202
4174
  }
4203
4175
  }).catch(() => {
4204
4176
  });
4205
4177
  };
4178
+ var waitForWsConnectedRestore = () => wsConnectedRestorePromise ?? Promise.resolve();
4206
4179
  var persistWsConnected = (connected) => {
4207
4180
  wsConnected = connected;
4208
4181
  chrome.storage.session.set({ [WS_CONNECTED_KEY]: connected }).catch(() => {
@@ -4239,22 +4212,24 @@ var handleWsState = (message, sendResponse) => {
4239
4212
  sendResponse({ ok: true });
4240
4213
  };
4241
4214
  var handleWsMessage = (message, sendResponse) => {
4242
- handleServerMessage(message.data);
4215
+ try {
4216
+ handleServerMessage(message.data);
4217
+ } catch (err2) {
4218
+ console.error("[opentabs:background] handleServerMessage threw:", err2);
4219
+ }
4243
4220
  sendResponse({ ok: true });
4244
4221
  };
4245
- var handleBgGetConnectionState = (_message, sendResponse) => {
4246
- sendResponse({
4247
- connected: wsConnected,
4248
- disconnectReason: wsConnected ? void 0 : lastDisconnectReason
4249
- });
4250
- };
4251
4222
  var handleBgGetFullState = (_message, sendResponse) => {
4252
4223
  (async () => {
4224
+ await waitForWsConnectedRestore();
4253
4225
  let tabStates = getLastKnownStates();
4254
4226
  let serverCache = getServerStateCache();
4255
4227
  if (wsConnected && tabStates.size === 0 && serverCache.plugins.length === 0) {
4256
- await Promise.all([loadLastKnownStateFromSession(), loadServerStateCacheFromSession()]);
4257
- tabStates = getLastKnownStates();
4228
+ await loadServerStateCacheFromSession();
4229
+ if (getCachesInitialized()) {
4230
+ await loadLastKnownStateFromSession();
4231
+ tabStates = getLastKnownStates();
4232
+ }
4258
4233
  serverCache = getServerStateCache();
4259
4234
  }
4260
4235
  const metaIndex = await getAllPluginMeta();
@@ -4301,7 +4276,8 @@ var handleBgGetFullState = (_message, sendResponse) => {
4301
4276
  plugins,
4302
4277
  failedPlugins: serverCache.failedPlugins,
4303
4278
  browserTools: serverCache.browserTools,
4304
- serverVersion: serverCache.serverVersion
4279
+ serverVersion: serverCache.serverVersion,
4280
+ pendingConfirmations: getPendingConfirmations()
4305
4281
  });
4306
4282
  })().catch(() => {
4307
4283
  sendResponse({
@@ -4310,7 +4286,8 @@ var handleBgGetFullState = (_message, sendResponse) => {
4310
4286
  plugins: [],
4311
4287
  failedPlugins: [],
4312
4288
  browserTools: [],
4313
- serverVersion: void 0
4289
+ serverVersion: void 0,
4290
+ pendingConfirmations: []
4314
4291
  });
4315
4292
  });
4316
4293
  };
@@ -4387,6 +4364,8 @@ var handleBgSetToolEnabled = (message, sendResponse) => {
4387
4364
  const tool = message.tool;
4388
4365
  const enabled = message.enabled;
4389
4366
  const cache2 = getServerStateCache();
4367
+ const pluginEntry = cache2.plugins.find((p) => p.name === plugin);
4368
+ const originalEnabled = pluginEntry?.tools.find((t) => t.name === tool)?.enabled ?? !enabled;
4390
4369
  const updatedPlugins = cache2.plugins.map((p) => {
4391
4370
  if (p.name !== plugin)
4392
4371
  return p;
@@ -4395,20 +4374,23 @@ var handleBgSetToolEnabled = (message, sendResponse) => {
4395
4374
  tools: p.tools.map((t) => t.name === tool ? { ...t, enabled } : t)
4396
4375
  };
4397
4376
  });
4377
+ addPendingPluginToolUpdate(plugin, tool, enabled);
4398
4378
  updateServerStateCache({ plugins: updatedPlugins });
4399
4379
  sendServerRequest("config.setToolEnabled", { plugin, tool, enabled }).then((result) => {
4380
+ removePendingPluginToolUpdate(plugin, tool);
4400
4381
  sendResponse(result);
4401
4382
  }).catch((err2) => {
4402
- const revertCache = getServerStateCache();
4403
- const revertPlugins = revertCache.plugins.map((p) => {
4383
+ removePendingPluginToolUpdate(plugin, tool);
4384
+ const currentCache = getServerStateCache();
4385
+ const revertedPlugins = currentCache.plugins.map((p) => {
4404
4386
  if (p.name !== plugin)
4405
4387
  return p;
4406
4388
  return {
4407
4389
  ...p,
4408
- tools: p.tools.map((t) => t.name === tool ? { ...t, enabled: !enabled } : t)
4390
+ tools: p.tools.map((t) => t.name === tool ? { ...t, enabled: originalEnabled } : t)
4409
4391
  };
4410
4392
  });
4411
- updateServerStateCache({ plugins: revertPlugins });
4393
+ updateServerStateCache({ plugins: revertedPlugins });
4412
4394
  sendResponse({ error: err2 instanceof Error ? err2.message : String(err2) });
4413
4395
  });
4414
4396
  };
@@ -4416,7 +4398,14 @@ var handleBgSetAllToolsEnabled = (message, sendResponse) => {
4416
4398
  const plugin = message.plugin;
4417
4399
  const enabled = message.enabled;
4418
4400
  const cache2 = getServerStateCache();
4419
- const originalPlugins = cache2.plugins;
4401
+ const pluginEntry = cache2.plugins.find((p) => p.name === plugin);
4402
+ const toolNames = pluginEntry ? pluginEntry.tools.map((t) => t.name) : [];
4403
+ const originalToolStates = /* @__PURE__ */ new Map();
4404
+ if (pluginEntry) {
4405
+ for (const t of pluginEntry.tools) {
4406
+ originalToolStates.set(t.name, t.enabled);
4407
+ }
4408
+ }
4420
4409
  const updatedPlugins = cache2.plugins.map((p) => {
4421
4410
  if (p.name !== plugin)
4422
4411
  return p;
@@ -4425,11 +4414,26 @@ var handleBgSetAllToolsEnabled = (message, sendResponse) => {
4425
4414
  tools: p.tools.map((t) => ({ ...t, enabled }))
4426
4415
  };
4427
4416
  });
4417
+ addPendingPluginAllToolsUpdate(plugin, toolNames, enabled);
4428
4418
  updateServerStateCache({ plugins: updatedPlugins });
4429
4419
  sendServerRequest("config.setAllToolsEnabled", { plugin, enabled }).then((result) => {
4420
+ removePendingPluginAllToolsUpdate(plugin, toolNames);
4430
4421
  sendResponse(result);
4431
4422
  }).catch((err2) => {
4432
- updateServerStateCache({ plugins: originalPlugins });
4423
+ removePendingPluginAllToolsUpdate(plugin, toolNames);
4424
+ const currentCache = getServerStateCache();
4425
+ const revertedPlugins = currentCache.plugins.map((p) => {
4426
+ if (p.name !== plugin)
4427
+ return p;
4428
+ return {
4429
+ ...p,
4430
+ tools: p.tools.map((t) => ({
4431
+ ...t,
4432
+ enabled: originalToolStates.get(t.name) ?? t.enabled
4433
+ }))
4434
+ };
4435
+ });
4436
+ updateServerStateCache({ plugins: revertedPlugins });
4433
4437
  sendResponse({ error: err2 instanceof Error ? err2.message : String(err2) });
4434
4438
  });
4435
4439
  };
@@ -4437,27 +4441,43 @@ var handleBgSetBrowserToolEnabled = (message, sendResponse) => {
4437
4441
  const tool = message.tool;
4438
4442
  const enabled = message.enabled;
4439
4443
  const cache2 = getServerStateCache();
4444
+ const originalEnabled = cache2.browserTools.find((bt) => bt.name === tool)?.enabled ?? !enabled;
4440
4445
  const updatedBrowserTools = cache2.browserTools.map((bt) => bt.name === tool ? { ...bt, enabled } : bt);
4446
+ addPendingBrowserToolUpdate(tool, enabled);
4441
4447
  updateServerStateCache({ browserTools: updatedBrowserTools });
4442
4448
  sendServerRequest("config.setBrowserToolEnabled", { tool, enabled }).then((result) => {
4449
+ removePendingBrowserToolUpdate(tool);
4443
4450
  sendResponse(result);
4444
4451
  }).catch((err2) => {
4445
- const revertCache = getServerStateCache();
4446
- const revertBrowserTools = revertCache.browserTools.map((bt) => bt.name === tool ? { ...bt, enabled: !enabled } : bt);
4447
- updateServerStateCache({ browserTools: revertBrowserTools });
4452
+ removePendingBrowserToolUpdate(tool);
4453
+ const currentCache = getServerStateCache();
4454
+ const revertedBrowserTools = currentCache.browserTools.map((bt) => bt.name === tool ? { ...bt, enabled: originalEnabled } : bt);
4455
+ updateServerStateCache({ browserTools: revertedBrowserTools });
4448
4456
  sendResponse({ error: err2 instanceof Error ? err2.message : String(err2) });
4449
4457
  });
4450
4458
  };
4451
4459
  var handleBgSetAllBrowserToolsEnabled = (message, sendResponse) => {
4452
4460
  const enabled = message.enabled;
4453
4461
  const cache2 = getServerStateCache();
4454
- const originalBrowserTools = cache2.browserTools;
4462
+ const toolNames = cache2.browserTools.map((bt) => bt.name);
4463
+ const originalToolStates = /* @__PURE__ */ new Map();
4464
+ for (const bt of cache2.browserTools) {
4465
+ originalToolStates.set(bt.name, bt.enabled);
4466
+ }
4455
4467
  const updatedBrowserTools = cache2.browserTools.map((bt) => ({ ...bt, enabled }));
4468
+ addPendingAllBrowserToolsUpdate(toolNames, enabled);
4456
4469
  updateServerStateCache({ browserTools: updatedBrowserTools });
4457
4470
  sendServerRequest("config.setAllBrowserToolsEnabled", { enabled }).then((result) => {
4471
+ removePendingAllBrowserToolsUpdate(toolNames);
4458
4472
  sendResponse(result);
4459
4473
  }).catch((err2) => {
4460
- updateServerStateCache({ browserTools: originalBrowserTools });
4474
+ removePendingAllBrowserToolsUpdate(toolNames);
4475
+ const currentCache = getServerStateCache();
4476
+ const revertedBrowserTools = currentCache.browserTools.map((bt) => ({
4477
+ ...bt,
4478
+ enabled: originalToolStates.get(bt.name) ?? bt.enabled
4479
+ }));
4480
+ updateServerStateCache({ browserTools: revertedBrowserTools });
4461
4481
  sendResponse({ error: err2 instanceof Error ? err2.message : String(err2) });
4462
4482
  });
4463
4483
  };
@@ -4502,7 +4522,6 @@ var backgroundHandlers = /* @__PURE__ */ new Map([
4502
4522
  ["offscreen:getUrl", handleOffscreenGetUrl],
4503
4523
  ["ws:state", handleWsState],
4504
4524
  ["ws:message", handleWsMessage],
4505
- ["bg:getConnectionState", handleBgGetConnectionState],
4506
4525
  ["bg:getFullState", handleBgGetFullState],
4507
4526
  ["bg:setToolEnabled", handleBgSetToolEnabled],
4508
4527
  ["bg:setAllToolsEnabled", handleBgSetAllToolsEnabled],
@@ -4522,7 +4541,6 @@ var EXTENSION_ONLY_TYPES = /* @__PURE__ */ new Set([
4522
4541
  "offscreen:getUrl",
4523
4542
  "ws:state",
4524
4543
  "ws:message",
4525
- "bg:getConnectionState",
4526
4544
  "bg:getFullState",
4527
4545
  "bg:setToolEnabled",
4528
4546
  "bg:setAllToolsEnabled",