@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
|
@@ -1,16 +1,13 @@
|
|
|
1
1
|
// @ts-check
|
|
2
2
|
|
|
3
|
-
import { spawn } from 'node:child_process';
|
|
4
3
|
import net from 'node:net';
|
|
5
|
-
import path from 'node:path';
|
|
6
|
-
import { fileURLToPath } from 'node:url';
|
|
7
4
|
|
|
8
5
|
import { createFailure, ERROR_CODES } from '../../protocol/src/index.js';
|
|
9
|
-
import {
|
|
6
|
+
import { createSocketBridgeTransport, getBridgeTransport } from './config.js';
|
|
7
|
+
import { spawnBridgeDaemonProcess } from './daemon-process.js';
|
|
10
8
|
import { createNativeMessageReader, writeJsonLine, writeNativeMessage } from './framing.js';
|
|
11
9
|
|
|
12
|
-
|
|
13
|
-
const daemonEntryPath = path.resolve(__dirname, '../bin/bridge-daemon.js');
|
|
10
|
+
/** @typedef {import('./config.js').BridgeTransport} BridgeTransport */
|
|
14
11
|
|
|
15
12
|
/**
|
|
16
13
|
* @typedef {{
|
|
@@ -54,10 +51,10 @@ const daemonEntryPath = path.resolve(__dirname, '../bin/bridge-daemon.js');
|
|
|
54
51
|
*/
|
|
55
52
|
function isHostBridgeRequest(message) {
|
|
56
53
|
return Boolean(
|
|
57
|
-
message
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
54
|
+
message &&
|
|
55
|
+
typeof message === 'object' &&
|
|
56
|
+
/** @type {Record<string, unknown>} */ (message).type === 'host.bridge_request' &&
|
|
57
|
+
typeof (/** @type {Record<string, unknown>} */ (message).request) === 'object'
|
|
61
58
|
);
|
|
62
59
|
}
|
|
63
60
|
|
|
@@ -67,10 +64,10 @@ function isHostBridgeRequest(message) {
|
|
|
67
64
|
*/
|
|
68
65
|
function isHostStatusRequest(message) {
|
|
69
66
|
return Boolean(
|
|
70
|
-
message
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
67
|
+
message &&
|
|
68
|
+
typeof message === 'object' &&
|
|
69
|
+
/** @type {Record<string, unknown>} */ (message).type === 'host.setup_status.request' &&
|
|
70
|
+
typeof (/** @type {Record<string, unknown>} */ (message).requestId) === 'string'
|
|
74
71
|
);
|
|
75
72
|
}
|
|
76
73
|
|
|
@@ -80,9 +77,9 @@ function isHostStatusRequest(message) {
|
|
|
80
77
|
*/
|
|
81
78
|
function isHostIdentity(message) {
|
|
82
79
|
return Boolean(
|
|
83
|
-
message
|
|
84
|
-
|
|
85
|
-
|
|
80
|
+
message &&
|
|
81
|
+
typeof message === 'object' &&
|
|
82
|
+
/** @type {Record<string, unknown>} */ (message).type === 'host.identity'
|
|
86
83
|
);
|
|
87
84
|
}
|
|
88
85
|
|
|
@@ -92,10 +89,10 @@ function isHostIdentity(message) {
|
|
|
92
89
|
*/
|
|
93
90
|
function isHostAccessUpdate(message) {
|
|
94
91
|
return Boolean(
|
|
95
|
-
message
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
92
|
+
message &&
|
|
93
|
+
typeof message === 'object' &&
|
|
94
|
+
/** @type {Record<string, unknown>} */ (message).type === 'host.access_update' &&
|
|
95
|
+
typeof (/** @type {Record<string, unknown>} */ (message).accessEnabled) === 'boolean'
|
|
99
96
|
);
|
|
100
97
|
}
|
|
101
98
|
|
|
@@ -105,20 +102,24 @@ function isHostAccessUpdate(message) {
|
|
|
105
102
|
*/
|
|
106
103
|
function isHostActivity(message) {
|
|
107
104
|
return Boolean(
|
|
108
|
-
message
|
|
109
|
-
|
|
110
|
-
|
|
105
|
+
message &&
|
|
106
|
+
typeof message === 'object' &&
|
|
107
|
+
/** @type {Record<string, unknown>} */ (message).type === 'host.activity'
|
|
111
108
|
);
|
|
112
109
|
}
|
|
113
110
|
|
|
114
111
|
/**
|
|
115
|
-
* @param {{ socketPath?: string }} [options={}]
|
|
112
|
+
* @param {{ transport?: BridgeTransport, socketPath?: string }} [options={}]
|
|
116
113
|
* @returns {Promise<void>}
|
|
117
114
|
*/
|
|
118
|
-
export async function runNativeHost({
|
|
115
|
+
export async function runNativeHost({
|
|
116
|
+
transport = getBridgeTransport(),
|
|
117
|
+
socketPath = undefined,
|
|
118
|
+
} = {}) {
|
|
119
|
+
const resolvedTransport = socketPath ? createSocketBridgeTransport(socketPath) : transport;
|
|
119
120
|
let socket;
|
|
120
121
|
try {
|
|
121
|
-
socket = await connectWithBootstrap(
|
|
122
|
+
socket = await connectWithBootstrap(resolvedTransport);
|
|
122
123
|
} catch (error) {
|
|
123
124
|
await writeNativeMessage(process.stdout, {
|
|
124
125
|
type: 'agent.response',
|
|
@@ -126,13 +127,23 @@ export async function runNativeHost({ socketPath = getSocketPath() } = {}) {
|
|
|
126
127
|
'native_bootstrap',
|
|
127
128
|
ERROR_CODES.NATIVE_HOST_UNAVAILABLE,
|
|
128
129
|
error instanceof Error ? error.message : String(error)
|
|
129
|
-
)
|
|
130
|
+
),
|
|
130
131
|
});
|
|
131
132
|
return;
|
|
132
133
|
}
|
|
133
134
|
|
|
134
135
|
socket.setEncoding('utf8');
|
|
135
136
|
bindBridgeSocketLifecycle(socket);
|
|
137
|
+
const handleStdinEnd = () => {
|
|
138
|
+
socket.destroy();
|
|
139
|
+
};
|
|
140
|
+
process.stdin.once('end', handleStdinEnd);
|
|
141
|
+
const cleanupStdinEndListener = () => {
|
|
142
|
+
process.stdin.removeListener('end', handleStdinEnd);
|
|
143
|
+
};
|
|
144
|
+
socket.once('close', cleanupStdinEndListener);
|
|
145
|
+
socket.once('end', cleanupStdinEndListener);
|
|
146
|
+
socket.once('error', cleanupStdinEndListener);
|
|
136
147
|
await writeJsonLine(socket, { type: 'register', role: 'extension' });
|
|
137
148
|
|
|
138
149
|
let lineBuffer = '';
|
|
@@ -159,72 +170,88 @@ export async function runNativeHost({ socketPath = getSocketPath() } = {}) {
|
|
|
159
170
|
if (message.type === 'agent.response') {
|
|
160
171
|
await writeNativeMessage(process.stdout, {
|
|
161
172
|
type: 'host.bridge_response',
|
|
162
|
-
response: message.response
|
|
173
|
+
response: message.response,
|
|
163
174
|
});
|
|
164
175
|
return;
|
|
165
176
|
}
|
|
166
|
-
if (
|
|
177
|
+
if (
|
|
178
|
+
message.type === 'extension.setup_status.response' ||
|
|
179
|
+
message.type === 'extension.setup_status.error'
|
|
180
|
+
) {
|
|
167
181
|
await writeNativeMessage(process.stdout, {
|
|
168
|
-
type:
|
|
169
|
-
|
|
170
|
-
|
|
182
|
+
type:
|
|
183
|
+
message.type === 'extension.setup_status.response'
|
|
184
|
+
? 'host.setup_status.response'
|
|
185
|
+
: 'host.setup_status.error',
|
|
171
186
|
requestId: message.requestId,
|
|
172
187
|
status: message.status,
|
|
173
|
-
error: message.error
|
|
188
|
+
error: message.error,
|
|
174
189
|
});
|
|
175
190
|
}
|
|
176
191
|
})().catch((err) => {
|
|
177
|
-
console.error(
|
|
192
|
+
console.error(
|
|
193
|
+
'native-host: socket message handler failed:',
|
|
194
|
+
err instanceof Error ? err.message : err
|
|
195
|
+
);
|
|
178
196
|
});
|
|
179
197
|
}
|
|
180
198
|
});
|
|
181
199
|
|
|
182
|
-
createNativeMessageReader(
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
200
|
+
createNativeMessageReader(
|
|
201
|
+
process.stdin,
|
|
202
|
+
(message) => {
|
|
203
|
+
void (async () => {
|
|
204
|
+
if (isHostBridgeRequest(message)) {
|
|
205
|
+
await writeJsonLine(socket, {
|
|
206
|
+
type: 'agent.request',
|
|
207
|
+
request: message.request,
|
|
208
|
+
});
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
if (isHostStatusRequest(message)) {
|
|
212
|
+
await writeJsonLine(socket, {
|
|
213
|
+
type: 'extension.setup_status.request',
|
|
214
|
+
requestId: message.requestId,
|
|
215
|
+
});
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
if (isHostIdentity(message)) {
|
|
219
|
+
await writeJsonLine(socket, {
|
|
220
|
+
type: 'extension.identity',
|
|
221
|
+
browserName: message.browserName,
|
|
222
|
+
profileLabel: message.profileLabel,
|
|
223
|
+
});
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
if (isHostAccessUpdate(message)) {
|
|
227
|
+
await writeJsonLine(socket, {
|
|
228
|
+
type: 'extension.access_update',
|
|
229
|
+
accessEnabled: message.accessEnabled,
|
|
230
|
+
});
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
if (isHostActivity(message)) {
|
|
234
|
+
await writeJsonLine(socket, {
|
|
235
|
+
type: 'extension.activity',
|
|
236
|
+
at: message.at,
|
|
237
|
+
});
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
214
240
|
await writeJsonLine(socket, {
|
|
215
|
-
type: 'extension.
|
|
216
|
-
|
|
241
|
+
type: 'extension.response',
|
|
242
|
+
response: message,
|
|
217
243
|
});
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
244
|
+
})().catch((err) => {
|
|
245
|
+
console.error(
|
|
246
|
+
'native-host: stdin message handler failed:',
|
|
247
|
+
err instanceof Error ? err.message : err
|
|
248
|
+
);
|
|
223
249
|
});
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
250
|
+
},
|
|
251
|
+
() => {
|
|
252
|
+
socket.destroy();
|
|
253
|
+
}
|
|
254
|
+
);
|
|
228
255
|
}
|
|
229
256
|
|
|
230
257
|
/**
|
|
@@ -235,9 +262,12 @@ export async function runNativeHost({ socketPath = getSocketPath() } = {}) {
|
|
|
235
262
|
* @param {() => void} [onTerminate]
|
|
236
263
|
* @returns {void}
|
|
237
264
|
*/
|
|
238
|
-
export function bindBridgeSocketLifecycle(
|
|
239
|
-
|
|
240
|
-
|
|
265
|
+
export function bindBridgeSocketLifecycle(
|
|
266
|
+
socket,
|
|
267
|
+
onTerminate = () => {
|
|
268
|
+
process.exit(0);
|
|
269
|
+
}
|
|
270
|
+
) {
|
|
241
271
|
let terminated = false;
|
|
242
272
|
|
|
243
273
|
/**
|
|
@@ -257,28 +287,49 @@ export function bindBridgeSocketLifecycle(socket, onTerminate = () => {
|
|
|
257
287
|
}
|
|
258
288
|
|
|
259
289
|
/**
|
|
260
|
-
* @
|
|
290
|
+
* @typedef {{
|
|
291
|
+
* connectSocketFn?: (transport: BridgeTransport) => Promise<net.Socket>,
|
|
292
|
+
* shouldBootstrapFn?: (error: unknown) => boolean,
|
|
293
|
+
* spawnBridgeDaemonFn?: () => void,
|
|
294
|
+
* delayFn?: (ms: number) => Promise<void>,
|
|
295
|
+
* maxAttempts?: number,
|
|
296
|
+
* }} ConnectWithBootstrapOptions
|
|
297
|
+
*/
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* @param {BridgeTransport | string} transport
|
|
301
|
+
* @param {ConnectWithBootstrapOptions} [options]
|
|
261
302
|
* @returns {Promise<net.Socket>}
|
|
262
303
|
*/
|
|
263
|
-
async function connectWithBootstrap(
|
|
304
|
+
export async function connectWithBootstrap(transport, options = {}) {
|
|
305
|
+
const {
|
|
306
|
+
connectSocketFn = connectSocket,
|
|
307
|
+
shouldBootstrapFn = shouldBootstrap,
|
|
308
|
+
spawnBridgeDaemonFn = spawnBridgeDaemon,
|
|
309
|
+
delayFn = delay,
|
|
310
|
+
maxAttempts = 10,
|
|
311
|
+
} = options;
|
|
312
|
+
const resolvedTransport =
|
|
313
|
+
typeof transport === 'string' ? createSocketBridgeTransport(transport) : transport;
|
|
314
|
+
|
|
264
315
|
try {
|
|
265
|
-
return await
|
|
316
|
+
return await connectSocketFn(resolvedTransport);
|
|
266
317
|
} catch (error) {
|
|
267
|
-
if (!
|
|
318
|
+
if (!shouldBootstrapFn(error)) {
|
|
268
319
|
throw error;
|
|
269
320
|
}
|
|
270
321
|
}
|
|
271
322
|
|
|
272
|
-
|
|
323
|
+
spawnBridgeDaemonFn();
|
|
273
324
|
|
|
274
325
|
let lastError = null;
|
|
275
|
-
for (let attempt = 0; attempt <
|
|
276
|
-
await
|
|
326
|
+
for (let attempt = 0; attempt < maxAttempts; attempt += 1) {
|
|
327
|
+
await delayFn(200);
|
|
277
328
|
try {
|
|
278
|
-
return await
|
|
329
|
+
return await connectSocketFn(resolvedTransport);
|
|
279
330
|
} catch (error) {
|
|
280
331
|
lastError = error;
|
|
281
|
-
if (!
|
|
332
|
+
if (!shouldBootstrapFn(error)) {
|
|
282
333
|
throw error;
|
|
283
334
|
}
|
|
284
335
|
}
|
|
@@ -288,12 +339,15 @@ async function connectWithBootstrap(socketPath) {
|
|
|
288
339
|
}
|
|
289
340
|
|
|
290
341
|
/**
|
|
291
|
-
* @param {
|
|
342
|
+
* @param {BridgeTransport} transport
|
|
292
343
|
* @returns {Promise<net.Socket>}
|
|
293
344
|
*/
|
|
294
|
-
function connectSocket(
|
|
345
|
+
function connectSocket(transport) {
|
|
295
346
|
return new Promise((resolve, reject) => {
|
|
296
|
-
const socket =
|
|
347
|
+
const socket =
|
|
348
|
+
transport.type === 'tcp'
|
|
349
|
+
? net.createConnection({ host: transport.host, port: transport.port })
|
|
350
|
+
: net.createConnection(transport.socketPath);
|
|
297
351
|
/**
|
|
298
352
|
* @param {Error} error
|
|
299
353
|
* @returns {void}
|
|
@@ -315,21 +369,19 @@ function connectSocket(socketPath) {
|
|
|
315
369
|
* @returns {void}
|
|
316
370
|
*/
|
|
317
371
|
function spawnBridgeDaemon() {
|
|
318
|
-
|
|
319
|
-
detached: true,
|
|
320
|
-
stdio: 'ignore'
|
|
321
|
-
});
|
|
322
|
-
child.unref();
|
|
372
|
+
spawnBridgeDaemonProcess();
|
|
323
373
|
}
|
|
324
374
|
|
|
325
375
|
/**
|
|
326
376
|
* @param {unknown} error
|
|
327
377
|
* @returns {boolean}
|
|
328
378
|
*/
|
|
329
|
-
function shouldBootstrap(error) {
|
|
330
|
-
return
|
|
331
|
-
|
|
332
|
-
|
|
379
|
+
export function shouldBootstrap(error) {
|
|
380
|
+
return (
|
|
381
|
+
error instanceof Error &&
|
|
382
|
+
'code' in error &&
|
|
383
|
+
(error.code === 'ENOENT' || error.code === 'ECONNREFUSED')
|
|
384
|
+
);
|
|
333
385
|
}
|
|
334
386
|
|
|
335
387
|
/**
|
|
@@ -4,11 +4,7 @@
|
|
|
4
4
|
/** @typedef {import('./types.js').BudgetOptions} BudgetOptions */
|
|
5
5
|
/** @typedef {import('./types.js').TruncateResult} TruncateResult */
|
|
6
6
|
|
|
7
|
-
import {
|
|
8
|
-
DEFAULT_MAX_DEPTH,
|
|
9
|
-
DEFAULT_MAX_NODES,
|
|
10
|
-
DEFAULT_TEXT_BUDGET,
|
|
11
|
-
} from './defaults.js';
|
|
7
|
+
import { DEFAULT_MAX_DEPTH, DEFAULT_MAX_NODES, DEFAULT_TEXT_BUDGET } from './defaults.js';
|
|
12
8
|
|
|
13
9
|
/**
|
|
14
10
|
* @param {BudgetOptions} [options={}]
|
|
@@ -20,7 +16,7 @@ export function applyBudget(options = {}) {
|
|
|
20
16
|
maxDepth: clamp(options.maxDepth ?? DEFAULT_MAX_DEPTH, 1, 20),
|
|
21
17
|
textBudget: clamp(options.textBudget ?? DEFAULT_TEXT_BUDGET, 32, 10000),
|
|
22
18
|
includeBbox: options.includeBbox !== false,
|
|
23
|
-
attributeAllowlist: normalizeList(options.attributeAllowlist)
|
|
19
|
+
attributeAllowlist: normalizeList(options.attributeAllowlist),
|
|
24
20
|
};
|
|
25
21
|
}
|
|
26
22
|
|
|
@@ -41,7 +37,7 @@ export function truncateText(value, budget) {
|
|
|
41
37
|
return {
|
|
42
38
|
value: `${value.slice(0, Math.max(0, budget - 1))}\u2026`,
|
|
43
39
|
truncated: true,
|
|
44
|
-
omitted: value.length - budget
|
|
40
|
+
omitted: value.length - budget,
|
|
45
41
|
};
|
|
46
42
|
}
|
|
47
43
|
|
|
@@ -17,10 +17,11 @@ export const CAPABILITIES = Object.freeze({
|
|
|
17
17
|
CDP_DOM_SNAPSHOT: 'cdp.dom_snapshot',
|
|
18
18
|
CDP_BOX_MODEL: 'cdp.box_model',
|
|
19
19
|
CDP_STYLES: 'cdp.styles',
|
|
20
|
+
CDP_INPUT: 'cdp.input',
|
|
20
21
|
AUTOMATION_INPUT: 'automation.input',
|
|
21
22
|
TABS_MANAGE: 'tabs.manage',
|
|
22
23
|
PERFORMANCE_READ: 'performance.read',
|
|
23
|
-
NETWORK_READ: 'network.read'
|
|
24
|
+
NETWORK_READ: 'network.read',
|
|
24
25
|
});
|
|
25
26
|
|
|
26
27
|
export const DEFAULT_CAPABILITIES = Object.freeze([
|
|
@@ -38,9 +39,10 @@ export const DEFAULT_CAPABILITIES = Object.freeze([
|
|
|
38
39
|
CAPABILITIES.CDP_DOM_SNAPSHOT,
|
|
39
40
|
CAPABILITIES.CDP_BOX_MODEL,
|
|
40
41
|
CAPABILITIES.CDP_STYLES,
|
|
42
|
+
CAPABILITIES.CDP_INPUT,
|
|
41
43
|
CAPABILITIES.TABS_MANAGE,
|
|
42
44
|
CAPABILITIES.PERFORMANCE_READ,
|
|
43
|
-
CAPABILITIES.NETWORK_READ
|
|
45
|
+
CAPABILITIES.NETWORK_READ,
|
|
44
46
|
]);
|
|
45
47
|
|
|
46
48
|
/** @type {Readonly<Record<CapabilityMethod, Capability | null>>} */
|
|
@@ -99,9 +101,10 @@ export const METHOD_CAPABILITIES = Object.freeze({
|
|
|
99
101
|
'cdp.get_dom_snapshot': CAPABILITIES.CDP_DOM_SNAPSHOT,
|
|
100
102
|
'cdp.get_box_model': CAPABILITIES.CDP_BOX_MODEL,
|
|
101
103
|
'cdp.get_computed_styles_for_node': CAPABILITIES.CDP_STYLES,
|
|
104
|
+
'cdp.dispatch_key_event': CAPABILITIES.CDP_INPUT,
|
|
102
105
|
'performance.get_metrics': CAPABILITIES.PERFORMANCE_READ,
|
|
103
106
|
'log.tail': null,
|
|
104
|
-
'health.ping': null
|
|
107
|
+
'health.ping': null,
|
|
105
108
|
});
|
|
106
109
|
|
|
107
110
|
/**
|
|
@@ -12,7 +12,7 @@ export const ERROR_CODES = Object.freeze({
|
|
|
12
12
|
INVALID_REQUEST: 'INVALID_REQUEST',
|
|
13
13
|
NATIVE_HOST_UNAVAILABLE: 'NATIVE_HOST_UNAVAILABLE',
|
|
14
14
|
EXTENSION_DISCONNECTED: 'EXTENSION_DISCONNECTED',
|
|
15
|
-
TIMEOUT: 'TIMEOUT'
|
|
15
|
+
TIMEOUT: 'TIMEOUT',
|
|
16
16
|
});
|
|
17
17
|
|
|
18
18
|
/**
|
|
@@ -23,47 +23,51 @@ export const ERROR_CODES = Object.freeze({
|
|
|
23
23
|
export const ERROR_RECOVERY = Object.freeze({
|
|
24
24
|
[ERROR_CODES.ACCESS_DENIED]: {
|
|
25
25
|
retry: false,
|
|
26
|
-
hint: 'Access is off for this window. Ask the user to click Enable in the Browser Bridge popup or side panel. Do not request access again until that window is enabled.'
|
|
26
|
+
hint: 'Access is off for this window. Ask the user to click Enable in the Browser Bridge popup or side panel. Do not request access again until that window is enabled.',
|
|
27
|
+
},
|
|
28
|
+
[ERROR_CODES.RESULT_TRUNCATED]: {
|
|
29
|
+
retry: false,
|
|
30
|
+
hint: 'Result was truncated to fit the response budget. Narrow the query or raise the relevant budget if more detail is required.',
|
|
27
31
|
},
|
|
28
32
|
[ERROR_CODES.ELEMENT_STALE]: {
|
|
29
33
|
retry: false,
|
|
30
34
|
alternativeMethod: 'dom.query',
|
|
31
|
-
hint: 'Element was removed from the DOM. Re-query with the same selector to get a fresh elementRef.'
|
|
35
|
+
hint: 'Element was removed from the DOM. Re-query with the same selector to get a fresh elementRef.',
|
|
32
36
|
},
|
|
33
37
|
[ERROR_CODES.TAB_MISMATCH]: {
|
|
34
38
|
retry: false,
|
|
35
39
|
alternativeMethod: 'tabs.list',
|
|
36
|
-
hint: 'Tab was closed or not found. Use tabs.list to find an available tab.'
|
|
40
|
+
hint: 'Tab was closed or not found. Use tabs.list to find an available tab.',
|
|
37
41
|
},
|
|
38
42
|
[ERROR_CODES.TIMEOUT]: {
|
|
39
43
|
retry: true,
|
|
40
44
|
retryAfterMs: 1000,
|
|
41
|
-
hint: 'Operation exceeded the time limit. Retry once, or simplify the request (smaller maxNodes, narrower selector).'
|
|
45
|
+
hint: 'Operation exceeded the time limit. Retry once, or simplify the request (smaller maxNodes, narrower selector).',
|
|
42
46
|
},
|
|
43
47
|
[ERROR_CODES.RATE_LIMITED]: {
|
|
44
48
|
retry: true,
|
|
45
49
|
retryAfterMs: 2000,
|
|
46
|
-
hint: 'Too many requests. Back off and retry after a short delay.'
|
|
50
|
+
hint: 'Too many requests. Back off and retry after a short delay.',
|
|
47
51
|
},
|
|
48
52
|
[ERROR_CODES.EXTENSION_DISCONNECTED]: {
|
|
49
53
|
retry: true,
|
|
50
54
|
retryAfterMs: 3000,
|
|
51
55
|
alternativeMethod: 'health.ping',
|
|
52
|
-
hint: 'Extension not connected. Check Chrome is running, then retry. Use health.ping to verify connectivity.'
|
|
56
|
+
hint: 'Extension not connected. Check Chrome is running, then retry. Use health.ping to verify connectivity.',
|
|
53
57
|
},
|
|
54
58
|
[ERROR_CODES.NATIVE_HOST_UNAVAILABLE]: {
|
|
55
59
|
retry: false,
|
|
56
|
-
hint: 'Native host not reachable. Run `bbx doctor` to diagnose the installation.'
|
|
60
|
+
hint: 'Native host not reachable. Run `bbx doctor` to diagnose the installation.',
|
|
57
61
|
},
|
|
58
62
|
[ERROR_CODES.INVALID_REQUEST]: {
|
|
59
63
|
retry: false,
|
|
60
|
-
hint: 'Malformed method or params. Check the method name and parameter types.'
|
|
64
|
+
hint: 'Malformed method or params. Check the method name and parameter types.',
|
|
61
65
|
},
|
|
62
66
|
[ERROR_CODES.INTERNAL_ERROR]: {
|
|
63
67
|
retry: true,
|
|
64
68
|
retryAfterMs: 1000,
|
|
65
|
-
hint: 'Unexpected extension error. Retry once; if persistent, check page.get_console for details.'
|
|
66
|
-
}
|
|
69
|
+
hint: 'Unexpected extension error. Retry once; if persistent, check page.get_console for details.',
|
|
70
|
+
},
|
|
67
71
|
});
|
|
68
72
|
|
|
69
73
|
/**
|
|
@@ -22,7 +22,9 @@ export function getUtf8ByteLength(value) {
|
|
|
22
22
|
if (!value) {
|
|
23
23
|
return 0;
|
|
24
24
|
}
|
|
25
|
-
return
|
|
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
|
-
*
|
|
50
|
+
* Serialize a JSON payload for transport-oriented cost estimation.
|
|
49
51
|
*
|
|
50
52
|
* @param {unknown} value
|
|
51
|
-
* @returns {
|
|
53
|
+
* @returns {string}
|
|
52
54
|
*/
|
|
53
|
-
export function
|
|
55
|
+
export function serializeJsonPayload(value) {
|
|
54
56
|
if (typeof value === 'undefined') {
|
|
55
|
-
return
|
|
57
|
+
return '';
|
|
56
58
|
}
|
|
57
|
-
return
|
|
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
|
}
|