@browserbridge/bbx 1.8.0 → 1.10.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 +29 -3
- package/README.md +10 -3
- package/SECURITY.md +12 -0
- package/package.json +7 -1
- package/packages/agent-client/src/cli-args.js +70 -6
- package/packages/agent-client/src/cli-batch.js +127 -59
- package/packages/agent-client/src/cli-helpers.js +8 -0
- package/packages/agent-client/src/cli-output.js +8 -1
- package/packages/agent-client/src/cli-setup-commands.js +3 -1
- package/packages/agent-client/src/cli.js +254 -26
- package/packages/agent-client/src/client.js +156 -3
- package/packages/agent-client/src/command-registry.js +14 -4
- package/packages/agent-client/src/config.js +195 -0
- package/packages/agent-client/src/mcp-config.js +8 -8
- 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 +99 -7
- package/packages/agent-client/src/types.ts +38 -0
- package/packages/mcp-server/src/guidance.js +37 -6
- package/packages/mcp-server/src/handlers-capture.js +29 -2
- package/packages/mcp-server/src/handlers-dom.js +54 -3
- package/packages/mcp-server/src/handlers-page.js +333 -60
- package/packages/mcp-server/src/handlers-utils.js +141 -151
- package/packages/mcp-server/src/handlers.js +17 -4
- package/packages/mcp-server/src/server.js +269 -51
- package/packages/mcp-server/src/toolset.js +48 -0
- 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 +457 -27
- package/packages/native-host/src/dom-baseline-owners.js +210 -0
- package/packages/native-host/src/native-host.js +38 -29
- package/packages/protocol/src/budget-presets.js +182 -0
- package/packages/protocol/src/capability-values.js +2 -0
- package/packages/protocol/src/defaults.js +28 -0
- package/packages/protocol/src/errors.js +48 -5
- package/packages/protocol/src/har.js +34 -0
- package/packages/protocol/src/incidental-sanitizer.js +197 -0
- package/packages/protocol/src/index.js +4 -0
- package/packages/protocol/src/json-lines.js +3 -2
- package/packages/protocol/src/protocol.js +316 -6
- package/packages/protocol/src/recovery-telemetry.js +302 -0
- package/packages/protocol/src/registry.js +226 -6
- package/packages/protocol/src/summary.js +116 -25
- package/packages/protocol/src/types.ts +490 -4
- package/skills/browser-bridge/SKILL.md +26 -8
- package/skills/browser-bridge/references/protocol.md +125 -15
- package/skills/browser-bridge/references/token-efficiency.md +17 -7
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
// @ts-check
|
|
2
2
|
|
|
3
|
+
import { randomUUID } from 'node:crypto';
|
|
4
|
+
|
|
3
5
|
import {
|
|
4
6
|
BridgeError,
|
|
5
7
|
bridgeMethodNeedsTab,
|
|
@@ -12,11 +14,21 @@ import {
|
|
|
12
14
|
DEFAULT_TEXT_BUDGET,
|
|
13
15
|
estimateJsonPayloadCost,
|
|
14
16
|
getBudgetPreset,
|
|
17
|
+
getBudgetPresetName,
|
|
15
18
|
getErrorRecovery,
|
|
16
|
-
isBudgetPresetName,
|
|
17
19
|
summarizeBatchErrorItem,
|
|
18
20
|
summarizeBatchResponseItem,
|
|
19
21
|
} from '../../protocol/src/index.js';
|
|
22
|
+
|
|
23
|
+
export {
|
|
24
|
+
applyHtmlBudgetPreset,
|
|
25
|
+
applyLimitBudgetPreset,
|
|
26
|
+
applyMethodBudgetPreset,
|
|
27
|
+
applyPageTextBudgetPreset,
|
|
28
|
+
applyTextBudgetPreset,
|
|
29
|
+
applyTreeBudgetPreset,
|
|
30
|
+
getBudgetPresetName,
|
|
31
|
+
} from '../../protocol/src/index.js';
|
|
20
32
|
import {
|
|
21
33
|
getDoctorReport,
|
|
22
34
|
requestBridge,
|
|
@@ -33,6 +45,7 @@ import { annotateBridgeSummary, summarizeBridgeResponse } from '../../agent-clie
|
|
|
33
45
|
/** @typedef {import('../../protocol/src/types.js').BridgeResponse} BridgeResponse */
|
|
34
46
|
|
|
35
47
|
export const REQUEST_SOURCE = 'mcp';
|
|
48
|
+
const MCP_CLIENT_ID = `mcp_${randomUUID()}`;
|
|
36
49
|
|
|
37
50
|
/** @type {ReadonlySet<BridgeMethod>} */
|
|
38
51
|
const RETRY_SAFE_METHODS = new Set([
|
|
@@ -47,6 +60,8 @@ const RETRY_SAFE_METHODS = new Set([
|
|
|
47
60
|
'page.get_text',
|
|
48
61
|
'page.extract_content',
|
|
49
62
|
'dom.query',
|
|
63
|
+
'dom.baseline.compare',
|
|
64
|
+
'dom.baseline.describe',
|
|
50
65
|
'dom.describe',
|
|
51
66
|
'dom.get_text',
|
|
52
67
|
'dom.get_attributes',
|
|
@@ -62,6 +77,8 @@ const RETRY_SAFE_METHODS = new Set([
|
|
|
62
77
|
'screenshot.capture_region',
|
|
63
78
|
'screenshot.capture_element',
|
|
64
79
|
'screenshot.capture_full_page',
|
|
80
|
+
'artifact.read',
|
|
81
|
+
'network.intercept.list',
|
|
65
82
|
'performance.get_metrics',
|
|
66
83
|
'cdp.get_document',
|
|
67
84
|
'cdp.get_dom_snapshot',
|
|
@@ -198,10 +215,14 @@ export function summarizeToolError(error) {
|
|
|
198
215
|
export async function withToolClient(callback, options = {}) {
|
|
199
216
|
try {
|
|
200
217
|
if (!options.destinationId) {
|
|
201
|
-
return await withBridgeClient(callback, {
|
|
218
|
+
return await withBridgeClient(callback, {
|
|
219
|
+
checkProtocolOnConnect: false,
|
|
220
|
+
clientId: MCP_CLIENT_ID,
|
|
221
|
+
});
|
|
202
222
|
}
|
|
203
223
|
const client = await createBridgeClientForDestination(options.destinationId, {
|
|
204
224
|
checkProtocolOnConnect: false,
|
|
225
|
+
clientId: MCP_CLIENT_ID,
|
|
205
226
|
});
|
|
206
227
|
await client.connect();
|
|
207
228
|
try {
|
|
@@ -230,14 +251,6 @@ export async function resolveToolRef(client, input, tabId = null) {
|
|
|
230
251
|
throw new BridgeError('INVALID_REQUEST', 'Provide either elementRef or selector.');
|
|
231
252
|
}
|
|
232
253
|
|
|
233
|
-
/**
|
|
234
|
-
* @param {unknown} value
|
|
235
|
-
* @returns {'quick' | 'normal' | 'deep' | null}
|
|
236
|
-
*/
|
|
237
|
-
export function getBudgetPresetName(value) {
|
|
238
|
-
return isBudgetPresetName(value) ? value : null;
|
|
239
|
-
}
|
|
240
|
-
|
|
241
254
|
/**
|
|
242
255
|
* @param {{ budgetPreset?: unknown, selector?: unknown, elementRef?: unknown }} args
|
|
243
256
|
* @returns {'quick' | 'normal' | 'deep' | null}
|
|
@@ -261,144 +274,6 @@ export function getToolTokenBudget(args) {
|
|
|
261
274
|
return presetName ? getBudgetPreset(presetName).tokenBudget : null;
|
|
262
275
|
}
|
|
263
276
|
|
|
264
|
-
/**
|
|
265
|
-
* @template {{ budgetPreset?: unknown, maxNodes?: unknown, maxDepth?: unknown, textBudget?: unknown }} T
|
|
266
|
-
* @param {T} args
|
|
267
|
-
* @returns {T}
|
|
268
|
-
*/
|
|
269
|
-
export function applyTreeBudgetPreset(args) {
|
|
270
|
-
const presetName = getBudgetPresetName(args.budgetPreset);
|
|
271
|
-
if (!presetName) {
|
|
272
|
-
return args;
|
|
273
|
-
}
|
|
274
|
-
const preset = getBudgetPreset(presetName);
|
|
275
|
-
return /** @type {T} */ ({
|
|
276
|
-
...args,
|
|
277
|
-
maxNodes: args.maxNodes ?? preset.maxNodes,
|
|
278
|
-
maxDepth: args.maxDepth ?? preset.maxDepth,
|
|
279
|
-
textBudget: args.textBudget ?? preset.textBudget,
|
|
280
|
-
});
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
/**
|
|
284
|
-
* @template {{ budgetPreset?: unknown, textBudget?: unknown }} T
|
|
285
|
-
* @param {T} args
|
|
286
|
-
* @returns {T}
|
|
287
|
-
*/
|
|
288
|
-
export function applyTextBudgetPreset(args) {
|
|
289
|
-
const presetName = getBudgetPresetName(args.budgetPreset);
|
|
290
|
-
if (!presetName) {
|
|
291
|
-
return args;
|
|
292
|
-
}
|
|
293
|
-
const preset = getBudgetPreset(presetName);
|
|
294
|
-
return /** @type {T} */ ({
|
|
295
|
-
...args,
|
|
296
|
-
textBudget: args.textBudget ?? preset.textBudget,
|
|
297
|
-
});
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
/**
|
|
301
|
-
* @template {{ budgetPreset?: unknown, textBudget?: unknown }} T
|
|
302
|
-
* @param {T} args
|
|
303
|
-
* @returns {T}
|
|
304
|
-
*/
|
|
305
|
-
export function applyPageTextBudgetPreset(args) {
|
|
306
|
-
const presetName = getBudgetPresetName(args.budgetPreset);
|
|
307
|
-
if (!presetName) {
|
|
308
|
-
return args;
|
|
309
|
-
}
|
|
310
|
-
const textBudgetByPreset = {
|
|
311
|
-
quick: 2000,
|
|
312
|
-
normal: DEFAULT_PAGE_TEXT_BUDGET,
|
|
313
|
-
deep: DEFAULT_PAGE_TEXT_BUDGET * 2,
|
|
314
|
-
};
|
|
315
|
-
return /** @type {T} */ ({
|
|
316
|
-
...args,
|
|
317
|
-
textBudget: args.textBudget ?? textBudgetByPreset[presetName],
|
|
318
|
-
});
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
/**
|
|
322
|
-
* @template {{ budgetPreset?: unknown, limit?: unknown }} T
|
|
323
|
-
* @param {T} args
|
|
324
|
-
* @param {{ quick: number, normal: number, deep: number }} defaults
|
|
325
|
-
* @returns {T}
|
|
326
|
-
*/
|
|
327
|
-
export function applyLimitBudgetPreset(args, defaults) {
|
|
328
|
-
const presetName = getBudgetPresetName(args.budgetPreset);
|
|
329
|
-
if (!presetName) {
|
|
330
|
-
return args;
|
|
331
|
-
}
|
|
332
|
-
return /** @type {T} */ ({
|
|
333
|
-
...args,
|
|
334
|
-
limit: args.limit ?? defaults[presetName],
|
|
335
|
-
});
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
/**
|
|
339
|
-
* @template {{ budgetPreset?: unknown, maxLength?: unknown }} T
|
|
340
|
-
* @param {T} args
|
|
341
|
-
* @returns {T}
|
|
342
|
-
*/
|
|
343
|
-
export function applyHtmlBudgetPreset(args) {
|
|
344
|
-
const presetName = getBudgetPresetName(args.budgetPreset);
|
|
345
|
-
if (!presetName) {
|
|
346
|
-
return args;
|
|
347
|
-
}
|
|
348
|
-
const maxLengthByPreset = {
|
|
349
|
-
quick: 600,
|
|
350
|
-
normal: DEFAULT_MAX_HTML_LENGTH,
|
|
351
|
-
deep: 6000,
|
|
352
|
-
};
|
|
353
|
-
return /** @type {T} */ ({
|
|
354
|
-
...args,
|
|
355
|
-
maxLength: args.maxLength ?? maxLengthByPreset[presetName],
|
|
356
|
-
});
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
/**
|
|
360
|
-
* Apply a preset to method parameters before dispatch. Explicit method params
|
|
361
|
-
* always win over preset defaults.
|
|
362
|
-
*
|
|
363
|
-
* @param {BridgeMethod} method
|
|
364
|
-
* @param {Record<string, unknown>} params
|
|
365
|
-
* @param {unknown} budgetPreset
|
|
366
|
-
* @returns {Record<string, unknown>}
|
|
367
|
-
*/
|
|
368
|
-
export function applyMethodBudgetPreset(method, params, budgetPreset) {
|
|
369
|
-
const args = { ...params, budgetPreset };
|
|
370
|
-
let normalized = args;
|
|
371
|
-
if (method === 'dom.query' || method === 'dom.get_accessibility_tree') {
|
|
372
|
-
normalized = applyTreeBudgetPreset(args);
|
|
373
|
-
} else if (method === 'dom.get_text') {
|
|
374
|
-
normalized = applyTextBudgetPreset(args);
|
|
375
|
-
} else if (method === 'dom.get_html') {
|
|
376
|
-
normalized = applyHtmlBudgetPreset(args);
|
|
377
|
-
} else if (method === 'page.get_text' || method === 'page.extract_content') {
|
|
378
|
-
normalized = applyPageTextBudgetPreset(args);
|
|
379
|
-
} else if (method === 'page.get_console') {
|
|
380
|
-
normalized = applyLimitBudgetPreset(args, {
|
|
381
|
-
quick: 10,
|
|
382
|
-
normal: DEFAULT_CONSOLE_LIMIT,
|
|
383
|
-
deep: 100,
|
|
384
|
-
});
|
|
385
|
-
} else if (method === 'page.get_network') {
|
|
386
|
-
normalized = applyLimitBudgetPreset(args, {
|
|
387
|
-
quick: 10,
|
|
388
|
-
normal: DEFAULT_NETWORK_LIMIT,
|
|
389
|
-
deep: 100,
|
|
390
|
-
});
|
|
391
|
-
} else if (method === 'log.tail') {
|
|
392
|
-
normalized = applyLimitBudgetPreset(args, {
|
|
393
|
-
quick: 10,
|
|
394
|
-
normal: DEFAULT_LOG_TAIL_LIMIT,
|
|
395
|
-
deep: 100,
|
|
396
|
-
});
|
|
397
|
-
}
|
|
398
|
-
const { budgetPreset: _budgetPreset, ...methodParams } = normalized;
|
|
399
|
-
return methodParams;
|
|
400
|
-
}
|
|
401
|
-
|
|
402
277
|
/**
|
|
403
278
|
* @param {unknown} input
|
|
404
279
|
* @param {{ maxEntries?: number, maxCharacters?: number, maxStringLength?: number }} [options]
|
|
@@ -558,6 +433,14 @@ function getRequestAwareEvidence(rawResult, method, params, fallback) {
|
|
|
558
433
|
limit: { maxEntries },
|
|
559
434
|
};
|
|
560
435
|
}
|
|
436
|
+
if (method?.startsWith('network.intercept.')) {
|
|
437
|
+
const bounded = boundToolValue(result, { maxEntries: 100, maxCharacters: 10_000 });
|
|
438
|
+
return {
|
|
439
|
+
value: bounded.value,
|
|
440
|
+
truncated: bounded.truncated,
|
|
441
|
+
limit: { maxEntries: 100, maxCharacters: 10_000 },
|
|
442
|
+
};
|
|
443
|
+
}
|
|
561
444
|
if (
|
|
562
445
|
(method === 'page.get_console' || method === 'page.get_network' || method === 'log.tail') &&
|
|
563
446
|
Array.isArray(result.entries)
|
|
@@ -633,15 +516,119 @@ function compactDomNode(value) {
|
|
|
633
516
|
return compact;
|
|
634
517
|
}
|
|
635
518
|
|
|
519
|
+
/**
|
|
520
|
+
* Socket-level Node error codes that mean the daemon connection itself was
|
|
521
|
+
* lost, as opposed to an application-level bridge failure.
|
|
522
|
+
*
|
|
523
|
+
* @type {ReadonlySet<string>}
|
|
524
|
+
*/
|
|
525
|
+
const CONNECTION_LOSS_CODES = new Set([
|
|
526
|
+
'ENOTCONN',
|
|
527
|
+
'ECONNREFUSED',
|
|
528
|
+
'ECONNRESET',
|
|
529
|
+
'EPIPE',
|
|
530
|
+
'ERR_SOCKET_CLOSED',
|
|
531
|
+
]);
|
|
532
|
+
|
|
533
|
+
const RECONNECT_WAIT_TIMEOUT_MS = 5_000;
|
|
534
|
+
const RECONNECT_POLL_INTERVAL_MS = 250;
|
|
535
|
+
|
|
536
|
+
/**
|
|
537
|
+
* Detect thrown transport errors caused by a lost daemon connection. These
|
|
538
|
+
* never arrive as bridge failure responses because the socket died before (or
|
|
539
|
+
* while) the daemon could reply.
|
|
540
|
+
*
|
|
541
|
+
* @param {unknown} error
|
|
542
|
+
* @returns {boolean}
|
|
543
|
+
*/
|
|
544
|
+
export function isConnectionLossError(error) {
|
|
545
|
+
if (!error || typeof error !== 'object') {
|
|
546
|
+
return false;
|
|
547
|
+
}
|
|
548
|
+
const code = /** @type {{ code?: unknown }} */ (error).code;
|
|
549
|
+
if (typeof code === 'string' && CONNECTION_LOSS_CODES.has(code)) {
|
|
550
|
+
return true;
|
|
551
|
+
}
|
|
552
|
+
const message = error instanceof Error ? error.message : '';
|
|
553
|
+
return /Bridge socket closed|BridgeClient is not connected/u.test(message);
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
/**
|
|
557
|
+
* Wait, bounded, for the client to regain its daemon connection.
|
|
558
|
+
*
|
|
559
|
+
* When the client's own auto-reconnect loop is active it owns reconnecting and
|
|
560
|
+
* emits `reconnected`; otherwise one manual connect is attempted per poll
|
|
561
|
+
* interval. Application state is never touched here - this only restores the
|
|
562
|
+
* transport.
|
|
563
|
+
*
|
|
564
|
+
* @param {import('../../agent-client/src/client.js').BridgeClient} client
|
|
565
|
+
* @param {number} [timeoutMs=RECONNECT_WAIT_TIMEOUT_MS]
|
|
566
|
+
* @returns {Promise<boolean>}
|
|
567
|
+
*/
|
|
568
|
+
export async function waitForClientReconnect(client, timeoutMs = RECONNECT_WAIT_TIMEOUT_MS) {
|
|
569
|
+
const deadline = Date.now() + timeoutMs;
|
|
570
|
+
while (!client.connected && Date.now() < deadline) {
|
|
571
|
+
if (client.autoReconnect) {
|
|
572
|
+
await new Promise((resolve) => {
|
|
573
|
+
const remaining = Math.max(0, deadline - Date.now());
|
|
574
|
+
/** @type {ReturnType<typeof setTimeout>} */
|
|
575
|
+
let timer;
|
|
576
|
+
const cleanup = () => {
|
|
577
|
+
clearTimeout(timer);
|
|
578
|
+
client.off('reconnected', onReconnected);
|
|
579
|
+
resolve(undefined);
|
|
580
|
+
};
|
|
581
|
+
const onReconnected = () => cleanup();
|
|
582
|
+
timer = setTimeout(cleanup, Math.min(RECONNECT_POLL_INTERVAL_MS, remaining));
|
|
583
|
+
client.on('reconnected', onReconnected);
|
|
584
|
+
});
|
|
585
|
+
continue;
|
|
586
|
+
}
|
|
587
|
+
try {
|
|
588
|
+
await client.connect();
|
|
589
|
+
} catch (error) {
|
|
590
|
+
if (error instanceof Error && /already connected/u.test(error.message)) {
|
|
591
|
+
break;
|
|
592
|
+
}
|
|
593
|
+
// Daemon still unreachable; keep waiting inside the bounded window.
|
|
594
|
+
}
|
|
595
|
+
if (!client.connected) {
|
|
596
|
+
const remaining = Math.max(0, deadline - Date.now());
|
|
597
|
+
await new Promise((resolve) =>
|
|
598
|
+
setTimeout(resolve, Math.min(RECONNECT_POLL_INTERVAL_MS, remaining))
|
|
599
|
+
);
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
return client.connected;
|
|
603
|
+
}
|
|
604
|
+
|
|
636
605
|
/**
|
|
637
606
|
* @param {import('../../agent-client/src/client.js').BridgeClient} client
|
|
638
607
|
* @param {BridgeMethod} method
|
|
639
608
|
* @param {Record<string, unknown>} params
|
|
640
|
-
* @param {{ tabId?: number | null, source?: import('../../protocol/src/types.js').BridgeRequestSource, tokenBudget?: number | null }} options
|
|
609
|
+
* @param {{ tabId?: number | null, source?: import('../../protocol/src/types.js').BridgeRequestSource, tokenBudget?: number | null, automaticRetry?: 'mcp_second_attempt' }} options
|
|
641
610
|
* @returns {Promise<BridgeResponse>}
|
|
642
611
|
*/
|
|
643
612
|
export async function requestBridgeWithRetry(client, method, params, options) {
|
|
644
|
-
|
|
613
|
+
/** @type {BridgeResponse} */
|
|
614
|
+
let response;
|
|
615
|
+
try {
|
|
616
|
+
response = await requestBridge(client, method, params, options);
|
|
617
|
+
} catch (error) {
|
|
618
|
+
if (!isConnectionLossError(error) || !isRetrySafeBridgeMethod(method, params)) {
|
|
619
|
+
throw error;
|
|
620
|
+
}
|
|
621
|
+
process.stderr.write(
|
|
622
|
+
`[bbx-mcp] ${method} lost its daemon connection; waiting up to ${RECONNECT_WAIT_TIMEOUT_MS}ms for reconnect before one retry.\n`
|
|
623
|
+
);
|
|
624
|
+
if (!(await waitForClientReconnect(client))) {
|
|
625
|
+
throw error;
|
|
626
|
+
}
|
|
627
|
+
return requestBridge(client, method, params, {
|
|
628
|
+
...options,
|
|
629
|
+
automaticRetry: 'mcp_second_attempt',
|
|
630
|
+
});
|
|
631
|
+
}
|
|
645
632
|
const recovery =
|
|
646
633
|
!response.ok && response.error
|
|
647
634
|
? (response.error.recovery ?? getErrorRecovery(response.error.code))
|
|
@@ -652,7 +639,10 @@ export async function requestBridgeWithRetry(client, method, params, options) {
|
|
|
652
639
|
`[bbx-mcp] Retrying ${method} after ${delay}ms (${response.error.code})\n`
|
|
653
640
|
);
|
|
654
641
|
await new Promise((r) => setTimeout(r, delay));
|
|
655
|
-
return requestBridge(client, method, params,
|
|
642
|
+
return requestBridge(client, method, params, {
|
|
643
|
+
...options,
|
|
644
|
+
automaticRetry: 'mcp_second_attempt',
|
|
645
|
+
});
|
|
656
646
|
}
|
|
657
647
|
return response;
|
|
658
648
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// @ts-check
|
|
2
2
|
|
|
3
3
|
import os from 'node:os';
|
|
4
|
+
|
|
4
5
|
import { DEFAULT_LOG_TAIL_LIMIT, createRuntimeContext } from '../../protocol/src/index.js';
|
|
5
6
|
import { collectSetupStatus } from '../../agent-client/src/setup-status.js';
|
|
6
7
|
import {
|
|
@@ -54,15 +55,19 @@ export {
|
|
|
54
55
|
} from './handlers-capture.js';
|
|
55
56
|
|
|
56
57
|
export {
|
|
57
|
-
MAX_BATCH_CALLS,
|
|
58
|
-
MAX_BATCH_CONCURRENCY,
|
|
59
58
|
PAGE_ACTIONS,
|
|
60
59
|
handlePageTool,
|
|
60
|
+
handleArtifactTool,
|
|
61
|
+
handleInterceptTool,
|
|
62
|
+
handleSensitiveReadTool,
|
|
61
63
|
handleBatchTool,
|
|
62
64
|
handleRawCallTool,
|
|
65
|
+
createHarExportResult,
|
|
63
66
|
handleInvestigateTool,
|
|
64
67
|
} from './handlers-page.js';
|
|
65
68
|
|
|
69
|
+
export { MAX_BATCH_CALLS, MAX_BATCH_CONCURRENCY } from '../../protocol/src/index.js';
|
|
70
|
+
|
|
66
71
|
/** @typedef {import('./handlers-utils.js').ToolAction} ToolAction */
|
|
67
72
|
/** @typedef {import('./handlers-utils.js').ToolResult} ToolResult */
|
|
68
73
|
|
|
@@ -97,8 +102,13 @@ export async function handleStatusTool(args = {}) {
|
|
|
97
102
|
}
|
|
98
103
|
|
|
99
104
|
const report = await getDoctorReport();
|
|
105
|
+
const configuredDestinations = await getBridgeDestinations();
|
|
106
|
+
const destinationsToInspect =
|
|
107
|
+
requestedDestinationId === 'local'
|
|
108
|
+
? configuredDestinations.filter((destination) => destination.local)
|
|
109
|
+
: configuredDestinations;
|
|
100
110
|
const destinations = await Promise.all(
|
|
101
|
-
|
|
111
|
+
destinationsToInspect.map(async (destination) => {
|
|
102
112
|
if (destination.local) {
|
|
103
113
|
return {
|
|
104
114
|
...destination,
|
|
@@ -187,7 +197,10 @@ export async function handleSetupTool(args) {
|
|
|
187
197
|
const configuredMcp = status.mcpClients.filter((e) => e.configured).length;
|
|
188
198
|
const installedSkills = status.skillTargets.filter((e) => e.installed).length;
|
|
189
199
|
const summary = `Optional agent integration status: ${configuredMcp}/${status.mcpClients.length} MCP clients configured, ${installedSkills}/${status.skillTargets.length} skills installed.`;
|
|
190
|
-
return createToolResult(summary, {
|
|
200
|
+
return createToolResult(summary, {
|
|
201
|
+
ok: true,
|
|
202
|
+
status,
|
|
203
|
+
});
|
|
191
204
|
}
|
|
192
205
|
|
|
193
206
|
/**
|