@browserbridge/bbx 1.7.8 → 1.9.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/PRIVACY.md +57 -12
- package/README.md +19 -8
- package/SECURITY.md +12 -0
- package/package.json +15 -6
- package/packages/agent-client/src/cli-args.js +59 -0
- package/packages/agent-client/src/cli-batch.js +121 -58
- package/packages/agent-client/src/cli-output.js +8 -1
- package/packages/agent-client/src/cli.js +272 -70
- package/packages/agent-client/src/client.js +296 -8
- package/packages/agent-client/src/command-registry.js +64 -55
- package/packages/agent-client/src/config.js +195 -0
- package/packages/agent-client/src/content-extract.js +198 -0
- package/packages/agent-client/src/npm-self-update.js +305 -0
- package/packages/agent-client/src/remotes.js +4 -2
- package/packages/agent-client/src/runtime.js +864 -39
- package/packages/agent-client/src/types.ts +169 -6
- package/packages/mcp-server/src/guidance.js +1 -1
- package/packages/mcp-server/src/handlers-capture.js +69 -6
- package/packages/mcp-server/src/handlers-dom.js +61 -4
- package/packages/mcp-server/src/handlers-navigation.js +2 -1
- package/packages/mcp-server/src/handlers-page.js +211 -54
- package/packages/mcp-server/src/handlers-utils.js +101 -10
- package/packages/mcp-server/src/handlers.js +14 -7
- package/packages/mcp-server/src/server.js +176 -11
- package/packages/native-host/src/artifact-store.js +353 -0
- package/packages/native-host/src/config.js +9 -1
- package/packages/native-host/src/daemon-logger.js +3 -1
- package/packages/native-host/src/daemon.js +701 -33
- package/packages/native-host/src/native-host.js +28 -3
- package/packages/protocol/src/capability-values.js +2 -0
- package/packages/protocol/src/defaults.js +31 -0
- package/packages/protocol/src/errors.js +102 -6
- package/packages/protocol/src/incidental-sanitizer.js +197 -0
- package/packages/protocol/src/index.js +2 -0
- package/packages/protocol/src/protocol.js +612 -6
- package/packages/protocol/src/recovery-telemetry.js +302 -0
- package/packages/protocol/src/registry.js +206 -38
- package/packages/protocol/src/summary.js +203 -30
- package/packages/protocol/src/types.ts +721 -2
- package/skills/browser-bridge/SKILL.md +54 -19
- package/skills/browser-bridge/references/interaction.md +55 -11
- package/skills/browser-bridge/references/patch-workflow.md +2 -0
- package/skills/browser-bridge/references/protocol.md +265 -86
- package/skills/browser-bridge/references/token-efficiency.md +29 -13
|
@@ -4,10 +4,21 @@ import type {
|
|
|
4
4
|
BridgeMethod,
|
|
5
5
|
BridgeRequestSource,
|
|
6
6
|
BridgeResponse,
|
|
7
|
+
RecoveryEventKind,
|
|
8
|
+
RecoveryTelemetrySummary,
|
|
9
|
+
ScreenshotResult,
|
|
10
|
+
SetupStatus,
|
|
7
11
|
} from '../../protocol/src/types.js';
|
|
8
12
|
import type { restartBridgeDaemon } from '../../native-host/src/daemon-process.js';
|
|
9
13
|
|
|
10
|
-
export type {
|
|
14
|
+
export type {
|
|
15
|
+
BridgeMeta,
|
|
16
|
+
BridgeMethod,
|
|
17
|
+
BridgeRequestSource,
|
|
18
|
+
BridgeResponse,
|
|
19
|
+
BridgeTransport,
|
|
20
|
+
ScreenshotResult,
|
|
21
|
+
};
|
|
11
22
|
|
|
12
23
|
export type McpClientName =
|
|
13
24
|
| 'codex'
|
|
@@ -21,6 +32,26 @@ export type McpClientName =
|
|
|
21
32
|
|
|
22
33
|
export type SupportedTarget = McpClientName;
|
|
23
34
|
|
|
35
|
+
export type AutoUpdatePolicy = 'off' | 'compatible';
|
|
36
|
+
|
|
37
|
+
export interface BrowserBridgeConfig {
|
|
38
|
+
autoUpdate: AutoUpdatePolicy;
|
|
39
|
+
[key: string]: unknown;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface NpmUpdateResult {
|
|
43
|
+
updated: boolean;
|
|
44
|
+
reason:
|
|
45
|
+
| 'updated'
|
|
46
|
+
| 'invalid_extension_version'
|
|
47
|
+
| 'invalid_installed_version'
|
|
48
|
+
| 'extension_not_newer'
|
|
49
|
+
| 'not_global_install'
|
|
50
|
+
| 'no_compatible_update';
|
|
51
|
+
previousVersion?: string;
|
|
52
|
+
version?: string;
|
|
53
|
+
}
|
|
54
|
+
|
|
24
55
|
export type Detector = () => boolean | Promise<boolean>;
|
|
25
56
|
|
|
26
57
|
export interface InstallAgentOptions {
|
|
@@ -42,7 +73,10 @@ export interface SetupStatusOptions {
|
|
|
42
73
|
|
|
43
74
|
export interface ProtocolHealthResult {
|
|
44
75
|
extensionConnected?: boolean;
|
|
76
|
+
extensionVersion?: string;
|
|
77
|
+
daemonVersion?: string;
|
|
45
78
|
supported_versions?: string[];
|
|
79
|
+
extension_supported_versions?: string[];
|
|
46
80
|
daemon_supported_versions?: string[];
|
|
47
81
|
deprecated_since?: string;
|
|
48
82
|
migration_hint?: string;
|
|
@@ -78,8 +112,15 @@ export interface BridgeClientOptions {
|
|
|
78
112
|
clientId?: string;
|
|
79
113
|
defaultTimeoutMs?: number;
|
|
80
114
|
autoReconnect?: boolean;
|
|
115
|
+
checkProtocolOnConnect?: boolean;
|
|
81
116
|
restartDaemonOnVersionMismatch?: boolean;
|
|
82
117
|
restartDaemonFn?: typeof restartBridgeDaemon;
|
|
118
|
+
updateNpmOnCompatibleVersion?: boolean;
|
|
119
|
+
exitProcessOnNpmUpdate?: boolean;
|
|
120
|
+
updateCompatibleNpmPackageFn?: (options: {
|
|
121
|
+
extensionVersion: string;
|
|
122
|
+
supportedVersions: readonly string[];
|
|
123
|
+
}) => Promise<NpmUpdateResult>;
|
|
83
124
|
authToken?: string | null;
|
|
84
125
|
}
|
|
85
126
|
|
|
@@ -110,6 +151,113 @@ export interface NativeHostManifestIssue {
|
|
|
110
151
|
message: string;
|
|
111
152
|
}
|
|
112
153
|
|
|
154
|
+
export interface DoctorTransportDiagnostics {
|
|
155
|
+
kind: 'socket' | 'tcp' | 'unknown';
|
|
156
|
+
local: true;
|
|
157
|
+
status: 'reachable' | 'offline' | 'authentication_failed';
|
|
158
|
+
proxyConfigured: boolean;
|
|
159
|
+
proxyExposed: boolean | null;
|
|
160
|
+
credentials: 'not_required' | 'accepted' | 'rejected' | 'unknown';
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export interface DoctorConnectionDiagnostics {
|
|
164
|
+
extensionCount: number;
|
|
165
|
+
profileCount: number;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export interface DoctorProtocolDiagnostics {
|
|
169
|
+
clientVersion: string;
|
|
170
|
+
daemonVersion: string | null;
|
|
171
|
+
daemonSupportedVersions: string[];
|
|
172
|
+
extensionSupportedVersions: string[];
|
|
173
|
+
daemonCompatible: boolean | null;
|
|
174
|
+
extensionCompatible: boolean | null;
|
|
175
|
+
compatible: boolean | null;
|
|
176
|
+
migration: 'none' | 'update_client' | 'restart_daemon' | 'update_extension' | 'unknown';
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export interface DoctorDebuggerDiagnostics {
|
|
180
|
+
state: 'idle' | 'active' | 'conflict' | 'detached' | 'unknown';
|
|
181
|
+
attachedTabCount: number | null;
|
|
182
|
+
heldTabCount: number | null;
|
|
183
|
+
pendingTabCount: number | null;
|
|
184
|
+
recentReason:
|
|
185
|
+
| 'debugger_conflict'
|
|
186
|
+
| 'debugger_detached'
|
|
187
|
+
| 'debugger_replaced'
|
|
188
|
+
| 'debugger_canceled'
|
|
189
|
+
| 'target_closed'
|
|
190
|
+
| null;
|
|
191
|
+
captureState:
|
|
192
|
+
| 'idle'
|
|
193
|
+
| 'stopped'
|
|
194
|
+
| 'armed'
|
|
195
|
+
| 'active'
|
|
196
|
+
| 'capturing'
|
|
197
|
+
| 'stop_failed'
|
|
198
|
+
| 'unavailable'
|
|
199
|
+
| 'unknown';
|
|
200
|
+
captureActiveTabCount: number | null;
|
|
201
|
+
captureOwnershipCount: number | null;
|
|
202
|
+
captureInflightCount: number | null;
|
|
203
|
+
interceptionActiveTabCount: number | null;
|
|
204
|
+
interceptionRuleCount: number | null;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
export interface DoctorDaemonMetrics {
|
|
208
|
+
uptimeMs: number;
|
|
209
|
+
activeAgents: number;
|
|
210
|
+
activeExtensions: number;
|
|
211
|
+
pendingRequests: number;
|
|
212
|
+
requestsProcessed: number;
|
|
213
|
+
requestsFailed: number;
|
|
214
|
+
avgResponseTimeMs: number;
|
|
215
|
+
recovery: RecoveryTelemetrySummary | null;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
export interface DoctorRecoveryDiagnostics {
|
|
219
|
+
daemon: RecoveryTelemetrySummary | null;
|
|
220
|
+
routedExtension: RecoveryTelemetrySummary | null;
|
|
221
|
+
activeLoops: RecoveryEventKind[];
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
export interface DoctorRecentEvent {
|
|
225
|
+
at?: string;
|
|
226
|
+
method?: BridgeMethod;
|
|
227
|
+
ok?: boolean;
|
|
228
|
+
source?: BridgeRequestSource;
|
|
229
|
+
cause?:
|
|
230
|
+
| 'debugger_conflict'
|
|
231
|
+
| 'debugger_detached'
|
|
232
|
+
| 'debugger_replaced'
|
|
233
|
+
| 'debugger_canceled'
|
|
234
|
+
| 'target_closed'
|
|
235
|
+
| 'dialog_conflict'
|
|
236
|
+
| 'extension_disconnected'
|
|
237
|
+
| 'wrong_window';
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
export interface DoctorSetupSummary {
|
|
241
|
+
source: 'daemon' | 'direct';
|
|
242
|
+
scope: 'global' | 'local';
|
|
243
|
+
mcp: {
|
|
244
|
+
detected: number;
|
|
245
|
+
configured: number;
|
|
246
|
+
};
|
|
247
|
+
skills: {
|
|
248
|
+
detected: number;
|
|
249
|
+
installed: number;
|
|
250
|
+
managed: number;
|
|
251
|
+
updatesAvailable: number;
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
export interface DoctorRemoteDiagnostics {
|
|
256
|
+
configuredCount: number;
|
|
257
|
+
status: 'not_configured' | 'not_probed_local_only' | 'config_unavailable';
|
|
258
|
+
credentials: 'not_configured' | 'unverified';
|
|
259
|
+
}
|
|
260
|
+
|
|
113
261
|
export interface DoctorReport {
|
|
114
262
|
manifestInstalled: boolean;
|
|
115
263
|
manifestPath: string;
|
|
@@ -117,6 +265,7 @@ export interface DoctorReport {
|
|
|
117
265
|
defaultExtensionId: string | null;
|
|
118
266
|
defaultExtensionIdSource: string;
|
|
119
267
|
daemonReachable: boolean;
|
|
268
|
+
healthAvailable: boolean;
|
|
120
269
|
extensionConnected: boolean;
|
|
121
270
|
accessEnabled: boolean;
|
|
122
271
|
enabledWindowId: number | null;
|
|
@@ -130,6 +279,17 @@ export interface DoctorReport {
|
|
|
130
279
|
issues: string[];
|
|
131
280
|
nextSteps: string[];
|
|
132
281
|
browserManifests: BrowserManifestStatus[];
|
|
282
|
+
transport: DoctorTransportDiagnostics;
|
|
283
|
+
connections: DoctorConnectionDiagnostics;
|
|
284
|
+
protocol: DoctorProtocolDiagnostics;
|
|
285
|
+
debugger: DoctorDebuggerDiagnostics;
|
|
286
|
+
recovery: DoctorRecoveryDiagnostics;
|
|
287
|
+
metrics: DoctorDaemonMetrics | null;
|
|
288
|
+
recentEvents: DoctorRecentEvent[];
|
|
289
|
+
recentCauses: DoctorRecentEvent['cause'][];
|
|
290
|
+
setup: DoctorSetupSummary | null;
|
|
291
|
+
remoteDestinations: DoctorRemoteDiagnostics;
|
|
292
|
+
diagnosticFailures: string[];
|
|
133
293
|
}
|
|
134
294
|
|
|
135
295
|
export interface DoctorReportOptions {
|
|
@@ -145,6 +305,14 @@ export interface DoctorReportOptions {
|
|
|
145
305
|
checkNativeHostManifestHealth?: (
|
|
146
306
|
browserManifests: BrowserManifestStatus[]
|
|
147
307
|
) => Promise<NativeHostManifestIssue[]>;
|
|
308
|
+
readInstalledExtensionIds?: (browserManifests: BrowserManifestStatus[]) => Promise<string[]>;
|
|
309
|
+
collectSetupStatus?: () => Promise<SetupStatus>;
|
|
310
|
+
readRemoteConfig?: () => Promise<{
|
|
311
|
+
remotes: Array<{ id: string; host: string; port: number; token: string }>;
|
|
312
|
+
}>;
|
|
313
|
+
getLocalTransport?: () => BridgeTransport;
|
|
314
|
+
readProxyConfig?: () => { enabled: boolean } | null;
|
|
315
|
+
includeSetupStatus?: boolean;
|
|
148
316
|
}
|
|
149
317
|
|
|
150
318
|
export type BridgeClientRequest = (options: {
|
|
@@ -154,8 +322,3 @@ export type BridgeClientRequest = (options: {
|
|
|
154
322
|
meta?: BridgeMeta;
|
|
155
323
|
timeoutMs?: number;
|
|
156
324
|
}) => Promise<BridgeResponse>;
|
|
157
|
-
|
|
158
|
-
export interface ScreenshotResult {
|
|
159
|
-
image: string;
|
|
160
|
-
rect: Record<string, unknown>;
|
|
161
|
-
}
|
|
@@ -5,7 +5,7 @@ export const MCP_SERVER_INSTRUCTIONS = [
|
|
|
5
5
|
'In permission-ask hosts, use browser_call as the default Browser Bridge MCP tool so the user can approve one BBX tool instead of separate browser_status, browser_page, browser_dom, browser_input, and other tools.',
|
|
6
6
|
'Prefer Browser Bridge MCP tools over shelling out to bbx. Use bbx only for explicit CLI setup, doctor, logs, or raw debugging requests.',
|
|
7
7
|
'Start with browser_call method health.ping. If window access is disabled, call browser_call method access.request once, ask the user to click Enable in the Browser Bridge popup or side panel, then retry once.',
|
|
8
|
-
'Use structured reads first through browser_call: page.get_state, dom.query, page.get_text, styles.get_computed, or layout.get_box_model. Use browser_batch only for parallel reads; run mutations sequentially and keep limits tight before widening.',
|
|
8
|
+
'Use structured reads first through browser_call: page.get_state, dom.query, page.get_text, page.extract_content, styles.get_computed, or layout.get_box_model. Prefer semantic extraction for articles and documentation. Use browser_batch only for parallel reads; run mutations sequentially and keep limits tight before widening.',
|
|
9
9
|
'Reuse elementRef values returned by DOM tools. Use attribute allowlists for focused DOM reads.',
|
|
10
10
|
'Escalate to screenshot.capture_element, screenshot.capture_region, dom.get_accessibility_tree, page.evaluate, viewport.resize, or CDP only when structured reads cannot answer the question.',
|
|
11
11
|
'Use patch.apply_styles or patch.apply_dom for temporary experiments, and rollback patches before finishing unless the user asks to keep them.',
|
|
@@ -22,23 +22,40 @@ export const CAPTURE_ACTIONS = {
|
|
|
22
22
|
element: {
|
|
23
23
|
ref: true,
|
|
24
24
|
method: 'screenshot.capture_element',
|
|
25
|
-
params: (
|
|
25
|
+
params: (a, r) => ({
|
|
26
|
+
elementRef: r,
|
|
27
|
+
...(a.format ? { format: a.format } : {}),
|
|
28
|
+
...(typeof a.quality === 'number' ? { quality: a.quality } : {}),
|
|
29
|
+
delivery: a.delivery ?? 'auto',
|
|
30
|
+
...(typeof a.scale === 'number' ? { scale: a.scale } : {}),
|
|
31
|
+
}),
|
|
26
32
|
},
|
|
27
33
|
region: {
|
|
28
34
|
ref: false,
|
|
29
35
|
method: 'screenshot.capture_region',
|
|
30
|
-
params: (a) =>
|
|
36
|
+
params: (a) => ({
|
|
37
|
+
.../** @type {Record<string, unknown>} */ (a.rect || {}),
|
|
38
|
+
...(a.format ? { format: a.format } : {}),
|
|
39
|
+
...(typeof a.quality === 'number' ? { quality: a.quality } : {}),
|
|
40
|
+
delivery: a.delivery ?? 'auto',
|
|
41
|
+
...(typeof a.scale === 'number' ? { scale: a.scale } : {}),
|
|
42
|
+
}),
|
|
31
43
|
},
|
|
32
44
|
full_page: {
|
|
33
45
|
ref: false,
|
|
34
46
|
method: 'screenshot.capture_full_page',
|
|
35
|
-
params: () => ({
|
|
47
|
+
params: (a) => ({
|
|
48
|
+
...(a.format ? { format: a.format } : {}),
|
|
49
|
+
...(typeof a.quality === 'number' ? { quality: a.quality } : {}),
|
|
50
|
+
delivery: a.delivery ?? 'auto',
|
|
51
|
+
...(typeof a.scale === 'number' ? { scale: a.scale } : {}),
|
|
52
|
+
}),
|
|
36
53
|
},
|
|
37
54
|
cdp_document: { ref: false, method: 'cdp.get_document', params: () => ({}) },
|
|
38
55
|
cdp_dom_snapshot: {
|
|
39
56
|
ref: false,
|
|
40
57
|
method: 'cdp.get_dom_snapshot',
|
|
41
|
-
params: () => ({}),
|
|
58
|
+
params: (a) => ({ computedStyles: a.computedStyles }),
|
|
42
59
|
},
|
|
43
60
|
cdp_box_model: {
|
|
44
61
|
ref: false,
|
|
@@ -82,7 +99,7 @@ function isValidCaptureRegion(rect) {
|
|
|
82
99
|
}
|
|
83
100
|
|
|
84
101
|
/**
|
|
85
|
-
* @param {{ action: string, elementRef?: string, selector?: string, rect?: Record<string, unknown>, nodeId?: number, tabId?: number, destinationId?: string, budgetPreset?: 'quick' | 'normal' | 'deep' }} args
|
|
102
|
+
* @param {{ action: string, elementRef?: string, selector?: string, rect?: Record<string, unknown>, format?: 'png' | 'jpeg' | 'webp', quality?: number, delivery?: 'auto' | 'inline' | 'artifact', scale?: number, nodeId?: number, computedStyles?: string[], tabId?: number, destinationId?: string, budgetPreset?: 'quick' | 'normal' | 'deep' }} args
|
|
86
103
|
* @returns {Promise<ToolResult>}
|
|
87
104
|
*/
|
|
88
105
|
export async function handleCaptureTool(args) {
|
|
@@ -130,6 +147,27 @@ export async function handleCaptureTool(args) {
|
|
|
130
147
|
*/
|
|
131
148
|
export function createScreenshotResult(response, method) {
|
|
132
149
|
const result = toRecord(response.result);
|
|
150
|
+
if (result.delivery === 'artifact') {
|
|
151
|
+
const artifact = toRecord(result.artifact);
|
|
152
|
+
if (typeof artifact.artifactId !== 'string' || typeof artifact.byteLength !== 'number') {
|
|
153
|
+
return summarizeToolError(new Error(`${method} returned invalid artifact metadata.`));
|
|
154
|
+
}
|
|
155
|
+
return createToolResult(
|
|
156
|
+
`Captured screenshot artifact (${artifact.byteLength} bytes, expires ${artifact.expiresAt ?? 'soon'}).`,
|
|
157
|
+
{
|
|
158
|
+
ok: true,
|
|
159
|
+
method,
|
|
160
|
+
delivery: 'artifact',
|
|
161
|
+
artifact,
|
|
162
|
+
mimeType: result.mimeType,
|
|
163
|
+
byteLength: result.byteLength,
|
|
164
|
+
dimensions: result.dimensions,
|
|
165
|
+
rect: boundedRect(result.rect),
|
|
166
|
+
complete: result.complete === true,
|
|
167
|
+
clipped: result.clipped === true,
|
|
168
|
+
}
|
|
169
|
+
);
|
|
170
|
+
}
|
|
133
171
|
if (typeof result.image !== 'string') {
|
|
134
172
|
return summarizeToolError(new Error(`${method} returned no image data.`));
|
|
135
173
|
}
|
|
@@ -148,6 +186,9 @@ export function createScreenshotResult(response, method) {
|
|
|
148
186
|
mimeType: image.mimeType,
|
|
149
187
|
byteLength: image.byteLength,
|
|
150
188
|
...(rect ? { rect } : {}),
|
|
189
|
+
format: result.format ?? image.mimeType.replace('image/', ''),
|
|
190
|
+
complete: result.complete === true,
|
|
191
|
+
clipped: result.clipped === true,
|
|
151
192
|
},
|
|
152
193
|
false,
|
|
153
194
|
[{ type: 'image', data: image.data, mimeType: image.mimeType }]
|
|
@@ -294,7 +335,20 @@ function createInputTarget(elementRef, selector) {
|
|
|
294
335
|
}
|
|
295
336
|
|
|
296
337
|
/**
|
|
297
|
-
*
|
|
338
|
+
* Avoid changing legacy handler payloads when new options are omitted.
|
|
339
|
+
*
|
|
340
|
+
* @param {{ executionMode?: 'dom' | 'cdp', recoverStale?: boolean }} args
|
|
341
|
+
* @returns {{ executionMode?: 'dom' | 'cdp', recoverStale?: boolean }}
|
|
342
|
+
*/
|
|
343
|
+
function createInputOptions(args) {
|
|
344
|
+
return {
|
|
345
|
+
...(args.executionMode ? { executionMode: args.executionMode } : {}),
|
|
346
|
+
...(typeof args.recoverStale === 'boolean' ? { recoverStale: args.recoverStale } : {}),
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
/**
|
|
351
|
+
* @param {{ action: string, elementRef?: string, selector?: string, button?: string, clickCount?: number, text?: string, value?: string, mode?: 'auto' | 'setter' | 'keystrokes', executionMode?: 'dom' | 'cdp', recoverStale?: boolean, clear?: boolean, submit?: boolean, key?: string, code?: string, modifiers?: string[], checked?: boolean, values?: string[], labels?: string[], indexes?: number[], duration?: number, sourceElementRef?: string, sourceSelector?: string, destinationElementRef?: string, destinationSelector?: string, offsetX?: number, offsetY?: number, tabId?: number, destinationId?: string, budgetPreset?: 'quick' | 'normal' | 'deep' }} args
|
|
298
352
|
* @returns {Promise<ToolResult>}
|
|
299
353
|
*/
|
|
300
354
|
export async function handleInputTool(args) {
|
|
@@ -337,6 +391,7 @@ export async function handleInputTool(args) {
|
|
|
337
391
|
button: args.button,
|
|
338
392
|
clickCount: args.clickCount,
|
|
339
393
|
modifiers: args.modifiers,
|
|
394
|
+
...createInputOptions(args),
|
|
340
395
|
},
|
|
341
396
|
{
|
|
342
397
|
tabId: requestedTabId,
|
|
@@ -352,6 +407,7 @@ export async function handleInputTool(args) {
|
|
|
352
407
|
'input.focus',
|
|
353
408
|
{
|
|
354
409
|
target: elementTarget(),
|
|
410
|
+
...createInputOptions(args),
|
|
355
411
|
},
|
|
356
412
|
{
|
|
357
413
|
tabId: requestedTabId,
|
|
@@ -371,6 +427,7 @@ export async function handleInputTool(args) {
|
|
|
371
427
|
clear: args.clear,
|
|
372
428
|
submit: args.submit,
|
|
373
429
|
modifiers: args.modifiers,
|
|
430
|
+
...createInputOptions(args),
|
|
374
431
|
},
|
|
375
432
|
{
|
|
376
433
|
tabId: requestedTabId,
|
|
@@ -388,6 +445,7 @@ export async function handleInputTool(args) {
|
|
|
388
445
|
target: elementTarget(),
|
|
389
446
|
value: args.value,
|
|
390
447
|
mode: args.mode,
|
|
448
|
+
...createInputOptions(args),
|
|
391
449
|
},
|
|
392
450
|
{
|
|
393
451
|
tabId: requestedTabId,
|
|
@@ -406,6 +464,7 @@ export async function handleInputTool(args) {
|
|
|
406
464
|
target,
|
|
407
465
|
key: args.key,
|
|
408
466
|
modifiers: args.modifiers,
|
|
467
|
+
...createInputOptions(args),
|
|
409
468
|
},
|
|
410
469
|
{
|
|
411
470
|
tabId: requestedTabId,
|
|
@@ -439,6 +498,7 @@ export async function handleInputTool(args) {
|
|
|
439
498
|
{
|
|
440
499
|
target: elementTarget(),
|
|
441
500
|
checked: args.checked,
|
|
501
|
+
...createInputOptions(args),
|
|
442
502
|
},
|
|
443
503
|
{
|
|
444
504
|
tabId: requestedTabId,
|
|
@@ -457,6 +517,7 @@ export async function handleInputTool(args) {
|
|
|
457
517
|
values: args.values,
|
|
458
518
|
labels: args.labels,
|
|
459
519
|
indexes: args.indexes,
|
|
520
|
+
...createInputOptions(args),
|
|
460
521
|
},
|
|
461
522
|
{
|
|
462
523
|
tabId: requestedTabId,
|
|
@@ -474,6 +535,7 @@ export async function handleInputTool(args) {
|
|
|
474
535
|
target: elementTarget(),
|
|
475
536
|
duration: args.duration,
|
|
476
537
|
modifiers: args.modifiers,
|
|
538
|
+
...createInputOptions(args),
|
|
477
539
|
},
|
|
478
540
|
{
|
|
479
541
|
tabId: requestedTabId,
|
|
@@ -502,6 +564,7 @@ export async function handleInputTool(args) {
|
|
|
502
564
|
destination,
|
|
503
565
|
offsetX: args.offsetX,
|
|
504
566
|
offsetY: args.offsetY,
|
|
567
|
+
...createInputOptions(args),
|
|
505
568
|
},
|
|
506
569
|
{
|
|
507
570
|
tabId: requestedTabId,
|
|
@@ -15,6 +15,32 @@ import {
|
|
|
15
15
|
|
|
16
16
|
/** @type {Record<string, ToolAction>} */
|
|
17
17
|
export const DOM_ACTIONS = {
|
|
18
|
+
baseline_create: {
|
|
19
|
+
ref: false,
|
|
20
|
+
method: 'dom.baseline.create',
|
|
21
|
+
params: (a) => ({
|
|
22
|
+
selector: a.selector,
|
|
23
|
+
maxNodes: a.maxNodes,
|
|
24
|
+
maxDepth: a.maxDepth,
|
|
25
|
+
textBudget: a.textBudget,
|
|
26
|
+
attributeAllowlist: a.attributeAllowlist,
|
|
27
|
+
}),
|
|
28
|
+
},
|
|
29
|
+
baseline_compare: {
|
|
30
|
+
ref: false,
|
|
31
|
+
method: 'dom.baseline.compare',
|
|
32
|
+
params: (a) => ({ baselineId: a.baselineId, maxChanges: a.maxChanges }),
|
|
33
|
+
},
|
|
34
|
+
baseline_describe: {
|
|
35
|
+
ref: false,
|
|
36
|
+
method: 'dom.baseline.describe',
|
|
37
|
+
params: (a) => ({ baselineId: a.baselineId }),
|
|
38
|
+
},
|
|
39
|
+
baseline_release: {
|
|
40
|
+
ref: false,
|
|
41
|
+
method: 'dom.baseline.release',
|
|
42
|
+
params: (a) => ({ baselineId: a.baselineId }),
|
|
43
|
+
},
|
|
18
44
|
query: {
|
|
19
45
|
ref: false,
|
|
20
46
|
method: 'dom.query',
|
|
@@ -85,12 +111,18 @@ export const DOM_ACTIONS = {
|
|
|
85
111
|
accessibility_tree: {
|
|
86
112
|
ref: false,
|
|
87
113
|
method: 'dom.get_accessibility_tree',
|
|
88
|
-
params: (a) => ({
|
|
114
|
+
params: (a) => ({
|
|
115
|
+
selector: a.selector,
|
|
116
|
+
maxNodes: a.maxNodes,
|
|
117
|
+
maxDepth: a.maxDepth,
|
|
118
|
+
compact: a.compact,
|
|
119
|
+
interactiveOnly: a.interactiveOnly,
|
|
120
|
+
}),
|
|
89
121
|
},
|
|
90
122
|
};
|
|
91
123
|
|
|
92
124
|
/**
|
|
93
|
-
* @param {{ action: string, selector?: string, elementRef?: string, withinRef?: string, maxNodes?: number, maxDepth?: number, textBudget?: number, includeBbox?: boolean, attributeAllowlist?: string[], attributes?: string[], text?: string, exact?: boolean, maxResults?: number, role?: string, name?: string, state?: string, timeoutMs?: number, outer?: boolean, maxLength?: number, tabId?: number, budgetPreset?: 'quick' | 'normal' | 'deep' }} args
|
|
125
|
+
* @param {{ action: string, baselineId?: string, maxChanges?: number, selector?: string, elementRef?: string, withinRef?: string, maxNodes?: number, maxDepth?: number, compact?: boolean, interactiveOnly?: boolean, textBudget?: number, includeBbox?: boolean, attributeAllowlist?: string[], attributes?: string[], text?: string, exact?: boolean, maxResults?: number, role?: string, name?: string, state?: string, timeoutMs?: number, outer?: boolean, maxLength?: number, tabId?: number, destinationId?: string, budgetPreset?: 'quick' | 'normal' | 'deep' }} args
|
|
94
126
|
* @returns {Promise<ToolResult>}
|
|
95
127
|
*/
|
|
96
128
|
export async function handleDomTool(args) {
|
|
@@ -103,7 +135,17 @@ export async function handleDomTool(args) {
|
|
|
103
135
|
if (args.action === 'find_role' && !hasText(args.role)) {
|
|
104
136
|
return summarizeToolError('role is required for dom.find_by_role.');
|
|
105
137
|
}
|
|
106
|
-
if (
|
|
138
|
+
if (
|
|
139
|
+
['baseline_compare', 'baseline_describe', 'baseline_release'].includes(args.action) &&
|
|
140
|
+
!hasText(args.baselineId)
|
|
141
|
+
) {
|
|
142
|
+
return summarizeToolError('baselineId is required for this DOM baseline action.');
|
|
143
|
+
}
|
|
144
|
+
if (
|
|
145
|
+
args.action === 'query' ||
|
|
146
|
+
args.action === 'accessibility_tree' ||
|
|
147
|
+
args.action === 'baseline_create'
|
|
148
|
+
) {
|
|
107
149
|
const inferred =
|
|
108
150
|
args.action === 'accessibility_tree' ? 'normal' : inferBudgetFromSelector(args);
|
|
109
151
|
const withBudget = inferred ? { ...args, budgetPreset: args.budgetPreset ?? inferred } : args;
|
|
@@ -112,6 +154,18 @@ export async function handleDomTool(args) {
|
|
|
112
154
|
if (args.action === 'text') {
|
|
113
155
|
return dispatchToolAction(DOM_ACTIONS, applyTextBudgetPreset(args), 'DOM');
|
|
114
156
|
}
|
|
157
|
+
if (args.action === 'baseline_compare') {
|
|
158
|
+
const byPreset = { quick: 10, normal: 50, deep: 100 };
|
|
159
|
+
return dispatchToolAction(
|
|
160
|
+
DOM_ACTIONS,
|
|
161
|
+
{
|
|
162
|
+
...args,
|
|
163
|
+
maxChanges:
|
|
164
|
+
args.maxChanges ?? (args.budgetPreset ? byPreset[args.budgetPreset] : undefined),
|
|
165
|
+
},
|
|
166
|
+
'DOM'
|
|
167
|
+
);
|
|
168
|
+
}
|
|
115
169
|
if (args.action === 'html') {
|
|
116
170
|
return dispatchToolAction(DOM_ACTIONS, applyHtmlBudgetPreset(args), 'DOM');
|
|
117
171
|
}
|
|
@@ -183,11 +237,14 @@ export const PATCH_ACTIONS = {
|
|
|
183
237
|
removeAttribute: 'remove_attribute',
|
|
184
238
|
addClass: 'add_class',
|
|
185
239
|
removeClass: 'remove_class',
|
|
240
|
+
toggleClass: 'toggle_class',
|
|
186
241
|
setTextContent: 'set_text',
|
|
187
242
|
};
|
|
188
243
|
const normalizedOperation = opMap[operation] || operation;
|
|
189
244
|
const value =
|
|
190
|
-
normalizedOperation === 'add_class' ||
|
|
245
|
+
normalizedOperation === 'add_class' ||
|
|
246
|
+
normalizedOperation === 'remove_class' ||
|
|
247
|
+
normalizedOperation === 'toggle_class'
|
|
191
248
|
? (a.value ?? a.name)
|
|
192
249
|
: a.value;
|
|
193
250
|
return {
|
|
@@ -32,7 +32,8 @@ export async function handleTabsTool(args) {
|
|
|
32
32
|
let client = null;
|
|
33
33
|
try {
|
|
34
34
|
client = await createBridgeClientForDestination(
|
|
35
|
-
destination.local ? null : destination.id
|
|
35
|
+
destination.local ? null : destination.id,
|
|
36
|
+
{ checkProtocolOnConnect: false }
|
|
36
37
|
);
|
|
37
38
|
await client.connect();
|
|
38
39
|
const response = await requestBridgeWithRetry(
|