@browserbridge/bbx 1.8.0 → 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 +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 +35 -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 +227 -19
- package/packages/agent-client/src/client.js +156 -3
- package/packages/agent-client/src/command-registry.js +8 -2
- package/packages/agent-client/src/config.js +195 -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 +97 -5
- package/packages/agent-client/src/types.ts +38 -0
- 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 +180 -60
- package/packages/mcp-server/src/handlers-utils.js +35 -4
- package/packages/mcp-server/src/handlers.js +10 -3
- package/packages/mcp-server/src/server.js +75 -6
- 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 +543 -27
- package/packages/native-host/src/native-host.js +25 -2
- 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/incidental-sanitizer.js +197 -0
- package/packages/protocol/src/index.js +2 -0
- package/packages/protocol/src/protocol.js +316 -6
- package/packages/protocol/src/recovery-telemetry.js +302 -0
- package/packages/protocol/src/registry.js +143 -6
- package/packages/protocol/src/summary.js +116 -25
- package/packages/protocol/src/types.ts +489 -4
- package/skills/browser-bridge/SKILL.md +22 -8
- package/skills/browser-bridge/references/protocol.md +117 -13
- package/skills/browser-bridge/references/token-efficiency.md +17 -7
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { applyBudget } from './budget.js';
|
|
4
4
|
import {
|
|
5
|
+
ARTIFACT_CHUNK_BYTES,
|
|
5
6
|
BUDGET_PRESETS,
|
|
6
7
|
DEFAULT_A11Y_MAX_DEPTH,
|
|
7
8
|
DEFAULT_A11Y_MAX_NODES,
|
|
@@ -9,6 +10,7 @@ import {
|
|
|
9
10
|
DEFAULT_DEVICE_SCALE_FACTOR,
|
|
10
11
|
DEFAULT_EVAL_TIMEOUT_MS,
|
|
11
12
|
DEFAULT_EXTRACT_SETTLE_TIMEOUT_MS,
|
|
13
|
+
DEFAULT_HAR_LIMIT,
|
|
12
14
|
DEFAULT_LOG_TAIL_LIMIT,
|
|
13
15
|
DEFAULT_MAX_DEPTH,
|
|
14
16
|
DEFAULT_MAX_HTML_LENGTH,
|
|
@@ -22,11 +24,16 @@ import {
|
|
|
22
24
|
DEFAULT_VIEWPORT_WIDTH,
|
|
23
25
|
DEFAULT_WAIT_TIMEOUT_MS,
|
|
24
26
|
MAX_EXTRACT_SETTLE_TIMEOUT_MS,
|
|
27
|
+
MAX_ARTIFACT_BYTES,
|
|
28
|
+
MAX_HAR_ENTRIES,
|
|
29
|
+
MAX_SENSITIVE_VALUE_BYTES,
|
|
25
30
|
} from './defaults.js';
|
|
26
31
|
import { BridgeError, ERROR_CODES, getErrorRecovery } from './errors.js';
|
|
27
32
|
import { BRIDGE_METHODS, METHOD_SET, createBridgeMethodGroups } from './registry.js';
|
|
28
33
|
|
|
29
34
|
/** @typedef {import('./types.js').AccessibilityTreeParams} AccessibilityTreeParams */
|
|
35
|
+
/** @typedef {import('./types.js').ArtifactDeleteParams} ArtifactDeleteParams */
|
|
36
|
+
/** @typedef {import('./types.js').ArtifactReadParams} ArtifactReadParams */
|
|
30
37
|
/** @typedef {import('./types.js').AccessRequestParams} AccessRequestParams */
|
|
31
38
|
/** @typedef {import('./types.js').BridgeFailureResponse} BridgeFailureResponse */
|
|
32
39
|
/** @typedef {import('./types.js').BridgeMeta} BridgeMeta */
|
|
@@ -35,9 +42,13 @@ import { BRIDGE_METHODS, METHOD_SET, createBridgeMethodGroups } from './registry
|
|
|
35
42
|
/** @typedef {import('./types.js').BridgeSuccessResponse} BridgeSuccessResponse */
|
|
36
43
|
/** @typedef {import('./types.js').CheckedActionParams} CheckedActionParams */
|
|
37
44
|
/** @typedef {import('./types.js').CdpDispatchKeyEventParams} CdpDispatchKeyEventParams */
|
|
45
|
+
/** @typedef {import('./types.js').CdpDomSnapshotParams} CdpDomSnapshotParams */
|
|
38
46
|
/** @typedef {import('./types.js').CdpNodeIdParams} CdpNodeIdParams */
|
|
39
47
|
/** @typedef {import('./types.js').ConsoleParams} ConsoleParams */
|
|
40
48
|
/** @typedef {import('./types.js').DomQueryParams} DomQueryParams */
|
|
49
|
+
/** @typedef {import('./types.js').DomBaselineCreateParams} DomBaselineCreateParams */
|
|
50
|
+
/** @typedef {import('./types.js').DomBaselineCompareParams} DomBaselineCompareParams */
|
|
51
|
+
/** @typedef {import('./types.js').DomBaselineHandleParams} DomBaselineHandleParams */
|
|
41
52
|
/** @typedef {import('./types.js').DragParams} DragParams */
|
|
42
53
|
/** @typedef {import('./types.js').EvaluateParams} EvaluateParams */
|
|
43
54
|
/** @typedef {import('./types.js').ExtractContentParams} ExtractContentParams */
|
|
@@ -45,6 +56,7 @@ import { BRIDGE_METHODS, METHOD_SET, createBridgeMethodGroups } from './registry
|
|
|
45
56
|
/** @typedef {import('./types.js').FindByTextParams} FindByTextParams */
|
|
46
57
|
/** @typedef {import('./types.js').GetHtmlParams} GetHtmlParams */
|
|
47
58
|
/** @typedef {import('./types.js').HoverParams} HoverParams */
|
|
59
|
+
/** @typedef {import('./types.js').HarExportParams} HarExportParams */
|
|
48
60
|
/** @typedef {import('./types.js').HandleDialogParams} HandleDialogParams */
|
|
49
61
|
/** @typedef {import('./types.js').InputActionParams} InputActionParams */
|
|
50
62
|
/** @typedef {import('./types.js').LogTailParams} LogTailParams */
|
|
@@ -52,12 +64,17 @@ import { BRIDGE_METHODS, METHOD_SET, createBridgeMethodGroups } from './registry
|
|
|
52
64
|
/** @typedef {import('./types.js').NetworkInterceptAddParams} NetworkInterceptAddParams */
|
|
53
65
|
/** @typedef {import('./types.js').NetworkParams} NetworkParams */
|
|
54
66
|
/** @typedef {import('./types.js').NormalizedAccessibilityTreeParams} NormalizedAccessibilityTreeParams */
|
|
67
|
+
/** @typedef {import('./types.js').NormalizedArtifactReadParams} NormalizedArtifactReadParams */
|
|
55
68
|
/** @typedef {import('./types.js').NormalizedAccessRequestParams} NormalizedAccessRequestParams */
|
|
56
69
|
/** @typedef {import('./types.js').NormalizedCheckedAction} NormalizedCheckedAction */
|
|
57
70
|
/** @typedef {import('./types.js').NormalizedCdpDispatchKeyEventParams} NormalizedCdpDispatchKeyEventParams */
|
|
71
|
+
/** @typedef {import('./types.js').NormalizedCdpDomSnapshotParams} NormalizedCdpDomSnapshotParams */
|
|
58
72
|
/** @typedef {import('./types.js').NormalizedCdpNodeIdParams} NormalizedCdpNodeIdParams */
|
|
59
73
|
/** @typedef {import('./types.js').NormalizedConsoleParams} NormalizedConsoleParams */
|
|
60
74
|
/** @typedef {import('./types.js').NormalizedDomQuery} NormalizedDomQuery */
|
|
75
|
+
/** @typedef {import('./types.js').NormalizedDomBaselineCreateParams} NormalizedDomBaselineCreateParams */
|
|
76
|
+
/** @typedef {import('./types.js').NormalizedDomBaselineCompareParams} NormalizedDomBaselineCompareParams */
|
|
77
|
+
/** @typedef {import('./types.js').NormalizedDomBaselineHandleParams} NormalizedDomBaselineHandleParams */
|
|
61
78
|
/** @typedef {import('./types.js').NormalizedDragParams} NormalizedDragParams */
|
|
62
79
|
/** @typedef {import('./types.js').NormalizedEvaluateParams} NormalizedEvaluateParams */
|
|
63
80
|
/** @typedef {import('./types.js').NormalizedExtractContentParams} NormalizedExtractContentParams */
|
|
@@ -65,6 +82,7 @@ import { BRIDGE_METHODS, METHOD_SET, createBridgeMethodGroups } from './registry
|
|
|
65
82
|
/** @typedef {import('./types.js').NormalizedFindByTextParams} NormalizedFindByTextParams */
|
|
66
83
|
/** @typedef {import('./types.js').NormalizedGetHtmlParams} NormalizedGetHtmlParams */
|
|
67
84
|
/** @typedef {import('./types.js').NormalizedHoverParams} NormalizedHoverParams */
|
|
85
|
+
/** @typedef {import('./types.js').NormalizedHarExportParams} NormalizedHarExportParams */
|
|
68
86
|
/** @typedef {import('./types.js').NormalizedHandleDialogParams} NormalizedHandleDialogParams */
|
|
69
87
|
/** @typedef {import('./types.js').NormalizedInputAction} NormalizedInputAction */
|
|
70
88
|
/** @typedef {import('./types.js').NormalizedLogTailParams} NormalizedLogTailParams */
|
|
@@ -76,6 +94,7 @@ import { BRIDGE_METHODS, METHOD_SET, createBridgeMethodGroups } from './registry
|
|
|
76
94
|
/** @typedef {import('./types.js').NormalizedSelectAction} NormalizedSelectAction */
|
|
77
95
|
/** @typedef {import('./types.js').NormalizedScreenshotParams} NormalizedScreenshotParams */
|
|
78
96
|
/** @typedef {import('./types.js').NormalizedStorageParams} NormalizedStorageParams */
|
|
97
|
+
/** @typedef {import('./types.js').NormalizedSensitiveReadParams} NormalizedSensitiveReadParams */
|
|
79
98
|
/** @typedef {import('./types.js').NormalizedStyleQuery} NormalizedStyleQuery */
|
|
80
99
|
/** @typedef {import('./types.js').NormalizedTabCloseParams} NormalizedTabCloseParams */
|
|
81
100
|
/** @typedef {import('./types.js').NormalizedTabCreateParams} NormalizedTabCreateParams */
|
|
@@ -88,6 +107,7 @@ import { BRIDGE_METHODS, METHOD_SET, createBridgeMethodGroups } from './registry
|
|
|
88
107
|
/** @typedef {import('./types.js').SelectActionParams} SelectActionParams */
|
|
89
108
|
/** @typedef {import('./types.js').ScreenshotParams} ScreenshotParams */
|
|
90
109
|
/** @typedef {import('./types.js').StorageParams} StorageParams */
|
|
110
|
+
/** @typedef {import('./types.js').SensitiveReadParams} SensitiveReadParams */
|
|
91
111
|
/** @typedef {import('./types.js').StyleQueryParams} StyleQueryParams */
|
|
92
112
|
/** @typedef {import('./types.js').TabCloseParams} TabCloseParams */
|
|
93
113
|
/** @typedef {import('./types.js').TabCreateParams} TabCreateParams */
|
|
@@ -244,6 +264,14 @@ export function createSuccess(id, result, meta = {}) {
|
|
|
244
264
|
*/
|
|
245
265
|
export function createFailure(id, code, message, details = null, meta = {}) {
|
|
246
266
|
const recovery = getErrorRecovery(code);
|
|
267
|
+
const effectiveRecovery =
|
|
268
|
+
recovery && meta.method === 'sensitive.read'
|
|
269
|
+
? {
|
|
270
|
+
retry: false,
|
|
271
|
+
...(recovery.alternativeMethod ? { alternativeMethod: recovery.alternativeMethod } : {}),
|
|
272
|
+
hint: `Sensitive reads are never retried automatically. ${recovery.hint}`,
|
|
273
|
+
}
|
|
274
|
+
: recovery;
|
|
247
275
|
return {
|
|
248
276
|
id,
|
|
249
277
|
ok: false,
|
|
@@ -252,7 +280,7 @@ export function createFailure(id, code, message, details = null, meta = {}) {
|
|
|
252
280
|
code,
|
|
253
281
|
message,
|
|
254
282
|
details,
|
|
255
|
-
...(
|
|
283
|
+
...(effectiveRecovery && { recovery: effectiveRecovery }),
|
|
256
284
|
},
|
|
257
285
|
meta: {
|
|
258
286
|
protocol_version: getProtocolVersion(),
|
|
@@ -344,10 +372,28 @@ export function validateBridgeRequest(request) {
|
|
|
344
372
|
typeof meta.protocol_version === 'string' ? meta.protocol_version : getProtocolVersion(),
|
|
345
373
|
token_budget: typeof meta.token_budget === 'number' ? meta.token_budget : null,
|
|
346
374
|
source: meta.source === 'cli' || meta.source === 'mcp' ? meta.source : undefined,
|
|
375
|
+
automatic_retry: normalizeAutomaticRetryMeta(meta.source, meta.automatic_retry),
|
|
347
376
|
},
|
|
348
377
|
};
|
|
349
378
|
}
|
|
350
379
|
|
|
380
|
+
/**
|
|
381
|
+
* @param {unknown} source
|
|
382
|
+
* @param {unknown} value
|
|
383
|
+
* @returns {{ attempt: 2, reason: 'retryable_error' } | undefined}
|
|
384
|
+
*/
|
|
385
|
+
function normalizeAutomaticRetryMeta(source, value) {
|
|
386
|
+
if (source !== 'mcp' || !isPlainRecord(value)) return undefined;
|
|
387
|
+
const keys = Object.keys(value).sort();
|
|
388
|
+
return keys.length === 2 &&
|
|
389
|
+
keys[0] === 'attempt' &&
|
|
390
|
+
keys[1] === 'reason' &&
|
|
391
|
+
value.attempt === 2 &&
|
|
392
|
+
value.reason === 'retryable_error'
|
|
393
|
+
? { attempt: 2, reason: 'retryable_error' }
|
|
394
|
+
: undefined;
|
|
395
|
+
}
|
|
396
|
+
|
|
351
397
|
/**
|
|
352
398
|
* Normalize request params early so malformed payloads fail at the shared
|
|
353
399
|
* protocol boundary before daemon or extension dispatch.
|
|
@@ -364,6 +410,13 @@ function normalizeRequestParams(method, params) {
|
|
|
364
410
|
return normalizeLogTailParams(params);
|
|
365
411
|
case 'dom.query':
|
|
366
412
|
return normalizeDomQuery(params);
|
|
413
|
+
case 'dom.baseline.create':
|
|
414
|
+
return normalizeDomBaselineCreateParams(params);
|
|
415
|
+
case 'dom.baseline.compare':
|
|
416
|
+
return normalizeDomBaselineCompareParams(params);
|
|
417
|
+
case 'dom.baseline.describe':
|
|
418
|
+
case 'dom.baseline.release':
|
|
419
|
+
return normalizeDomBaselineHandleParams(params);
|
|
367
420
|
case 'page.evaluate':
|
|
368
421
|
return normalizeEvaluateParams(params);
|
|
369
422
|
case 'page.get_console':
|
|
@@ -374,12 +427,16 @@ function normalizeRequestParams(method, params) {
|
|
|
374
427
|
return normalizeWaitForLoadStateParams(params);
|
|
375
428
|
case 'page.get_storage':
|
|
376
429
|
return normalizeStorageParams(params);
|
|
430
|
+
case 'sensitive.read':
|
|
431
|
+
return normalizeSensitiveReadParams(params);
|
|
377
432
|
case 'page.get_text':
|
|
378
433
|
return normalizePageTextParams(params);
|
|
379
434
|
case 'page.extract_content':
|
|
380
435
|
return normalizeExtractContentParams(params);
|
|
381
436
|
case 'page.get_network':
|
|
382
437
|
return normalizeNetworkParams(params);
|
|
438
|
+
case 'network.export_har':
|
|
439
|
+
return normalizeHarExportParams(params);
|
|
383
440
|
case 'network.intercept.add':
|
|
384
441
|
return normalizeNetworkInterceptAddParams(params);
|
|
385
442
|
case 'navigation.navigate':
|
|
@@ -415,6 +472,8 @@ function normalizeRequestParams(method, params) {
|
|
|
415
472
|
case 'cdp.get_box_model':
|
|
416
473
|
case 'cdp.get_computed_styles_for_node':
|
|
417
474
|
return normalizeCdpNodeIdParams(params);
|
|
475
|
+
case 'cdp.get_dom_snapshot':
|
|
476
|
+
return normalizeCdpDomSnapshotParams(params);
|
|
418
477
|
case 'input.set_checked':
|
|
419
478
|
return normalizeCheckedAction(params);
|
|
420
479
|
case 'input.select_option':
|
|
@@ -424,9 +483,18 @@ function normalizeRequestParams(method, params) {
|
|
|
424
483
|
case 'input.drag':
|
|
425
484
|
return normalizeDragParams(params);
|
|
426
485
|
case 'screenshot.capture_region':
|
|
486
|
+
return { ...params, ...normalizeScreenshotParams(params) };
|
|
427
487
|
case 'screenshot.capture_element':
|
|
428
488
|
case 'screenshot.capture_full_page':
|
|
429
|
-
return {
|
|
489
|
+
return {
|
|
490
|
+
...params,
|
|
491
|
+
...normalizeScreenshotParams(params),
|
|
492
|
+
...(params.scale === undefined ? { scale: undefined } : {}),
|
|
493
|
+
};
|
|
494
|
+
case 'artifact.read':
|
|
495
|
+
return normalizeArtifactReadParams(params);
|
|
496
|
+
case 'artifact.delete':
|
|
497
|
+
return normalizeArtifactDeleteParams(params);
|
|
430
498
|
case 'patch.apply_styles':
|
|
431
499
|
case 'patch.apply_dom':
|
|
432
500
|
case 'patch.list':
|
|
@@ -510,6 +578,82 @@ export function normalizeDomQuery(params = {}) {
|
|
|
510
578
|
};
|
|
511
579
|
}
|
|
512
580
|
|
|
581
|
+
/**
|
|
582
|
+
* @param {DomBaselineCreateParams} [params={}]
|
|
583
|
+
* @returns {NormalizedDomBaselineCreateParams}
|
|
584
|
+
*/
|
|
585
|
+
export function normalizeDomBaselineCreateParams(params = {}) {
|
|
586
|
+
const selector = typeof params.selector === 'string' ? params.selector.trim() : '';
|
|
587
|
+
if (selector.length > 2_048) {
|
|
588
|
+
throw new BridgeError(
|
|
589
|
+
ERROR_CODES.INVALID_REQUEST,
|
|
590
|
+
'selector must not exceed 2,048 characters.'
|
|
591
|
+
);
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
const attributeAllowlist = Array.isArray(params.attributeAllowlist)
|
|
595
|
+
? [
|
|
596
|
+
...new Set(
|
|
597
|
+
params.attributeAllowlist
|
|
598
|
+
.filter((name) => typeof name === 'string')
|
|
599
|
+
.map((name) => name.trim().toLowerCase())
|
|
600
|
+
.filter(
|
|
601
|
+
(name) =>
|
|
602
|
+
name.length > 0 &&
|
|
603
|
+
name.length <= 128 &&
|
|
604
|
+
name !== 'value' &&
|
|
605
|
+
!name.startsWith('on') &&
|
|
606
|
+
/^[a-z_][a-z0-9_.:-]*$/u.test(name)
|
|
607
|
+
)
|
|
608
|
+
),
|
|
609
|
+
]
|
|
610
|
+
: [];
|
|
611
|
+
if (attributeAllowlist.length > 16) {
|
|
612
|
+
throw new BridgeError(
|
|
613
|
+
ERROR_CODES.INVALID_REQUEST,
|
|
614
|
+
'attributeAllowlist must contain at most 16 unique attribute names.'
|
|
615
|
+
);
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
return {
|
|
619
|
+
selector: selector || 'body',
|
|
620
|
+
maxNodes: clampInt(params.maxNodes, 1, 1_000, 100),
|
|
621
|
+
maxDepth: clampInt(params.maxDepth, 1, 20, 8),
|
|
622
|
+
textBudget: clampInt(params.textBudget, 32, 1_000, 160),
|
|
623
|
+
attributeAllowlist,
|
|
624
|
+
};
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
/**
|
|
628
|
+
* @param {DomBaselineCompareParams} [params={}]
|
|
629
|
+
* @returns {NormalizedDomBaselineCompareParams}
|
|
630
|
+
*/
|
|
631
|
+
export function normalizeDomBaselineCompareParams(params = {}) {
|
|
632
|
+
return {
|
|
633
|
+
baselineId: normalizeDomBaselineId(params.baselineId),
|
|
634
|
+
maxChanges: clampInt(params.maxChanges, 1, 200, 50),
|
|
635
|
+
};
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
/**
|
|
639
|
+
* @param {DomBaselineHandleParams} [params={}]
|
|
640
|
+
* @returns {NormalizedDomBaselineHandleParams}
|
|
641
|
+
*/
|
|
642
|
+
export function normalizeDomBaselineHandleParams(params = {}) {
|
|
643
|
+
return { baselineId: normalizeDomBaselineId(params.baselineId) };
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
/**
|
|
647
|
+
* @param {unknown} value
|
|
648
|
+
* @returns {string}
|
|
649
|
+
*/
|
|
650
|
+
function normalizeDomBaselineId(value) {
|
|
651
|
+
if (typeof value !== 'string' || !/^baseline_[A-Za-z0-9_-]{32,64}$/u.test(value)) {
|
|
652
|
+
throw new BridgeError(ERROR_CODES.INVALID_REQUEST, 'baselineId is invalid.');
|
|
653
|
+
}
|
|
654
|
+
return value;
|
|
655
|
+
}
|
|
656
|
+
|
|
513
657
|
/**
|
|
514
658
|
* @param {StyleQueryParams} [params={}]
|
|
515
659
|
* @returns {NormalizedStyleQuery}
|
|
@@ -651,6 +795,25 @@ export function normalizeCdpNodeIdParams(params = {}) {
|
|
|
651
795
|
};
|
|
652
796
|
}
|
|
653
797
|
|
|
798
|
+
/**
|
|
799
|
+
* @param {CdpDomSnapshotParams} [params={}]
|
|
800
|
+
* @returns {NormalizedCdpDomSnapshotParams}
|
|
801
|
+
*/
|
|
802
|
+
export function normalizeCdpDomSnapshotParams(params = {}) {
|
|
803
|
+
const computedStyles = Array.isArray(params.computedStyles)
|
|
804
|
+
? params.computedStyles.filter(
|
|
805
|
+
(property) => typeof property === 'string' && property.length > 0 && property.length <= 128
|
|
806
|
+
)
|
|
807
|
+
: [];
|
|
808
|
+
if (computedStyles.length > 100) {
|
|
809
|
+
throw new BridgeError(
|
|
810
|
+
ERROR_CODES.INVALID_REQUEST,
|
|
811
|
+
'computedStyles must contain at most 100 CSS property names.'
|
|
812
|
+
);
|
|
813
|
+
}
|
|
814
|
+
return { computedStyles };
|
|
815
|
+
}
|
|
816
|
+
|
|
654
817
|
/**
|
|
655
818
|
* @param {CheckedActionParams} [params={}]
|
|
656
819
|
* @returns {NormalizedCheckedAction}
|
|
@@ -745,13 +908,30 @@ export function normalizeNavigationAction(params = {}) {
|
|
|
745
908
|
* @returns {NormalizedPatchOperation}
|
|
746
909
|
*/
|
|
747
910
|
export function normalizePatchOperation(params = {}) {
|
|
911
|
+
const operation = typeof params.operation === 'string' ? params.operation : null;
|
|
912
|
+
if (
|
|
913
|
+
operation !== null &&
|
|
914
|
+
![
|
|
915
|
+
'set_text',
|
|
916
|
+
'set_attribute',
|
|
917
|
+
'remove_attribute',
|
|
918
|
+
'add_class',
|
|
919
|
+
'remove_class',
|
|
920
|
+
'toggle_class',
|
|
921
|
+
].includes(operation)
|
|
922
|
+
) {
|
|
923
|
+
throw new BridgeError(
|
|
924
|
+
ERROR_CODES.INVALID_REQUEST,
|
|
925
|
+
`Unsupported DOM patch operation: ${operation}`
|
|
926
|
+
);
|
|
927
|
+
}
|
|
748
928
|
return {
|
|
749
929
|
patchId: typeof params.patchId === 'string' ? params.patchId : null,
|
|
750
930
|
target:
|
|
751
931
|
params.target && typeof params.target === 'object'
|
|
752
932
|
? /** @type {Record<string, unknown>} */ (params.target)
|
|
753
933
|
: {},
|
|
754
|
-
operation
|
|
934
|
+
operation,
|
|
755
935
|
name: typeof params.name === 'string' ? params.name : null,
|
|
756
936
|
declarations:
|
|
757
937
|
params.declarations && typeof params.declarations === 'object'
|
|
@@ -975,9 +1155,42 @@ export function normalizeDragParams(params = {}) {
|
|
|
975
1155
|
* @returns {NormalizedStorageParams}
|
|
976
1156
|
*/
|
|
977
1157
|
export function normalizeStorageParams(params = {}) {
|
|
1158
|
+
const keys = Array.isArray(params.keys)
|
|
1159
|
+
? params.keys.filter((key) => typeof key === 'string')
|
|
1160
|
+
: null;
|
|
1161
|
+
if (keys && keys.length > 100) {
|
|
1162
|
+
throw new BridgeError(ERROR_CODES.INVALID_REQUEST, 'keys must contain at most 100 strings.');
|
|
1163
|
+
}
|
|
978
1164
|
return {
|
|
979
1165
|
type: params.type === 'session' ? 'session' : 'local',
|
|
980
|
-
keys
|
|
1166
|
+
keys,
|
|
1167
|
+
};
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1170
|
+
/**
|
|
1171
|
+
* @param {Record<string, unknown>} params
|
|
1172
|
+
* @returns {NormalizedSensitiveReadParams}
|
|
1173
|
+
*/
|
|
1174
|
+
export function normalizeSensitiveReadParams(params) {
|
|
1175
|
+
if (params?.source !== 'local_storage' && params?.source !== 'session_storage') {
|
|
1176
|
+
throw new BridgeError(
|
|
1177
|
+
ERROR_CODES.INVALID_REQUEST,
|
|
1178
|
+
'source must be local_storage or session_storage.'
|
|
1179
|
+
);
|
|
1180
|
+
}
|
|
1181
|
+
if (typeof params.key !== 'string') {
|
|
1182
|
+
throw new BridgeError(ERROR_CODES.INVALID_REQUEST, 'key must be a string.');
|
|
1183
|
+
}
|
|
1184
|
+
if (params.key.length > 2_048) {
|
|
1185
|
+
throw new BridgeError(
|
|
1186
|
+
ERROR_CODES.INVALID_REQUEST,
|
|
1187
|
+
'key must contain at most 2,048 characters.'
|
|
1188
|
+
);
|
|
1189
|
+
}
|
|
1190
|
+
return {
|
|
1191
|
+
source: params.source,
|
|
1192
|
+
key: params.key,
|
|
1193
|
+
maxBytes: MAX_SENSITIVE_VALUE_BYTES,
|
|
981
1194
|
};
|
|
982
1195
|
}
|
|
983
1196
|
|
|
@@ -1142,6 +1355,13 @@ export function normalizeScreenshotParams(params = {}) {
|
|
|
1142
1355
|
throw new BridgeError(ERROR_CODES.INVALID_REQUEST, 'format must be png, jpeg, or webp.');
|
|
1143
1356
|
}
|
|
1144
1357
|
const numericQuality = Number(params.quality);
|
|
1358
|
+
const delivery = params.delivery ?? 'inline';
|
|
1359
|
+
if (delivery !== 'auto' && delivery !== 'inline' && delivery !== 'artifact') {
|
|
1360
|
+
throw new BridgeError(
|
|
1361
|
+
ERROR_CODES.INVALID_REQUEST,
|
|
1362
|
+
'delivery must be auto, inline, or artifact.'
|
|
1363
|
+
);
|
|
1364
|
+
}
|
|
1145
1365
|
return {
|
|
1146
1366
|
format,
|
|
1147
1367
|
quality:
|
|
@@ -1150,9 +1370,42 @@ export function normalizeScreenshotParams(params = {}) {
|
|
|
1150
1370
|
: Number.isFinite(numericQuality)
|
|
1151
1371
|
? Math.min(100, Math.max(0, Math.trunc(numericQuality)))
|
|
1152
1372
|
: 80,
|
|
1373
|
+
delivery,
|
|
1374
|
+
scale:
|
|
1375
|
+
Number.isFinite(Number(params.scale)) && Number(params.scale) > 0
|
|
1376
|
+
? clampNumber(Number(params.scale), 0.1, 4, 1)
|
|
1377
|
+
: 1,
|
|
1378
|
+
};
|
|
1379
|
+
}
|
|
1380
|
+
|
|
1381
|
+
/**
|
|
1382
|
+
* @param {ArtifactReadParams} [params={}]
|
|
1383
|
+
* @returns {NormalizedArtifactReadParams}
|
|
1384
|
+
*/
|
|
1385
|
+
export function normalizeArtifactReadParams(params = {}) {
|
|
1386
|
+
return {
|
|
1387
|
+
artifactId: normalizeArtifactId(params.artifactId),
|
|
1388
|
+
offset: clampInt(params.offset, 0, MAX_ARTIFACT_BYTES, 0),
|
|
1389
|
+
maxBytes: clampInt(params.maxBytes, 1, ARTIFACT_CHUNK_BYTES, ARTIFACT_CHUNK_BYTES),
|
|
1153
1390
|
};
|
|
1154
1391
|
}
|
|
1155
1392
|
|
|
1393
|
+
/**
|
|
1394
|
+
* @param {ArtifactDeleteParams} [params={}]
|
|
1395
|
+
* @returns {{ artifactId: string }}
|
|
1396
|
+
*/
|
|
1397
|
+
export function normalizeArtifactDeleteParams(params = {}) {
|
|
1398
|
+
return { artifactId: normalizeArtifactId(params.artifactId) };
|
|
1399
|
+
}
|
|
1400
|
+
|
|
1401
|
+
/** @param {unknown} value */
|
|
1402
|
+
function normalizeArtifactId(value) {
|
|
1403
|
+
if (typeof value !== 'string' || !/^art_[A-Za-z0-9_-]{32,64}$/u.test(value)) {
|
|
1404
|
+
throw new BridgeError(ERROR_CODES.INVALID_REQUEST, 'artifactId is invalid.');
|
|
1405
|
+
}
|
|
1406
|
+
return value;
|
|
1407
|
+
}
|
|
1408
|
+
|
|
1156
1409
|
/**
|
|
1157
1410
|
* @param {NetworkParams} [params={}]
|
|
1158
1411
|
* @returns {NormalizedNetworkParams}
|
|
@@ -1184,6 +1437,54 @@ export function normalizeNetworkParams(params = {}) {
|
|
|
1184
1437
|
};
|
|
1185
1438
|
}
|
|
1186
1439
|
|
|
1440
|
+
/**
|
|
1441
|
+
* @param {HarExportParams | NormalizedHarExportParams} [params={}]
|
|
1442
|
+
* @returns {NormalizedHarExportParams}
|
|
1443
|
+
*/
|
|
1444
|
+
export function normalizeHarExportParams(params = {}) {
|
|
1445
|
+
if (
|
|
1446
|
+
params.limit !== undefined &&
|
|
1447
|
+
(typeof params.limit !== 'number' ||
|
|
1448
|
+
!Number.isSafeInteger(params.limit) ||
|
|
1449
|
+
params.limit < 1 ||
|
|
1450
|
+
params.limit > MAX_HAR_ENTRIES)
|
|
1451
|
+
) {
|
|
1452
|
+
throw new BridgeError(
|
|
1453
|
+
ERROR_CODES.INVALID_REQUEST,
|
|
1454
|
+
`limit must be an integer between 1 and ${MAX_HAR_ENTRIES}.`
|
|
1455
|
+
);
|
|
1456
|
+
}
|
|
1457
|
+
|
|
1458
|
+
if (
|
|
1459
|
+
params.urlPattern !== undefined &&
|
|
1460
|
+
params.urlPattern !== null &&
|
|
1461
|
+
typeof params.urlPattern !== 'string'
|
|
1462
|
+
) {
|
|
1463
|
+
throw new BridgeError(ERROR_CODES.INVALID_REQUEST, 'urlPattern must be a string.');
|
|
1464
|
+
}
|
|
1465
|
+
const urlPattern = params.urlPattern?.trim() || null;
|
|
1466
|
+
if (urlPattern && urlPattern.length > 2_048) {
|
|
1467
|
+
throw new BridgeError(
|
|
1468
|
+
ERROR_CODES.INVALID_REQUEST,
|
|
1469
|
+
'urlPattern must contain at most 2048 characters.'
|
|
1470
|
+
);
|
|
1471
|
+
}
|
|
1472
|
+
|
|
1473
|
+
const delivery = params.delivery ?? 'auto';
|
|
1474
|
+
if (delivery !== 'auto' && delivery !== 'inline' && delivery !== 'artifact') {
|
|
1475
|
+
throw new BridgeError(
|
|
1476
|
+
ERROR_CODES.INVALID_REQUEST,
|
|
1477
|
+
'delivery must be auto, inline, or artifact.'
|
|
1478
|
+
);
|
|
1479
|
+
}
|
|
1480
|
+
|
|
1481
|
+
return {
|
|
1482
|
+
limit: params.limit ?? DEFAULT_HAR_LIMIT,
|
|
1483
|
+
urlPattern,
|
|
1484
|
+
delivery,
|
|
1485
|
+
};
|
|
1486
|
+
}
|
|
1487
|
+
|
|
1187
1488
|
/**
|
|
1188
1489
|
* @param {NetworkInterceptAddParams} [params={}]
|
|
1189
1490
|
* @returns {NormalizedNetworkInterceptAddParams}
|
|
@@ -1363,13 +1664,16 @@ export function createRuntimeContext() {
|
|
|
1363
1664
|
ELEMENT_NOT_ACTIONABLE: 'Input target is hidden, disabled, inert, or not rendered',
|
|
1364
1665
|
ELEMENT_OBSCURED: 'Pointer target is blocked by an unrelated element',
|
|
1365
1666
|
ELEMENT_NOT_FOUND: 'Input selector did not match an element',
|
|
1667
|
+
DOM_BASELINE_NOT_FOUND: 'DOM baseline is missing, expired, or released',
|
|
1668
|
+
DOM_BASELINE_INVALIDATED: 'DOM baseline belongs to a different document or scope',
|
|
1669
|
+
DOM_BASELINE_QUOTA_EXCEEDED: 'DOM baseline retention quota was exceeded',
|
|
1366
1670
|
INPUT_UNSUPPORTED: 'Execution mode does not support this input operation',
|
|
1367
1671
|
INPUT_INVALID_TARGET: 'Input target is incompatible with the requested operation',
|
|
1368
1672
|
INPUT_FOCUS_CHANGED: 'Focus moved before native text dispatch',
|
|
1369
1673
|
DIALOG_NOT_OPEN: 'No JavaScript dialog is currently observable',
|
|
1370
1674
|
INVALID_REQUEST: 'Malformed method or params',
|
|
1371
1675
|
TIMEOUT: 'Operation exceeded time limit',
|
|
1372
|
-
|
|
1676
|
+
CONTENT_SCRIPT_UNAVAILABLE: 'Page is restricted or cannot host the content script',
|
|
1373
1677
|
INTERNAL_ERROR: 'Unexpected extension error',
|
|
1374
1678
|
EXTENSION_DISCONNECTED: 'Extension not connected to daemon - check Chrome',
|
|
1375
1679
|
},
|
|
@@ -1377,6 +1681,7 @@ export function createRuntimeContext() {
|
|
|
1377
1681
|
'dom.query quick budget first; widen only if truncated',
|
|
1378
1682
|
"Reuse elementRef; don't re-query",
|
|
1379
1683
|
'Set attributeAllowlist for focused DOM reads',
|
|
1684
|
+
'Use dom.baseline.create / compare for bounded semantic DOM change detection; release when done',
|
|
1380
1685
|
'patch.apply_styles before patch.apply_dom',
|
|
1381
1686
|
'Verify with get_box_model not screenshots',
|
|
1382
1687
|
'batch independent reads',
|
|
@@ -1390,7 +1695,7 @@ export function createRuntimeContext() {
|
|
|
1390
1695
|
'page.get_network for fetch/XHR reads or explicit all-resource CDP capture',
|
|
1391
1696
|
'page.get_text for full-page content extraction',
|
|
1392
1697
|
'input.hover before screenshot to inspect hover states',
|
|
1393
|
-
'performance.get_metrics
|
|
1698
|
+
'performance.get_metrics returns raw Chrome/CDP counters whose names and units vary; it is a point sample with no BBX navigation window or LCP/CLS/INP measurement',
|
|
1394
1699
|
'viewport.resize to test responsive layouts',
|
|
1395
1700
|
'Prefer screenshot.capture_element, or a tight screenshot.capture_region when element capture cannot express the needed area',
|
|
1396
1701
|
'page.get_storage reads localStorage/sessionStorage without evaluate',
|
|
@@ -1415,12 +1720,17 @@ export function createRuntimeContext() {
|
|
|
1415
1720
|
maxHtmlLength: { min: 32, max: 50000, default: DEFAULT_MAX_HTML_LENGTH },
|
|
1416
1721
|
a11yMaxNodes: { min: 10, max: 5000, default: DEFAULT_A11Y_MAX_NODES },
|
|
1417
1722
|
networkLimit: { min: 1, max: 500, default: DEFAULT_NETWORK_LIMIT },
|
|
1723
|
+
harLimit: { min: 1, max: MAX_HAR_ENTRIES, default: DEFAULT_HAR_LIMIT },
|
|
1418
1724
|
consoleLimit: { min: 1, max: 200, default: DEFAULT_CONSOLE_LIMIT },
|
|
1419
1725
|
pageTextBudget: {
|
|
1420
1726
|
min: 100,
|
|
1421
1727
|
max: 100000,
|
|
1422
1728
|
default: DEFAULT_PAGE_TEXT_BUDGET,
|
|
1423
1729
|
},
|
|
1730
|
+
baselineMaxNodes: { min: 1, max: 1000, default: 100 },
|
|
1731
|
+
baselineMaxDepth: { min: 1, max: 20, default: 8 },
|
|
1732
|
+
baselineTextBudget: { min: 32, max: 1000, default: 160 },
|
|
1733
|
+
baselineMaxChanges: { min: 1, max: 200, default: 50 },
|
|
1424
1734
|
},
|
|
1425
1735
|
};
|
|
1426
1736
|
}
|