@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.
- package/README.md +6 -4
- package/package.json +53 -53
- package/packages/agent-client/src/cli-helpers.js +43 -5
- package/packages/agent-client/src/cli.js +176 -171
- package/packages/agent-client/src/client.js +66 -21
- package/packages/agent-client/src/command-registry.js +104 -69
- package/packages/agent-client/src/detect.js +162 -54
- package/packages/agent-client/src/install.js +34 -28
- package/packages/agent-client/src/mcp-config.js +40 -40
- package/packages/agent-client/src/runtime.js +41 -20
- package/packages/agent-client/src/setup-status.js +23 -30
- package/packages/mcp-server/src/bin.js +57 -5
- package/packages/mcp-server/src/handlers.js +573 -256
- package/packages/mcp-server/src/server.js +568 -257
- package/packages/native-host/bin/bridge-daemon.js +39 -6
- package/packages/native-host/bin/install-manifest.js +26 -4
- package/packages/native-host/bin/postinstall.js +4 -2
- package/packages/native-host/src/config.js +142 -13
- package/packages/native-host/src/daemon-process.js +396 -0
- package/packages/native-host/src/daemon.js +350 -150
- package/packages/native-host/src/framing.js +131 -11
- package/packages/native-host/src/install-manifest.js +194 -29
- package/packages/native-host/src/native-host.js +154 -102
- package/packages/protocol/src/budget.js +3 -7
- package/packages/protocol/src/capabilities.js +6 -3
- package/packages/protocol/src/defaults.js +1 -0
- package/packages/protocol/src/errors.js +15 -11
- package/packages/protocol/src/payload-cost.js +19 -6
- package/packages/protocol/src/protocol.js +242 -73
- package/packages/protocol/src/registry.js +311 -45
- package/packages/protocol/src/summary.js +260 -109
- package/packages/protocol/src/types.js +29 -4
- package/skills/browser-bridge/SKILL.md +3 -2
- package/skills/browser-bridge/agents/openai.yaml +3 -3
- package/skills/browser-bridge/references/interaction.md +34 -11
- package/skills/browser-bridge/references/patch-workflow.md +3 -0
- package/skills/browser-bridge/references/protocol.md +127 -71
- package/skills/browser-bridge/references/tailwind.md +12 -11
- package/skills/browser-bridge/references/token-efficiency.md +23 -22
- package/skills/browser-bridge/references/ui-workflows.md +8 -0
- package/CHANGELOG.md +0 -55
- package/assets/banner.jpg +0 -0
- package/assets/logo.png +0 -0
- package/assets/logo.svg +0 -65
- package/docs/api-reference.md +0 -157
- package/docs/cli-guide.md +0 -128
- package/docs/index.md +0 -25
- package/docs/manual-setup.md +0 -140
- package/docs/mcp-vs-cli.md +0 -258
- package/docs/publishing.md +0 -114
- package/docs/quickstart.md +0 -104
- package/docs/troubleshooting.md +0 -59
- package/docs/usage-scenarios.md +0 -136
- package/manifest.json +0 -52
- package/packages/extension/assets/icon-128.png +0 -0
- package/packages/extension/assets/icon-16.png +0 -0
- package/packages/extension/assets/icon-32.png +0 -0
- package/packages/extension/assets/icon-48.png +0 -0
- package/packages/extension/src/background-helpers.js +0 -459
- package/packages/extension/src/background-routing.js +0 -91
- package/packages/extension/src/background.js +0 -3227
- package/packages/extension/src/content-script-helpers.js +0 -281
- package/packages/extension/src/content-script.js +0 -1977
- package/packages/extension/src/debugger-coordinator.js +0 -188
- package/packages/extension/src/sidepanel-helpers.js +0 -102
- package/packages/extension/ui/offscreen.html +0 -6
- package/packages/extension/ui/offscreen.js +0 -61
- package/packages/extension/ui/popup.html +0 -35
- package/packages/extension/ui/popup.js +0 -279
- package/packages/extension/ui/sidepanel.html +0 -102
- package/packages/extension/ui/sidepanel.js +0 -1854
- 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 {
|
|
8
|
-
|
|
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
|
-
|
|
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.
|
|
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 =
|
|
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(
|
|
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({
|
|
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 }} */ (
|
|
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(() => {
|
|
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 {
|
|
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:
|
|
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
|
-
'
|
|
42
|
-
|
|
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) => ({
|
|
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) => ({
|
|
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) => ({
|
|
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) => ({
|
|
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
|
-
|
|
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
|
-
{
|
|
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 {
|
|
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(
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
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
|
-
'
|
|
151
|
-
|
|
152
|
-
|
|
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
|
-
{
|
|
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) => ({
|
|
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 {
|
|
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 {
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
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(
|
|
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
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
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
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
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
|
]);
|