@btraut/browser-bridge 0.14.0 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +30 -0
- package/README.md +27 -2
- package/dist/api.js +893 -166
- package/dist/api.js.map +4 -4
- package/dist/index.js +349 -115
- package/dist/index.js.map +4 -4
- package/extension/dist/background.js +468 -18
- package/extension/dist/background.js.map +4 -4
- package/extension/dist/content.js +71 -8
- package/extension/dist/content.js.map +2 -2
- package/extension/dist/permissions-request-ui.js +111 -0
- package/extension/dist/permissions-request-ui.js.map +7 -0
- package/extension/manifest.json +2 -2
- package/package.json +1 -1
- package/skills/browser-bridge/SKILL.md +9 -0
- package/skills/browser-bridge/skill.json +1 -1
package/dist/api.js
CHANGED
|
@@ -687,9 +687,10 @@ var createCoreReadinessController = (options = {}) => {
|
|
|
687
687
|
return;
|
|
688
688
|
}
|
|
689
689
|
if (!ensurePromise) {
|
|
690
|
-
ensurePromise =
|
|
690
|
+
ensurePromise = (async () => {
|
|
691
|
+
await ensureCoreRunning();
|
|
692
|
+
})().finally(() => {
|
|
691
693
|
ensurePromise = null;
|
|
692
|
-
throw error;
|
|
693
694
|
});
|
|
694
695
|
}
|
|
695
696
|
await ensurePromise;
|
|
@@ -1096,6 +1097,63 @@ var SessionCloseInputSchema = SessionIdSchema;
|
|
|
1096
1097
|
var SessionCloseOutputSchema = import_zod3.z.object({
|
|
1097
1098
|
ok: import_zod3.z.boolean()
|
|
1098
1099
|
});
|
|
1100
|
+
var PermissionsModeSchema = import_zod3.z.enum(["granular", "bypass"]);
|
|
1101
|
+
var PermissionsRequestSourceSchema = import_zod3.z.enum(["cli", "mcp", "api"]);
|
|
1102
|
+
var PermissionsPendingRequestKindSchema = import_zod3.z.enum([
|
|
1103
|
+
"allow_site",
|
|
1104
|
+
"revoke_site",
|
|
1105
|
+
"set_mode"
|
|
1106
|
+
]);
|
|
1107
|
+
var PermissionsPendingRequestStatusSchema = import_zod3.z.enum([
|
|
1108
|
+
"pending",
|
|
1109
|
+
"approved",
|
|
1110
|
+
"denied",
|
|
1111
|
+
"timed_out"
|
|
1112
|
+
]);
|
|
1113
|
+
var PermissionsSiteEntrySchema = import_zod3.z.object({
|
|
1114
|
+
site: import_zod3.z.string().min(1),
|
|
1115
|
+
created_at: import_zod3.z.string().datetime(),
|
|
1116
|
+
last_used_at: import_zod3.z.string().datetime()
|
|
1117
|
+
});
|
|
1118
|
+
var PermissionsListInputSchema = import_zod3.z.object({}).strict().default({});
|
|
1119
|
+
var PermissionsListOutputSchema = import_zod3.z.object({
|
|
1120
|
+
sites: import_zod3.z.array(PermissionsSiteEntrySchema)
|
|
1121
|
+
});
|
|
1122
|
+
var PermissionsGetModeInputSchema = import_zod3.z.object({}).strict().default({});
|
|
1123
|
+
var PermissionsGetModeOutputSchema = import_zod3.z.object({
|
|
1124
|
+
mode: PermissionsModeSchema
|
|
1125
|
+
});
|
|
1126
|
+
var PermissionsPendingRequestSchema = import_zod3.z.object({
|
|
1127
|
+
request_id: import_zod3.z.string().min(1),
|
|
1128
|
+
kind: PermissionsPendingRequestKindSchema,
|
|
1129
|
+
status: PermissionsPendingRequestStatusSchema,
|
|
1130
|
+
requested_at: import_zod3.z.string().datetime(),
|
|
1131
|
+
site: import_zod3.z.string().min(1).optional(),
|
|
1132
|
+
mode: PermissionsModeSchema.optional(),
|
|
1133
|
+
source: PermissionsRequestSourceSchema.optional(),
|
|
1134
|
+
warning: import_zod3.z.string().optional(),
|
|
1135
|
+
message: import_zod3.z.string().optional()
|
|
1136
|
+
});
|
|
1137
|
+
var PermissionsListPendingRequestsInputSchema = import_zod3.z.object({}).strict().default({});
|
|
1138
|
+
var PermissionsListPendingRequestsOutputSchema = import_zod3.z.object({
|
|
1139
|
+
requests: import_zod3.z.array(PermissionsPendingRequestSchema)
|
|
1140
|
+
});
|
|
1141
|
+
var PermissionsRequestAllowSiteInputSchema = import_zod3.z.object({
|
|
1142
|
+
site: import_zod3.z.string().min(1),
|
|
1143
|
+
timeout_ms: import_zod3.z.number().int().positive().max(3e5).optional(),
|
|
1144
|
+
source: PermissionsRequestSourceSchema.optional()
|
|
1145
|
+
});
|
|
1146
|
+
var PermissionsRequestRevokeSiteInputSchema = import_zod3.z.object({
|
|
1147
|
+
site: import_zod3.z.string().min(1),
|
|
1148
|
+
timeout_ms: import_zod3.z.number().int().positive().max(3e5).optional(),
|
|
1149
|
+
source: PermissionsRequestSourceSchema.optional()
|
|
1150
|
+
});
|
|
1151
|
+
var PermissionsRequestSetModeInputSchema = import_zod3.z.object({
|
|
1152
|
+
mode: PermissionsModeSchema,
|
|
1153
|
+
timeout_ms: import_zod3.z.number().int().positive().max(3e5).optional(),
|
|
1154
|
+
source: PermissionsRequestSourceSchema.optional()
|
|
1155
|
+
});
|
|
1156
|
+
var PermissionsRequestOutputSchema = PermissionsPendingRequestSchema;
|
|
1099
1157
|
var DriveWaitConditionSchema = import_zod3.z.object({
|
|
1100
1158
|
kind: import_zod3.z.enum(["locator_visible", "text_present", "url_matches"]),
|
|
1101
1159
|
value: import_zod3.z.string().min(1)
|
|
@@ -1282,6 +1340,12 @@ var FormFieldInfoSchema = import_zod3.z.object({
|
|
|
1282
1340
|
name: import_zod3.z.string(),
|
|
1283
1341
|
type: import_zod3.z.string(),
|
|
1284
1342
|
value: import_zod3.z.string(),
|
|
1343
|
+
selector: import_zod3.z.string().optional(),
|
|
1344
|
+
label: import_zod3.z.string().optional(),
|
|
1345
|
+
placeholder: import_zod3.z.string().optional(),
|
|
1346
|
+
checked: import_zod3.z.boolean().optional(),
|
|
1347
|
+
disabled: import_zod3.z.boolean().optional(),
|
|
1348
|
+
visible: import_zod3.z.boolean().optional(),
|
|
1285
1349
|
options: import_zod3.z.array(import_zod3.z.string()).optional()
|
|
1286
1350
|
});
|
|
1287
1351
|
var FormInfoSchema = import_zod3.z.object({
|
|
@@ -1294,7 +1358,31 @@ var StorageEntrySchema = import_zod3.z.object({
|
|
|
1294
1358
|
key: import_zod3.z.string(),
|
|
1295
1359
|
value: import_zod3.z.string()
|
|
1296
1360
|
});
|
|
1361
|
+
var FocusedElementSchema = import_zod3.z.object({
|
|
1362
|
+
selector: import_zod3.z.string().optional(),
|
|
1363
|
+
name: import_zod3.z.string().optional(),
|
|
1364
|
+
label: import_zod3.z.string().optional(),
|
|
1365
|
+
role: import_zod3.z.string().optional(),
|
|
1366
|
+
type: import_zod3.z.string().optional(),
|
|
1367
|
+
text: import_zod3.z.string().optional()
|
|
1368
|
+
});
|
|
1369
|
+
var PageActionSchema = import_zod3.z.object({
|
|
1370
|
+
selector: import_zod3.z.string(),
|
|
1371
|
+
role: import_zod3.z.string(),
|
|
1372
|
+
name: import_zod3.z.string()
|
|
1373
|
+
});
|
|
1374
|
+
var StorageSummarySchema = import_zod3.z.object({
|
|
1375
|
+
localStorageCount: import_zod3.z.number().int().nonnegative(),
|
|
1376
|
+
sessionStorageCount: import_zod3.z.number().int().nonnegative(),
|
|
1377
|
+
cookieCount: import_zod3.z.number().int().nonnegative()
|
|
1378
|
+
});
|
|
1297
1379
|
var PageStateSchema = import_zod3.z.object({
|
|
1380
|
+
url: import_zod3.z.string().optional(),
|
|
1381
|
+
title: import_zod3.z.string().optional(),
|
|
1382
|
+
readyState: import_zod3.z.string().optional(),
|
|
1383
|
+
focused: FocusedElementSchema.optional(),
|
|
1384
|
+
primaryActions: import_zod3.z.array(PageActionSchema).optional(),
|
|
1385
|
+
storageSummary: StorageSummarySchema.optional(),
|
|
1298
1386
|
forms: import_zod3.z.array(FormInfoSchema),
|
|
1299
1387
|
localStorage: import_zod3.z.array(StorageEntrySchema),
|
|
1300
1388
|
sessionStorage: import_zod3.z.array(StorageEntrySchema),
|
|
@@ -1357,6 +1445,7 @@ var InspectFindOutputSchema = import_zod3.z.object({
|
|
|
1357
1445
|
warnings: import_zod3.z.array(import_zod3.z.string()).optional()
|
|
1358
1446
|
});
|
|
1359
1447
|
var InspectPageStateInputSchema = SessionIdSchema.extend({
|
|
1448
|
+
include_values: import_zod3.z.boolean().default(false),
|
|
1360
1449
|
target: TargetHintSchema.optional()
|
|
1361
1450
|
});
|
|
1362
1451
|
var InspectPageStateOutputSchema = PageStateSchema;
|
|
@@ -1367,6 +1456,7 @@ var InspectExtractContentFormatSchema = import_zod3.z.enum([
|
|
|
1367
1456
|
]);
|
|
1368
1457
|
var InspectExtractContentInputSchema = SessionIdSchema.extend({
|
|
1369
1458
|
format: InspectExtractContentFormatSchema.default("markdown"),
|
|
1459
|
+
consistency: InspectConsistencySchema.default("quiesce"),
|
|
1370
1460
|
include_metadata: import_zod3.z.boolean().default(true),
|
|
1371
1461
|
target: TargetHintSchema.optional()
|
|
1372
1462
|
});
|
|
@@ -1379,6 +1469,7 @@ var InspectExtractContentOutputSchema = import_zod3.z.object({
|
|
|
1379
1469
|
warnings: import_zod3.z.array(import_zod3.z.string()).optional()
|
|
1380
1470
|
});
|
|
1381
1471
|
var InspectConsoleListInputSchema = SessionIdSchema.extend({
|
|
1472
|
+
since: import_zod3.z.string().optional(),
|
|
1382
1473
|
target: TargetHintSchema.optional()
|
|
1383
1474
|
});
|
|
1384
1475
|
var ConsoleSourceLocationSchema = import_zod3.z.object({
|
|
@@ -1983,6 +2074,7 @@ var ExtensionBridge = class {
|
|
|
1983
2074
|
constructor(options = {}) {
|
|
1984
2075
|
this.socket = null;
|
|
1985
2076
|
this.pending = /* @__PURE__ */ new Map();
|
|
2077
|
+
this.readyWaiters = /* @__PURE__ */ new Set();
|
|
1986
2078
|
this.connected = false;
|
|
1987
2079
|
this.capabilityNegotiated = false;
|
|
1988
2080
|
this.capabilities = {};
|
|
@@ -2031,6 +2123,36 @@ var ExtensionBridge = class {
|
|
|
2031
2123
|
tabs: this.tabs
|
|
2032
2124
|
};
|
|
2033
2125
|
}
|
|
2126
|
+
async waitForReady(timeoutMs = 1500) {
|
|
2127
|
+
if (this.isReadyForRequests()) {
|
|
2128
|
+
return true;
|
|
2129
|
+
}
|
|
2130
|
+
if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {
|
|
2131
|
+
return this.isReadyForRequests();
|
|
2132
|
+
}
|
|
2133
|
+
return await new Promise((resolve3) => {
|
|
2134
|
+
let settled = false;
|
|
2135
|
+
const finish = (value) => {
|
|
2136
|
+
if (settled) {
|
|
2137
|
+
return;
|
|
2138
|
+
}
|
|
2139
|
+
settled = true;
|
|
2140
|
+
clearTimeout(timeout);
|
|
2141
|
+
this.readyWaiters.delete(waiter);
|
|
2142
|
+
resolve3(value);
|
|
2143
|
+
};
|
|
2144
|
+
const waiter = () => {
|
|
2145
|
+
if (this.isReadyForRequests()) {
|
|
2146
|
+
finish(true);
|
|
2147
|
+
}
|
|
2148
|
+
};
|
|
2149
|
+
const timeout = setTimeout(() => {
|
|
2150
|
+
finish(this.isReadyForRequests());
|
|
2151
|
+
}, timeoutMs);
|
|
2152
|
+
this.readyWaiters.add(waiter);
|
|
2153
|
+
waiter();
|
|
2154
|
+
});
|
|
2155
|
+
}
|
|
2034
2156
|
async request(action, params, timeoutMs = 3e4) {
|
|
2035
2157
|
const response = await this.requestInternal(action, params, timeoutMs);
|
|
2036
2158
|
return response;
|
|
@@ -2102,6 +2224,7 @@ var ExtensionBridge = class {
|
|
|
2102
2224
|
status: "request",
|
|
2103
2225
|
params
|
|
2104
2226
|
};
|
|
2227
|
+
const envelope2 = request;
|
|
2105
2228
|
const response = await new Promise((resolve3, reject) => {
|
|
2106
2229
|
const timeout = setTimeout(() => {
|
|
2107
2230
|
this.pending.delete(id);
|
|
@@ -2119,7 +2242,7 @@ var ExtensionBridge = class {
|
|
|
2119
2242
|
reject,
|
|
2120
2243
|
timeout
|
|
2121
2244
|
});
|
|
2122
|
-
this.socket?.send(JSON.stringify(
|
|
2245
|
+
this.socket?.send(JSON.stringify(envelope2));
|
|
2123
2246
|
});
|
|
2124
2247
|
return response;
|
|
2125
2248
|
}
|
|
@@ -2310,6 +2433,7 @@ var ExtensionBridge = class {
|
|
|
2310
2433
|
expected: "drive.hello.capabilities"
|
|
2311
2434
|
};
|
|
2312
2435
|
}
|
|
2436
|
+
this.flushReadyWaiters();
|
|
2313
2437
|
}
|
|
2314
2438
|
}
|
|
2315
2439
|
if (typeof message.action === "string" && message.action.startsWith("debugger.")) {
|
|
@@ -2325,6 +2449,23 @@ var ExtensionBridge = class {
|
|
|
2325
2449
|
}
|
|
2326
2450
|
}
|
|
2327
2451
|
}
|
|
2452
|
+
isReadyForRequests() {
|
|
2453
|
+
return this.connected && this.capabilityNegotiated;
|
|
2454
|
+
}
|
|
2455
|
+
flushReadyWaiters() {
|
|
2456
|
+
if (!this.isReadyForRequests() || this.readyWaiters.size === 0) {
|
|
2457
|
+
return;
|
|
2458
|
+
}
|
|
2459
|
+
const waiters = Array.from(this.readyWaiters);
|
|
2460
|
+
this.readyWaiters.clear();
|
|
2461
|
+
for (const waiter of waiters) {
|
|
2462
|
+
try {
|
|
2463
|
+
waiter();
|
|
2464
|
+
} catch (error) {
|
|
2465
|
+
console.debug("Extension ready waiter failed.", error);
|
|
2466
|
+
}
|
|
2467
|
+
}
|
|
2468
|
+
}
|
|
2328
2469
|
applyDriveConnected() {
|
|
2329
2470
|
if (!this.registry) {
|
|
2330
2471
|
return;
|
|
@@ -2379,7 +2520,8 @@ var toDriveError = (error) => ({
|
|
|
2379
2520
|
// packages/core/src/drive.ts
|
|
2380
2521
|
var LOOPBACK_NAVIGATION_PREFLIGHT_TIMEOUT_MS = 1200;
|
|
2381
2522
|
var POST_CLICK_SETTLE_MS = 75;
|
|
2382
|
-
var
|
|
2523
|
+
var TRANSIENT_LOCATOR_RETRY_DELAYS_MS = [150, 300, 600];
|
|
2524
|
+
var EXTENSION_READY_WAIT_MS = 1500;
|
|
2383
2525
|
var LOOPBACK_HOSTS = /* @__PURE__ */ new Set(["localhost", "127.0.0.1", "::1"]);
|
|
2384
2526
|
var ACTIONS_WITH_OPTIONAL_TAB_ID = /* @__PURE__ */ new Set([
|
|
2385
2527
|
"drive.navigate",
|
|
@@ -2497,6 +2639,16 @@ var DriveController = class {
|
|
|
2497
2639
|
async sleep(ms) {
|
|
2498
2640
|
await new Promise((resolve3) => setTimeout(resolve3, ms));
|
|
2499
2641
|
}
|
|
2642
|
+
async waitForBridgeReady() {
|
|
2643
|
+
if (this.bridge.isConnected()) {
|
|
2644
|
+
return true;
|
|
2645
|
+
}
|
|
2646
|
+
const waitForReady = this.bridge.waitForReady;
|
|
2647
|
+
if (typeof waitForReady === "function") {
|
|
2648
|
+
return await waitForReady.call(this.bridge, EXTENSION_READY_WAIT_MS);
|
|
2649
|
+
}
|
|
2650
|
+
return this.bridge.isConnected();
|
|
2651
|
+
}
|
|
2500
2652
|
isTransientLocatorError(action, error) {
|
|
2501
2653
|
if (action !== "drive.click") {
|
|
2502
2654
|
return false;
|
|
@@ -2538,7 +2690,7 @@ var DriveController = class {
|
|
|
2538
2690
|
error: errorInfo
|
|
2539
2691
|
};
|
|
2540
2692
|
}
|
|
2541
|
-
if (!this.
|
|
2693
|
+
if (!await this.waitForBridgeReady()) {
|
|
2542
2694
|
const errorInfo = {
|
|
2543
2695
|
code: "EXTENSION_DISCONNECTED",
|
|
2544
2696
|
message: "Extension is not connected. Open Chrome with the Browser Bridge extension enabled, then retry.",
|
|
@@ -2604,9 +2756,10 @@ var DriveController = class {
|
|
|
2604
2756
|
max_attempts: 1
|
|
2605
2757
|
}
|
|
2606
2758
|
};
|
|
2607
|
-
if (attempt
|
|
2759
|
+
if (attempt < TRANSIENT_LOCATOR_RETRY_DELAYS_MS.length && this.isTransientLocatorError(action, errorInfo)) {
|
|
2760
|
+
const delayMs = TRANSIENT_LOCATOR_RETRY_DELAYS_MS[attempt] ?? 0;
|
|
2608
2761
|
attempt += 1;
|
|
2609
|
-
await this.sleep(
|
|
2762
|
+
await this.sleep(delayMs);
|
|
2610
2763
|
continue;
|
|
2611
2764
|
}
|
|
2612
2765
|
if (shouldRetryDriveOp({
|
|
@@ -2884,6 +3037,10 @@ var filterAxSnapshot = (snapshot, predicate) => {
|
|
|
2884
3037
|
}
|
|
2885
3038
|
return replaceAxNodes(snapshot, filtered);
|
|
2886
3039
|
};
|
|
3040
|
+
var filterAxSnapshotByRefs = (snapshot, allowedRefs) => filterAxSnapshot(snapshot, (node) => {
|
|
3041
|
+
const ref = node.ref;
|
|
3042
|
+
return typeof ref === "string" && allowedRefs.has(ref);
|
|
3043
|
+
});
|
|
2887
3044
|
var collectKeptDescendants = (nodeId, nodeById, keepIds, visited = /* @__PURE__ */ new Set()) => {
|
|
2888
3045
|
if (visited.has(nodeId)) {
|
|
2889
3046
|
return [];
|
|
@@ -3514,7 +3671,8 @@ var captureHtml = async (tabId, options) => {
|
|
|
3514
3671
|
const result = await options.debuggerCommand(tabId, "Runtime.evaluate", {
|
|
3515
3672
|
expression,
|
|
3516
3673
|
returnByValue: true,
|
|
3517
|
-
awaitPromise: true
|
|
3674
|
+
awaitPromise: true,
|
|
3675
|
+
...typeof options.executionContextId === "number" ? { contextId: options.executionContextId } : {}
|
|
3518
3676
|
});
|
|
3519
3677
|
if (result && typeof result === "object" && "exceptionDetails" in result) {
|
|
3520
3678
|
return options.onEvaluationFailed();
|
|
@@ -4033,111 +4191,209 @@ var selectInspectTab = (options) => {
|
|
|
4033
4191
|
};
|
|
4034
4192
|
|
|
4035
4193
|
// packages/core/src/page-state-script.ts
|
|
4036
|
-
var
|
|
4037
|
-
|
|
4038
|
-
|
|
4039
|
-
|
|
4040
|
-
"
|
|
4041
|
-
"
|
|
4042
|
-
|
|
4043
|
-
|
|
4044
|
-
|
|
4045
|
-
|
|
4046
|
-
|
|
4047
|
-
|
|
4048
|
-
|
|
4049
|
-
|
|
4050
|
-
|
|
4051
|
-
"
|
|
4052
|
-
|
|
4053
|
-
|
|
4054
|
-
|
|
4055
|
-
|
|
4056
|
-
|
|
4057
|
-
|
|
4058
|
-
|
|
4059
|
-
|
|
4060
|
-
|
|
4061
|
-
|
|
4062
|
-
|
|
4063
|
-
|
|
4064
|
-
|
|
4065
|
-
"
|
|
4066
|
-
|
|
4067
|
-
|
|
4068
|
-
|
|
4069
|
-
|
|
4070
|
-
|
|
4071
|
-
|
|
4072
|
-
|
|
4073
|
-
|
|
4074
|
-
|
|
4075
|
-
|
|
4076
|
-
|
|
4077
|
-
|
|
4078
|
-
|
|
4079
|
-
|
|
4080
|
-
|
|
4081
|
-
|
|
4082
|
-
|
|
4083
|
-
|
|
4084
|
-
|
|
4085
|
-
|
|
4086
|
-
|
|
4087
|
-
|
|
4088
|
-
|
|
4089
|
-
|
|
4090
|
-
|
|
4091
|
-
|
|
4092
|
-
|
|
4093
|
-
|
|
4094
|
-
|
|
4095
|
-
|
|
4096
|
-
|
|
4097
|
-
|
|
4098
|
-
|
|
4099
|
-
|
|
4100
|
-
|
|
4101
|
-
|
|
4102
|
-
|
|
4103
|
-
|
|
4104
|
-
|
|
4105
|
-
|
|
4106
|
-
|
|
4107
|
-
|
|
4108
|
-
|
|
4109
|
-
|
|
4110
|
-
|
|
4111
|
-
|
|
4112
|
-
|
|
4113
|
-
|
|
4114
|
-
|
|
4115
|
-
|
|
4116
|
-
|
|
4117
|
-
|
|
4118
|
-
|
|
4119
|
-
|
|
4120
|
-
|
|
4121
|
-
|
|
4122
|
-
|
|
4123
|
-
|
|
4124
|
-
|
|
4125
|
-
|
|
4126
|
-
|
|
4127
|
-
|
|
4128
|
-
|
|
4129
|
-
|
|
4130
|
-
|
|
4131
|
-
|
|
4132
|
-
|
|
4133
|
-
|
|
4134
|
-
]
|
|
4194
|
+
var buildPageStateScript = (options) => {
|
|
4195
|
+
const includeValues = options?.includeValues === true;
|
|
4196
|
+
return [
|
|
4197
|
+
"(() => {",
|
|
4198
|
+
` const includeValues = ${includeValues ? "true" : "false"};`,
|
|
4199
|
+
" const escape = (value) => {",
|
|
4200
|
+
" if (typeof CSS !== 'undefined' && typeof CSS.escape === 'function') {",
|
|
4201
|
+
" return CSS.escape(value);",
|
|
4202
|
+
" }",
|
|
4203
|
+
` return String(value).replace(/["'\\\\]/g, '\\\\$&');`,
|
|
4204
|
+
" };",
|
|
4205
|
+
" const truncate = (value, max) => {",
|
|
4206
|
+
" const text = String(value ?? '');",
|
|
4207
|
+
" return text.length > max ? text.slice(0, max) : text;",
|
|
4208
|
+
" };",
|
|
4209
|
+
' const redact = (value) => (includeValues ? truncate(value, 500) : "[redacted]");',
|
|
4210
|
+
' const textFor = (element) => truncate((element?.textContent || "").replace(/\\s+/g, " ").trim(), 120);',
|
|
4211
|
+
" const isVisible = (element) => {",
|
|
4212
|
+
' if (!element || typeof element.getClientRects !== "function") {',
|
|
4213
|
+
" return false;",
|
|
4214
|
+
" }",
|
|
4215
|
+
" const style = window.getComputedStyle(element);",
|
|
4216
|
+
' if (style.display === "none" || style.visibility === "hidden") {',
|
|
4217
|
+
" return false;",
|
|
4218
|
+
" }",
|
|
4219
|
+
' if (element.hidden || element.getAttribute("aria-hidden") === "true") {',
|
|
4220
|
+
" return false;",
|
|
4221
|
+
" }",
|
|
4222
|
+
" return element.getClientRects().length > 0;",
|
|
4223
|
+
" };",
|
|
4224
|
+
" const selectorFor = (element) => {",
|
|
4225
|
+
" if (!element || element.nodeType !== 1) {",
|
|
4226
|
+
' return "";',
|
|
4227
|
+
" }",
|
|
4228
|
+
" if (element.id) {",
|
|
4229
|
+
" return `#${escape(element.id)}`;",
|
|
4230
|
+
" }",
|
|
4231
|
+
' const testId = element.getAttribute("data-testid");',
|
|
4232
|
+
" if (testId) {",
|
|
4233
|
+
' return `[data-testid="${escape(testId)}"]`;',
|
|
4234
|
+
" }",
|
|
4235
|
+
' const name = element.getAttribute("name");',
|
|
4236
|
+
" if (name) {",
|
|
4237
|
+
' return `${element.tagName.toLowerCase()}[name="${escape(name)}"]`;',
|
|
4238
|
+
" }",
|
|
4239
|
+
' const ariaLabel = element.getAttribute("aria-label");',
|
|
4240
|
+
" if (ariaLabel) {",
|
|
4241
|
+
' return `${element.tagName.toLowerCase()}[aria-label="${escape(ariaLabel)}"]`;',
|
|
4242
|
+
" }",
|
|
4243
|
+
" const parts = [];",
|
|
4244
|
+
" let node = element;",
|
|
4245
|
+
" while (node && node.nodeType === 1 && parts.length < 4) {",
|
|
4246
|
+
" let part = node.tagName.toLowerCase();",
|
|
4247
|
+
" const parent = node.parentElement;",
|
|
4248
|
+
" if (parent) {",
|
|
4249
|
+
" const siblings = Array.from(parent.children).filter(",
|
|
4250
|
+
" (child) => child.tagName === node.tagName",
|
|
4251
|
+
" );",
|
|
4252
|
+
" if (siblings.length > 1) {",
|
|
4253
|
+
" part += `:nth-of-type(${siblings.indexOf(node) + 1})`;",
|
|
4254
|
+
" }",
|
|
4255
|
+
" }",
|
|
4256
|
+
" parts.unshift(part);",
|
|
4257
|
+
" node = parent;",
|
|
4258
|
+
" }",
|
|
4259
|
+
" return parts.join('>');",
|
|
4260
|
+
" };",
|
|
4261
|
+
" const labelFor = (element) => {",
|
|
4262
|
+
" if (!element) {",
|
|
4263
|
+
" return undefined;",
|
|
4264
|
+
" }",
|
|
4265
|
+
' const ariaLabel = element.getAttribute("aria-label");',
|
|
4266
|
+
" if (ariaLabel) {",
|
|
4267
|
+
" return truncate(ariaLabel, 120);",
|
|
4268
|
+
" }",
|
|
4269
|
+
" if (element.id) {",
|
|
4270
|
+
' const explicitLabel = document.querySelector(`label[for="${escape(element.id)}"]`);',
|
|
4271
|
+
" if (explicitLabel) {",
|
|
4272
|
+
" return textFor(explicitLabel);",
|
|
4273
|
+
" }",
|
|
4274
|
+
" }",
|
|
4275
|
+
' const parentLabel = element.closest("label");',
|
|
4276
|
+
" if (parentLabel) {",
|
|
4277
|
+
" return textFor(parentLabel);",
|
|
4278
|
+
" }",
|
|
4279
|
+
" return undefined;",
|
|
4280
|
+
" };",
|
|
4281
|
+
" const readStorage = (storage, limit) => {",
|
|
4282
|
+
" try {",
|
|
4283
|
+
" const keys = Object.keys(storage);",
|
|
4284
|
+
" return {",
|
|
4285
|
+
" count: keys.length,",
|
|
4286
|
+
" entries: keys.slice(0, limit).map((key) => ({",
|
|
4287
|
+
" key,",
|
|
4288
|
+
" value: redact(storage.getItem(key)),",
|
|
4289
|
+
" })),",
|
|
4290
|
+
" };",
|
|
4291
|
+
" } catch {",
|
|
4292
|
+
" return { count: 0, entries: [] };",
|
|
4293
|
+
" }",
|
|
4294
|
+
" };",
|
|
4295
|
+
' const forms = Array.from(document.querySelectorAll("form"))',
|
|
4296
|
+
" .filter((form) => isVisible(form))",
|
|
4297
|
+
" .map((form) => {",
|
|
4298
|
+
" const fields = Array.from(form.elements)",
|
|
4299
|
+
" .filter((element) => element && element.tagName)",
|
|
4300
|
+
" .map((element) => {",
|
|
4301
|
+
" const tag = element.tagName.toLowerCase();",
|
|
4302
|
+
' const type = "type" in element && element.type ? element.type : tag;',
|
|
4303
|
+
' const name = element.name || element.getAttribute("name") || element.id || "";',
|
|
4304
|
+
' let value = "";',
|
|
4305
|
+
" let options;",
|
|
4306
|
+
' if (tag === "select") {',
|
|
4307
|
+
" const select = element;",
|
|
4308
|
+
' value = redact(select.value ?? "");',
|
|
4309
|
+
" options = Array.from(select.options).map((option) => truncate(option.text, 120));",
|
|
4310
|
+
' } else if (tag === "input" && element.type === "password") {',
|
|
4311
|
+
' value = "[redacted]";',
|
|
4312
|
+
' } else if (tag === "input" || tag === "textarea") {',
|
|
4313
|
+
' value = redact(element.value ?? "");',
|
|
4314
|
+
" } else if (element.isContentEditable) {",
|
|
4315
|
+
' value = redact(element.textContent ?? "");',
|
|
4316
|
+
' } else if ("value" in element) {',
|
|
4317
|
+
' value = redact(element.value ?? "");',
|
|
4318
|
+
" }",
|
|
4319
|
+
" return {",
|
|
4320
|
+
" name,",
|
|
4321
|
+
" type,",
|
|
4322
|
+
" value,",
|
|
4323
|
+
" selector: selectorFor(element),",
|
|
4324
|
+
" label: labelFor(element),",
|
|
4325
|
+
' placeholder: truncate(element.getAttribute?.("placeholder") ?? "", 120) || undefined,',
|
|
4326
|
+
' checked: typeof element.checked === "boolean" ? element.checked : undefined,',
|
|
4327
|
+
" disabled: !!element.disabled,",
|
|
4328
|
+
" visible: isVisible(element),",
|
|
4329
|
+
" ...(options ? { options } : {}),",
|
|
4330
|
+
" };",
|
|
4331
|
+
" });",
|
|
4332
|
+
" return {",
|
|
4333
|
+
" selector: selectorFor(form),",
|
|
4334
|
+
' action: form.getAttribute("action") || undefined,',
|
|
4335
|
+
' method: form.getAttribute("method") || undefined,',
|
|
4336
|
+
" fields,",
|
|
4337
|
+
" };",
|
|
4338
|
+
" });",
|
|
4339
|
+
" const localStorageData = readStorage(window.localStorage, 25);",
|
|
4340
|
+
" const sessionStorageData = readStorage(window.sessionStorage, 25);",
|
|
4341
|
+
' const cookies = (document.cookie ? document.cookie.split(";") : [])',
|
|
4342
|
+
" .map((entry) => entry.trim())",
|
|
4343
|
+
" .filter((entry) => entry.length > 0)",
|
|
4344
|
+
" .slice(0, 25)",
|
|
4345
|
+
" .map((entry) => {",
|
|
4346
|
+
' const [key, ...rest] = entry.split("=");',
|
|
4347
|
+
" return {",
|
|
4348
|
+
" key,",
|
|
4349
|
+
' value: includeValues ? truncate(rest.join("="), 500) : "[redacted]",',
|
|
4350
|
+
" };",
|
|
4351
|
+
" });",
|
|
4352
|
+
" const focused = document.activeElement && document.activeElement !== document.body",
|
|
4353
|
+
" ? {",
|
|
4354
|
+
" selector: selectorFor(document.activeElement),",
|
|
4355
|
+
' name: document.activeElement.getAttribute?.("name") || document.activeElement.id || undefined,',
|
|
4356
|
+
" label: labelFor(document.activeElement),",
|
|
4357
|
+
' role: document.activeElement.getAttribute?.("role") || document.activeElement.tagName?.toLowerCase(),',
|
|
4358
|
+
' type: document.activeElement.getAttribute?.("type") || undefined,',
|
|
4359
|
+
" text: textFor(document.activeElement) || undefined,",
|
|
4360
|
+
" }",
|
|
4361
|
+
" : undefined;",
|
|
4362
|
+
' const primaryActions = Array.from(document.querySelectorAll("button, a[href], input[type=button], input[type=submit]"))',
|
|
4363
|
+
" .filter((element) => isVisible(element))",
|
|
4364
|
+
" .map((element) => ({",
|
|
4365
|
+
" selector: selectorFor(element),",
|
|
4366
|
+
' role: element.getAttribute("role") || element.tagName.toLowerCase(),',
|
|
4367
|
+
' name: truncate(element.getAttribute("aria-label") || element.innerText || element.value || "", 120),',
|
|
4368
|
+
" }))",
|
|
4369
|
+
" .filter((entry) => entry.name.length > 0)",
|
|
4370
|
+
" .slice(0, 12);",
|
|
4371
|
+
" return {",
|
|
4372
|
+
" url: location.href,",
|
|
4373
|
+
" title: document.title || undefined,",
|
|
4374
|
+
" readyState: document.readyState || undefined,",
|
|
4375
|
+
" focused,",
|
|
4376
|
+
" primaryActions,",
|
|
4377
|
+
" storageSummary: {",
|
|
4378
|
+
" localStorageCount: localStorageData.count,",
|
|
4379
|
+
" sessionStorageCount: sessionStorageData.count,",
|
|
4380
|
+
" cookieCount: cookies.length,",
|
|
4381
|
+
" },",
|
|
4382
|
+
" forms,",
|
|
4383
|
+
" localStorage: localStorageData.entries,",
|
|
4384
|
+
" sessionStorage: sessionStorageData.entries,",
|
|
4385
|
+
" cookies,",
|
|
4386
|
+
" };",
|
|
4387
|
+
"})()"
|
|
4388
|
+
].join("\n");
|
|
4389
|
+
};
|
|
4135
4390
|
|
|
4136
4391
|
// packages/core/src/inspect/service.ts
|
|
4137
4392
|
var DEFAULT_MAX_SNAPSHOTS_PER_SESSION = 20;
|
|
4138
4393
|
var DEFAULT_MAX_SNAPSHOT_HISTORY = 100;
|
|
4139
4394
|
var InspectService = class {
|
|
4140
4395
|
constructor(options) {
|
|
4396
|
+
this.consoleSinceBySessionTab = /* @__PURE__ */ new Map();
|
|
4141
4397
|
this.registry = options.registry;
|
|
4142
4398
|
this.debugger = options.debuggerBridge;
|
|
4143
4399
|
this.extensionBridge = options.extensionBridge;
|
|
@@ -4198,11 +4454,15 @@ var InspectService = class {
|
|
|
4198
4454
|
this.requireSession(input.sessionId);
|
|
4199
4455
|
const selection = await this.resolveTab(input.sessionId, input.targetHint);
|
|
4200
4456
|
const debuggerCommand = this.debuggerCommand.bind(this);
|
|
4457
|
+
const executionContextId = await this.resolveMainFrameExecutionContextId(
|
|
4458
|
+
selection.tabId
|
|
4459
|
+
);
|
|
4201
4460
|
const work = async () => {
|
|
4202
4461
|
if (input.format === "html") {
|
|
4203
4462
|
const html = await captureHtml(selection.tabId, {
|
|
4204
4463
|
selector: input.selector,
|
|
4205
4464
|
debuggerCommand,
|
|
4465
|
+
executionContextId,
|
|
4206
4466
|
onEvaluationFailed: () => {
|
|
4207
4467
|
const error = new InspectError(
|
|
4208
4468
|
"EVALUATION_FAILED",
|
|
@@ -4296,18 +4556,36 @@ var InspectService = class {
|
|
|
4296
4556
|
refMap,
|
|
4297
4557
|
debuggerCommand
|
|
4298
4558
|
);
|
|
4559
|
+
let actionableRefs = refResult.appliedRefs;
|
|
4560
|
+
let actionableBindings = refResult.appliedBindings;
|
|
4561
|
+
const actionabilityWarnings = [];
|
|
4562
|
+
if (input.interactive && refResult.appliedBindings.length > 0) {
|
|
4563
|
+
const actionableResult = await this.collectActionableSnapshotRefs(
|
|
4564
|
+
selection.tabId,
|
|
4565
|
+
refResult.appliedBindings
|
|
4566
|
+
);
|
|
4567
|
+
actionabilityWarnings.push(...actionableResult.warnings);
|
|
4568
|
+
if (actionableResult.actionableRefs) {
|
|
4569
|
+
actionableRefs = actionableResult.actionableRefs;
|
|
4570
|
+
actionableBindings = refResult.appliedBindings.filter(
|
|
4571
|
+
(binding) => actionableRefs.has(binding.ref)
|
|
4572
|
+
);
|
|
4573
|
+
snapshot = filterAxSnapshotByRefs(snapshot, actionableRefs);
|
|
4574
|
+
}
|
|
4575
|
+
}
|
|
4299
4576
|
const persistResult = refMap.size === 0 ? { warnings: [] } : await persistSnapshotRefRegistry(
|
|
4300
4577
|
selection.tabId,
|
|
4301
|
-
|
|
4578
|
+
actionableBindings,
|
|
4302
4579
|
debuggerCommand
|
|
4303
4580
|
);
|
|
4304
|
-
pruneUnappliedRefsFromSnapshot(snapshot,
|
|
4581
|
+
pruneUnappliedRefsFromSnapshot(snapshot, actionableRefs);
|
|
4305
4582
|
const warnings = [
|
|
4306
4583
|
...selection.warnings ?? [],
|
|
4307
4584
|
...selectorWarnings,
|
|
4308
4585
|
...truncationWarnings,
|
|
4309
4586
|
...clearResult.warnings,
|
|
4310
4587
|
...refResult.warnings,
|
|
4588
|
+
...actionabilityWarnings,
|
|
4311
4589
|
...persistResult.warnings
|
|
4312
4590
|
];
|
|
4313
4591
|
return {
|
|
@@ -4329,6 +4607,7 @@ var InspectService = class {
|
|
|
4329
4607
|
const html = await captureHtml(selection.tabId, {
|
|
4330
4608
|
selector: input.selector,
|
|
4331
4609
|
debuggerCommand,
|
|
4610
|
+
executionContextId,
|
|
4332
4611
|
onEvaluationFailed: () => {
|
|
4333
4612
|
const error2 = new InspectError(
|
|
4334
4613
|
"EVALUATION_FAILED",
|
|
@@ -4433,11 +4712,27 @@ var InspectService = class {
|
|
|
4433
4712
|
};
|
|
4434
4713
|
}
|
|
4435
4714
|
async consoleList(input) {
|
|
4436
|
-
this.requireSession(input.sessionId);
|
|
4715
|
+
const session = this.requireSession(input.sessionId);
|
|
4716
|
+
const tabCount = this.extensionBridge?.getStatus().tabs.length ?? 0;
|
|
4717
|
+
if (!input.targetHint && typeof session.selectedTabId !== "number" && tabCount > 1) {
|
|
4718
|
+
const error = new InspectError(
|
|
4719
|
+
"TAB_NOT_FOUND",
|
|
4720
|
+
"Console inspection requires an explicit target or a session-selected tab.",
|
|
4721
|
+
{ retryable: false }
|
|
4722
|
+
);
|
|
4723
|
+
this.recordError(error);
|
|
4724
|
+
throw error;
|
|
4725
|
+
}
|
|
4437
4726
|
const selection = await this.resolveTab(input.sessionId, input.targetHint);
|
|
4438
4727
|
await this.enableConsole(selection.tabId);
|
|
4728
|
+
const since = this.resolveConsoleSince({
|
|
4729
|
+
session,
|
|
4730
|
+
tabId: selection.tabId,
|
|
4731
|
+
requestedSince: input.since,
|
|
4732
|
+
tabLastActiveAt: selection.tab.last_active_at
|
|
4733
|
+
});
|
|
4439
4734
|
const events = this.ensureDebugger().getConsoleEvents(selection.tabId);
|
|
4440
|
-
const entries = events.map((event) => toConsoleEntry(event)).filter((entry) => entry !== null);
|
|
4735
|
+
const entries = events.filter((event) => this.isEventOnOrAfter(event.timestamp, since)).map((event) => toConsoleEntry(event)).filter((entry) => entry !== null);
|
|
4441
4736
|
const result = {
|
|
4442
4737
|
entries,
|
|
4443
4738
|
warnings: selection.warnings
|
|
@@ -4476,16 +4771,7 @@ var InspectService = class {
|
|
|
4476
4771
|
this.requireSession(input.sessionId);
|
|
4477
4772
|
const selection = await this.resolveTab(input.sessionId, input.targetHint);
|
|
4478
4773
|
const expression = input.expression ?? "undefined";
|
|
4479
|
-
await this.
|
|
4480
|
-
const result = await this.debuggerCommand(
|
|
4481
|
-
selection.tabId,
|
|
4482
|
-
"Runtime.evaluate",
|
|
4483
|
-
{
|
|
4484
|
-
expression,
|
|
4485
|
-
returnByValue: true,
|
|
4486
|
-
awaitPromise: true
|
|
4487
|
-
}
|
|
4488
|
-
);
|
|
4774
|
+
const result = await this.evaluateInMainFrame(selection.tabId, expression);
|
|
4489
4775
|
if (result && typeof result === "object" && "exceptionDetails" in result) {
|
|
4490
4776
|
const output2 = {
|
|
4491
4777
|
exception: result.exceptionDetails,
|
|
@@ -4504,54 +4790,58 @@ var InspectService = class {
|
|
|
4504
4790
|
async extractContent(input) {
|
|
4505
4791
|
this.requireSession(input.sessionId);
|
|
4506
4792
|
const selection = await this.resolveTab(input.sessionId, input.targetHint);
|
|
4793
|
+
const consistency = input.consistency ?? "quiesce";
|
|
4507
4794
|
const debuggerCommand = this.debuggerCommand.bind(this);
|
|
4508
|
-
const
|
|
4509
|
-
|
|
4510
|
-
|
|
4511
|
-
const error = new InspectError(
|
|
4512
|
-
"EVALUATION_FAILED",
|
|
4513
|
-
"Failed to evaluate HTML snapshot.",
|
|
4514
|
-
{ retryable: false }
|
|
4515
|
-
);
|
|
4516
|
-
this.recordError(error);
|
|
4517
|
-
throw error;
|
|
4518
|
-
}
|
|
4519
|
-
});
|
|
4795
|
+
const executionContextId = await this.resolveMainFrameExecutionContextId(
|
|
4796
|
+
selection.tabId
|
|
4797
|
+
);
|
|
4520
4798
|
const url = selection.tab.url ?? "about:blank";
|
|
4521
|
-
|
|
4522
|
-
|
|
4523
|
-
|
|
4524
|
-
|
|
4525
|
-
|
|
4526
|
-
|
|
4527
|
-
|
|
4528
|
-
|
|
4529
|
-
|
|
4530
|
-
|
|
4531
|
-
|
|
4799
|
+
const work = async () => {
|
|
4800
|
+
if (consistency === "quiesce") {
|
|
4801
|
+
await this.waitForDomSettled(selection.tabId, executionContextId);
|
|
4802
|
+
}
|
|
4803
|
+
const html = await captureHtml(selection.tabId, {
|
|
4804
|
+
debuggerCommand,
|
|
4805
|
+
executionContextId,
|
|
4806
|
+
onEvaluationFailed: () => {
|
|
4807
|
+
const error = new InspectError(
|
|
4808
|
+
"EVALUATION_FAILED",
|
|
4809
|
+
"Failed to evaluate HTML snapshot.",
|
|
4810
|
+
{ retryable: false }
|
|
4811
|
+
);
|
|
4812
|
+
this.recordError(error);
|
|
4813
|
+
throw error;
|
|
4814
|
+
}
|
|
4532
4815
|
});
|
|
4533
|
-
|
|
4534
|
-
|
|
4535
|
-
|
|
4536
|
-
|
|
4537
|
-
|
|
4816
|
+
try {
|
|
4817
|
+
const { article, semanticMainCandidate } = parseExtractContentSource({
|
|
4818
|
+
html,
|
|
4819
|
+
url
|
|
4820
|
+
});
|
|
4821
|
+
return renderExtractContent({
|
|
4822
|
+
format: input.format,
|
|
4823
|
+
article,
|
|
4824
|
+
semanticMainCandidate,
|
|
4825
|
+
includeMetadata: input.includeMetadata,
|
|
4826
|
+
warnings: selection.warnings
|
|
4827
|
+
});
|
|
4828
|
+
} catch (error) {
|
|
4829
|
+
if (error instanceof InspectError) {
|
|
4830
|
+
this.recordError(error);
|
|
4831
|
+
}
|
|
4832
|
+
throw error;
|
|
4538
4833
|
}
|
|
4539
|
-
|
|
4540
|
-
|
|
4834
|
+
};
|
|
4835
|
+
const output = consistency === "quiesce" ? await driveMutex.runExclusive(work) : await work();
|
|
4836
|
+
this.markInspectConnected(input.sessionId);
|
|
4837
|
+
return output;
|
|
4541
4838
|
}
|
|
4542
4839
|
async pageState(input) {
|
|
4543
4840
|
this.requireSession(input.sessionId);
|
|
4544
4841
|
const selection = await this.resolveTab(input.sessionId, input.targetHint);
|
|
4545
|
-
await this.
|
|
4546
|
-
const expression = PAGE_STATE_SCRIPT;
|
|
4547
|
-
const result = await this.debuggerCommand(
|
|
4842
|
+
const result = await this.evaluateInMainFrame(
|
|
4548
4843
|
selection.tabId,
|
|
4549
|
-
|
|
4550
|
-
{
|
|
4551
|
-
expression,
|
|
4552
|
-
returnByValue: true,
|
|
4553
|
-
awaitPromise: true
|
|
4554
|
-
}
|
|
4844
|
+
buildPageStateScript({ includeValues: input.includeValues })
|
|
4555
4845
|
);
|
|
4556
4846
|
if (result && typeof result === "object" && "exceptionDetails" in result) {
|
|
4557
4847
|
const error = new InspectError(
|
|
@@ -4563,16 +4853,22 @@ var InspectService = class {
|
|
|
4563
4853
|
throw error;
|
|
4564
4854
|
}
|
|
4565
4855
|
const value = result?.result?.value;
|
|
4566
|
-
const
|
|
4856
|
+
const parsed = PageStateSchema.safeParse(value);
|
|
4857
|
+
if (!parsed.success) {
|
|
4858
|
+
const error = new InspectError(
|
|
4859
|
+
"EVALUATION_FAILED",
|
|
4860
|
+
"Captured page state did not match the expected schema.",
|
|
4861
|
+
{ retryable: false }
|
|
4862
|
+
);
|
|
4863
|
+
this.recordError(error);
|
|
4864
|
+
throw error;
|
|
4865
|
+
}
|
|
4567
4866
|
const warnings = [
|
|
4568
|
-
...
|
|
4867
|
+
...parsed.data.warnings ?? [],
|
|
4569
4868
|
...selection.warnings ?? []
|
|
4570
4869
|
];
|
|
4571
4870
|
const output = {
|
|
4572
|
-
|
|
4573
|
-
localStorage: Array.isArray(raw.localStorage) ? raw.localStorage : [],
|
|
4574
|
-
sessionStorage: Array.isArray(raw.sessionStorage) ? raw.sessionStorage : [],
|
|
4575
|
-
cookies: Array.isArray(raw.cookies) ? raw.cookies : [],
|
|
4871
|
+
...parsed.data,
|
|
4576
4872
|
...warnings.length > 0 ? { warnings } : {}
|
|
4577
4873
|
};
|
|
4578
4874
|
this.markInspectConnected(input.sessionId);
|
|
@@ -4796,6 +5092,186 @@ var InspectService = class {
|
|
|
4796
5092
|
async enableAccessibility(tabId) {
|
|
4797
5093
|
await this.debuggerCommand(tabId, "Accessibility.enable", {});
|
|
4798
5094
|
}
|
|
5095
|
+
async collectActionableSnapshotRefs(tabId, bindings) {
|
|
5096
|
+
try {
|
|
5097
|
+
const result = await this.debuggerCommand(tabId, "Runtime.evaluate", {
|
|
5098
|
+
expression: `(() => {
|
|
5099
|
+
const refs = new Set(${JSON.stringify(
|
|
5100
|
+
bindings.map((binding) => binding.ref)
|
|
5101
|
+
)});
|
|
5102
|
+
const isVisible = (element) => {
|
|
5103
|
+
if (!(element instanceof HTMLElement)) {
|
|
5104
|
+
return false;
|
|
5105
|
+
}
|
|
5106
|
+
const style = window.getComputedStyle(element);
|
|
5107
|
+
if (style.visibility === 'hidden' || style.display === 'none') {
|
|
5108
|
+
return false;
|
|
5109
|
+
}
|
|
5110
|
+
const rect = element.getBoundingClientRect();
|
|
5111
|
+
if (rect.width === 0 && rect.height === 0) {
|
|
5112
|
+
return false;
|
|
5113
|
+
}
|
|
5114
|
+
if (
|
|
5115
|
+
element.offsetWidth === 0 &&
|
|
5116
|
+
element.offsetHeight === 0 &&
|
|
5117
|
+
element.getClientRects().length === 0
|
|
5118
|
+
) {
|
|
5119
|
+
return false;
|
|
5120
|
+
}
|
|
5121
|
+
let current = element;
|
|
5122
|
+
while (current) {
|
|
5123
|
+
const currentStyle = window.getComputedStyle(current);
|
|
5124
|
+
if (currentStyle.display === 'none') {
|
|
5125
|
+
return false;
|
|
5126
|
+
}
|
|
5127
|
+
if (
|
|
5128
|
+
currentStyle.visibility === 'hidden' ||
|
|
5129
|
+
currentStyle.visibility === 'collapse'
|
|
5130
|
+
) {
|
|
5131
|
+
return false;
|
|
5132
|
+
}
|
|
5133
|
+
const opacity = Number.parseFloat(currentStyle.opacity ?? '1');
|
|
5134
|
+
if (Number.isFinite(opacity) && opacity <= 0) {
|
|
5135
|
+
return false;
|
|
5136
|
+
}
|
|
5137
|
+
if (currentStyle.pointerEvents === 'none') {
|
|
5138
|
+
return false;
|
|
5139
|
+
}
|
|
5140
|
+
current = current.parentElement;
|
|
5141
|
+
}
|
|
5142
|
+
return true;
|
|
5143
|
+
};
|
|
5144
|
+
|
|
5145
|
+
return Array.from(document.querySelectorAll('[data-bv-ref]'))
|
|
5146
|
+
.filter((element) => {
|
|
5147
|
+
const ref = element.getAttribute('data-bv-ref');
|
|
5148
|
+
return typeof ref === 'string' && refs.has(ref) && isVisible(element);
|
|
5149
|
+
})
|
|
5150
|
+
.map((element) => element.getAttribute('data-bv-ref'));
|
|
5151
|
+
})() /* browser-bridge:collect-actionable-snapshot-refs */`,
|
|
5152
|
+
returnByValue: true,
|
|
5153
|
+
awaitPromise: true
|
|
5154
|
+
});
|
|
5155
|
+
const rawRefs = result.value;
|
|
5156
|
+
if (!Array.isArray(rawRefs)) {
|
|
5157
|
+
return {
|
|
5158
|
+
warnings: [
|
|
5159
|
+
"Interactive AX snapshot could not verify live actionability."
|
|
5160
|
+
]
|
|
5161
|
+
};
|
|
5162
|
+
}
|
|
5163
|
+
const refs = rawRefs.filter(
|
|
5164
|
+
(ref) => typeof ref === "string"
|
|
5165
|
+
);
|
|
5166
|
+
return {
|
|
5167
|
+
actionableRefs: new Set(refs),
|
|
5168
|
+
warnings: []
|
|
5169
|
+
};
|
|
5170
|
+
} catch {
|
|
5171
|
+
return {
|
|
5172
|
+
warnings: ["Interactive AX snapshot could not prune hidden controls."]
|
|
5173
|
+
};
|
|
5174
|
+
}
|
|
5175
|
+
}
|
|
5176
|
+
async waitForDomSettled(tabId, executionContextId) {
|
|
5177
|
+
const result = await this.debuggerCommand(tabId, "Runtime.evaluate", {
|
|
5178
|
+
expression: `(() => {
|
|
5179
|
+
const quietMs = 100;
|
|
5180
|
+
const timeoutMs = 2000;
|
|
5181
|
+
return new Promise((resolve) => {
|
|
5182
|
+
const doc = document;
|
|
5183
|
+
const root = doc.documentElement || doc.body || doc;
|
|
5184
|
+
let finished = false;
|
|
5185
|
+
let quietTimer;
|
|
5186
|
+
let timeoutTimer;
|
|
5187
|
+
let raf1 = 0;
|
|
5188
|
+
let raf2 = 0;
|
|
5189
|
+
const finish = () => {
|
|
5190
|
+
if (finished) {
|
|
5191
|
+
return;
|
|
5192
|
+
}
|
|
5193
|
+
finished = true;
|
|
5194
|
+
if (observer) {
|
|
5195
|
+
observer.disconnect();
|
|
5196
|
+
}
|
|
5197
|
+
clearTimeout(quietTimer);
|
|
5198
|
+
clearTimeout(timeoutTimer);
|
|
5199
|
+
if (raf1) cancelAnimationFrame(raf1);
|
|
5200
|
+
if (raf2) cancelAnimationFrame(raf2);
|
|
5201
|
+
resolve(true);
|
|
5202
|
+
};
|
|
5203
|
+
const scheduleQuiet = () => {
|
|
5204
|
+
clearTimeout(quietTimer);
|
|
5205
|
+
quietTimer = setTimeout(() => {
|
|
5206
|
+
raf1 = requestAnimationFrame(() => {
|
|
5207
|
+
raf2 = requestAnimationFrame(finish);
|
|
5208
|
+
});
|
|
5209
|
+
}, quietMs);
|
|
5210
|
+
};
|
|
5211
|
+
const observer =
|
|
5212
|
+
typeof MutationObserver === 'function'
|
|
5213
|
+
? new MutationObserver(() => {
|
|
5214
|
+
scheduleQuiet();
|
|
5215
|
+
})
|
|
5216
|
+
: null;
|
|
5217
|
+
if (observer && root) {
|
|
5218
|
+
observer.observe(root, {
|
|
5219
|
+
subtree: true,
|
|
5220
|
+
childList: true,
|
|
5221
|
+
attributes: true,
|
|
5222
|
+
characterData: true,
|
|
5223
|
+
});
|
|
5224
|
+
}
|
|
5225
|
+
scheduleQuiet();
|
|
5226
|
+
timeoutTimer = setTimeout(finish, timeoutMs);
|
|
5227
|
+
});
|
|
5228
|
+
})()`,
|
|
5229
|
+
returnByValue: true,
|
|
5230
|
+
awaitPromise: true,
|
|
5231
|
+
...typeof executionContextId === "number" ? { contextId: executionContextId } : {}
|
|
5232
|
+
});
|
|
5233
|
+
if (result && typeof result === "object" && "exceptionDetails" in result) {
|
|
5234
|
+
const error = new InspectError(
|
|
5235
|
+
"EVALUATION_FAILED",
|
|
5236
|
+
"Failed while waiting for the page to settle.",
|
|
5237
|
+
{ retryable: false }
|
|
5238
|
+
);
|
|
5239
|
+
this.recordError(error);
|
|
5240
|
+
throw error;
|
|
5241
|
+
}
|
|
5242
|
+
}
|
|
5243
|
+
async evaluateInMainFrame(tabId, expression) {
|
|
5244
|
+
await this.debuggerCommand(tabId, "Runtime.enable", {});
|
|
5245
|
+
const executionContextId = await this.resolveMainFrameExecutionContextId(tabId);
|
|
5246
|
+
return await this.debuggerCommand(tabId, "Runtime.evaluate", {
|
|
5247
|
+
expression,
|
|
5248
|
+
returnByValue: true,
|
|
5249
|
+
awaitPromise: true,
|
|
5250
|
+
...typeof executionContextId === "number" ? { contextId: executionContextId } : {}
|
|
5251
|
+
});
|
|
5252
|
+
}
|
|
5253
|
+
async resolveMainFrameExecutionContextId(tabId) {
|
|
5254
|
+
const debuggerBridge = this.ensureDebugger();
|
|
5255
|
+
const runOptional = async (method, params) => {
|
|
5256
|
+
const result = await debuggerBridge.command(tabId, method, params);
|
|
5257
|
+
if (!result.ok) {
|
|
5258
|
+
return void 0;
|
|
5259
|
+
}
|
|
5260
|
+
return result.result;
|
|
5261
|
+
};
|
|
5262
|
+
await runOptional("Page.enable", {});
|
|
5263
|
+
const frameTree = await runOptional("Page.getFrameTree", {});
|
|
5264
|
+
const frameId = frameTree?.frameTree?.frame?.id;
|
|
5265
|
+
if (typeof frameId !== "string" || frameId.length === 0) {
|
|
5266
|
+
return void 0;
|
|
5267
|
+
}
|
|
5268
|
+
const isolatedWorld = await runOptional("Page.createIsolatedWorld", {
|
|
5269
|
+
frameId,
|
|
5270
|
+
worldName: "browser_bridge_inspect"
|
|
5271
|
+
});
|
|
5272
|
+
const contextId = isolatedWorld?.executionContextId;
|
|
5273
|
+
return typeof contextId === "number" ? contextId : void 0;
|
|
5274
|
+
}
|
|
4799
5275
|
async debuggerCommand(tabId, method, params, timeoutMs) {
|
|
4800
5276
|
const debuggerBridge = this.ensureDebugger();
|
|
4801
5277
|
const result = await debuggerBridge.command(
|
|
@@ -4874,6 +5350,32 @@ var InspectService = class {
|
|
|
4874
5350
|
throw wrapped;
|
|
4875
5351
|
}
|
|
4876
5352
|
}
|
|
5353
|
+
resolveConsoleSince(options) {
|
|
5354
|
+
if (typeof options.requestedSince === "string") {
|
|
5355
|
+
return options.requestedSince;
|
|
5356
|
+
}
|
|
5357
|
+
const key = `${options.session.id}:${options.tabId}`;
|
|
5358
|
+
const existing = this.consoleSinceBySessionTab.get(key);
|
|
5359
|
+
if (existing) {
|
|
5360
|
+
return existing;
|
|
5361
|
+
}
|
|
5362
|
+
const candidates = [options.session.createdAt.toISOString()];
|
|
5363
|
+
if (typeof options.tabLastActiveAt === "string") {
|
|
5364
|
+
candidates.push(options.tabLastActiveAt);
|
|
5365
|
+
}
|
|
5366
|
+
const baseline = candidates.map((value) => ({ value, time: Date.parse(value) })).filter((entry) => Number.isFinite(entry.time)).sort((a, b) => b.time - a.time)[0]?.value;
|
|
5367
|
+
const resolved = baseline ?? options.session.createdAt.toISOString();
|
|
5368
|
+
this.consoleSinceBySessionTab.set(key, resolved);
|
|
5369
|
+
return resolved;
|
|
5370
|
+
}
|
|
5371
|
+
isEventOnOrAfter(timestamp, since) {
|
|
5372
|
+
const eventTime = Date.parse(timestamp);
|
|
5373
|
+
const sinceTime = Date.parse(since);
|
|
5374
|
+
if (!Number.isFinite(eventTime) || !Number.isFinite(sinceTime)) {
|
|
5375
|
+
return true;
|
|
5376
|
+
}
|
|
5377
|
+
return eventTime >= sinceTime;
|
|
5378
|
+
}
|
|
4877
5379
|
};
|
|
4878
5380
|
var createInspectService = (options) => new InspectService(options);
|
|
4879
5381
|
|
|
@@ -5299,6 +5801,7 @@ var registerDiagnosticsRoutes = (router, options = {}) => {
|
|
|
5299
5801
|
const extensionStatus = options.extensionBridge?.getStatus();
|
|
5300
5802
|
sendResult(res, {
|
|
5301
5803
|
started_at: PROCESS_STARTED_AT,
|
|
5804
|
+
pid: process.pid,
|
|
5302
5805
|
uptime_ms: Math.floor(process.uptime() * 1e3),
|
|
5303
5806
|
memory: process.memoryUsage(),
|
|
5304
5807
|
sessions: { active: sessionsActive },
|
|
@@ -5948,6 +6451,7 @@ var registerInspectRoutes = (router, options) => {
|
|
|
5948
6451
|
return await inspect.extractContent({
|
|
5949
6452
|
sessionId: body.session_id,
|
|
5950
6453
|
format: body.format,
|
|
6454
|
+
consistency: body.consistency,
|
|
5951
6455
|
includeMetadata: body.include_metadata,
|
|
5952
6456
|
targetHint: resolveInspectTargetHint({
|
|
5953
6457
|
sessionId: body.session_id,
|
|
@@ -5963,6 +6467,7 @@ var registerInspectRoutes = (router, options) => {
|
|
|
5963
6467
|
makeHandler2(InspectPageStateInputSchema, async (body) => {
|
|
5964
6468
|
return await inspect.pageState({
|
|
5965
6469
|
sessionId: body.session_id,
|
|
6470
|
+
includeValues: body.include_values,
|
|
5966
6471
|
targetHint: resolveInspectTargetHint({
|
|
5967
6472
|
sessionId: body.session_id,
|
|
5968
6473
|
target: body.target,
|
|
@@ -5977,6 +6482,7 @@ var registerInspectRoutes = (router, options) => {
|
|
|
5977
6482
|
makeHandler2(InspectConsoleListInputSchema, async (body) => {
|
|
5978
6483
|
return await inspect.consoleList({
|
|
5979
6484
|
sessionId: body.session_id,
|
|
6485
|
+
since: body.since,
|
|
5980
6486
|
targetHint: resolveInspectTargetHint({
|
|
5981
6487
|
sessionId: body.session_id,
|
|
5982
6488
|
target: body.target,
|
|
@@ -6031,6 +6537,157 @@ var registerInspectRoutes = (router, options) => {
|
|
|
6031
6537
|
);
|
|
6032
6538
|
};
|
|
6033
6539
|
|
|
6540
|
+
// packages/core/src/routes/permissions.ts
|
|
6541
|
+
var EXTENSION_READY_WAIT_MS2 = 1500;
|
|
6542
|
+
var parseBody3 = (schema, body) => {
|
|
6543
|
+
const result = schema.safeParse(body);
|
|
6544
|
+
if (result.success) {
|
|
6545
|
+
return { data: result.data };
|
|
6546
|
+
}
|
|
6547
|
+
const issue = result.error.issues[0];
|
|
6548
|
+
const details = issue && issue.path.length > 0 ? { field: issue.path.map((part) => part.toString()).join(".") } : void 0;
|
|
6549
|
+
return {
|
|
6550
|
+
error: {
|
|
6551
|
+
message: issue?.message ?? "Request body is invalid.",
|
|
6552
|
+
...details ? { details } : {}
|
|
6553
|
+
}
|
|
6554
|
+
};
|
|
6555
|
+
};
|
|
6556
|
+
var sendBridgeUnavailable = (res) => {
|
|
6557
|
+
sendError(res, errorStatus("EXTENSION_DISCONNECTED"), {
|
|
6558
|
+
code: "EXTENSION_DISCONNECTED",
|
|
6559
|
+
message: "Extension bridge is unavailable.",
|
|
6560
|
+
retryable: true
|
|
6561
|
+
});
|
|
6562
|
+
};
|
|
6563
|
+
var waitForExtensionReady = async (extensionBridge) => {
|
|
6564
|
+
if (extensionBridge.isConnected()) {
|
|
6565
|
+
return true;
|
|
6566
|
+
}
|
|
6567
|
+
const waitForReady = extensionBridge.waitForReady;
|
|
6568
|
+
if (typeof waitForReady !== "function") {
|
|
6569
|
+
return extensionBridge.isConnected();
|
|
6570
|
+
}
|
|
6571
|
+
return await waitForReady.call(extensionBridge, EXTENSION_READY_WAIT_MS2);
|
|
6572
|
+
};
|
|
6573
|
+
var makePermissionsHandler = (schema, action, options) => {
|
|
6574
|
+
return (req, res) => {
|
|
6575
|
+
const parsed = parseBody3(schema, req.body ?? {});
|
|
6576
|
+
if (parsed.error) {
|
|
6577
|
+
sendError(res, errorStatus("INVALID_ARGUMENT"), {
|
|
6578
|
+
code: "INVALID_ARGUMENT",
|
|
6579
|
+
message: parsed.error.message,
|
|
6580
|
+
retryable: false,
|
|
6581
|
+
...parsed.error.details ? { details: parsed.error.details } : {}
|
|
6582
|
+
});
|
|
6583
|
+
return;
|
|
6584
|
+
}
|
|
6585
|
+
if (!options.extensionBridge) {
|
|
6586
|
+
sendBridgeUnavailable(res);
|
|
6587
|
+
return;
|
|
6588
|
+
}
|
|
6589
|
+
const extensionBridge = options.extensionBridge;
|
|
6590
|
+
void waitForExtensionReady(extensionBridge).then(async (ready) => {
|
|
6591
|
+
if (!ready) {
|
|
6592
|
+
throw new ExtensionBridgeError(
|
|
6593
|
+
"EXTENSION_DISCONNECTED",
|
|
6594
|
+
"Extension is not connected.",
|
|
6595
|
+
true
|
|
6596
|
+
);
|
|
6597
|
+
}
|
|
6598
|
+
return await extensionBridge.request(
|
|
6599
|
+
action,
|
|
6600
|
+
parsed.data
|
|
6601
|
+
);
|
|
6602
|
+
}).then((envelope2) => {
|
|
6603
|
+
if (envelope2.status === "error") {
|
|
6604
|
+
const error = envelope2.error ?? {
|
|
6605
|
+
code: "INTERNAL",
|
|
6606
|
+
message: "Extension request failed.",
|
|
6607
|
+
retryable: false
|
|
6608
|
+
};
|
|
6609
|
+
sendError(res, errorStatus(error.code), {
|
|
6610
|
+
code: error.code,
|
|
6611
|
+
message: error.message,
|
|
6612
|
+
retryable: error.retryable,
|
|
6613
|
+
details: error.details
|
|
6614
|
+
});
|
|
6615
|
+
return;
|
|
6616
|
+
}
|
|
6617
|
+
sendResult(res, envelope2.result ?? {});
|
|
6618
|
+
}).catch((error) => {
|
|
6619
|
+
if (error instanceof ExtensionBridgeError) {
|
|
6620
|
+
sendError(res, errorStatus(error.code), {
|
|
6621
|
+
code: error.code,
|
|
6622
|
+
message: error.message,
|
|
6623
|
+
retryable: error.retryable,
|
|
6624
|
+
details: error.details
|
|
6625
|
+
});
|
|
6626
|
+
return;
|
|
6627
|
+
}
|
|
6628
|
+
sendError(res, errorStatus("INTERNAL"), {
|
|
6629
|
+
code: "INTERNAL",
|
|
6630
|
+
message: "Unexpected permissions route error.",
|
|
6631
|
+
retryable: false,
|
|
6632
|
+
details: {
|
|
6633
|
+
hint: error instanceof Error ? error.message : "Unknown error.",
|
|
6634
|
+
action
|
|
6635
|
+
}
|
|
6636
|
+
});
|
|
6637
|
+
});
|
|
6638
|
+
};
|
|
6639
|
+
};
|
|
6640
|
+
var registerPermissionsRoutes = (router, options) => {
|
|
6641
|
+
router.post(
|
|
6642
|
+
"/permissions/list",
|
|
6643
|
+
makePermissionsHandler(
|
|
6644
|
+
PermissionsListInputSchema,
|
|
6645
|
+
"permissions.list",
|
|
6646
|
+
options
|
|
6647
|
+
)
|
|
6648
|
+
);
|
|
6649
|
+
router.post(
|
|
6650
|
+
"/permissions/get_mode",
|
|
6651
|
+
makePermissionsHandler(
|
|
6652
|
+
PermissionsGetModeInputSchema,
|
|
6653
|
+
"permissions.get_mode",
|
|
6654
|
+
options
|
|
6655
|
+
)
|
|
6656
|
+
);
|
|
6657
|
+
router.post(
|
|
6658
|
+
"/permissions/list_pending_requests",
|
|
6659
|
+
makePermissionsHandler(
|
|
6660
|
+
PermissionsListPendingRequestsInputSchema,
|
|
6661
|
+
"permissions.list_pending_requests",
|
|
6662
|
+
options
|
|
6663
|
+
)
|
|
6664
|
+
);
|
|
6665
|
+
router.post(
|
|
6666
|
+
"/permissions/request_allow_site",
|
|
6667
|
+
makePermissionsHandler(
|
|
6668
|
+
PermissionsRequestAllowSiteInputSchema,
|
|
6669
|
+
"permissions.request_allow_site",
|
|
6670
|
+
options
|
|
6671
|
+
)
|
|
6672
|
+
);
|
|
6673
|
+
router.post(
|
|
6674
|
+
"/permissions/request_revoke_site",
|
|
6675
|
+
makePermissionsHandler(
|
|
6676
|
+
PermissionsRequestRevokeSiteInputSchema,
|
|
6677
|
+
"permissions.request_revoke_site",
|
|
6678
|
+
options
|
|
6679
|
+
)
|
|
6680
|
+
);
|
|
6681
|
+
router.post(
|
|
6682
|
+
"/permissions/request_set_mode",
|
|
6683
|
+
makePermissionsHandler(
|
|
6684
|
+
PermissionsRequestSetModeInputSchema,
|
|
6685
|
+
"permissions.request_set_mode",
|
|
6686
|
+
options
|
|
6687
|
+
)
|
|
6688
|
+
);
|
|
6689
|
+
};
|
|
6690
|
+
|
|
6034
6691
|
// packages/core/src/recovery.ts
|
|
6035
6692
|
var RecoveryTracker = class {
|
|
6036
6693
|
constructor() {
|
|
@@ -6446,6 +7103,9 @@ var createCoreServer = (options = {}) => {
|
|
|
6446
7103
|
})
|
|
6447
7104
|
);
|
|
6448
7105
|
registerDriveRoutes(app, { drive, registry });
|
|
7106
|
+
registerPermissionsRoutes(app, {
|
|
7107
|
+
extensionBridge
|
|
7108
|
+
});
|
|
6449
7109
|
registerInspectRoutes(app, {
|
|
6450
7110
|
registry,
|
|
6451
7111
|
extensionBridge,
|
|
@@ -6857,6 +7517,10 @@ var readSessionId2 = (args) => {
|
|
|
6857
7517
|
const sessionId = args.session_id;
|
|
6858
7518
|
return typeof sessionId === "string" && sessionId.length > 0 ? sessionId : void 0;
|
|
6859
7519
|
};
|
|
7520
|
+
var withPermissionsSource = (source) => (args) => isRecord3(args) ? {
|
|
7521
|
+
...args,
|
|
7522
|
+
source
|
|
7523
|
+
} : args;
|
|
6860
7524
|
var supportsSessionMigration = (corePath) => corePath.startsWith("/drive/") || corePath.startsWith("/inspect/") || corePath.startsWith("/artifacts/") || corePath === "/diagnostics/doctor";
|
|
6861
7525
|
var isSessionNotFoundEnvelope = (envelopeResult) => {
|
|
6862
7526
|
if (envelopeResult.ok) {
|
|
@@ -6970,6 +7634,69 @@ var TOOL_DEFINITIONS = [
|
|
|
6970
7634
|
corePath: "/session/close"
|
|
6971
7635
|
}
|
|
6972
7636
|
},
|
|
7637
|
+
{
|
|
7638
|
+
name: "permissions.list",
|
|
7639
|
+
config: {
|
|
7640
|
+
title: "Permissions List",
|
|
7641
|
+
description: "List allowlisted Browser Bridge sites.",
|
|
7642
|
+
inputSchema: PermissionsListInputSchema,
|
|
7643
|
+
outputSchema: envelope(PermissionsListOutputSchema),
|
|
7644
|
+
corePath: "/permissions/list"
|
|
7645
|
+
}
|
|
7646
|
+
},
|
|
7647
|
+
{
|
|
7648
|
+
name: "permissions.get_mode",
|
|
7649
|
+
config: {
|
|
7650
|
+
title: "Permissions Get Mode",
|
|
7651
|
+
description: "Read the current Browser Bridge permissions mode.",
|
|
7652
|
+
inputSchema: PermissionsGetModeInputSchema,
|
|
7653
|
+
outputSchema: envelope(PermissionsGetModeOutputSchema),
|
|
7654
|
+
corePath: "/permissions/get_mode"
|
|
7655
|
+
}
|
|
7656
|
+
},
|
|
7657
|
+
{
|
|
7658
|
+
name: "permissions.list_pending_requests",
|
|
7659
|
+
config: {
|
|
7660
|
+
title: "Permissions List Pending Requests",
|
|
7661
|
+
description: "List pending external Browser Bridge permission-change requests.",
|
|
7662
|
+
inputSchema: PermissionsListPendingRequestsInputSchema,
|
|
7663
|
+
outputSchema: envelope(PermissionsListPendingRequestsOutputSchema),
|
|
7664
|
+
corePath: "/permissions/list_pending_requests"
|
|
7665
|
+
}
|
|
7666
|
+
},
|
|
7667
|
+
{
|
|
7668
|
+
name: "permissions.request_allow_site",
|
|
7669
|
+
config: {
|
|
7670
|
+
title: "Permissions Request Allow Site",
|
|
7671
|
+
description: "Request allowlisting a site. A human must approve the change in Chrome before it applies.",
|
|
7672
|
+
inputSchema: PermissionsRequestAllowSiteInputSchema,
|
|
7673
|
+
outputSchema: envelope(PermissionsRequestOutputSchema),
|
|
7674
|
+
corePath: "/permissions/request_allow_site",
|
|
7675
|
+
transformInput: withPermissionsSource("mcp")
|
|
7676
|
+
}
|
|
7677
|
+
},
|
|
7678
|
+
{
|
|
7679
|
+
name: "permissions.request_revoke_site",
|
|
7680
|
+
config: {
|
|
7681
|
+
title: "Permissions Request Revoke Site",
|
|
7682
|
+
description: "Request revoking a site from the allowlist. A human must approve the change in Chrome before it applies.",
|
|
7683
|
+
inputSchema: PermissionsRequestRevokeSiteInputSchema,
|
|
7684
|
+
outputSchema: envelope(PermissionsRequestOutputSchema),
|
|
7685
|
+
corePath: "/permissions/request_revoke_site",
|
|
7686
|
+
transformInput: withPermissionsSource("mcp")
|
|
7687
|
+
}
|
|
7688
|
+
},
|
|
7689
|
+
{
|
|
7690
|
+
name: "permissions.request_set_mode",
|
|
7691
|
+
config: {
|
|
7692
|
+
title: "Permissions Request Set Mode",
|
|
7693
|
+
description: "Request changing Browser Bridge permission mode. A human must approve the change in Chrome before it applies.",
|
|
7694
|
+
inputSchema: PermissionsRequestSetModeInputSchema,
|
|
7695
|
+
outputSchema: envelope(PermissionsRequestOutputSchema),
|
|
7696
|
+
corePath: "/permissions/request_set_mode",
|
|
7697
|
+
transformInput: withPermissionsSource("mcp")
|
|
7698
|
+
}
|
|
7699
|
+
},
|
|
6973
7700
|
{
|
|
6974
7701
|
name: "drive.navigate",
|
|
6975
7702
|
config: {
|