@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.
Files changed (39) hide show
  1. package/PRIVACY.md +29 -3
  2. package/README.md +10 -3
  3. package/SECURITY.md +12 -0
  4. package/package.json +7 -1
  5. package/packages/agent-client/src/cli-args.js +35 -0
  6. package/packages/agent-client/src/cli-batch.js +121 -58
  7. package/packages/agent-client/src/cli-output.js +8 -1
  8. package/packages/agent-client/src/cli.js +227 -19
  9. package/packages/agent-client/src/client.js +156 -3
  10. package/packages/agent-client/src/command-registry.js +8 -2
  11. package/packages/agent-client/src/config.js +195 -0
  12. package/packages/agent-client/src/npm-self-update.js +305 -0
  13. package/packages/agent-client/src/remotes.js +4 -2
  14. package/packages/agent-client/src/runtime.js +97 -5
  15. package/packages/agent-client/src/types.ts +38 -0
  16. package/packages/mcp-server/src/handlers-capture.js +29 -2
  17. package/packages/mcp-server/src/handlers-dom.js +54 -3
  18. package/packages/mcp-server/src/handlers-page.js +180 -60
  19. package/packages/mcp-server/src/handlers-utils.js +35 -4
  20. package/packages/mcp-server/src/handlers.js +10 -3
  21. package/packages/mcp-server/src/server.js +75 -6
  22. package/packages/native-host/src/artifact-store.js +353 -0
  23. package/packages/native-host/src/config.js +9 -1
  24. package/packages/native-host/src/daemon-logger.js +3 -1
  25. package/packages/native-host/src/daemon.js +543 -27
  26. package/packages/native-host/src/native-host.js +25 -2
  27. package/packages/protocol/src/capability-values.js +2 -0
  28. package/packages/protocol/src/defaults.js +28 -0
  29. package/packages/protocol/src/errors.js +48 -5
  30. package/packages/protocol/src/incidental-sanitizer.js +197 -0
  31. package/packages/protocol/src/index.js +2 -0
  32. package/packages/protocol/src/protocol.js +316 -6
  33. package/packages/protocol/src/recovery-telemetry.js +302 -0
  34. package/packages/protocol/src/registry.js +143 -6
  35. package/packages/protocol/src/summary.js +116 -25
  36. package/packages/protocol/src/types.ts +489 -4
  37. package/skills/browser-bridge/SKILL.md +22 -8
  38. package/skills/browser-bridge/references/protocol.md +117 -13
  39. 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 = metaTransportTokens ?? transportCost?.approxTokens ?? 0;
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
- `Performance: ${keys.length} metrics collected.`,
638
- protocolWarning
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: safe,
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
- if (!url || url.length <= 120) return url;
966
+ const sanitized = sanitizeIncidentalUrl(url);
967
+ if (!sanitized || sanitized.length <= 120) return sanitized;
877
968
  try {
878
- const u = new URL(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 url.length > 120 ? `${url.slice(0, 119)}\u2026` : url;
975
+ return sanitized.length > 120 ? `${sanitized.slice(0, 119)}\u2026` : sanitized;
885
976
  }
886
977
  }
887
978