@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
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
getProtocolVersion,
|
|
8
8
|
serializeJsonPayload,
|
|
9
9
|
} from './index.js';
|
|
10
|
+
import { sanitizeIncidentalUrl } from './incidental-sanitizer.js';
|
|
10
11
|
|
|
11
12
|
/** @typedef {import('./types.js').BridgeResponse} BridgeResponse */
|
|
12
13
|
/** @typedef {import('./types.js').BridgeMethod} SummaryMethod */
|
|
@@ -121,6 +122,7 @@ const ACTION_SUMMARIES = {
|
|
|
121
122
|
* @returns {AnnotatedBridgeSummary}
|
|
122
123
|
*/
|
|
123
124
|
export function annotateBridgeSummary(summary, response) {
|
|
125
|
+
const sensitiveRead = response.meta?.method === 'sensitive.read';
|
|
124
126
|
const metaTransportBytes =
|
|
125
127
|
getNumericMetaField(response.meta, 'transport_bytes') ??
|
|
126
128
|
getNumericMetaField(response.meta, 'response_bytes');
|
|
@@ -128,13 +130,15 @@ export function annotateBridgeSummary(summary, response) {
|
|
|
128
130
|
getNumericMetaField(response.meta, 'transport_approx_tokens') ??
|
|
129
131
|
getNumericMetaField(response.meta, 'approx_tokens');
|
|
130
132
|
const transportCost =
|
|
131
|
-
metaTransportBytes == null || metaTransportTokens == null
|
|
133
|
+
!sensitiveRead && (metaTransportBytes == null || metaTransportTokens == null)
|
|
132
134
|
? estimateSerializedPayloadCost(
|
|
133
135
|
serializeJsonPayload(response.ok ? response.result : { error: response.error })
|
|
134
136
|
)
|
|
135
137
|
: null;
|
|
136
|
-
const transportBytes = metaTransportBytes ?? transportCost?.bytes ?? 0;
|
|
137
|
-
const transportTokens =
|
|
138
|
+
const transportBytes = sensitiveRead ? 0 : (metaTransportBytes ?? transportCost?.bytes ?? 0);
|
|
139
|
+
const transportTokens = sensitiveRead
|
|
140
|
+
? 0
|
|
141
|
+
: (metaTransportTokens ?? transportCost?.approxTokens ?? 0);
|
|
138
142
|
const summaryCost = estimateSerializedPayloadCost(serializeJsonPayload(summary));
|
|
139
143
|
|
|
140
144
|
return {
|
|
@@ -194,6 +198,44 @@ export function summarizeBridgeResponse(response, method) {
|
|
|
194
198
|
}
|
|
195
199
|
|
|
196
200
|
const result = toRecord(response.result);
|
|
201
|
+
if (method === 'dom.baseline.compare') {
|
|
202
|
+
const counts = toRecord(result.counts);
|
|
203
|
+
const added = typeof counts.added === 'number' ? counts.added : 0;
|
|
204
|
+
const removed = typeof counts.removed === 'number' ? counts.removed : 0;
|
|
205
|
+
const changed = typeof counts.changed === 'number' ? counts.changed : 0;
|
|
206
|
+
const moved = typeof counts.moved === 'number' ? counts.moved : 0;
|
|
207
|
+
return {
|
|
208
|
+
ok: true,
|
|
209
|
+
summary: appendProtocolWarning(
|
|
210
|
+
result.equal === true
|
|
211
|
+
? `DOM baseline ${result.baselineId ?? ''} matches the current DOM.`
|
|
212
|
+
: `DOM baseline ${result.baselineId ?? ''} differs: ${added} added, ${removed} removed, ${changed} changed, ${moved} moved${result.truncated === true ? `; ${result.omittedChanges ?? 0} omitted` : ''}.`,
|
|
213
|
+
protocolWarning
|
|
214
|
+
),
|
|
215
|
+
evidence: result,
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
if (method === 'dom.baseline.release') {
|
|
219
|
+
return {
|
|
220
|
+
ok: true,
|
|
221
|
+
summary: appendProtocolWarning(
|
|
222
|
+
`DOM baseline ${result.baselineId ?? ''} ${result.released === true ? 'released' : 'was not released'}.`,
|
|
223
|
+
protocolWarning
|
|
224
|
+
),
|
|
225
|
+
evidence: result,
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
if (method === 'dom.baseline.create' || method === 'dom.baseline.describe') {
|
|
229
|
+
const snapshot = toRecord(result.snapshot);
|
|
230
|
+
return {
|
|
231
|
+
ok: true,
|
|
232
|
+
summary: appendProtocolWarning(
|
|
233
|
+
`DOM baseline ${result.baselineId ?? ''} ${method === 'dom.baseline.create' ? 'created' : 'described'}: ${snapshot.nodeCount ?? 0} node(s), ${snapshot.byteLength ?? 0} bytes.`,
|
|
234
|
+
protocolWarning
|
|
235
|
+
),
|
|
236
|
+
evidence: result,
|
|
237
|
+
};
|
|
238
|
+
}
|
|
197
239
|
if (method === 'page.handle_dialog') {
|
|
198
240
|
const action = typeof result.action === 'string' ? result.action : 'inspect';
|
|
199
241
|
return {
|
|
@@ -217,6 +259,29 @@ export function summarizeBridgeResponse(response, method) {
|
|
|
217
259
|
evidence: result,
|
|
218
260
|
};
|
|
219
261
|
}
|
|
262
|
+
if (method === 'network.export_har') {
|
|
263
|
+
const delivery = result.delivery === 'artifact' ? 'artifact' : 'inline';
|
|
264
|
+
const entryCount = typeof result.entryCount === 'number' ? result.entryCount : 0;
|
|
265
|
+
const totalEntries = typeof result.totalEntries === 'number' ? result.totalEntries : entryCount;
|
|
266
|
+
const dropped = typeof result.dropped === 'number' ? result.dropped : 0;
|
|
267
|
+
const abandoned = typeof result.abandoned === 'number' ? result.abandoned : 0;
|
|
268
|
+
const inflight = typeof result.inflight === 'number' ? result.inflight : 0;
|
|
269
|
+
return {
|
|
270
|
+
ok: true,
|
|
271
|
+
summary: appendProtocolWarning(
|
|
272
|
+
`HAR export delivered ${delivery}: ${entryCount}/${totalEntries} entries (${dropped} dropped, ${abandoned} abandoned, ${inflight} inflight).`,
|
|
273
|
+
protocolWarning
|
|
274
|
+
),
|
|
275
|
+
evidence: /** @type {import('./types.js').HarExportEvidence} */ ({
|
|
276
|
+
delivery,
|
|
277
|
+
entryCount,
|
|
278
|
+
totalEntries,
|
|
279
|
+
dropped,
|
|
280
|
+
abandoned,
|
|
281
|
+
inflight,
|
|
282
|
+
}),
|
|
283
|
+
};
|
|
284
|
+
}
|
|
220
285
|
if (typeof result.daemon === 'string') {
|
|
221
286
|
const access =
|
|
222
287
|
result.access && typeof result.access === 'object'
|
|
@@ -545,6 +610,43 @@ export function summarizeBridgeResponse(response, method) {
|
|
|
545
610
|
})),
|
|
546
611
|
};
|
|
547
612
|
}
|
|
613
|
+
if (
|
|
614
|
+
result.exact === true &&
|
|
615
|
+
typeof result.source === 'string' &&
|
|
616
|
+
typeof result.value === 'string'
|
|
617
|
+
) {
|
|
618
|
+
return {
|
|
619
|
+
ok: true,
|
|
620
|
+
summary: appendProtocolWarning('Sensitive storage value read exactly.', protocolWarning),
|
|
621
|
+
evidence: { source: result.source, exact: true },
|
|
622
|
+
};
|
|
623
|
+
}
|
|
624
|
+
if (typeof result.count === 'number' && typeof result.type === 'string' && result.entries) {
|
|
625
|
+
const entries = Array.isArray(result.entries)
|
|
626
|
+
? result.entries.map((entry) => {
|
|
627
|
+
const candidate =
|
|
628
|
+
entry && typeof entry === 'object'
|
|
629
|
+
? /** @type {Record<string, unknown>} */ (entry)
|
|
630
|
+
: {};
|
|
631
|
+
return {
|
|
632
|
+
key: typeof candidate.key === 'string' ? candidate.key : '',
|
|
633
|
+
present: candidate.present === true,
|
|
634
|
+
};
|
|
635
|
+
})
|
|
636
|
+
: [];
|
|
637
|
+
return {
|
|
638
|
+
ok: true,
|
|
639
|
+
summary: appendProtocolWarning(
|
|
640
|
+
`Storage (${result.type}): ${result.count} entries.`,
|
|
641
|
+
protocolWarning
|
|
642
|
+
),
|
|
643
|
+
evidence: {
|
|
644
|
+
entries,
|
|
645
|
+
total: typeof result.total === 'number' ? result.total : result.count,
|
|
646
|
+
truncated: result.truncated === true,
|
|
647
|
+
},
|
|
648
|
+
};
|
|
649
|
+
}
|
|
548
650
|
if (Array.isArray(result.entries)) {
|
|
549
651
|
const consoleEntries = /** @type {Array<Record<string, unknown>>} */ (result.entries);
|
|
550
652
|
return {
|
|
@@ -631,30 +733,18 @@ export function summarizeBridgeResponse(response, method) {
|
|
|
631
733
|
}
|
|
632
734
|
if (typeof result.metrics === 'object' && result.metrics !== null) {
|
|
633
735
|
const keys = Object.keys(result.metrics);
|
|
736
|
+
const measurement = toRecord(result.measurement);
|
|
737
|
+
const rawCdpCounters =
|
|
738
|
+
method === 'performance.get_metrics' || measurement.kind === 'raw_cdp_counters';
|
|
634
739
|
return {
|
|
635
740
|
ok: true,
|
|
636
741
|
summary: appendProtocolWarning(
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
evidence: result.metrics,
|
|
641
|
-
};
|
|
642
|
-
}
|
|
643
|
-
if (typeof result.count === 'number' && typeof result.type === 'string' && result.entries) {
|
|
644
|
-
const entries = /** @type {Record<string, unknown>} */ (result.entries);
|
|
645
|
-
/** @type {Record<string, string>} */
|
|
646
|
-
const safe = {};
|
|
647
|
-
for (const [k, v] of Object.entries(entries)) {
|
|
648
|
-
const s = typeof v === 'string' ? v : JSON.stringify(v);
|
|
649
|
-
safe[k] = s.length > 80 ? `${s.slice(0, 79)}…` : s;
|
|
650
|
-
}
|
|
651
|
-
return {
|
|
652
|
-
ok: true,
|
|
653
|
-
summary: appendProtocolWarning(
|
|
654
|
-
`Storage (${result.type}): ${result.count} entries.`,
|
|
742
|
+
rawCdpCounters
|
|
743
|
+
? `Raw CDP performance counters: ${keys.length} collected in a browser-maintained point sample; Web Vitals not measured.`
|
|
744
|
+
: `Performance: ${keys.length} metrics collected.`,
|
|
655
745
|
protocolWarning
|
|
656
746
|
),
|
|
657
|
-
evidence:
|
|
747
|
+
evidence: rawCdpCounters ? { metrics: result.metrics, measurement } : result.metrics,
|
|
658
748
|
};
|
|
659
749
|
}
|
|
660
750
|
if (
|
|
@@ -873,15 +963,16 @@ function appendProtocolWarning(summary, warning) {
|
|
|
873
963
|
* @returns {string}
|
|
874
964
|
*/
|
|
875
965
|
function truncateUrl(url) {
|
|
876
|
-
|
|
966
|
+
const sanitized = sanitizeIncidentalUrl(url);
|
|
967
|
+
if (!sanitized || sanitized.length <= 120) return sanitized;
|
|
877
968
|
try {
|
|
878
|
-
const u = new URL(
|
|
969
|
+
const u = new URL(sanitized);
|
|
879
970
|
const base = `${u.origin}${u.pathname}`;
|
|
880
971
|
if (base.length > 120) return `${base.slice(0, 119)}\u2026`;
|
|
881
972
|
if (u.search) return `${base}?…`;
|
|
882
973
|
return base;
|
|
883
974
|
} catch {
|
|
884
|
-
return
|
|
975
|
+
return sanitized.length > 120 ? `${sanitized.slice(0, 119)}\u2026` : sanitized;
|
|
885
976
|
}
|
|
886
977
|
}
|
|
887
978
|
|