@browserbridge/bbx 1.0.1 → 1.2.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 (70) hide show
  1. package/README.md +4 -4
  2. package/package.json +11 -13
  3. package/packages/agent-client/src/cli-helpers.js +33 -0
  4. package/packages/agent-client/src/cli.js +122 -45
  5. package/packages/agent-client/src/client.js +134 -8
  6. package/packages/agent-client/src/command-registry.js +4 -1
  7. package/packages/agent-client/src/detect.js +159 -48
  8. package/packages/agent-client/src/install.js +24 -1
  9. package/packages/agent-client/src/mcp-config.js +29 -10
  10. package/packages/agent-client/src/setup-status.js +12 -4
  11. package/packages/mcp-server/src/bin.js +57 -5
  12. package/packages/mcp-server/src/handlers-capture.js +279 -0
  13. package/packages/mcp-server/src/handlers-dom.js +196 -0
  14. package/packages/mcp-server/src/handlers-navigation.js +79 -0
  15. package/packages/mcp-server/src/handlers-page.js +365 -0
  16. package/packages/mcp-server/src/handlers-utils.js +296 -0
  17. package/packages/mcp-server/src/handlers.js +63 -1159
  18. package/packages/mcp-server/src/server.js +13 -3
  19. package/packages/native-host/bin/bridge-daemon.js +34 -4
  20. package/packages/native-host/bin/install-manifest.js +32 -2
  21. package/packages/native-host/bin/postinstall.js +16 -0
  22. package/packages/native-host/src/config.js +131 -6
  23. package/packages/native-host/src/daemon-logger.js +157 -0
  24. package/packages/native-host/src/daemon-process.js +422 -0
  25. package/packages/native-host/src/daemon.js +322 -77
  26. package/packages/native-host/src/framing.js +131 -11
  27. package/packages/native-host/src/install-manifest.js +121 -7
  28. package/packages/native-host/src/native-host.js +110 -73
  29. package/packages/protocol/src/capabilities.js +4 -0
  30. package/packages/protocol/src/defaults.js +1 -0
  31. package/packages/protocol/src/errors.js +4 -0
  32. package/packages/protocol/src/payload-cost.js +19 -6
  33. package/packages/protocol/src/protocol.js +143 -7
  34. package/packages/protocol/src/registry.js +13 -0
  35. package/packages/protocol/src/summary.js +18 -10
  36. package/packages/protocol/src/types.js +28 -3
  37. package/skills/browser-bridge/SKILL.md +2 -1
  38. package/skills/browser-bridge/references/interaction.md +1 -0
  39. package/skills/browser-bridge/references/protocol.md +2 -1
  40. package/CHANGELOG.md +0 -55
  41. package/assets/banner.jpg +0 -0
  42. package/assets/logo.png +0 -0
  43. package/assets/logo.svg +0 -65
  44. package/docs/api-reference.md +0 -157
  45. package/docs/cli-guide.md +0 -128
  46. package/docs/index.md +0 -25
  47. package/docs/manual-setup.md +0 -140
  48. package/docs/mcp-vs-cli.md +0 -258
  49. package/docs/publishing.md +0 -112
  50. package/docs/quickstart.md +0 -104
  51. package/docs/troubleshooting.md +0 -59
  52. package/docs/unpacked-extension.md +0 -72
  53. package/docs/usage-scenarios.md +0 -136
  54. package/manifest.json +0 -38
  55. package/packages/extension/assets/icon-128.png +0 -0
  56. package/packages/extension/assets/icon-16.png +0 -0
  57. package/packages/extension/assets/icon-32.png +0 -0
  58. package/packages/extension/assets/icon-48.png +0 -0
  59. package/packages/extension/src/background-helpers.js +0 -474
  60. package/packages/extension/src/background-routing.js +0 -89
  61. package/packages/extension/src/background.js +0 -3490
  62. package/packages/extension/src/content-script-helpers.js +0 -282
  63. package/packages/extension/src/content-script.js +0 -2043
  64. package/packages/extension/src/debugger-coordinator.js +0 -188
  65. package/packages/extension/src/sidepanel-helpers.js +0 -104
  66. package/packages/extension/ui/popup.html +0 -35
  67. package/packages/extension/ui/popup.js +0 -298
  68. package/packages/extension/ui/sidepanel.html +0 -102
  69. package/packages/extension/ui/sidepanel.js +0 -1771
  70. package/packages/extension/ui/ui.css +0 -1160
@@ -22,7 +22,9 @@ export function getUtf8ByteLength(value) {
22
22
  if (!value) {
23
23
  return 0;
24
24
  }
25
- return textEncoder.encode(value).length;
25
+ return typeof Buffer !== 'undefined'
26
+ ? Buffer.byteLength(value, 'utf8')
27
+ : textEncoder.encode(value).length;
26
28
  }
27
29
 
28
30
  /**
@@ -45,14 +47,25 @@ export function estimateSerializedPayloadCost(serialized) {
45
47
  }
46
48
 
47
49
  /**
48
- * Estimate cost for a JSON-serializable payload.
50
+ * Serialize a JSON payload for transport-oriented cost estimation.
49
51
  *
50
52
  * @param {unknown} value
51
- * @returns {PayloadCost}
53
+ * @returns {string}
52
54
  */
53
- export function estimateJsonPayloadCost(value) {
55
+ export function serializeJsonPayload(value) {
54
56
  if (typeof value === 'undefined') {
55
- return estimateSerializedPayloadCost('');
57
+ return '';
56
58
  }
57
- return estimateSerializedPayloadCost(JSON.stringify(value) ?? '');
59
+ return JSON.stringify(value) ?? '';
60
+ }
61
+
62
+ /**
63
+ * Estimate cost for a JSON-serializable payload.
64
+ *
65
+ * @param {unknown} value
66
+ * @param {string} [serialized]
67
+ * @returns {PayloadCost}
68
+ */
69
+ export function estimateJsonPayloadCost(value, serialized = serializeJsonPayload(value)) {
70
+ return estimateSerializedPayloadCost(serialized);
58
71
  }
@@ -19,7 +19,7 @@ import {
19
19
  DEFAULT_WAIT_TIMEOUT_MS,
20
20
  } from './defaults.js';
21
21
  import { BridgeError, ERROR_CODES, getErrorRecovery } from './errors.js';
22
- import { BRIDGE_METHODS, createBridgeMethodGroups } from './registry.js';
22
+ import { BRIDGE_METHODS, METHOD_SET, createBridgeMethodGroups } from './registry.js';
23
23
 
24
24
  /** @typedef {import('./types.js').AccessibilityTreeParams} AccessibilityTreeParams */
25
25
  /** @typedef {import('./types.js').BridgeFailureResponse} BridgeFailureResponse */
@@ -28,6 +28,7 @@ import { BRIDGE_METHODS, createBridgeMethodGroups } from './registry.js';
28
28
  /** @typedef {import('./types.js').BridgeRequest} BridgeRequest */
29
29
  /** @typedef {import('./types.js').BridgeSuccessResponse} BridgeSuccessResponse */
30
30
  /** @typedef {import('./types.js').CheckedActionParams} CheckedActionParams */
31
+ /** @typedef {import('./types.js').CdpDispatchKeyEventParams} CdpDispatchKeyEventParams */
31
32
  /** @typedef {import('./types.js').ConsoleParams} ConsoleParams */
32
33
  /** @typedef {import('./types.js').DomQueryParams} DomQueryParams */
33
34
  /** @typedef {import('./types.js').DragParams} DragParams */
@@ -41,6 +42,7 @@ import { BRIDGE_METHODS, createBridgeMethodGroups } from './registry.js';
41
42
  /** @typedef {import('./types.js').NetworkParams} NetworkParams */
42
43
  /** @typedef {import('./types.js').NormalizedAccessibilityTreeParams} NormalizedAccessibilityTreeParams */
43
44
  /** @typedef {import('./types.js').NormalizedCheckedAction} NormalizedCheckedAction */
45
+ /** @typedef {import('./types.js').NormalizedCdpDispatchKeyEventParams} NormalizedCdpDispatchKeyEventParams */
44
46
  /** @typedef {import('./types.js').NormalizedConsoleParams} NormalizedConsoleParams */
45
47
  /** @typedef {import('./types.js').NormalizedDomQuery} NormalizedDomQuery */
46
48
  /** @typedef {import('./types.js').NormalizedDragParams} NormalizedDragParams */
@@ -182,7 +184,7 @@ export function validateBridgeRequest(request) {
182
184
 
183
185
  if (
184
186
  typeof candidate.method !== 'string' ||
185
- !METHODS.includes(/** @type {BridgeMethod} */ (candidate.method))
187
+ !METHOD_SET.has(/** @type {BridgeMethod} */ (candidate.method))
186
188
  ) {
187
189
  throw new BridgeError(
188
190
  ERROR_CODES.INVALID_REQUEST,
@@ -190,6 +192,13 @@ export function validateBridgeRequest(request) {
190
192
  );
191
193
  }
192
194
 
195
+ if (
196
+ candidate.meta != null &&
197
+ (typeof candidate.meta !== 'object' || Array.isArray(candidate.meta))
198
+ ) {
199
+ throw new BridgeError(ERROR_CODES.INVALID_REQUEST, 'Request meta must be an object.');
200
+ }
201
+
193
202
  const meta =
194
203
  candidate.meta && typeof candidate.meta === 'object'
195
204
  ? /** @type {Record<string, unknown>} */ (candidate.meta)
@@ -202,14 +211,17 @@ export function validateBridgeRequest(request) {
202
211
  }
203
212
  const parsedTabId = Number(candidate.tab_id);
204
213
 
214
+ const method = /** @type {BridgeMethod} */ (candidate.method);
215
+ const params =
216
+ candidate.params && typeof candidate.params === 'object'
217
+ ? /** @type {Record<string, unknown>} */ (candidate.params)
218
+ : {};
219
+
205
220
  return {
206
221
  id: candidate.id,
207
- method: /** @type {BridgeMethod} */ (candidate.method),
222
+ method,
208
223
  tab_id: Number.isFinite(parsedTabId) && parsedTabId > 0 ? parsedTabId : null,
209
- params:
210
- candidate.params && typeof candidate.params === 'object'
211
- ? /** @type {Record<string, unknown>} */ (candidate.params)
212
- : {},
224
+ params: normalizeRequestParams(method, params),
213
225
  meta: {
214
226
  ...meta,
215
227
  protocol_version:
@@ -220,6 +232,82 @@ export function validateBridgeRequest(request) {
220
232
  };
221
233
  }
222
234
 
235
+ /**
236
+ * Normalize request params early so malformed payloads fail at the shared
237
+ * protocol boundary before daemon or extension dispatch.
238
+ *
239
+ * @param {BridgeMethod} method
240
+ * @param {Record<string, unknown>} params
241
+ * @returns {Record<string, unknown>}
242
+ */
243
+ function normalizeRequestParams(method, params) {
244
+ switch (method) {
245
+ case 'dom.query':
246
+ return normalizeDomQuery(params);
247
+ case 'page.evaluate':
248
+ return normalizeEvaluateParams(params);
249
+ case 'page.get_console':
250
+ return normalizeConsoleParams(params);
251
+ case 'page.wait_for_load_state':
252
+ return normalizeWaitForLoadStateParams(params);
253
+ case 'page.get_storage':
254
+ return normalizeStorageParams(params);
255
+ case 'page.get_text':
256
+ return normalizePageTextParams(params);
257
+ case 'page.get_network':
258
+ return normalizeNetworkParams(params);
259
+ case 'navigation.navigate':
260
+ case 'navigation.reload':
261
+ case 'navigation.go_back':
262
+ case 'navigation.go_forward':
263
+ return normalizeNavigationAction(params);
264
+ case 'dom.wait_for':
265
+ return normalizeWaitForParams(params);
266
+ case 'dom.find_by_text':
267
+ return normalizeFindByTextParams(params);
268
+ case 'dom.find_by_role':
269
+ return normalizeFindByRoleParams(params);
270
+ case 'dom.get_html':
271
+ return normalizeGetHtmlParams(params);
272
+ case 'dom.get_accessibility_tree':
273
+ return normalizeAccessibilityTreeParams(params);
274
+ case 'styles.get_computed':
275
+ case 'styles.get_matched_rules':
276
+ return normalizeStyleQuery(params);
277
+ case 'viewport.scroll':
278
+ return normalizeViewportAction(params);
279
+ case 'viewport.resize':
280
+ return normalizeViewportResizeParams(params);
281
+ case 'input.click':
282
+ case 'input.focus':
283
+ case 'input.type':
284
+ case 'input.press_key':
285
+ return normalizeInputAction(params);
286
+ case 'cdp.dispatch_key_event':
287
+ return normalizeCdpDispatchKeyEventParams(params);
288
+ case 'input.set_checked':
289
+ return normalizeCheckedAction(params);
290
+ case 'input.select_option':
291
+ return normalizeSelectAction(params);
292
+ case 'input.hover':
293
+ return normalizeHoverParams(params);
294
+ case 'input.drag':
295
+ return normalizeDragParams(params);
296
+ case 'patch.apply_styles':
297
+ case 'patch.apply_dom':
298
+ case 'patch.list':
299
+ case 'patch.rollback':
300
+ case 'patch.commit_session_baseline':
301
+ return normalizePatchOperation(params);
302
+ case 'tabs.create':
303
+ return normalizeTabCreateParams(params);
304
+ case 'tabs.close':
305
+ return normalizeTabCloseParams(params);
306
+ default:
307
+ return params;
308
+ }
309
+ }
310
+
223
311
  /**
224
312
  * @param {DomQueryParams} [params={}]
225
313
  * @returns {NormalizedDomQuery}
@@ -293,6 +381,54 @@ export function normalizeInputAction(params = {}) {
293
381
  };
294
382
  }
295
383
 
384
+ /**
385
+ * @param {CdpDispatchKeyEventParams} [params={}]
386
+ * @returns {NormalizedCdpDispatchKeyEventParams}
387
+ */
388
+ export function normalizeCdpDispatchKeyEventParams(params = {}) {
389
+ if (typeof params.key !== 'string' || !params.key.trim()) {
390
+ throw new BridgeError(ERROR_CODES.INVALID_REQUEST, 'key must be a non-empty string.');
391
+ }
392
+
393
+ if (
394
+ params.modifiers != null &&
395
+ typeof params.modifiers !== 'number' &&
396
+ !Array.isArray(params.modifiers)
397
+ ) {
398
+ throw new BridgeError(
399
+ ERROR_CODES.INVALID_REQUEST,
400
+ 'modifiers must be an array of Alt, Control, Meta, Shift or a bitmask 0-15.'
401
+ );
402
+ }
403
+
404
+ if (
405
+ typeof params.modifiers === 'number' &&
406
+ (!Number.isInteger(params.modifiers) || params.modifiers < 0 || params.modifiers > 15)
407
+ ) {
408
+ throw new BridgeError(
409
+ ERROR_CODES.INVALID_REQUEST,
410
+ 'modifiers must be an array of Alt, Control, Meta, Shift or a bitmask 0-15.'
411
+ );
412
+ }
413
+
414
+ if (Array.isArray(params.modifiers)) {
415
+ for (const modifier of params.modifiers) {
416
+ if (typeof modifier !== 'string' || !['Alt', 'Control', 'Meta', 'Shift'].includes(modifier)) {
417
+ throw new BridgeError(
418
+ ERROR_CODES.INVALID_REQUEST,
419
+ 'modifiers must contain only Alt, Control, Meta, or Shift.'
420
+ );
421
+ }
422
+ }
423
+ }
424
+
425
+ return {
426
+ key: params.key,
427
+ code: typeof params.code === 'string' ? params.code.trim() : '',
428
+ modifiers: params.modifiers ?? [],
429
+ };
430
+ }
431
+
296
432
  /**
297
433
  * @param {CheckedActionParams} [params={}]
298
434
  * @returns {NormalizedCheckedAction}
@@ -78,9 +78,11 @@ const BRIDGE_METHOD_DESCRIPTIONS = Object.freeze({
78
78
  'cdp.get_dom_snapshot': 'Read a CDP DOM snapshot.',
79
79
  'cdp.get_box_model': 'Read a CDP box model for a node.',
80
80
  'cdp.get_computed_styles_for_node': 'Read CDP computed styles for a node.',
81
+ 'cdp.dispatch_key_event': 'Dispatch a key press through Chrome DevTools Protocol input.',
81
82
  'performance.get_metrics': 'Read browser performance metrics.',
82
83
  'log.tail': 'Tail recent bridge log entries.',
83
84
  'health.ping': 'Check daemon, extension, and access-routing health.',
85
+ 'daemon.metrics': 'Daemon health and performance metrics.',
84
86
  });
85
87
 
86
88
  /**
@@ -123,6 +125,7 @@ export const BRIDGE_METHOD_REGISTRY = Object.freeze({
123
125
  ),
124
126
  'log.tail': createRegistryEntry('log.tail', 'system', false, [], 'trivial'),
125
127
  'health.ping': createRegistryEntry('health.ping', 'system', false, [], 'trivial'),
128
+ 'daemon.metrics': createRegistryEntry('daemon.metrics', 'system', false, [], 'trivial'),
126
129
  // tabs — trivial
127
130
  'tabs.list': createRegistryEntry('tabs.list', 'tabs', false, [], 'trivial'),
128
131
  'tabs.create': createRegistryEntry('tabs.create', 'tabs', false, ['url', 'active'], 'trivial'),
@@ -424,6 +427,13 @@ export const BRIDGE_METHOD_REGISTRY = Object.freeze({
424
427
  ['elementRef'],
425
428
  'high'
426
429
  ),
430
+ 'cdp.dispatch_key_event': createRegistryEntry(
431
+ 'cdp.dispatch_key_event',
432
+ 'cdp',
433
+ true,
434
+ ['key', 'code', 'modifiers'],
435
+ 'moderate'
436
+ ),
427
437
  // performance — moderate (debugger-backed)
428
438
  'performance.get_metrics': createRegistryEntry(
429
439
  'performance.get_metrics',
@@ -439,6 +449,9 @@ export const BRIDGE_METHODS = Object.freeze(
439
449
  /** @type {import('./types.js').BridgeMethod[]} */ (Object.keys(BRIDGE_METHOD_REGISTRY))
440
450
  );
441
451
 
452
+ /** @type {ReadonlySet<import('./types.js').BridgeMethod>} */
453
+ export const METHOD_SET = new Set(BRIDGE_METHODS);
454
+
442
455
  /**
443
456
  * @returns {Record<string, import('./types.js').BridgeMethod[]>}
444
457
  */
@@ -1,6 +1,6 @@
1
1
  // @ts-check
2
2
 
3
- import { estimateJsonPayloadCost, getCostClass } from './index.js';
3
+ import { estimateSerializedPayloadCost, getCostClass, serializeJsonPayload } from './index.js';
4
4
 
5
5
  /** @typedef {import('./types.js').BridgeResponse} BridgeResponse */
6
6
  /** @typedef {import('./types.js').BridgeMethod} SummaryMethod */
@@ -99,15 +99,21 @@ const ACTION_SUMMARIES = {
99
99
  * @returns {AnnotatedBridgeSummary}
100
100
  */
101
101
  export function annotateBridgeSummary(summary, response) {
102
- const transportBytes =
102
+ const metaTransportBytes =
103
103
  getNumericMetaField(response.meta, 'transport_bytes') ??
104
- getNumericMetaField(response.meta, 'response_bytes') ??
105
- estimateJsonPayloadCost(response.ok ? response.result : { error: response.error }).bytes;
106
- const transportTokens =
104
+ getNumericMetaField(response.meta, 'response_bytes');
105
+ const metaTransportTokens =
107
106
  getNumericMetaField(response.meta, 'transport_approx_tokens') ??
108
- getNumericMetaField(response.meta, 'approx_tokens') ??
109
- estimateJsonPayloadCost(response.ok ? response.result : { error: response.error }).approxTokens;
110
- const summaryCost = estimateJsonPayloadCost(summary);
107
+ getNumericMetaField(response.meta, 'approx_tokens');
108
+ const transportCost =
109
+ metaTransportBytes == null || metaTransportTokens == null
110
+ ? estimateSerializedPayloadCost(
111
+ serializeJsonPayload(response.ok ? response.result : { error: response.error })
112
+ )
113
+ : null;
114
+ const transportBytes = metaTransportBytes ?? transportCost?.bytes ?? 0;
115
+ const transportTokens = metaTransportTokens ?? transportCost?.approxTokens ?? 0;
116
+ const summaryCost = estimateSerializedPayloadCost(serializeJsonPayload(summary));
111
117
 
112
118
  return {
113
119
  ...summary,
@@ -659,13 +665,12 @@ export function summarizeBridgeResponse(response, method) {
659
665
  */
660
666
  export function summarizeBatchResponseItem({ method, tabId, response, durationMs }) {
661
667
  const summary = annotateBridgeSummary(summarizeBridgeResponse(response, method), response);
662
- const cost = estimateJsonPayloadCost(response.ok ? response.result : { error: response.error });
663
668
  return {
664
669
  method,
665
670
  tabId,
666
671
  ...summary,
667
672
  durationMs,
668
- approxTokens: cost.approxTokens,
673
+ approxTokens: summary.transportTokens,
669
674
  meta: response.meta,
670
675
  error: response.ok ? null : response.error,
671
676
  response: response.ok ? response.result : null,
@@ -756,6 +761,9 @@ function summarizeErrorHint(code) {
756
761
  if (code === 'ELEMENT_STALE') {
757
762
  return 'Re-query the current page after navigation or DOM updates.';
758
763
  }
764
+ if (code === 'RESULT_TRUNCATED') {
765
+ return 'Narrow the query or raise the relevant budget if more detail is required.';
766
+ }
759
767
  return '';
760
768
  }
761
769
 
@@ -7,7 +7,7 @@ export {};
7
7
  */
8
8
 
9
9
  /**
10
- * @typedef {'page.read' | 'page.evaluate' | 'dom.read' | 'styles.read' | 'layout.read' | 'viewport.control' | 'navigation.control' | 'screenshot.partial' | 'patch.dom' | 'patch.styles' | 'cdp.dom_snapshot' | 'cdp.box_model' | 'cdp.styles' | 'automation.input' | 'tabs.manage' | 'performance.read' | 'network.read'} Capability
10
+ * @typedef {'page.read' | 'page.evaluate' | 'dom.read' | 'styles.read' | 'layout.read' | 'viewport.control' | 'navigation.control' | 'screenshot.partial' | 'patch.dom' | 'patch.styles' | 'cdp.dom_snapshot' | 'cdp.box_model' | 'cdp.styles' | 'cdp.input' | 'automation.input' | 'tabs.manage' | 'performance.read' | 'network.read'} Capability
11
11
  */
12
12
 
13
13
  /**
@@ -15,7 +15,7 @@ export {};
15
15
  */
16
16
 
17
17
  /**
18
- * @typedef {'access.request' | 'tabs.list' | 'tabs.create' | 'tabs.close' | 'skill.get_runtime_context' | 'setup.get_status' | 'setup.install' | 'page.get_state' | 'page.evaluate' | 'page.get_console' | 'page.wait_for_load_state' | 'page.get_storage' | 'page.get_text' | 'page.get_network' | 'navigation.navigate' | 'navigation.reload' | 'navigation.go_back' | 'navigation.go_forward' | 'dom.query' | 'dom.describe' | 'dom.get_text' | 'dom.get_attributes' | 'dom.wait_for' | 'dom.find_by_text' | 'dom.find_by_role' | 'dom.get_html' | 'dom.get_accessibility_tree' | 'layout.get_box_model' | 'layout.hit_test' | 'styles.get_computed' | 'styles.get_matched_rules' | 'viewport.scroll' | 'viewport.resize' | 'input.click' | 'input.focus' | 'input.type' | 'input.press_key' | 'input.set_checked' | 'input.select_option' | 'input.hover' | 'input.drag' | 'input.scroll_into_view' | 'screenshot.capture_region' | 'screenshot.capture_element' | 'screenshot.capture_full_page' | 'patch.apply_styles' | 'patch.apply_dom' | 'patch.list' | 'patch.rollback' | 'patch.commit_session_baseline' | 'cdp.get_document' | 'cdp.get_dom_snapshot' | 'cdp.get_box_model' | 'cdp.get_computed_styles_for_node' | 'performance.get_metrics' | 'log.tail' | 'health.ping'} BridgeMethod
18
+ * @typedef {'access.request' | 'tabs.list' | 'tabs.create' | 'tabs.close' | 'skill.get_runtime_context' | 'setup.get_status' | 'setup.install' | 'page.get_state' | 'page.evaluate' | 'page.get_console' | 'page.wait_for_load_state' | 'page.get_storage' | 'page.get_text' | 'page.get_network' | 'navigation.navigate' | 'navigation.reload' | 'navigation.go_back' | 'navigation.go_forward' | 'dom.query' | 'dom.describe' | 'dom.get_text' | 'dom.get_attributes' | 'dom.wait_for' | 'dom.find_by_text' | 'dom.find_by_role' | 'dom.get_html' | 'dom.get_accessibility_tree' | 'layout.get_box_model' | 'layout.hit_test' | 'styles.get_computed' | 'styles.get_matched_rules' | 'viewport.scroll' | 'viewport.resize' | 'input.click' | 'input.focus' | 'input.type' | 'input.press_key' | 'input.set_checked' | 'input.select_option' | 'input.hover' | 'input.drag' | 'input.scroll_into_view' | 'screenshot.capture_region' | 'screenshot.capture_element' | 'screenshot.capture_full_page' | 'patch.apply_styles' | 'patch.apply_dom' | 'patch.list' | 'patch.rollback' | 'patch.commit_session_baseline' | 'cdp.get_document' | 'cdp.get_dom_snapshot' | 'cdp.get_box_model' | 'cdp.get_computed_styles_for_node' | 'cdp.dispatch_key_event' | 'performance.get_metrics' | 'log.tail' | 'health.ping' | 'daemon.metrics'} BridgeMethod
19
19
  */
20
20
 
21
21
  /**
@@ -52,11 +52,20 @@ export {};
52
52
  * }} BridgeRequest
53
53
  */
54
54
 
55
+ /**
56
+ * @typedef {{
57
+ * hint: string,
58
+ * retry?: boolean,
59
+ * retryAfterMs?: number
60
+ * }} BridgeRecovery
61
+ */
62
+
55
63
  /**
56
64
  * @typedef {{
57
65
  * code: ErrorCode,
58
66
  * message: string,
59
- * details: unknown
67
+ * details: unknown,
68
+ * recovery?: BridgeRecovery
60
69
  * }} BridgeFailure
61
70
  */
62
71
 
@@ -187,6 +196,22 @@ export {};
187
196
  * }} NormalizedInputAction
188
197
  */
189
198
 
199
+ /**
200
+ * @typedef {{
201
+ * key?: string,
202
+ * code?: string,
203
+ * modifiers?: string[] | number
204
+ * }} CdpDispatchKeyEventParams
205
+ */
206
+
207
+ /**
208
+ * @typedef {{
209
+ * key: string,
210
+ * code: string,
211
+ * modifiers: string[] | number
212
+ * }} NormalizedCdpDispatchKeyEventParams
213
+ */
214
+
190
215
  /**
191
216
  * @typedef {{
192
217
  * target?: InputTarget,
@@ -69,6 +69,7 @@ bbx click <ref> [button] # click element
69
69
  bbx focus <ref> # focus element
70
70
  bbx type <ref> <text...> # type into element
71
71
  bbx press-key <key> [ref] # send key event
72
+ bbx cdp-press-key --tab <id> Escape # CDP key event without foreground focus
72
73
  bbx hover <ref> # hover over element
73
74
  bbx call input.scroll_into_view '{"target":{"elementRef":"el_123"}}' # ensure target is visible
74
75
  bbx patch-style <ref> prop=val... # apply style patch
@@ -202,7 +203,7 @@ bbx page-text 2000 # extract page content
202
203
  | Find | `dom.find_by_text`, `dom.find_by_role`, `dom.wait_for`, `dom.get_accessibility_tree` |
203
204
  | Page State | `page.get_console`, `page.get_storage`, `page.get_text`, `page.wait_for_load_state`, `page.evaluate` (debugger-backed) |
204
205
  | Network | `page.get_network` |
205
- | Interact | `input.click`, `input.type`, `input.focus`, `input.press_key`, `input.hover`, `input.drag`, `input.scroll_into_view` |
206
+ | Interact | `input.click`, `input.type`, `input.focus`, `input.press_key`, `cdp.dispatch_key_event`, `input.hover`, `input.drag`, `input.scroll_into_view` |
206
207
  | Tabs | `tabs.list` (preferred), `tabs.create` (avoid unless necessary), `tabs.close` |
207
208
  | Patch | `patch.apply_styles`, `patch.apply_dom`, `patch.rollback` |
208
209
  | Navigate | `navigation.navigate`, `viewport.scroll`, `viewport.resize` |
@@ -8,6 +8,7 @@
8
8
  | `input.focus` | `focus <ref>` | Focus an element |
9
9
  | `input.type` | `type <ref> <text>` | Type into input/textarea/contenteditable |
10
10
  | `input.press_key` | `press-key <key> [ref]` | Send keyboard key (Enter, Backspace, etc.) |
11
+ | `cdp.dispatch_key_event` | `cdp-press-key --tab <id> <key>` | CDP keyDown/keyUp without focusing the target tab |
11
12
  | `input.set_checked` | `call input.set_checked '{...}'` | Toggle checkbox/radio |
12
13
  | `input.select_option` | `call input.select_option '{...}'` | Select native `<select>` by value/label/index |
13
14
  | `input.hover` | `hover <ref>` | Trigger CSS `:hover` state (mouseenter/mouseover/mousemove) |
@@ -88,6 +88,7 @@ The table below includes the legacy capability bucket for each method so agents
88
88
  | 55 | `cdp.get_dom_snapshot` | Yes | CDP | cdp | `cdp.dom_snapshot` | DevTools DOM snapshot |
89
89
  | 56 | `cdp.get_box_model` | Yes | CDP | cdp | `cdp.box_model` | DevTools-backed element geometry |
90
90
  | 57 | `cdp.get_computed_styles_for_node` | Yes | CDP | cdp | `cdp.styles` | DevTools-backed computed styles |
91
+ | 58 | `cdp.dispatch_key_event` | Yes | CDP | cdp | `cdp.input` | DevTools keyDown/keyUp without foreground focus |
91
92
 
92
93
  ## CLI
93
94
 
@@ -98,7 +99,7 @@ bbx call --tab 123 <method> '{...}' # explicit tab target inside enabled
98
99
  bbx batch '[{"method":"...","params":{}}]' # parallel calls
99
100
  ```
100
101
 
101
- **Convenience shortcuts:** `access-request`, `dom-query`, `describe`, `text`, `styles`, `box`, `click`, `focus`, `type`, `press-key`, `patch-style`, `patch-text`, `patches`, `rollback`, `screenshot`, `eval`, `console`, `wait`, `find`, `find-role`, `html`, `hover`, `navigate`, `storage`, `tab-create`, `tab-close`, `page-text`, `network`, `a11y-tree`, `perf`, `resize`, `reload`, `back`, `forward`, `attrs`, `matched-rules`
102
+ **Convenience shortcuts:** `access-request`, `dom-query`, `describe`, `text`, `styles`, `box`, `click`, `focus`, `type`, `press-key`, `cdp-press-key`, `patch-style`, `patch-text`, `patches`, `rollback`, `screenshot`, `eval`, `console`, `wait`, `find`, `find-role`, `html`, `hover`, `navigate`, `storage`, `tab-create`, `tab-close`, `page-text`, `network`, `a11y-tree`, `perf`, `resize`, `reload`, `back`, `forward`, `attrs`, `matched-rules`
102
103
 
103
104
  Newer bridge methods such as `input.scroll_into_view` and `screenshot.capture_full_page` currently use the raw path: `bbx call <method> '{...}'`.
104
105
 
package/CHANGELOG.md DELETED
@@ -1,55 +0,0 @@
1
- # Changelog
2
-
3
- All notable changes to this project will be documented in this file.
4
-
5
- The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
- but this file is maintained as a cumulative feature log rather than a sequence
7
- of tagged releases.
8
-
9
- ## [Unreleased]
10
-
11
- ### Added
12
-
13
- - **Core platform:** Browser Bridge as a local bridge between coding agents and
14
- a real Chrome tab, preserving the existing browser state instead of starting
15
- from a clean automation session. This includes the Chrome extension, native
16
- messaging host, long-lived daemon, shared protocol package, agent client, and
17
- MCP server.
18
- - **Browser inspection:** Structured DOM inspection for subtree queries,
19
- element description, text, HTML, attributes, semantic search by text or ARIA
20
- role, wait conditions, and accessibility-tree reads.
21
- - **Page and runtime reads:** Page state, storage, visible page text, console
22
- output, network activity, performance metrics, and targeted JavaScript
23
- evaluation when structured reads are insufficient.
24
- - **Layout and styling reads:** Computed styles, matched CSS rules, box model
25
- data, hit testing, scrolling, and viewport resizing.
26
- - **Browser control:** Clicking, typing, focusing, hovering, keyboard input,
27
- form controls, drag-and-drop, tab management, and page navigation.
28
- - **Capture and DevTools fallback:** Partial screenshots, DOM snapshots, and
29
- CDP-backed geometry and style reads for cases where standard DOM inspection is
30
- not enough.
31
- - **Live patching:** Reversible style and DOM patching, plus patch listing,
32
- rollback, and session-baseline commit support so fixes can be proven in the
33
- browser before being moved into source.
34
- - **CLI integration:** The `bbx` CLI with raw RPC access, shortcut commands,
35
- batch execution, diagnostics, and runtime skill/context helpers.
36
- - **MCP integration:** The `bbx-mcp` server so MCP-capable agents can use
37
- Browser Bridge through native tool calls.
38
- - **Agent setup flows:** Managed MCP and skill installation for Codex, Claude
39
- Code, Cursor, GitHub Copilot, OpenCode, Antigravity, Windsurf, and generic
40
- `.agents` setups.
41
- - **Documentation and skills:** Agent-facing skill packaging plus quick-start,
42
- API, publishing, capability-matrix, contributor, and workflow documentation.
43
- - **Validation and release tooling:** Automated tests across the protocol,
44
- native host, agent client, MCP server, and extension, plus linting, coverage
45
- checks, extension packaging validation, and release verification steps.
46
- - **Access model and routing:** Explicit per-window enablement with active-tab
47
- default routing instead of ambient browser-wide access.
48
- - **Protocol and efficiency model:** Structured, token-efficient browser reads
49
- with shared protocol types, normalization, error codes, recovery hints, and
50
- budget presets.
51
- - **Runtime hardening:** Setup readiness checks, health and diagnostic flows,
52
- socket and config permission hardening, protocol negotiation, reconnect
53
- behavior, and disconnected-client handling.
54
- - **Typing expectations:** Strict JSDoc-backed typing across the JavaScript
55
- codebase with repository-wide `npm run typecheck` validation.
package/assets/banner.jpg DELETED
Binary file
package/assets/logo.png DELETED
Binary file
package/assets/logo.svg DELETED
@@ -1,65 +0,0 @@
1
- <?xml version="1.0" encoding="UTF-8" standalone="no"?>
2
- <!-- Created with Inkscape (http://www.inkscape.org/) -->
3
-
4
- <svg
5
- width="78.190598mm"
6
- height="81.232712mm"
7
- viewBox="0 0 78.190598 81.232712"
8
- version="1.1"
9
- id="svg1"
10
- xml:space="preserve"
11
- xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
12
- xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
13
- xmlns="http://www.w3.org/2000/svg"
14
- xmlns:svg="http://www.w3.org/2000/svg"><sodipodi:namedview
15
- id="namedview1"
16
- pagecolor="#ffffff"
17
- bordercolor="#000000"
18
- borderopacity="0.25"
19
- inkscape:showpageshadow="2"
20
- inkscape:pageopacity="0.0"
21
- inkscape:pagecheckerboard="0"
22
- inkscape:deskcolor="#d1d1d1"
23
- inkscape:document-units="mm"><inkscape:page
24
- x="0"
25
- y="0"
26
- width="78.190598"
27
- height="81.232712"
28
- id="page2"
29
- margin="0"
30
- bleed="0" /></sodipodi:namedview><defs
31
- id="defs1" /><g
32
- inkscape:label="Layer 1"
33
- inkscape:groupmode="layer"
34
- id="layer1"
35
- transform="translate(-42.680867,-100)"><g
36
- id="g2"><path
37
- id="rect2-6"
38
- style="fill:#d34a1e;fill-opacity:1;stroke:none;stroke-width:0.221409"
39
- d="m -20.02,163.43555 -21.040492,2.4e-4 a 16.5,16.5 0 0 1 -1.090567,14.99993 l 22.283145,-8e-5 A 16.5,16.5 0 0 1 -20.02,163.43555 Z"
40
- transform="rotate(-40.047795)" /><path
41
- id="rect2"
42
- style="fill:#d34a1e;fill-opacity:1;stroke:none;stroke-width:0.221409"
43
- d="m 136.94552,-47.53585 a 16.5,16.5 0 0 1 1.66989,10.051485 16.5,16.5 0 0 1 -1.57039,4.948831 l 22.89662,-6.5e-5 a 16.5,16.5 0 0 1 -2.02368,-10.697809 16.5,16.5 0 0 1 1.26306,-4.302113 z"
44
- transform="rotate(81.447977)" /><path
45
- id="rect2-6-4"
46
- style="fill:#d34a1e;fill-opacity:1;stroke:none;stroke-width:0.221409"
47
- d="m 110.73366,76.358885 a 16.5,16.5 0 0 1 1.19568,2.379247 16.5,16.5 0 0 1 -0.096,12.620524 l 21.61171,9e-6 a 16.5,16.5 0 0 1 -0.2602,-0.606591 16.5,16.5 0 0 1 0.97427,-14.393019 z"
48
- transform="rotate(22.050959)" /><circle
49
- style="fill:#d34a1e;fill-opacity:1;stroke:none;stroke-width:0.424555"
50
- id="path1-0-7"
51
- cx="66.236229"
52
- cy="166.23273"
53
- r="15"
54
- inkscape:transform-center-x="-0.58870168"
55
- inkscape:transform-center-y="1.4717542" /><circle
56
- style="fill:#d34a1e;fill-opacity:1;stroke:none;stroke-width:0.424555"
57
- id="path1"
58
- cx="57.68087"
59
- cy="115.00001"
60
- r="15" /><circle
61
- style="fill:#d34a1e;fill-opacity:1;stroke:none;stroke-width:0.424555"
62
- id="path1-0"
63
- cx="105.87147"
64
- cy="134.11552"
65
- r="15" /></g></g></svg>