@browserbridge/bbx 1.0.0 → 1.0.1
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/README.md +3 -1
- package/docs/api-reference.md +33 -33
- package/docs/mcp-vs-cli.md +104 -104
- package/docs/publishing.md +1 -3
- package/docs/quickstart.md +6 -6
- package/docs/unpacked-extension.md +72 -0
- package/manifest.json +3 -17
- package/package.json +44 -42
- package/packages/agent-client/src/cli-helpers.js +10 -5
- package/packages/agent-client/src/cli.js +65 -135
- package/packages/agent-client/src/client.js +37 -17
- package/packages/agent-client/src/command-registry.js +101 -69
- package/packages/agent-client/src/detect.js +3 -6
- package/packages/agent-client/src/install.js +10 -27
- package/packages/agent-client/src/mcp-config.js +11 -30
- package/packages/agent-client/src/runtime.js +41 -20
- package/packages/agent-client/src/setup-status.js +13 -28
- package/packages/extension/src/background-helpers.js +51 -36
- package/packages/extension/src/background-routing.js +11 -13
- package/packages/extension/src/background.js +562 -299
- package/packages/extension/src/content-script-helpers.js +17 -16
- package/packages/extension/src/content-script.js +175 -109
- package/packages/extension/src/sidepanel-helpers.js +3 -1
- package/packages/extension/ui/popup.js +39 -20
- package/packages/extension/ui/sidepanel.js +108 -191
- package/packages/extension/ui/ui.css +2 -1
- package/packages/mcp-server/src/handlers.js +546 -250
- package/packages/mcp-server/src/server.js +558 -257
- package/packages/native-host/bin/bridge-daemon.js +6 -2
- package/packages/native-host/bin/install-manifest.js +2 -2
- package/packages/native-host/bin/postinstall.js +4 -2
- package/packages/native-host/src/config.js +11 -7
- package/packages/native-host/src/daemon.js +143 -92
- package/packages/native-host/src/install-manifest.js +73 -22
- package/packages/native-host/src/native-host.js +55 -40
- package/packages/protocol/src/budget.js +3 -7
- package/packages/protocol/src/capabilities.js +3 -3
- package/packages/protocol/src/errors.js +11 -11
- package/packages/protocol/src/protocol.js +104 -71
- package/packages/protocol/src/registry.js +300 -45
- package/packages/protocol/src/summary.js +249 -106
- package/packages/protocol/src/types.js +1 -1
- package/skills/browser-bridge/SKILL.md +1 -1
- package/skills/browser-bridge/agents/openai.yaml +3 -3
- package/skills/browser-bridge/references/interaction.md +33 -11
- package/skills/browser-bridge/references/patch-workflow.md +3 -0
- package/skills/browser-bridge/references/protocol.md +125 -70
- package/skills/browser-bridge/references/tailwind.md +12 -11
- package/skills/browser-bridge/references/token-efficiency.md +23 -22
- package/skills/browser-bridge/references/ui-workflows.md +8 -0
- package/packages/extension/ui/offscreen.html +0 -6
- package/packages/extension/ui/offscreen.js +0 -61
|
@@ -3,7 +3,11 @@
|
|
|
3
3
|
import fs from 'node:fs';
|
|
4
4
|
import path from 'node:path';
|
|
5
5
|
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
APP_NAME,
|
|
8
|
+
getManifestInstallDir,
|
|
9
|
+
SUPPORTED_BROWSERS,
|
|
10
|
+
} from '../../native-host/src/config.js';
|
|
7
11
|
import { resolveDefaultExtensionId } from '../../native-host/src/install-manifest.js';
|
|
8
12
|
import { methodNeedsTab } from './cli-helpers.js';
|
|
9
13
|
import { BridgeClient } from './client.js';
|
|
@@ -65,7 +69,7 @@ export async function requestBridge(client, method, params = {}, options = {}) {
|
|
|
65
69
|
method,
|
|
66
70
|
params,
|
|
67
71
|
tabId: methodNeedsTab(method) ? (options.tabId ?? null) : null,
|
|
68
|
-
meta: withRequestMeta(options.source, options.tokenBudget)
|
|
72
|
+
meta: withRequestMeta(options.source, options.tokenBudget),
|
|
69
73
|
});
|
|
70
74
|
}
|
|
71
75
|
|
|
@@ -81,9 +85,14 @@ export async function resolveRef(client, refOrSelector, tabId = null, source) {
|
|
|
81
85
|
return refOrSelector;
|
|
82
86
|
}
|
|
83
87
|
|
|
84
|
-
const response = await requestBridge(
|
|
85
|
-
|
|
86
|
-
|
|
88
|
+
const response = await requestBridge(
|
|
89
|
+
client,
|
|
90
|
+
'dom.query',
|
|
91
|
+
{
|
|
92
|
+
selector: refOrSelector,
|
|
93
|
+
},
|
|
94
|
+
{ tabId, source }
|
|
95
|
+
);
|
|
87
96
|
|
|
88
97
|
if (!response.ok) {
|
|
89
98
|
throw new Error(response.error.message);
|
|
@@ -184,9 +193,7 @@ export async function checkBrowserManifests() {
|
|
|
184
193
|
*/
|
|
185
194
|
export async function getDoctorReport(options = {}) {
|
|
186
195
|
const manifest = await (options.loadManifest || loadInstalledManifest)();
|
|
187
|
-
const allowedOrigins = Array.isArray(manifest?.allowed_origins)
|
|
188
|
-
? manifest.allowed_origins
|
|
189
|
-
: [];
|
|
196
|
+
const allowedOrigins = Array.isArray(manifest?.allowed_origins) ? manifest.allowed_origins : [];
|
|
190
197
|
const manifestInstalled = Boolean(manifest);
|
|
191
198
|
const defaultExtensionId = options.defaultExtensionIdInfo || resolveDefaultExtensionId();
|
|
192
199
|
|
|
@@ -208,7 +215,7 @@ export async function getDoctorReport(options = {}) {
|
|
|
208
215
|
routeReason: 'access_disabled',
|
|
209
216
|
issues: [],
|
|
210
217
|
nextSteps: [],
|
|
211
|
-
browserManifests
|
|
218
|
+
browserManifests,
|
|
212
219
|
};
|
|
213
220
|
|
|
214
221
|
try {
|
|
@@ -217,7 +224,8 @@ export async function getDoctorReport(options = {}) {
|
|
|
217
224
|
if (!response.ok) {
|
|
218
225
|
throw new Error(response.error.message);
|
|
219
226
|
}
|
|
220
|
-
const result =
|
|
227
|
+
const result =
|
|
228
|
+
/** @type {{ daemon?: string, extensionConnected?: boolean, access?: {
|
|
221
229
|
enabled?: boolean,
|
|
222
230
|
windowId?: number | null,
|
|
223
231
|
routeTabId?: number | null,
|
|
@@ -227,10 +235,13 @@ export async function getDoctorReport(options = {}) {
|
|
|
227
235
|
report.daemonReachable = result.daemon === 'ok';
|
|
228
236
|
report.extensionConnected = result.extensionConnected === true;
|
|
229
237
|
report.accessEnabled = result.access?.enabled === true;
|
|
230
|
-
report.enabledWindowId =
|
|
231
|
-
|
|
238
|
+
report.enabledWindowId =
|
|
239
|
+
typeof result.access?.windowId === 'number' ? result.access.windowId : null;
|
|
240
|
+
report.routeTabId =
|
|
241
|
+
typeof result.access?.routeTabId === 'number' ? result.access.routeTabId : null;
|
|
232
242
|
report.routeReady = result.access?.routeReady === true;
|
|
233
|
-
report.routeReason =
|
|
243
|
+
report.routeReason =
|
|
244
|
+
typeof result.access?.reason === 'string' ? result.access.reason : 'access_disabled';
|
|
234
245
|
});
|
|
235
246
|
} catch {
|
|
236
247
|
report.daemonReachable = false;
|
|
@@ -241,13 +252,17 @@ export async function getDoctorReport(options = {}) {
|
|
|
241
252
|
|
|
242
253
|
if (!report.manifestInstalled) {
|
|
243
254
|
report.issues.push('native_host_manifest_missing');
|
|
244
|
-
report.nextSteps.push(
|
|
245
|
-
|
|
246
|
-
|
|
255
|
+
report.nextSteps.push(
|
|
256
|
+
defaultExtensionId.extensionId
|
|
257
|
+
? 'Run `bbx install` (or `bbx install --all` for all browsers) to install the native host manifest.'
|
|
258
|
+
: 'Run `bbx install <extension-id>` (or `bbx install --all`) to install the native host manifest.'
|
|
259
|
+
);
|
|
247
260
|
} else if (browsersWithoutManifest.length > 0) {
|
|
248
261
|
report.issues.push('native_host_manifest_partial');
|
|
249
262
|
const missing = browsersWithoutManifest.map((b) => b.browser).join(', ');
|
|
250
|
-
report.nextSteps.push(
|
|
263
|
+
report.nextSteps.push(
|
|
264
|
+
`Manifests missing for: ${missing}. Run \`bbx install --all\` to install for all supported browsers.`
|
|
265
|
+
);
|
|
251
266
|
}
|
|
252
267
|
if (!report.daemonReachable) {
|
|
253
268
|
report.issues.push('daemon_offline');
|
|
@@ -255,14 +270,20 @@ export async function getDoctorReport(options = {}) {
|
|
|
255
270
|
}
|
|
256
271
|
if (report.daemonReachable && !report.extensionConnected) {
|
|
257
272
|
report.issues.push('extension_disconnected');
|
|
258
|
-
report.nextSteps.push(
|
|
273
|
+
report.nextSteps.push(
|
|
274
|
+
'Open Chrome and make sure the Browser Bridge extension is installed and active.'
|
|
275
|
+
);
|
|
259
276
|
}
|
|
260
277
|
if (report.daemonReachable && report.extensionConnected && !report.accessEnabled) {
|
|
261
278
|
report.issues.push('access_disabled');
|
|
262
|
-
report.nextSteps.push(
|
|
279
|
+
report.nextSteps.push(
|
|
280
|
+
'If a Browser Bridge call returns ACCESS_DENIED, stop requesting access. Ask the user to click Enable for the needed window, then tell you when that window is ready.'
|
|
281
|
+
);
|
|
263
282
|
} else if (report.daemonReachable && report.extensionConnected && !report.routeReady) {
|
|
264
283
|
report.issues.push(report.routeReason || 'no_routable_active_tab');
|
|
265
|
-
report.nextSteps.push(
|
|
284
|
+
report.nextSteps.push(
|
|
285
|
+
'Switch to a supported page in the enabled window, or use an explicit tabId override.'
|
|
286
|
+
);
|
|
266
287
|
}
|
|
267
288
|
|
|
268
289
|
return report;
|
|
@@ -77,13 +77,9 @@ export async function collectSetupStatus(options = {}) {
|
|
|
77
77
|
const access = options.access || fs.promises.access.bind(fs.promises);
|
|
78
78
|
const readFile = options.readFile || fs.promises.readFile.bind(fs.promises);
|
|
79
79
|
const detectedMcpClients = new Set(detectMcpClients(options.mcpDetectors));
|
|
80
|
-
const detectedSkillTargets = new Set(
|
|
81
|
-
detectSkillTargets(options.skillDetectors),
|
|
82
|
-
);
|
|
80
|
+
const detectedSkillTargets = new Set(detectSkillTargets(options.skillDetectors));
|
|
83
81
|
for (const clientName of detectedMcpClients) {
|
|
84
|
-
if (
|
|
85
|
-
SUPPORTED_TARGETS.includes(/** @type {SupportedTarget} */ (clientName))
|
|
86
|
-
) {
|
|
82
|
+
if (SUPPORTED_TARGETS.includes(/** @type {SupportedTarget} */ (clientName))) {
|
|
87
83
|
detectedSkillTargets.add(/** @type {SupportedTarget} */ (clientName));
|
|
88
84
|
}
|
|
89
85
|
}
|
|
@@ -96,7 +92,7 @@ export async function collectSetupStatus(options = {}) {
|
|
|
96
92
|
detected: detectedMcpClients.has(clientName),
|
|
97
93
|
readFile,
|
|
98
94
|
});
|
|
99
|
-
})
|
|
95
|
+
})
|
|
100
96
|
);
|
|
101
97
|
const skillTargets = await Promise.all(
|
|
102
98
|
SUPPORTED_TARGETS.map(async (target) => {
|
|
@@ -107,7 +103,7 @@ export async function collectSetupStatus(options = {}) {
|
|
|
107
103
|
access,
|
|
108
104
|
readFile,
|
|
109
105
|
});
|
|
110
|
-
})
|
|
106
|
+
})
|
|
111
107
|
);
|
|
112
108
|
|
|
113
109
|
return {
|
|
@@ -138,12 +134,8 @@ async function collectMcpClientStatus(clientName, options) {
|
|
|
138
134
|
});
|
|
139
135
|
const entries = await Promise.all(
|
|
140
136
|
configPaths.map(async (configPath) => {
|
|
141
|
-
return readBrowserBridgeMcpEntry(
|
|
142
|
-
|
|
143
|
-
configPath,
|
|
144
|
-
options.readFile,
|
|
145
|
-
);
|
|
146
|
-
}),
|
|
137
|
+
return readBrowserBridgeMcpEntry(clientName, configPath, options.readFile);
|
|
138
|
+
})
|
|
147
139
|
);
|
|
148
140
|
const preferredEntry =
|
|
149
141
|
entries.find((entry) => entry.configured) ||
|
|
@@ -188,20 +180,17 @@ async function collectSkillTargetStatus(target, options) {
|
|
|
188
180
|
skillName,
|
|
189
181
|
sentinelFilename,
|
|
190
182
|
options.access,
|
|
191
|
-
options.readFile
|
|
183
|
+
options.readFile
|
|
192
184
|
);
|
|
193
|
-
})
|
|
185
|
+
})
|
|
194
186
|
);
|
|
195
187
|
const skillByName = new Map(skills.map((skill) => [skill.name, skill]));
|
|
196
188
|
const coreSkill = skillByName.get(coreSkillName) || null;
|
|
197
189
|
const coreInstalled = Boolean(coreSkill?.exists);
|
|
198
190
|
const coreManaged = Boolean(coreSkill?.exists && coreSkill.managed);
|
|
199
|
-
const installedVersion = getInstalledSkillBundleVersion(
|
|
200
|
-
coreSkill ? [coreSkill] : [],
|
|
201
|
-
);
|
|
191
|
+
const installedVersion = getInstalledSkillBundleVersion(coreSkill ? [coreSkill] : []);
|
|
202
192
|
const updateAvailable =
|
|
203
|
-
coreManaged &&
|
|
204
|
-
isManagedVersionOutdated(coreSkill?.version || null, currentVersion);
|
|
193
|
+
coreManaged && isManagedVersionOutdated(coreSkill?.version || null, currentVersion);
|
|
205
194
|
|
|
206
195
|
return {
|
|
207
196
|
key: target,
|
|
@@ -230,15 +219,13 @@ async function collectInstalledSkillStatus(
|
|
|
230
219
|
skillName,
|
|
231
220
|
sentinelFilename,
|
|
232
221
|
access,
|
|
233
|
-
readFile
|
|
222
|
+
readFile
|
|
234
223
|
) {
|
|
235
224
|
const skillPath = path.join(basePath, skillName);
|
|
236
225
|
const exists = await pathExists(skillPath, access);
|
|
237
226
|
const sentinelPath = path.join(skillPath, sentinelFilename);
|
|
238
227
|
const managed = exists && (await pathExists(sentinelPath, access));
|
|
239
|
-
const version = managed
|
|
240
|
-
? await readManagedSkillVersion(sentinelPath, readFile)
|
|
241
|
-
: null;
|
|
228
|
+
const version = managed ? await readManagedSkillVersion(sentinelPath, readFile) : null;
|
|
242
229
|
|
|
243
230
|
return {
|
|
244
231
|
name: skillName,
|
|
@@ -261,9 +248,7 @@ function getInstalledSkillBundleVersion(skills) {
|
|
|
261
248
|
if (!first || typeof first.version !== 'string') {
|
|
262
249
|
return null;
|
|
263
250
|
}
|
|
264
|
-
return skills.every((skill) => skill.version === first.version)
|
|
265
|
-
? first.version
|
|
266
|
-
: null;
|
|
251
|
+
return skills.every((skill) => skill.version === first.version) ? first.version : null;
|
|
267
252
|
}
|
|
268
253
|
|
|
269
254
|
/**
|
|
@@ -14,9 +14,22 @@ import {
|
|
|
14
14
|
/** @typedef {import('../../protocol/src/types.js').ErrorCode} ErrorCode */
|
|
15
15
|
|
|
16
16
|
const INTERACTIVE_AX_ROLES = new Set([
|
|
17
|
-
'button',
|
|
18
|
-
'
|
|
19
|
-
'
|
|
17
|
+
'button',
|
|
18
|
+
'link',
|
|
19
|
+
'textbox',
|
|
20
|
+
'checkbox',
|
|
21
|
+
'radio',
|
|
22
|
+
'combobox',
|
|
23
|
+
'listbox',
|
|
24
|
+
'menuitem',
|
|
25
|
+
'tab',
|
|
26
|
+
'switch',
|
|
27
|
+
'slider',
|
|
28
|
+
'spinbutton',
|
|
29
|
+
'searchbox',
|
|
30
|
+
'menuitemcheckbox',
|
|
31
|
+
'menuitemradio',
|
|
32
|
+
'option',
|
|
20
33
|
]);
|
|
21
34
|
|
|
22
35
|
/**
|
|
@@ -31,7 +44,7 @@ export function summarizeTabResult(tab, method) {
|
|
|
31
44
|
windowId: typeof tab.windowId === 'number' ? tab.windowId : null,
|
|
32
45
|
url: tab.url ?? '',
|
|
33
46
|
title: tab.title ?? '',
|
|
34
|
-
status: tab.status ?? 'unknown'
|
|
47
|
+
status: tab.status ?? 'unknown',
|
|
35
48
|
};
|
|
36
49
|
}
|
|
37
50
|
|
|
@@ -84,7 +97,7 @@ export function simplifyAXNode(node) {
|
|
|
84
97
|
checked: axTristateValue(node.checked),
|
|
85
98
|
disabled: axBool(node.disabled),
|
|
86
99
|
interactive: INTERACTIVE_AX_ROLES.has(role) || axBool(node.focusable),
|
|
87
|
-
childIds: Array.isArray(node.childIds) ? node.childIds.map(String) : []
|
|
100
|
+
childIds: Array.isArray(node.childIds) ? node.childIds.map(String) : [],
|
|
88
101
|
};
|
|
89
102
|
}
|
|
90
103
|
|
|
@@ -99,7 +112,7 @@ export function shouldLogAction(method) {
|
|
|
99
112
|
'skill.get_runtime_context',
|
|
100
113
|
'setup.get_status',
|
|
101
114
|
'setup.install',
|
|
102
|
-
'tabs.list'
|
|
115
|
+
'tabs.list',
|
|
103
116
|
].includes(method);
|
|
104
117
|
}
|
|
105
118
|
|
|
@@ -130,9 +143,10 @@ export function summarizeActionResult(response) {
|
|
|
130
143
|
return response.error.message;
|
|
131
144
|
}
|
|
132
145
|
|
|
133
|
-
const result =
|
|
134
|
-
|
|
135
|
-
|
|
146
|
+
const result =
|
|
147
|
+
response.result && typeof response.result === 'object'
|
|
148
|
+
? /** @type {Record<string, unknown>} */ (response.result)
|
|
149
|
+
: {};
|
|
136
150
|
|
|
137
151
|
if (typeof result.patchId === 'string') {
|
|
138
152
|
return `Patch ${result.patchId} applied.`;
|
|
@@ -165,24 +179,19 @@ export function summarizeActionResult(response) {
|
|
|
165
179
|
* }}
|
|
166
180
|
*/
|
|
167
181
|
export function estimateResponseTokens(response) {
|
|
168
|
-
const payload = response.ok
|
|
169
|
-
? response.result
|
|
170
|
-
: { error: response.error };
|
|
182
|
+
const payload = response.ok ? response.result : { error: response.error };
|
|
171
183
|
const estimate = estimateJsonPayloadCost(payload);
|
|
172
184
|
const responseBytes = estimate.bytes;
|
|
173
|
-
const result =
|
|
174
|
-
|
|
175
|
-
|
|
185
|
+
const result =
|
|
186
|
+
response.ok && response.result && typeof response.result === 'object'
|
|
187
|
+
? /** @type {Record<string, unknown>} */ (response.result)
|
|
188
|
+
: null;
|
|
176
189
|
const hasScreenshot = result != null && typeof result.image === 'string';
|
|
177
190
|
const nodeCount = result != null && Array.isArray(result.nodes) ? result.nodes.length : null;
|
|
178
|
-
const textPayload = hasScreenshot && result != null
|
|
179
|
-
? omitScreenshotImage(result)
|
|
180
|
-
: payload;
|
|
191
|
+
const textPayload = hasScreenshot && result != null ? omitScreenshotImage(result) : payload;
|
|
181
192
|
const textEstimate = estimateJsonPayloadCost(textPayload);
|
|
182
193
|
const imageTransportBytes = Math.max(0, responseBytes - textEstimate.bytes);
|
|
183
|
-
const imageBytes = hasScreenshot && result != null
|
|
184
|
-
? estimateInlineImageBytes(result.image)
|
|
185
|
-
: 0;
|
|
194
|
+
const imageBytes = hasScreenshot && result != null ? estimateInlineImageBytes(result.image) : 0;
|
|
186
195
|
|
|
187
196
|
return {
|
|
188
197
|
responseBytes,
|
|
@@ -258,11 +267,7 @@ function estimateInlineImageBytes(image) {
|
|
|
258
267
|
return 0;
|
|
259
268
|
}
|
|
260
269
|
|
|
261
|
-
const padding = base64.endsWith('==')
|
|
262
|
-
? 2
|
|
263
|
-
: base64.endsWith('=')
|
|
264
|
-
? 1
|
|
265
|
-
: 0;
|
|
270
|
+
const padding = base64.endsWith('==') ? 2 : base64.endsWith('=') ? 1 : 0;
|
|
266
271
|
return Math.max(0, Math.floor((base64.length * 3) / 4) - padding);
|
|
267
272
|
}
|
|
268
273
|
|
|
@@ -277,7 +282,12 @@ function estimateInlineImageBytes(image) {
|
|
|
277
282
|
* @returns {BridgeResponse}
|
|
278
283
|
*/
|
|
279
284
|
export function enforceTokenBudget(method, response, tokenBudget) {
|
|
280
|
-
if (
|
|
285
|
+
if (
|
|
286
|
+
!response.ok ||
|
|
287
|
+
typeof tokenBudget !== 'number' ||
|
|
288
|
+
!Number.isFinite(tokenBudget) ||
|
|
289
|
+
tokenBudget <= 0
|
|
290
|
+
) {
|
|
281
291
|
return response;
|
|
282
292
|
}
|
|
283
293
|
|
|
@@ -299,7 +309,11 @@ export function enforceTokenBudget(method, response, tokenBudget) {
|
|
|
299
309
|
let truncated = false;
|
|
300
310
|
let iterations = 0;
|
|
301
311
|
const MAX_BUDGET_ITERATIONS = 100;
|
|
302
|
-
while (
|
|
312
|
+
while (
|
|
313
|
+
estimateJsonPayloadCost(cloned).bytes > maxBytes &&
|
|
314
|
+
shrinkForBudget(cloned) &&
|
|
315
|
+
iterations < MAX_BUDGET_ITERATIONS
|
|
316
|
+
) {
|
|
303
317
|
truncated = true;
|
|
304
318
|
iterations += 1;
|
|
305
319
|
}
|
|
@@ -355,9 +369,10 @@ function shrinkForBudget(value) {
|
|
|
355
369
|
|
|
356
370
|
for (const key of ['image', 'html', 'text', 'value']) {
|
|
357
371
|
if (typeof value[key] === 'string' && value[key].length > 64) {
|
|
358
|
-
value[key] =
|
|
359
|
-
|
|
360
|
-
|
|
372
|
+
value[key] =
|
|
373
|
+
key === 'image'
|
|
374
|
+
? '[omitted image over token budget]'
|
|
375
|
+
: `${value[key].slice(0, Math.max(32, Math.floor(value[key].length * 0.75) - 1))}\u2026`;
|
|
361
376
|
if (typeof value.truncated !== 'boolean') {
|
|
362
377
|
value.truncated = true;
|
|
363
378
|
}
|
|
@@ -419,9 +434,7 @@ export function getErrorMessage(error) {
|
|
|
419
434
|
* @returns {string}
|
|
420
435
|
*/
|
|
421
436
|
export function normalizeRuntimeErrorMessage(message) {
|
|
422
|
-
return /^No tab with id[: ]/i.test(message)
|
|
423
|
-
? ERROR_CODES.TAB_MISMATCH
|
|
424
|
-
: message;
|
|
437
|
+
return /^No tab with id[: ]/i.test(message) ? ERROR_CODES.TAB_MISMATCH : message;
|
|
425
438
|
}
|
|
426
439
|
|
|
427
440
|
/**
|
|
@@ -434,7 +447,7 @@ export function normalizeCropRect(rect = {}) {
|
|
|
434
447
|
x: Math.max(0, Math.round((rect.x || 0) * scale)),
|
|
435
448
|
y: Math.max(0, Math.round((rect.y || 0) * scale)),
|
|
436
449
|
width: Math.max(1, Math.round((rect.width || 1) * scale)),
|
|
437
|
-
height: Math.max(1, Math.round((rect.height || 1) * scale))
|
|
450
|
+
height: Math.max(1, Math.round((rect.height || 1) * scale)),
|
|
438
451
|
};
|
|
439
452
|
}
|
|
440
453
|
|
|
@@ -455,5 +468,7 @@ export function safeOrigin(url) {
|
|
|
455
468
|
* @returns {Capability | null}
|
|
456
469
|
*/
|
|
457
470
|
export function inferCapability(method) {
|
|
458
|
-
return getMethodCapability(
|
|
471
|
+
return getMethodCapability(
|
|
472
|
+
/** @type {import('../../protocol/src/types.js').BridgeMethod} */ (method)
|
|
473
|
+
);
|
|
459
474
|
}
|
|
@@ -16,7 +16,9 @@ import { ERROR_CODES } from '../../protocol/src/index.js';
|
|
|
16
16
|
* @returns {boolean}
|
|
17
17
|
*/
|
|
18
18
|
export function isRestrictedAutomationUrl(url) {
|
|
19
|
-
return /^(about:|chrome:|chrome-extension:|chrome-search:|devtools:|edge:|brave:|moz-extension:|view-source:)/i.test(
|
|
19
|
+
return /^(about:|chrome:|chrome-extension:|chrome-search:|devtools:|edge:|brave:|moz-extension:|view-source:)/i.test(
|
|
20
|
+
url
|
|
21
|
+
);
|
|
20
22
|
}
|
|
21
23
|
|
|
22
24
|
/**
|
|
@@ -40,11 +42,7 @@ export function selectRequestTabCandidate(requestTabId, explicitTab, activeTab)
|
|
|
40
42
|
*/
|
|
41
43
|
export function resolveWindowScopedTab(tab, enabledWindowId, options = {}) {
|
|
42
44
|
const requireScriptable = options.requireScriptable !== false;
|
|
43
|
-
if (
|
|
44
|
-
typeof tab?.id !== 'number'
|
|
45
|
-
|| !Number.isFinite(tab.id)
|
|
46
|
-
|| typeof tab.windowId !== 'number'
|
|
47
|
-
) {
|
|
45
|
+
if (typeof tab?.id !== 'number' || !Number.isFinite(tab.id) || typeof tab.windowId !== 'number') {
|
|
48
46
|
throw new Error(ERROR_CODES.TAB_MISMATCH);
|
|
49
47
|
}
|
|
50
48
|
if (tab.windowId !== enabledWindowId) {
|
|
@@ -61,7 +59,7 @@ export function resolveWindowScopedTab(tab, enabledWindowId, options = {}) {
|
|
|
61
59
|
tabId: tab.id,
|
|
62
60
|
windowId: tab.windowId,
|
|
63
61
|
title: tab.title ?? '',
|
|
64
|
-
url: tab.url
|
|
62
|
+
url: tab.url,
|
|
65
63
|
};
|
|
66
64
|
}
|
|
67
65
|
|
|
@@ -71,11 +69,11 @@ export function resolveWindowScopedTab(tab, enabledWindowId, options = {}) {
|
|
|
71
69
|
*/
|
|
72
70
|
export function normalizeRequestedAccessTab(tab) {
|
|
73
71
|
if (
|
|
74
|
-
typeof tab?.id !== 'number'
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
72
|
+
typeof tab?.id !== 'number' ||
|
|
73
|
+
!Number.isFinite(tab.id) ||
|
|
74
|
+
typeof tab.windowId !== 'number' ||
|
|
75
|
+
typeof tab.url !== 'string' ||
|
|
76
|
+
!tab.url
|
|
79
77
|
) {
|
|
80
78
|
return null;
|
|
81
79
|
}
|
|
@@ -86,6 +84,6 @@ export function normalizeRequestedAccessTab(tab) {
|
|
|
86
84
|
tabId: tab.id,
|
|
87
85
|
windowId: tab.windowId,
|
|
88
86
|
title: tab.title ?? '',
|
|
89
|
-
url: tab.url
|
|
87
|
+
url: tab.url,
|
|
90
88
|
};
|
|
91
89
|
}
|