@browserbridge/bbx 1.0.0 → 1.1.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 (72) hide show
  1. package/README.md +6 -4
  2. package/package.json +53 -53
  3. package/packages/agent-client/src/cli-helpers.js +43 -5
  4. package/packages/agent-client/src/cli.js +176 -171
  5. package/packages/agent-client/src/client.js +66 -21
  6. package/packages/agent-client/src/command-registry.js +104 -69
  7. package/packages/agent-client/src/detect.js +162 -54
  8. package/packages/agent-client/src/install.js +34 -28
  9. package/packages/agent-client/src/mcp-config.js +40 -40
  10. package/packages/agent-client/src/runtime.js +41 -20
  11. package/packages/agent-client/src/setup-status.js +23 -30
  12. package/packages/mcp-server/src/bin.js +57 -5
  13. package/packages/mcp-server/src/handlers.js +573 -256
  14. package/packages/mcp-server/src/server.js +568 -257
  15. package/packages/native-host/bin/bridge-daemon.js +39 -6
  16. package/packages/native-host/bin/install-manifest.js +26 -4
  17. package/packages/native-host/bin/postinstall.js +4 -2
  18. package/packages/native-host/src/config.js +142 -13
  19. package/packages/native-host/src/daemon-process.js +396 -0
  20. package/packages/native-host/src/daemon.js +350 -150
  21. package/packages/native-host/src/framing.js +131 -11
  22. package/packages/native-host/src/install-manifest.js +194 -29
  23. package/packages/native-host/src/native-host.js +154 -102
  24. package/packages/protocol/src/budget.js +3 -7
  25. package/packages/protocol/src/capabilities.js +6 -3
  26. package/packages/protocol/src/defaults.js +1 -0
  27. package/packages/protocol/src/errors.js +15 -11
  28. package/packages/protocol/src/payload-cost.js +19 -6
  29. package/packages/protocol/src/protocol.js +242 -73
  30. package/packages/protocol/src/registry.js +311 -45
  31. package/packages/protocol/src/summary.js +260 -109
  32. package/packages/protocol/src/types.js +29 -4
  33. package/skills/browser-bridge/SKILL.md +3 -2
  34. package/skills/browser-bridge/agents/openai.yaml +3 -3
  35. package/skills/browser-bridge/references/interaction.md +34 -11
  36. package/skills/browser-bridge/references/patch-workflow.md +3 -0
  37. package/skills/browser-bridge/references/protocol.md +127 -71
  38. package/skills/browser-bridge/references/tailwind.md +12 -11
  39. package/skills/browser-bridge/references/token-efficiency.md +23 -22
  40. package/skills/browser-bridge/references/ui-workflows.md +8 -0
  41. package/CHANGELOG.md +0 -55
  42. package/assets/banner.jpg +0 -0
  43. package/assets/logo.png +0 -0
  44. package/assets/logo.svg +0 -65
  45. package/docs/api-reference.md +0 -157
  46. package/docs/cli-guide.md +0 -128
  47. package/docs/index.md +0 -25
  48. package/docs/manual-setup.md +0 -140
  49. package/docs/mcp-vs-cli.md +0 -258
  50. package/docs/publishing.md +0 -114
  51. package/docs/quickstart.md +0 -104
  52. package/docs/troubleshooting.md +0 -59
  53. package/docs/usage-scenarios.md +0 -136
  54. package/manifest.json +0 -52
  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 -459
  60. package/packages/extension/src/background-routing.js +0 -91
  61. package/packages/extension/src/background.js +0 -3227
  62. package/packages/extension/src/content-script-helpers.js +0 -281
  63. package/packages/extension/src/content-script.js +0 -1977
  64. package/packages/extension/src/debugger-coordinator.js +0 -188
  65. package/packages/extension/src/sidepanel-helpers.js +0 -102
  66. package/packages/extension/ui/offscreen.html +0 -6
  67. package/packages/extension/ui/offscreen.js +0 -61
  68. package/packages/extension/ui/popup.html +0 -35
  69. package/packages/extension/ui/popup.js +0 -279
  70. package/packages/extension/ui/sidepanel.html +0 -102
  71. package/packages/extension/ui/sidepanel.js +0 -1854
  72. package/packages/extension/ui/ui.css +0 -1159
@@ -4,8 +4,19 @@ import { EventEmitter, once } from 'node:events';
4
4
  import net from 'node:net';
5
5
  import { randomUUID } from 'node:crypto';
6
6
 
7
- import { createRequest, DEFAULT_CLIENT_REQUEST_TIMEOUT_MS, PROTOCOL_VERSION, parseJsonLines } from '../../protocol/src/index.js';
8
- import { getSocketPath } from '../../native-host/src/config.js';
7
+ import {
8
+ createRequest,
9
+ DEFAULT_CLIENT_REQUEST_TIMEOUT_MS,
10
+ PROTOCOL_VERSION,
11
+ parseJsonLines,
12
+ } from '../../protocol/src/index.js';
13
+ import {
14
+ createSocketBridgeTransport,
15
+ getBridgeTransport,
16
+ getSocketPath,
17
+ } from '../../native-host/src/config.js';
18
+
19
+ /** @typedef {import('../../native-host/src/config.js').BridgeTransport} BridgeTransport */
9
20
 
10
21
  /** @typedef {import('../../protocol/src/types.js').BridgeResponse} BridgeResponse */
11
22
  /** @typedef {import('../../protocol/src/types.js').BridgeMeta} BridgeMeta */
@@ -37,6 +48,16 @@ import { getSocketPath } from '../../native-host/src/config.js';
37
48
  * }} PendingRequest
38
49
  */
39
50
 
51
+ /**
52
+ * @typedef {{
53
+ * transport?: BridgeTransport,
54
+ * socketPath?: string,
55
+ * clientId?: string,
56
+ * defaultTimeoutMs?: number,
57
+ * autoReconnect?: boolean,
58
+ * }} BridgeClientOptions
59
+ */
60
+
40
61
  /**
41
62
  * @param {string} method
42
63
  * @param {number} timeoutMs
@@ -51,14 +72,20 @@ function createTimeoutError(method, timeoutMs) {
51
72
  }
52
73
 
53
74
  export class BridgeClient extends EventEmitter {
75
+ /**
76
+ * @param {BridgeClientOptions} [options={}]
77
+ */
54
78
  constructor({
55
- socketPath = getSocketPath(),
79
+ transport = getBridgeTransport(),
80
+ socketPath = undefined,
56
81
  clientId = `agent_${randomUUID()}`,
57
82
  defaultTimeoutMs = DEFAULT_CLIENT_REQUEST_TIMEOUT_MS,
58
- autoReconnect = false
83
+ autoReconnect = false,
59
84
  } = {}) {
60
85
  super();
61
- this.socketPath = socketPath;
86
+ this.transport = socketPath ? createSocketBridgeTransport(socketPath) : transport;
87
+ this.socketPath =
88
+ this.transport.type === 'socket' ? this.transport.socketPath : getSocketPath();
62
89
  this.clientId = clientId;
63
90
  this.defaultTimeoutMs = defaultTimeoutMs;
64
91
  this.autoReconnect = autoReconnect;
@@ -78,7 +105,10 @@ export class BridgeClient extends EventEmitter {
78
105
  if (this.socket) {
79
106
  throw new Error('BridgeClient is already connected.');
80
107
  }
81
- const socket = net.createConnection(this.socketPath);
108
+ const socket =
109
+ this.transport.type === 'tcp'
110
+ ? net.createConnection({ host: this.transport.host, port: this.transport.port })
111
+ : net.createConnection(this.transport.socketPath);
82
112
  this.socket = socket;
83
113
  try {
84
114
  await new Promise((resolve, reject) => {
@@ -128,7 +158,9 @@ export class BridgeClient extends EventEmitter {
128
158
  // 'close' fires after 'error'; reconnect is triggered there.
129
159
  });
130
160
 
131
- this.socket.write(`${JSON.stringify({ type: 'register', role: 'agent', clientId: this.clientId })}\n`);
161
+ this.socket.write(
162
+ `${JSON.stringify({ type: 'register', role: 'agent', clientId: this.clientId })}\n`
163
+ );
132
164
  await new Promise((resolve, reject) => {
133
165
  const timeoutId = setTimeout(() => {
134
166
  this.waiting.delete('registered');
@@ -137,13 +169,13 @@ export class BridgeClient extends EventEmitter {
137
169
  this.waiting.set('registered', {
138
170
  resolve,
139
171
  reject,
140
- timeoutId
172
+ timeoutId,
141
173
  });
142
174
  });
143
175
 
144
176
  try {
145
177
  const healthResponse = await this.request({
146
- method: 'health.ping'
178
+ method: 'health.ping',
147
179
  });
148
180
  if (healthResponse.ok) {
149
181
  this.protocolCompatibility = BridgeClient.checkProtocolVersion(
@@ -167,9 +199,17 @@ export class BridgeClient extends EventEmitter {
167
199
  * }} options
168
200
  * @returns {Promise<BridgeResponse>}
169
201
  */
170
- async request({ method, params = {}, tabId = null, meta = {}, timeoutMs = this.defaultTimeoutMs }) {
202
+ async request({
203
+ method,
204
+ params = {},
205
+ tabId = null,
206
+ meta = {},
207
+ timeoutMs = this.defaultTimeoutMs,
208
+ }) {
171
209
  if (!this.socket || this.socket.destroyed || !this.socket.writable) {
172
- const err = /** @type {Error & { code: string }} */ (new Error('BridgeClient is not connected.'));
210
+ const err = /** @type {Error & { code: string }} */ (
211
+ new Error('BridgeClient is not connected.')
212
+ );
173
213
  err.code = 'ENOTCONN';
174
214
  throw err;
175
215
  }
@@ -179,7 +219,7 @@ export class BridgeClient extends EventEmitter {
179
219
  method,
180
220
  params,
181
221
  tabId,
182
- meta
222
+ meta,
183
223
  });
184
224
 
185
225
  const responsePromise = new Promise((resolve, reject) => {
@@ -191,14 +231,16 @@ export class BridgeClient extends EventEmitter {
191
231
  this.waiting.set(request.id, {
192
232
  resolve,
193
233
  reject,
194
- timeoutId
234
+ timeoutId,
195
235
  });
196
236
  });
197
237
 
198
238
  if (!this.socket.write(`${JSON.stringify({ type: 'agent.request', request })}\n`)) {
199
239
  await Promise.race([
200
240
  once(this.socket, 'drain'),
201
- once(this.socket, 'close').then(() => { throw new Error('Bridge socket closed while writing.'); })
241
+ once(this.socket, 'close').then(() => {
242
+ throw new Error('Bridge socket closed while writing.');
243
+ }),
202
244
  ]);
203
245
  }
204
246
  const response = /** @type {BridgeResponse} */ (await responsePromise);
@@ -275,7 +317,11 @@ export class BridgeClient extends EventEmitter {
275
317
  ? healthResult.supported_versions
276
318
  : [];
277
319
  if (remoteVersions.length === 0) {
278
- return { compatible: true, localVersion: PROTOCOL_VERSION, remoteVersions };
320
+ return {
321
+ compatible: true,
322
+ localVersion: PROTOCOL_VERSION,
323
+ remoteVersions,
324
+ };
279
325
  }
280
326
  const compatible = remoteVersions.includes(PROTOCOL_VERSION);
281
327
  return {
@@ -284,11 +330,10 @@ export class BridgeClient extends EventEmitter {
284
330
  remoteVersions,
285
331
  ...(!compatible && {
286
332
  warning:
287
- typeof healthResult?.migration_hint === 'string' &&
288
- healthResult.migration_hint
333
+ typeof healthResult?.migration_hint === 'string' && healthResult.migration_hint
289
334
  ? healthResult.migration_hint
290
- : `Protocol mismatch: client speaks ${PROTOCOL_VERSION} but remote supports [${remoteVersions.join(', ')}]. Update the ${remoteVersions[0] > PROTOCOL_VERSION ? 'client (npm)' : 'extension'} to match.`
291
- })
335
+ : `Protocol mismatch: client speaks ${PROTOCOL_VERSION} but remote supports [${remoteVersions.join(', ')}]. Update the ${remoteVersions[0] > PROTOCOL_VERSION ? 'client (npm)' : 'extension'} to match.`,
336
+ }),
292
337
  };
293
338
  }
294
339
 
@@ -316,8 +361,8 @@ export class BridgeClient extends EventEmitter {
316
361
  ...response,
317
362
  meta: {
318
363
  ...response.meta,
319
- protocol_warning: this.protocolWarning
320
- }
364
+ protocol_warning: this.protocolWarning,
365
+ },
321
366
  };
322
367
  }
323
368
  }
@@ -27,21 +27,20 @@ function createShortcutCommand(method, usage, build, options = {}) {
27
27
  return {
28
28
  method,
29
29
  usage,
30
- description: options.description ?? BRIDGE_METHOD_REGISTRY[method].description.replace(/\.$/, ''),
30
+ description:
31
+ options.description ?? BRIDGE_METHOD_REGISTRY[method].description.replace(/\.$/, ''),
31
32
  build,
32
33
  ...(options.resolve ? { resolve: true } : {}),
33
- ...(options.printMethod ? { printMethod: options.printMethod } : {})
34
+ ...(options.printMethod ? { printMethod: options.printMethod } : {}),
34
35
  };
35
36
  }
36
37
 
37
38
  /** @type {Record<string, ShortcutCommand>} */
38
39
  export const SHORTCUT_COMMANDS = {
39
40
  'access-request': createShortcutCommand('access.request', 'bbx access-request', () => ({})),
40
- 'dom-query': createShortcutCommand(
41
- 'dom.query',
42
- 'bbx dom-query [selector]',
43
- (r) => ({ selector: r[0] || 'body' })
44
- ),
41
+ 'dom-query': createShortcutCommand('dom.query', 'bbx dom-query [selector]', (r) => ({
42
+ selector: r[0] || 'body',
43
+ })),
45
44
  describe: createShortcutCommand(
46
45
  'dom.describe',
47
46
  'bbx describe <ref|selector>',
@@ -51,7 +50,10 @@ export const SHORTCUT_COMMANDS = {
51
50
  text: createShortcutCommand(
52
51
  'dom.get_text',
53
52
  'bbx text <ref|selector> [budget]',
54
- (r, ref) => ({ elementRef: ref, textBudget: r[1] ? parseIntArg(r[1], 'budget') : undefined }),
53
+ (r, ref) => ({
54
+ elementRef: ref,
55
+ textBudget: r[1] ? parseIntArg(r[1], 'budget') : undefined,
56
+ }),
55
57
  { resolve: true, printMethod: 'dom.get_text' }
56
58
  ),
57
59
  styles: createShortcutCommand(
@@ -93,27 +95,34 @@ export const SHORTCUT_COMMANDS = {
93
95
  html: createShortcutCommand(
94
96
  'dom.get_html',
95
97
  'bbx html <ref|selector> [maxLen]',
96
- (r, ref) => ({ elementRef: ref, maxLength: r[1] ? parseIntArg(r[1], 'maxLen') : undefined }),
98
+ (r, ref) => ({
99
+ elementRef: ref,
100
+ maxLength: r[1] ? parseIntArg(r[1], 'maxLen') : undefined,
101
+ }),
97
102
  { resolve: true }
98
103
  ),
99
104
  'patch-style': createShortcutCommand(
100
105
  'patch.apply_styles',
101
106
  'bbx patch-style <ref|sel> prop=val',
102
- (r, ref) => ({ target: { elementRef: ref }, declarations: parsePropertyAssignments(r.slice(1)) }),
107
+ (r, ref) => ({
108
+ target: { elementRef: ref },
109
+ declarations: parsePropertyAssignments(r.slice(1)),
110
+ }),
103
111
  { resolve: true }
104
112
  ),
105
113
  'patch-text': createShortcutCommand(
106
114
  'patch.apply_dom',
107
115
  'bbx patch-text <ref|sel> <text...>',
108
- (r, ref) => ({ target: { elementRef: ref }, operation: 'set_text', value: r.slice(1).join(' ') }),
116
+ (r, ref) => ({
117
+ target: { elementRef: ref },
118
+ operation: 'set_text',
119
+ value: r.slice(1).join(' '),
120
+ }),
109
121
  { resolve: true, description: 'Apply a reversible DOM text patch' }
110
122
  ),
111
- patches: createShortcutCommand(
112
- 'patch.list',
113
- 'bbx patches',
114
- () => ({}),
115
- { printMethod: 'patch.list' }
116
- ),
123
+ patches: createShortcutCommand('patch.list', 'bbx patches', () => ({}), {
124
+ printMethod: 'patch.list',
125
+ }),
117
126
  rollback: createShortcutCommand('patch.rollback', 'bbx rollback <patchId>', (r) => {
118
127
  if (!r[0]) throw new Error('Usage: rollback <patchId>');
119
128
  return { patchId: r[0] };
@@ -122,11 +131,17 @@ export const SHORTCUT_COMMANDS = {
122
131
  'page.get_console',
123
132
  'bbx console [level]',
124
133
  (r) => ({ level: r[0] || 'all', clear: false }),
125
- { printMethod: 'page.get_console', description: 'Read buffered console output (log|warn|error|all)' }
134
+ {
135
+ printMethod: 'page.get_console',
136
+ description: 'Read buffered console output (log|warn|error|all)',
137
+ }
126
138
  ),
127
139
  wait: createShortcutCommand('dom.wait_for', 'bbx wait <selector> [timeoutMs]', (r) => {
128
140
  if (!r[0]) throw new Error('Usage: wait <selector> [timeoutMs]');
129
- return { selector: r[0], timeoutMs: r[1] ? parseIntArg(r[1], 'timeoutMs') : 5000 };
141
+ return {
142
+ selector: r[0],
143
+ timeoutMs: r[1] ? parseIntArg(r[1], 'timeoutMs') : 5000,
144
+ };
130
145
  }),
131
146
  find: createShortcutCommand(
132
147
  'dom.find_by_text',
@@ -138,19 +153,23 @@ export const SHORTCUT_COMMANDS = {
138
153
  },
139
154
  { printMethod: 'dom.find_by_text' }
140
155
  ),
141
- 'find-role': createShortcutCommand('dom.find_by_role', 'bbx find-role <role> [name]', (r) => {
142
- if (!r[0]) throw new Error('Usage: find-role <role> [name]');
143
- return { role: r[0], name: r.slice(1).join(' ') || undefined };
144
- }, { printMethod: 'dom.find_by_role' }),
156
+ 'find-role': createShortcutCommand(
157
+ 'dom.find_by_role',
158
+ 'bbx find-role <role> [name]',
159
+ (r) => {
160
+ if (!r[0]) throw new Error('Usage: find-role <role> [name]');
161
+ return { role: r[0], name: r.slice(1).join(' ') || undefined };
162
+ },
163
+ { printMethod: 'dom.find_by_role' }
164
+ ),
145
165
  navigate: createShortcutCommand('navigation.navigate', 'bbx navigate <url>', (r) => {
146
166
  if (!r[0]) throw new Error('Usage: navigate <url>');
147
167
  return { url: r[0] };
148
168
  }),
149
- storage: createShortcutCommand(
150
- 'page.get_storage',
151
- 'bbx storage [local|session] [keys]',
152
- (r) => ({ type: r[0] === 'session' ? 'session' : 'local', keys: r.slice(1).length ? r.slice(1) : undefined })
153
- ),
169
+ storage: createShortcutCommand('page.get_storage', 'bbx storage [local|session] [keys]', (r) => ({
170
+ type: r[0] === 'session' ? 'session' : 'local',
171
+ keys: r.slice(1).length ? r.slice(1) : undefined,
172
+ })),
154
173
  'page-text': createShortcutCommand(
155
174
  'page.get_text',
156
175
  'bbx page-text [textBudget]',
@@ -161,21 +180,33 @@ export const SHORTCUT_COMMANDS = {
161
180
  'page.get_network',
162
181
  'bbx network [limit]',
163
182
  (r) => ({ limit: r[0] ? parseIntArg(r[0], 'limit') : undefined }),
164
- { printMethod: 'page.get_network', description: 'Read buffered network requests (fetch/XHR)' }
183
+ {
184
+ printMethod: 'page.get_network',
185
+ description: 'Read buffered network requests (fetch/XHR)',
186
+ }
165
187
  ),
166
188
  'a11y-tree': createShortcutCommand(
167
189
  'dom.get_accessibility_tree',
168
190
  'bbx a11y-tree [maxNodes] [maxDepth]',
169
- (r) => ({ maxNodes: r[0] ? parseIntArg(r[0], 'maxNodes') : undefined, maxDepth: r[1] ? parseIntArg(r[1], 'maxDepth') : undefined })
191
+ (r) => ({
192
+ maxNodes: r[0] ? parseIntArg(r[0], 'maxNodes') : undefined,
193
+ maxDepth: r[1] ? parseIntArg(r[1], 'maxDepth') : undefined,
194
+ })
170
195
  ),
171
196
  perf: createShortcutCommand('performance.get_metrics', 'bbx perf', () => ({})),
172
197
  scroll: createShortcutCommand('viewport.scroll', 'bbx scroll <top> [left]', (r) => {
173
198
  if (!r[0] && !r[1]) throw new Error('Usage: scroll <top> [left]');
174
- return { top: r[0] ? parseIntArg(r[0], 'top') : undefined, left: r[1] ? parseIntArg(r[1], 'left') : undefined };
199
+ return {
200
+ top: r[0] ? parseIntArg(r[0], 'top') : undefined,
201
+ left: r[1] ? parseIntArg(r[1], 'left') : undefined,
202
+ };
175
203
  }),
176
204
  resize: createShortcutCommand('viewport.resize', 'bbx resize <width> <height>', (r) => {
177
205
  if (!r[0] || !r[1]) throw new Error('Usage: resize <width> <height>');
178
- return { width: parseIntArg(r[0], 'width'), height: parseIntArg(r[1], 'height') };
206
+ return {
207
+ width: parseIntArg(r[0], 'width'),
208
+ height: parseIntArg(r[1], 'height'),
209
+ };
179
210
  }),
180
211
  reload: createShortcutCommand('navigation.reload', 'bbx reload', () => ({})),
181
212
  back: createShortcutCommand('navigation.go_back', 'bbx back', () => ({})),
@@ -191,7 +222,7 @@ export const SHORTCUT_COMMANDS = {
191
222
  'bbx matched-rules <ref|selector>',
192
223
  (_r, ref) => ({ elementRef: ref }),
193
224
  { resolve: true, printMethod: 'styles.get_matched_rules' }
194
- )
225
+ ),
195
226
  };
196
227
 
197
228
  /** @type {Readonly<Record<string, BridgeMethod>>} */
@@ -206,8 +237,9 @@ export const CLI_METHOD_BINDINGS = Object.freeze({
206
237
  ),
207
238
  ...Object.fromEntries(BRIDGE_METHODS.map((method) => [method, method])),
208
239
  'press-key': 'input.press_key',
240
+ 'cdp-press-key': 'cdp.dispatch_key_event',
209
241
  screenshot: 'screenshot.capture_element',
210
- eval: 'page.evaluate'
242
+ eval: 'page.evaluate',
211
243
  });
212
244
 
213
245
  /** @type {ReadonlyArray<{ title: string, lines: readonly string[] }>} */
@@ -222,24 +254,25 @@ export const CLI_HELP_SECTIONS = Object.freeze([
222
254
  'bbx install-mcp [client|all] [--local] Write MCP config for codex|claude|cursor|copilot|opencode|antigravity|windsurf',
223
255
  'bbx status Check bridge connection',
224
256
  'bbx doctor Diagnose install, daemon, extension, and access readiness',
257
+ 'bbx restart Restart the local Browser Bridge daemon',
225
258
  'bbx access-request Request Browser Bridge access for the focused window',
226
259
  'bbx logs Recent bridge logs',
227
260
  'bbx tabs List available tabs',
228
261
  'bbx tab-create [url] Create a new tab',
229
262
  'bbx tab-close <tabId> Close a tab',
230
263
  'bbx skill Runtime budget presets and method groups',
231
- 'bbx mcp serve Start Browser Bridge as an MCP stdio server'
232
- ]
264
+ 'bbx mcp serve Start Browser Bridge as an MCP stdio server',
265
+ ],
233
266
  },
234
267
  {
235
268
  title: 'Generic RPC',
236
269
  lines: [
237
270
  'bbx call [--tab <tabId>] <method> [paramsJson|-] Call any bridge method (- reads JSON from stdin)',
238
271
  'bbx <method> [--tab <tabId>] [paramsJson|-] Direct alias for exact bridge methods such as page.get_state',
239
- 'bbx batch \'[{method,params,tabId?},...]\' Parallel method calls',
272
+ "bbx batch '[{method,params,tabId?},...]' Parallel method calls",
240
273
  'Advanced bridge params stay available through `bbx call`, even when shortcuts expose only the common case.',
241
- 'For open-ended investigation, start with `bbx batch` on `page.get_state`, `dom.query`, and `page.get_text` before any screenshot or CDP call.'
242
- ]
274
+ 'For open-ended investigation, start with `bbx batch` on `page.get_state`, `dom.query`, and `page.get_text` before any screenshot or CDP call.',
275
+ ],
243
276
  },
244
277
  {
245
278
  title: 'Inspect',
@@ -253,19 +286,21 @@ export const CLI_HELP_SECTIONS = Object.freeze([
253
286
  'attrs',
254
287
  'matched-rules',
255
288
  'box',
256
- 'a11y-tree'
257
- ].map((command) => `${SHORTCUT_COMMANDS[command].usage.padEnd(64)} ${SHORTCUT_COMMANDS[command].description}`)
258
- ]
289
+ 'a11y-tree',
290
+ ].map(
291
+ (command) =>
292
+ `${SHORTCUT_COMMANDS[command].usage.padEnd(64)} ${SHORTCUT_COMMANDS[command].description}`
293
+ ),
294
+ ],
259
295
  },
260
296
  {
261
297
  title: 'Find',
262
298
  lines: [
263
- ...[
264
- 'find',
265
- 'find-role',
266
- 'wait'
267
- ].map((command) => `${SHORTCUT_COMMANDS[command].usage.padEnd(64)} ${SHORTCUT_COMMANDS[command].description}`)
268
- ]
299
+ ...['find', 'find-role', 'wait'].map(
300
+ (command) =>
301
+ `${SHORTCUT_COMMANDS[command].usage.padEnd(64)} ${SHORTCUT_COMMANDS[command].description}`
302
+ ),
303
+ ],
269
304
  },
270
305
  {
271
306
  title: 'Page',
@@ -282,37 +317,37 @@ export const CLI_HELP_SECTIONS = Object.freeze([
282
317
  'forward',
283
318
  'perf',
284
319
  'scroll',
285
- 'resize'
286
- ].map((command) => `${SHORTCUT_COMMANDS[command].usage.padEnd(64)} ${SHORTCUT_COMMANDS[command].description}`)
287
- ]
320
+ 'resize',
321
+ ].map(
322
+ (command) =>
323
+ `${SHORTCUT_COMMANDS[command].usage.padEnd(64)} ${SHORTCUT_COMMANDS[command].description}`
324
+ ),
325
+ ],
288
326
  },
289
327
  {
290
328
  title: 'Interact',
291
329
  lines: [
292
- ...[
293
- 'click',
294
- 'focus',
295
- 'type',
296
- 'hover'
297
- ].map((command) => `${SHORTCUT_COMMANDS[command].usage.padEnd(64)} ${SHORTCUT_COMMANDS[command].description}`),
298
- 'bbx press-key <key> [ref|selector] Send key event'
299
- ]
330
+ ...['click', 'focus', 'type', 'hover'].map(
331
+ (command) =>
332
+ `${SHORTCUT_COMMANDS[command].usage.padEnd(64)} ${SHORTCUT_COMMANDS[command].description}`
333
+ ),
334
+ 'bbx press-key <key> [ref|selector] Send key event',
335
+ 'bbx cdp-press-key [--tab <tabId>] <key> [code] Dispatch CDP keyDown/keyUp without focusing the tab',
336
+ ],
300
337
  },
301
338
  {
302
339
  title: 'Patch',
303
340
  lines: [
304
- ...[
305
- 'patch-style',
306
- 'patch-text',
307
- 'patches',
308
- 'rollback'
309
- ].map((command) => `${SHORTCUT_COMMANDS[command].usage.padEnd(64)} ${SHORTCUT_COMMANDS[command].description}`)
310
- ]
341
+ ...['patch-style', 'patch-text', 'patches', 'rollback'].map(
342
+ (command) =>
343
+ `${SHORTCUT_COMMANDS[command].usage.padEnd(64)} ${SHORTCUT_COMMANDS[command].description}`
344
+ ),
345
+ ],
311
346
  },
312
347
  {
313
348
  title: 'Capture',
314
349
  lines: [
315
- 'bbx screenshot <ref|selector> [path] Capture partial element screenshot'
316
- ]
317
- }
350
+ 'bbx screenshot <ref|selector> [path] Capture partial element screenshot',
351
+ ],
352
+ },
318
353
  ]);