@browserbridge/bbx 1.6.1 → 1.6.2
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/package.json +1 -1
- package/packages/agent-client/src/cli.js +264 -0
- package/packages/agent-client/src/command-registry.js +4 -0
- package/packages/agent-client/src/remotes.js +202 -0
- package/packages/mcp-server/src/handlers-capture.js +201 -198
- package/packages/mcp-server/src/handlers-navigation.js +81 -9
- package/packages/mcp-server/src/handlers-page.js +110 -88
- package/packages/mcp-server/src/handlers-utils.js +55 -27
- package/packages/mcp-server/src/handlers.js +52 -10
- package/packages/mcp-server/src/server.js +22 -3
- package/packages/native-host/src/auth-token.js +56 -1
- package/packages/native-host/src/config.js +122 -5
|
@@ -81,7 +81,7 @@ function isValidCaptureRegion(rect) {
|
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
/**
|
|
84
|
-
* @param {{ action: string, elementRef?: string, selector?: string, rect?: Record<string, unknown>, nodeId?: number, tabId?: number, budgetPreset?: 'quick' | 'normal' | 'deep' }} args
|
|
84
|
+
* @param {{ action: string, elementRef?: string, selector?: string, rect?: Record<string, unknown>, nodeId?: number, tabId?: number, destinationId?: string, budgetPreset?: 'quick' | 'normal' | 'deep' }} args
|
|
85
85
|
* @returns {Promise<ToolResult>}
|
|
86
86
|
*/
|
|
87
87
|
export async function handleCaptureTool(args) {
|
|
@@ -115,7 +115,7 @@ export const INPUT_ACTION_METHODS = {
|
|
|
115
115
|
};
|
|
116
116
|
|
|
117
117
|
/**
|
|
118
|
-
* @param {{ action: string, elementRef?: string, selector?: string, button?: string, clickCount?: number, text?: string, 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, budgetPreset?: 'quick' | 'normal' | 'deep' }} args
|
|
118
|
+
* @param {{ action: string, elementRef?: string, selector?: string, button?: string, clickCount?: number, text?: string, 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
|
|
119
119
|
* @returns {Promise<ToolResult>}
|
|
120
120
|
*/
|
|
121
121
|
export async function handleInputTool(args) {
|
|
@@ -134,207 +134,210 @@ export async function handleInputTool(args) {
|
|
|
134
134
|
return summarizeToolError('values, labels, or indexes are required for input.select_option.');
|
|
135
135
|
}
|
|
136
136
|
|
|
137
|
-
return withToolClient(
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
137
|
+
return withToolClient(
|
|
138
|
+
async (client) => {
|
|
139
|
+
const requestedTabId = typeof args.tabId === 'number' ? args.tabId : null;
|
|
140
|
+
const elementTarget = async () => ({
|
|
141
|
+
elementRef: await resolveToolRef(client, args, requestedTabId),
|
|
142
|
+
});
|
|
142
143
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
);
|
|
160
|
-
return summarizeToolResponse(response, 'input.click');
|
|
161
|
-
}
|
|
162
|
-
case 'focus': {
|
|
163
|
-
const response = await requestBridgeWithRetry(
|
|
164
|
-
client,
|
|
165
|
-
'input.focus',
|
|
166
|
-
{
|
|
167
|
-
target: await elementTarget(),
|
|
168
|
-
},
|
|
169
|
-
{
|
|
170
|
-
tabId: requestedTabId,
|
|
171
|
-
source: REQUEST_SOURCE,
|
|
172
|
-
tokenBudget: getToolTokenBudget(args),
|
|
173
|
-
}
|
|
174
|
-
);
|
|
175
|
-
return summarizeToolResponse(response, 'input.focus');
|
|
176
|
-
}
|
|
177
|
-
case 'type': {
|
|
178
|
-
const response = await requestBridgeWithRetry(
|
|
179
|
-
client,
|
|
180
|
-
'input.type',
|
|
181
|
-
{
|
|
182
|
-
target: await elementTarget(),
|
|
183
|
-
text: args.text,
|
|
184
|
-
clear: args.clear,
|
|
185
|
-
submit: args.submit,
|
|
186
|
-
modifiers: args.modifiers,
|
|
187
|
-
},
|
|
188
|
-
{
|
|
189
|
-
tabId: requestedTabId,
|
|
190
|
-
source: REQUEST_SOURCE,
|
|
191
|
-
tokenBudget: getToolTokenBudget(args),
|
|
192
|
-
}
|
|
193
|
-
);
|
|
194
|
-
return summarizeToolResponse(response, 'input.type');
|
|
195
|
-
}
|
|
196
|
-
case 'press_key': {
|
|
197
|
-
const target = args.elementRef || args.selector ? await elementTarget() : undefined;
|
|
198
|
-
const response = await requestBridgeWithRetry(
|
|
199
|
-
client,
|
|
200
|
-
'input.press_key',
|
|
201
|
-
{
|
|
202
|
-
target,
|
|
203
|
-
key: args.key,
|
|
204
|
-
modifiers: args.modifiers,
|
|
205
|
-
},
|
|
206
|
-
{
|
|
207
|
-
tabId: requestedTabId,
|
|
208
|
-
source: REQUEST_SOURCE,
|
|
209
|
-
tokenBudget: getToolTokenBudget(args),
|
|
210
|
-
}
|
|
211
|
-
);
|
|
212
|
-
return summarizeToolResponse(response, 'input.press_key');
|
|
213
|
-
}
|
|
214
|
-
case 'cdp_press_key': {
|
|
215
|
-
const response = await requestBridgeWithRetry(
|
|
216
|
-
client,
|
|
217
|
-
'cdp.dispatch_key_event',
|
|
218
|
-
{
|
|
219
|
-
key: args.key,
|
|
220
|
-
code: args.code,
|
|
221
|
-
modifiers: args.modifiers,
|
|
222
|
-
},
|
|
223
|
-
{
|
|
224
|
-
tabId: requestedTabId,
|
|
225
|
-
source: REQUEST_SOURCE,
|
|
226
|
-
tokenBudget: getToolTokenBudget(args),
|
|
227
|
-
}
|
|
228
|
-
);
|
|
229
|
-
return summarizeToolResponse(response, 'cdp.dispatch_key_event');
|
|
230
|
-
}
|
|
231
|
-
case 'set_checked': {
|
|
232
|
-
const response = await requestBridgeWithRetry(
|
|
233
|
-
client,
|
|
234
|
-
'input.set_checked',
|
|
235
|
-
{
|
|
236
|
-
target: await elementTarget(),
|
|
237
|
-
checked: args.checked,
|
|
238
|
-
},
|
|
239
|
-
{
|
|
240
|
-
tabId: requestedTabId,
|
|
241
|
-
source: REQUEST_SOURCE,
|
|
242
|
-
tokenBudget: getToolTokenBudget(args),
|
|
243
|
-
}
|
|
244
|
-
);
|
|
245
|
-
return summarizeToolResponse(response, 'input.set_checked');
|
|
246
|
-
}
|
|
247
|
-
case 'select_option': {
|
|
248
|
-
const response = await requestBridgeWithRetry(
|
|
249
|
-
client,
|
|
250
|
-
'input.select_option',
|
|
251
|
-
{
|
|
252
|
-
target: await elementTarget(),
|
|
253
|
-
values: args.values,
|
|
254
|
-
labels: args.labels,
|
|
255
|
-
indexes: args.indexes,
|
|
256
|
-
},
|
|
257
|
-
{
|
|
258
|
-
tabId: requestedTabId,
|
|
259
|
-
source: REQUEST_SOURCE,
|
|
260
|
-
tokenBudget: getToolTokenBudget(args),
|
|
261
|
-
}
|
|
262
|
-
);
|
|
263
|
-
return summarizeToolResponse(response, 'input.select_option');
|
|
264
|
-
}
|
|
265
|
-
case 'hover': {
|
|
266
|
-
const response = await requestBridgeWithRetry(
|
|
267
|
-
client,
|
|
268
|
-
'input.hover',
|
|
269
|
-
{
|
|
270
|
-
target: await elementTarget(),
|
|
271
|
-
duration: args.duration,
|
|
272
|
-
modifiers: args.modifiers,
|
|
273
|
-
},
|
|
274
|
-
{
|
|
275
|
-
tabId: requestedTabId,
|
|
276
|
-
source: REQUEST_SOURCE,
|
|
277
|
-
tokenBudget: getToolTokenBudget(args),
|
|
278
|
-
}
|
|
279
|
-
);
|
|
280
|
-
return summarizeToolResponse(response, 'input.hover');
|
|
281
|
-
}
|
|
282
|
-
case 'drag': {
|
|
283
|
-
const source = {
|
|
284
|
-
elementRef:
|
|
285
|
-
args.sourceElementRef ||
|
|
286
|
-
(args.sourceSelector
|
|
287
|
-
? await resolveRef(client, args.sourceSelector, requestedTabId, REQUEST_SOURCE)
|
|
288
|
-
: ''),
|
|
289
|
-
};
|
|
290
|
-
const destination = {
|
|
291
|
-
elementRef:
|
|
292
|
-
args.destinationElementRef ||
|
|
293
|
-
(args.destinationSelector
|
|
294
|
-
? await resolveRef(client, args.destinationSelector, requestedTabId, REQUEST_SOURCE)
|
|
295
|
-
: ''),
|
|
296
|
-
};
|
|
297
|
-
if (!source.elementRef || !destination.elementRef) {
|
|
298
|
-
return summarizeToolError(
|
|
299
|
-
'sourceElementRef/sourceSelector and destinationElementRef/destinationSelector are required for drag.'
|
|
144
|
+
switch (args.action) {
|
|
145
|
+
case 'click': {
|
|
146
|
+
const response = await requestBridgeWithRetry(
|
|
147
|
+
client,
|
|
148
|
+
'input.click',
|
|
149
|
+
{
|
|
150
|
+
target: await elementTarget(),
|
|
151
|
+
button: args.button,
|
|
152
|
+
clickCount: args.clickCount,
|
|
153
|
+
modifiers: args.modifiers,
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
tabId: requestedTabId,
|
|
157
|
+
source: REQUEST_SOURCE,
|
|
158
|
+
tokenBudget: getToolTokenBudget(args),
|
|
159
|
+
}
|
|
300
160
|
);
|
|
161
|
+
return summarizeToolResponse(response, 'input.click');
|
|
301
162
|
}
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
163
|
+
case 'focus': {
|
|
164
|
+
const response = await requestBridgeWithRetry(
|
|
165
|
+
client,
|
|
166
|
+
'input.focus',
|
|
167
|
+
{
|
|
168
|
+
target: await elementTarget(),
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
tabId: requestedTabId,
|
|
172
|
+
source: REQUEST_SOURCE,
|
|
173
|
+
tokenBudget: getToolTokenBudget(args),
|
|
174
|
+
}
|
|
175
|
+
);
|
|
176
|
+
return summarizeToolResponse(response, 'input.focus');
|
|
177
|
+
}
|
|
178
|
+
case 'type': {
|
|
179
|
+
const response = await requestBridgeWithRetry(
|
|
180
|
+
client,
|
|
181
|
+
'input.type',
|
|
182
|
+
{
|
|
183
|
+
target: await elementTarget(),
|
|
184
|
+
text: args.text,
|
|
185
|
+
clear: args.clear,
|
|
186
|
+
submit: args.submit,
|
|
187
|
+
modifiers: args.modifiers,
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
tabId: requestedTabId,
|
|
191
|
+
source: REQUEST_SOURCE,
|
|
192
|
+
tokenBudget: getToolTokenBudget(args),
|
|
193
|
+
}
|
|
194
|
+
);
|
|
195
|
+
return summarizeToolResponse(response, 'input.type');
|
|
196
|
+
}
|
|
197
|
+
case 'press_key': {
|
|
198
|
+
const target = args.elementRef || args.selector ? await elementTarget() : undefined;
|
|
199
|
+
const response = await requestBridgeWithRetry(
|
|
200
|
+
client,
|
|
201
|
+
'input.press_key',
|
|
202
|
+
{
|
|
203
|
+
target,
|
|
204
|
+
key: args.key,
|
|
205
|
+
modifiers: args.modifiers,
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
tabId: requestedTabId,
|
|
209
|
+
source: REQUEST_SOURCE,
|
|
210
|
+
tokenBudget: getToolTokenBudget(args),
|
|
211
|
+
}
|
|
212
|
+
);
|
|
213
|
+
return summarizeToolResponse(response, 'input.press_key');
|
|
214
|
+
}
|
|
215
|
+
case 'cdp_press_key': {
|
|
216
|
+
const response = await requestBridgeWithRetry(
|
|
217
|
+
client,
|
|
218
|
+
'cdp.dispatch_key_event',
|
|
219
|
+
{
|
|
220
|
+
key: args.key,
|
|
221
|
+
code: args.code,
|
|
222
|
+
modifiers: args.modifiers,
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
tabId: requestedTabId,
|
|
226
|
+
source: REQUEST_SOURCE,
|
|
227
|
+
tokenBudget: getToolTokenBudget(args),
|
|
228
|
+
}
|
|
229
|
+
);
|
|
230
|
+
return summarizeToolResponse(response, 'cdp.dispatch_key_event');
|
|
231
|
+
}
|
|
232
|
+
case 'set_checked': {
|
|
233
|
+
const response = await requestBridgeWithRetry(
|
|
234
|
+
client,
|
|
235
|
+
'input.set_checked',
|
|
236
|
+
{
|
|
237
|
+
target: await elementTarget(),
|
|
238
|
+
checked: args.checked,
|
|
239
|
+
},
|
|
240
|
+
{
|
|
241
|
+
tabId: requestedTabId,
|
|
242
|
+
source: REQUEST_SOURCE,
|
|
243
|
+
tokenBudget: getToolTokenBudget(args),
|
|
244
|
+
}
|
|
245
|
+
);
|
|
246
|
+
return summarizeToolResponse(response, 'input.set_checked');
|
|
247
|
+
}
|
|
248
|
+
case 'select_option': {
|
|
249
|
+
const response = await requestBridgeWithRetry(
|
|
250
|
+
client,
|
|
251
|
+
'input.select_option',
|
|
252
|
+
{
|
|
253
|
+
target: await elementTarget(),
|
|
254
|
+
values: args.values,
|
|
255
|
+
labels: args.labels,
|
|
256
|
+
indexes: args.indexes,
|
|
257
|
+
},
|
|
258
|
+
{
|
|
259
|
+
tabId: requestedTabId,
|
|
260
|
+
source: REQUEST_SOURCE,
|
|
261
|
+
tokenBudget: getToolTokenBudget(args),
|
|
262
|
+
}
|
|
263
|
+
);
|
|
264
|
+
return summarizeToolResponse(response, 'input.select_option');
|
|
265
|
+
}
|
|
266
|
+
case 'hover': {
|
|
267
|
+
const response = await requestBridgeWithRetry(
|
|
268
|
+
client,
|
|
269
|
+
'input.hover',
|
|
270
|
+
{
|
|
271
|
+
target: await elementTarget(),
|
|
272
|
+
duration: args.duration,
|
|
273
|
+
modifiers: args.modifiers,
|
|
274
|
+
},
|
|
275
|
+
{
|
|
276
|
+
tabId: requestedTabId,
|
|
277
|
+
source: REQUEST_SOURCE,
|
|
278
|
+
tokenBudget: getToolTokenBudget(args),
|
|
279
|
+
}
|
|
280
|
+
);
|
|
281
|
+
return summarizeToolResponse(response, 'input.hover');
|
|
282
|
+
}
|
|
283
|
+
case 'drag': {
|
|
284
|
+
const source = {
|
|
285
|
+
elementRef:
|
|
286
|
+
args.sourceElementRef ||
|
|
287
|
+
(args.sourceSelector
|
|
288
|
+
? await resolveRef(client, args.sourceSelector, requestedTabId, REQUEST_SOURCE)
|
|
289
|
+
: ''),
|
|
290
|
+
};
|
|
291
|
+
const destination = {
|
|
292
|
+
elementRef:
|
|
293
|
+
args.destinationElementRef ||
|
|
294
|
+
(args.destinationSelector
|
|
295
|
+
? await resolveRef(client, args.destinationSelector, requestedTabId, REQUEST_SOURCE)
|
|
296
|
+
: ''),
|
|
297
|
+
};
|
|
298
|
+
if (!source.elementRef || !destination.elementRef) {
|
|
299
|
+
return summarizeToolError(
|
|
300
|
+
'sourceElementRef/sourceSelector and destinationElementRef/destinationSelector are required for drag.'
|
|
301
|
+
);
|
|
330
302
|
}
|
|
331
|
-
|
|
332
|
-
|
|
303
|
+
const response = await requestBridgeWithRetry(
|
|
304
|
+
client,
|
|
305
|
+
'input.drag',
|
|
306
|
+
{
|
|
307
|
+
source,
|
|
308
|
+
destination,
|
|
309
|
+
offsetX: args.offsetX,
|
|
310
|
+
offsetY: args.offsetY,
|
|
311
|
+
},
|
|
312
|
+
{
|
|
313
|
+
tabId: requestedTabId,
|
|
314
|
+
source: REQUEST_SOURCE,
|
|
315
|
+
tokenBudget: getToolTokenBudget(args),
|
|
316
|
+
}
|
|
317
|
+
);
|
|
318
|
+
return summarizeToolResponse(response, 'input.drag');
|
|
319
|
+
}
|
|
320
|
+
case 'scroll_into_view': {
|
|
321
|
+
const response = await requestBridgeWithRetry(
|
|
322
|
+
client,
|
|
323
|
+
'input.scroll_into_view',
|
|
324
|
+
{
|
|
325
|
+
target: await elementTarget(),
|
|
326
|
+
},
|
|
327
|
+
{
|
|
328
|
+
tabId: requestedTabId,
|
|
329
|
+
source: REQUEST_SOURCE,
|
|
330
|
+
tokenBudget: getToolTokenBudget(args),
|
|
331
|
+
}
|
|
332
|
+
);
|
|
333
|
+
return summarizeToolResponse(response, 'input.scroll_into_view');
|
|
334
|
+
}
|
|
335
|
+
default:
|
|
336
|
+
return summarizeToolError(`Unsupported input action "${args.action}".`);
|
|
333
337
|
}
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
});
|
|
338
|
+
},
|
|
339
|
+
{ destinationId: args.destinationId ?? null }
|
|
340
|
+
);
|
|
338
341
|
}
|
|
339
342
|
|
|
340
343
|
/**
|
|
@@ -1,29 +1,100 @@
|
|
|
1
1
|
// @ts-check
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { createBridgeClientForDestination } from '../../agent-client/src/remotes.js';
|
|
4
|
+
import {
|
|
5
|
+
callBridgeTool,
|
|
6
|
+
createToolResult,
|
|
7
|
+
getBridgeDestinations,
|
|
8
|
+
getToolTokenBudget,
|
|
9
|
+
summarizeToolError,
|
|
10
|
+
} from './handlers-utils.js';
|
|
4
11
|
|
|
5
12
|
/** @typedef {import('../../protocol/src/types.js').BridgeMethod} BridgeMethod */
|
|
6
13
|
/** @typedef {import('./handlers-utils.js').ToolResult} ToolResult */
|
|
7
14
|
|
|
8
15
|
/**
|
|
9
|
-
* @param {{ action: string, url?: string, active?: boolean, tabId?: number }} args
|
|
16
|
+
* @param {{ action: string, url?: string, active?: boolean, tabId?: number, destinationId?: string }} args
|
|
10
17
|
* @returns {Promise<ToolResult>}
|
|
11
18
|
*/
|
|
12
19
|
export async function handleTabsTool(args) {
|
|
13
20
|
if (args.action === 'list') {
|
|
14
|
-
|
|
21
|
+
if (typeof args.destinationId === 'string') {
|
|
22
|
+
return callBridgeTool('tabs.list', {}, { destinationId: args.destinationId });
|
|
23
|
+
}
|
|
24
|
+
const destinations = await getBridgeDestinations();
|
|
25
|
+
if (destinations.length === 1) {
|
|
26
|
+
return callBridgeTool('tabs.list');
|
|
27
|
+
}
|
|
28
|
+
const results = await Promise.all(
|
|
29
|
+
destinations.map(async (destination) => {
|
|
30
|
+
const client = await createBridgeClientForDestination(
|
|
31
|
+
destination.local ? null : destination.id
|
|
32
|
+
);
|
|
33
|
+
try {
|
|
34
|
+
await client.connect();
|
|
35
|
+
const response = await client.request({
|
|
36
|
+
method: 'tabs.list',
|
|
37
|
+
meta: { source: 'mcp' },
|
|
38
|
+
});
|
|
39
|
+
if (!response.ok) {
|
|
40
|
+
return {
|
|
41
|
+
destinationId: destination.id,
|
|
42
|
+
ok: false,
|
|
43
|
+
error: response.error,
|
|
44
|
+
tabs: [],
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
const result = /** @type {{ tabs?: Array<Record<string, unknown>> }} */ (response.result);
|
|
48
|
+
return {
|
|
49
|
+
destinationId: destination.id,
|
|
50
|
+
ok: true,
|
|
51
|
+
tabs: (result.tabs ?? []).map((tab) => ({ destinationId: destination.id, ...tab })),
|
|
52
|
+
};
|
|
53
|
+
} catch (error) {
|
|
54
|
+
return {
|
|
55
|
+
destinationId: destination.id,
|
|
56
|
+
ok: false,
|
|
57
|
+
error: { message: error instanceof Error ? error.message : String(error) },
|
|
58
|
+
tabs: [],
|
|
59
|
+
};
|
|
60
|
+
} finally {
|
|
61
|
+
await client.close();
|
|
62
|
+
}
|
|
63
|
+
})
|
|
64
|
+
);
|
|
65
|
+
const tabs = results.flatMap((result) => result.tabs);
|
|
66
|
+
const failures = results.filter((result) => !result.ok);
|
|
67
|
+
return createToolResult(
|
|
68
|
+
failures.length === 0
|
|
69
|
+
? `Listed ${tabs.length} tab(s) across ${results.length} destination(s).`
|
|
70
|
+
: `Listed ${tabs.length} tab(s) with ${failures.length} destination error(s).`,
|
|
71
|
+
{
|
|
72
|
+
ok: failures.length === 0,
|
|
73
|
+
tabs,
|
|
74
|
+
destinations: results,
|
|
75
|
+
},
|
|
76
|
+
false
|
|
77
|
+
);
|
|
15
78
|
}
|
|
16
79
|
if (args.action === 'create') {
|
|
17
|
-
return callBridgeTool(
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
80
|
+
return callBridgeTool(
|
|
81
|
+
'tabs.create',
|
|
82
|
+
{
|
|
83
|
+
url: args.url,
|
|
84
|
+
active: args.active,
|
|
85
|
+
},
|
|
86
|
+
{ destinationId: args.destinationId ?? null }
|
|
87
|
+
);
|
|
21
88
|
}
|
|
22
89
|
if (args.action === 'close') {
|
|
23
90
|
if (typeof args.tabId !== 'number') {
|
|
24
91
|
return summarizeToolError('tabId is required for tabs.close.');
|
|
25
92
|
}
|
|
26
|
-
return callBridgeTool(
|
|
93
|
+
return callBridgeTool(
|
|
94
|
+
'tabs.close',
|
|
95
|
+
{ tabId: args.tabId },
|
|
96
|
+
{ destinationId: args.destinationId ?? null }
|
|
97
|
+
);
|
|
27
98
|
}
|
|
28
99
|
return summarizeToolError(`Unsupported tabs action "${args.action}".`);
|
|
29
100
|
}
|
|
@@ -71,7 +142,7 @@ export const NAVIGATION_ACTIONS = {
|
|
|
71
142
|
};
|
|
72
143
|
|
|
73
144
|
/**
|
|
74
|
-
* @param {{ action: string, url?: string, waitForLoad?: boolean, timeoutMs?: number, top?: number, left?: number, behavior?: string, relative?: boolean, width?: number, height?: number, deviceScaleFactor?: number, reset?: boolean, tabId?: number, budgetPreset?: 'quick' | 'normal' | 'deep' }} args
|
|
145
|
+
* @param {{ action: string, url?: string, waitForLoad?: boolean, timeoutMs?: number, top?: number, left?: number, behavior?: string, relative?: boolean, width?: number, height?: number, deviceScaleFactor?: number, reset?: boolean, tabId?: number, destinationId?: string, budgetPreset?: 'quick' | 'normal' | 'deep' }} args
|
|
75
146
|
* @returns {Promise<ToolResult>}
|
|
76
147
|
*/
|
|
77
148
|
export async function handleNavigationTool(args) {
|
|
@@ -95,5 +166,6 @@ export async function handleNavigationTool(args) {
|
|
|
95
166
|
return callBridgeTool(entry.method, entry.params(args), {
|
|
96
167
|
tabId: typeof args.tabId === 'number' ? args.tabId : null,
|
|
97
168
|
tokenBudget: getToolTokenBudget(args),
|
|
169
|
+
destinationId: args.destinationId ?? null,
|
|
98
170
|
});
|
|
99
171
|
}
|