@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.
- package/dist/background-message-handlers.d.ts +10 -4
- package/dist/background-message-handlers.d.ts.map +1 -1
- package/dist/background-message-handlers.js +110 -37
- package/dist/background-message-handlers.js.map +1 -1
- package/dist/background.js +392 -374
- package/dist/browser-commands/extension-commands.d.ts.map +1 -1
- package/dist/browser-commands/extension-commands.js +5 -2
- package/dist/browser-commands/extension-commands.js.map +1 -1
- package/dist/browser-commands/key-press-command.d.ts +2 -0
- package/dist/browser-commands/key-press-command.d.ts.map +1 -1
- package/dist/browser-commands/key-press-command.js +42 -4
- package/dist/browser-commands/key-press-command.js.map +1 -1
- package/dist/confirmation-badge.d.ts +15 -1
- package/dist/confirmation-badge.d.ts.map +1 -1
- package/dist/confirmation-badge.js +13 -8
- package/dist/confirmation-badge.js.map +1 -1
- package/dist/dispatch-helpers.d.ts +1 -1
- package/dist/extension-messages.d.ts +1 -17
- package/dist/extension-messages.d.ts.map +1 -1
- package/dist/known-methods.d.ts +2 -2
- package/dist/known-methods.d.ts.map +1 -1
- package/dist/known-methods.js +0 -2
- package/dist/known-methods.js.map +1 -1
- package/dist/message-router.d.ts.map +1 -1
- package/dist/message-router.js +52 -21
- package/dist/message-router.js.map +1 -1
- package/dist/offscreen/index.js +6 -5
- package/dist/offscreen/index.js.map +1 -1
- package/dist/server-request.js +1 -1
- package/dist/server-request.js.map +1 -1
- package/dist/server-state-cache.d.ts +30 -3
- package/dist/server-state-cache.d.ts.map +1 -1
- package/dist/server-state-cache.js +139 -6
- package/dist/server-state-cache.js.map +1 -1
- package/dist/side-panel/App.d.ts.map +1 -1
- package/dist/side-panel/App.js +37 -4
- package/dist/side-panel/App.js.map +1 -1
- package/dist/side-panel/bridge.d.ts +12 -1
- package/dist/side-panel/bridge.d.ts.map +1 -1
- package/dist/side-panel/bridge.js.map +1 -1
- package/dist/side-panel/components/ConfirmationDialog.d.ts +7 -2
- package/dist/side-panel/components/ConfirmationDialog.d.ts.map +1 -1
- package/dist/side-panel/components/ConfirmationDialog.js.map +1 -1
- package/dist/side-panel/components/ToolIcon.js +1 -1
- package/dist/side-panel/hooks/useServerNotifications.d.ts.map +1 -1
- package/dist/side-panel/hooks/useServerNotifications.js +13 -3
- package/dist/side-panel/hooks/useServerNotifications.js.map +1 -1
- package/dist/side-panel/side-panel.js +73 -28
- package/dist/side-panel/styles.css +1 -1
- package/dist/tab-state.d.ts +8 -1
- package/dist/tab-state.d.ts.map +1 -1
- package/dist/tab-state.js +36 -9
- package/dist/tab-state.js.map +1 -1
- package/dist/tool-dispatch.d.ts.map +1 -1
- package/dist/tool-dispatch.js +3 -2
- package/dist/tool-dispatch.js.map +1 -1
- package/manifest.json +1 -1
- package/package.json +1 -1
package/dist/background.js
CHANGED
|
@@ -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
|
|
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 &&
|
|
120
|
-
const info =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
1339
|
-
|
|
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
|
-
|
|
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) =>
|
|
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
|
-
|
|
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
|
|
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(
|
|
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.
|
|
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"
|
|
3906
|
-
|
|
3907
|
-
|
|
3908
|
-
|
|
3909
|
-
|
|
3910
|
-
|
|
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
|
-
|
|
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",
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
4257
|
-
|
|
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
|
-
|
|
4403
|
-
const
|
|
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:
|
|
4390
|
+
tools: p.tools.map((t) => t.name === tool ? { ...t, enabled: originalEnabled } : t)
|
|
4409
4391
|
};
|
|
4410
4392
|
});
|
|
4411
|
-
updateServerStateCache({ plugins:
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
4446
|
-
const
|
|
4447
|
-
|
|
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
|
|
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
|
-
|
|
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",
|