@browserbridge/bbx 1.3.0 → 1.5.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 +3 -1
- package/package.json +2 -2
- package/packages/agent-client/src/cli.js +45 -16
- package/packages/agent-client/src/client.js +74 -20
- package/packages/agent-client/src/command-registry.js +2 -3
- package/packages/agent-client/src/mcp-config.js +30 -27
- package/packages/agent-client/src/runtime.js +2 -10
- package/packages/agent-client/src/types.ts +10 -1
- package/packages/mcp-server/src/guidance.js +241 -0
- package/packages/mcp-server/src/handlers-capture.js +74 -11
- package/packages/mcp-server/src/handlers-dom.js +48 -0
- package/packages/mcp-server/src/handlers-navigation.js +22 -2
- package/packages/mcp-server/src/handlers-page.js +10 -9
- package/packages/mcp-server/src/handlers-utils.js +47 -1
- package/packages/mcp-server/src/server.js +111 -29
- package/packages/native-host/src/auth-token.js +92 -0
- package/packages/native-host/src/daemon-process.js +26 -4
- package/packages/native-host/src/daemon.js +174 -28
- package/packages/native-host/src/framing.js +7 -2
- package/packages/native-host/src/native-host.js +18 -2
- package/packages/protocol/src/defaults.js +3 -0
- package/packages/protocol/src/json-lines.js +29 -1
- package/packages/protocol/src/protocol.js +6 -1
- package/packages/protocol/src/types.ts +2 -0
- package/skills/browser-bridge/SKILL.md +21 -5
- package/skills/browser-bridge/agents/openai.yaml +1 -1
- package/skills/browser-bridge/references/interaction.md +6 -6
- package/skills/browser-bridge/references/protocol.md +57 -54
- package/skills/browser-bridge/references/ui-workflows.md +1 -1
package/README.md
CHANGED
|
@@ -110,7 +110,9 @@ Browser Bridge is optimized for the opposite starting point: **inspect the state
|
|
|
110
110
|
3. Run `bbx install`, or target a specific browser with `bbx install --browser edge`, `bbx install --browser brave`, `bbx install --browser chromium`, or `bbx install --browser arc`
|
|
111
111
|
4. In the extension side panel, install MCP or CLI (skill) for your agent of choice
|
|
112
112
|
5. Enable Browser Bridge for the browser window you want to inspect/control with the AI agent
|
|
113
|
-
6. Ask your agent to use Browser Bridge via MCP (`BB MCP` or `Browser Bridge MCP`), or invoke the
|
|
113
|
+
6. Ask your agent to use Browser Bridge via MCP (`BB MCP` or `Browser Bridge MCP`), or invoke the installed Browser Bridge skill in CLI mode (`/browser-bridge`, `browser-bridge`, or the client-specific skill trigger)
|
|
114
|
+
|
|
115
|
+
MCP mode is self-contained: the server exposes tools, startup instructions, and prompt templates, so a separate CLI skill is not required for MCP guidance.
|
|
114
116
|
|
|
115
117
|
## How it works
|
|
116
118
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@browserbridge/bbx",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agent-tools",
|
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
"postinstall": "node packages/native-host/bin/postinstall.js",
|
|
62
62
|
"package:extension": "node scripts/package-extension.mjs",
|
|
63
63
|
"check:extension-zip": "node scripts/check-extension-zip.mjs",
|
|
64
|
-
"release:check": "npm run lint && npm run typecheck && npm test && npm run coverage:check && npm run package:extension && npm pack --dry-run",
|
|
64
|
+
"release:check": "npm run lint && npm run typecheck && npm test && npm run coverage:check && npm run package:extension && npm run check:extension-zip && npm pack --dry-run",
|
|
65
65
|
"prepublishOnly": "npm run lint && npm run typecheck && npm test && npm run coverage:check",
|
|
66
66
|
"status": "node packages/agent-client/src/cli.js status",
|
|
67
67
|
"daemon": "node packages/native-host/bin/bridge-daemon.js",
|
|
@@ -128,15 +128,21 @@ if (command === 'install-skill') {
|
|
|
128
128
|
const positional = rest.filter((a) => !a.startsWith('--'));
|
|
129
129
|
|
|
130
130
|
if (positional.length === 0) {
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
131
|
+
let scopeOptions;
|
|
132
|
+
try {
|
|
133
|
+
scopeOptions = parseInstallAgentArgs(rest);
|
|
134
|
+
} catch (error) {
|
|
135
|
+
process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
|
|
136
|
+
process.exit(1);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const isGlobal = scopeOptions.global !== false;
|
|
140
|
+
const projectPath = isGlobal ? os.homedir() : scopeOptions.projectPath;
|
|
135
141
|
|
|
136
142
|
const setupStatus = await collectSetupStatus({
|
|
137
143
|
global: isGlobal,
|
|
138
144
|
cwd: process.cwd(),
|
|
139
|
-
projectPath
|
|
145
|
+
projectPath,
|
|
140
146
|
...getSetupStatusTestOverrides(),
|
|
141
147
|
});
|
|
142
148
|
/** @type {import('./types.js').SupportedTarget[]} */
|
|
@@ -176,7 +182,6 @@ if (command === 'install-skill') {
|
|
|
176
182
|
targets = /** @type {import('./types.js').SupportedTarget[]} */ (selected);
|
|
177
183
|
}
|
|
178
184
|
|
|
179
|
-
const projectPath = isGlobal ? os.homedir() : process.cwd();
|
|
180
185
|
if (selected !== null) {
|
|
181
186
|
const deselectedTargets =
|
|
182
187
|
/** @type {import('./types.js').SupportedTarget[]} */ (
|
|
@@ -226,20 +231,32 @@ if (command === 'install-skill') {
|
|
|
226
231
|
}
|
|
227
232
|
|
|
228
233
|
if (command === 'install-mcp') {
|
|
229
|
-
const argsLeft = [...rest];
|
|
230
234
|
let isGlobal = true;
|
|
235
|
+
/** @type {string[]} */
|
|
236
|
+
const positionals = [];
|
|
231
237
|
|
|
232
|
-
const
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
238
|
+
for (const arg of rest) {
|
|
239
|
+
if (arg === '--local') {
|
|
240
|
+
isGlobal = false;
|
|
241
|
+
continue;
|
|
242
|
+
}
|
|
243
|
+
if (arg === '--global') {
|
|
244
|
+
isGlobal = true;
|
|
245
|
+
continue;
|
|
246
|
+
}
|
|
247
|
+
if (arg.startsWith('--')) {
|
|
248
|
+
process.stderr.write(`Unknown install-mcp option "${arg}".\n`);
|
|
249
|
+
process.exit(1);
|
|
250
|
+
}
|
|
251
|
+
positionals.push(arg);
|
|
236
252
|
}
|
|
237
|
-
|
|
238
|
-
if (
|
|
239
|
-
|
|
253
|
+
|
|
254
|
+
if (positionals.length > 1) {
|
|
255
|
+
process.stderr.write(`Unexpected extra argument "${positionals[1]}".\n`);
|
|
256
|
+
process.exit(1);
|
|
240
257
|
}
|
|
241
258
|
|
|
242
|
-
const clientArg =
|
|
259
|
+
const clientArg = positionals[0];
|
|
243
260
|
|
|
244
261
|
/** @type {import('./types.js').McpClientName[]} */
|
|
245
262
|
let clients;
|
|
@@ -670,7 +687,7 @@ async function main() {
|
|
|
670
687
|
process.exitCode = 1;
|
|
671
688
|
} catch (error) {
|
|
672
689
|
const message = error instanceof Error ? error.message : String(error);
|
|
673
|
-
const raw =
|
|
690
|
+
const raw = getErrorCode(error);
|
|
674
691
|
let code = 'ERROR';
|
|
675
692
|
if (raw === 'ENOENT' || raw === 'ECONNREFUSED' || raw === 'EINVAL') {
|
|
676
693
|
code = 'DAEMON_OFFLINE';
|
|
@@ -692,6 +709,18 @@ async function main() {
|
|
|
692
709
|
}
|
|
693
710
|
}
|
|
694
711
|
|
|
712
|
+
/**
|
|
713
|
+
* @param {unknown} error
|
|
714
|
+
* @returns {string}
|
|
715
|
+
*/
|
|
716
|
+
function getErrorCode(error) {
|
|
717
|
+
if (!(error instanceof Error) || !('code' in error)) {
|
|
718
|
+
return '';
|
|
719
|
+
}
|
|
720
|
+
const code = /** @type {{ code?: unknown }} */ (error).code;
|
|
721
|
+
return typeof code === 'string' ? code : '';
|
|
722
|
+
}
|
|
723
|
+
|
|
695
724
|
/**
|
|
696
725
|
* Allow tests to shrink request timeouts without changing the shared default.
|
|
697
726
|
*
|
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
getBridgeTransport,
|
|
16
16
|
getSocketPath,
|
|
17
17
|
} from '../../native-host/src/config.js';
|
|
18
|
+
import { normalizeBridgeAuthToken, readBridgeAuthToken } from '../../native-host/src/auth-token.js';
|
|
18
19
|
import { restartBridgeDaemon } from '../../native-host/src/daemon-process.js';
|
|
19
20
|
|
|
20
21
|
/** @typedef {import('./types.js').BridgeMeta} BridgeMeta */
|
|
@@ -56,6 +57,22 @@ function createTimeoutError(method, timeoutMs) {
|
|
|
56
57
|
return error;
|
|
57
58
|
}
|
|
58
59
|
|
|
60
|
+
/**
|
|
61
|
+
* @param {net.Socket} socket
|
|
62
|
+
* @param {string} line
|
|
63
|
+
* @returns {Promise<void>}
|
|
64
|
+
*/
|
|
65
|
+
async function writeSocketLine(socket, line) {
|
|
66
|
+
if (!socket.write(line)) {
|
|
67
|
+
await Promise.race([
|
|
68
|
+
once(socket, 'drain'),
|
|
69
|
+
once(socket, 'close').then(() => {
|
|
70
|
+
throw new Error('Bridge socket closed while writing.');
|
|
71
|
+
}),
|
|
72
|
+
]);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
59
76
|
export class BridgeClient extends EventEmitter {
|
|
60
77
|
/**
|
|
61
78
|
* @param {BridgeClientOptions} [options={}]
|
|
@@ -68,6 +85,7 @@ export class BridgeClient extends EventEmitter {
|
|
|
68
85
|
autoReconnect = false,
|
|
69
86
|
restartDaemonOnVersionMismatch = true,
|
|
70
87
|
restartDaemonFn = restartBridgeDaemon,
|
|
88
|
+
authToken = undefined,
|
|
71
89
|
} = {}) {
|
|
72
90
|
super();
|
|
73
91
|
this.transport = socketPath ? createSocketBridgeTransport(socketPath) : transport;
|
|
@@ -78,6 +96,7 @@ export class BridgeClient extends EventEmitter {
|
|
|
78
96
|
this.autoReconnect = autoReconnect;
|
|
79
97
|
this.restartDaemonOnVersionMismatch = restartDaemonOnVersionMismatch;
|
|
80
98
|
this.restartDaemonFn = restartDaemonFn;
|
|
99
|
+
this.authToken = authToken;
|
|
81
100
|
this.socket = null;
|
|
82
101
|
this.connected = false;
|
|
83
102
|
this.protocolCompatibility = null;
|
|
@@ -111,6 +130,18 @@ export class BridgeClient extends EventEmitter {
|
|
|
111
130
|
throw error;
|
|
112
131
|
}
|
|
113
132
|
|
|
133
|
+
const registrationPromise = new Promise((resolve, reject) => {
|
|
134
|
+
const timeoutId = setTimeout(() => {
|
|
135
|
+
this.waiting.delete('registered');
|
|
136
|
+
reject(createTimeoutError('register', this.defaultTimeoutMs));
|
|
137
|
+
}, this.defaultTimeoutMs);
|
|
138
|
+
this.waiting.set('registered', {
|
|
139
|
+
resolve,
|
|
140
|
+
reject,
|
|
141
|
+
timeoutId,
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
|
|
114
145
|
parseJsonLines(socket, (raw) => {
|
|
115
146
|
const message = /** @type {ClientMessage} */ (raw);
|
|
116
147
|
if (message.type === 'registered') {
|
|
@@ -124,6 +155,16 @@ export class BridgeClient extends EventEmitter {
|
|
|
124
155
|
return;
|
|
125
156
|
}
|
|
126
157
|
|
|
158
|
+
if (message.type === 'registration_failed') {
|
|
159
|
+
const pending = this.waiting.get('registered');
|
|
160
|
+
if (pending) {
|
|
161
|
+
this.waiting.delete('registered');
|
|
162
|
+
clearTimeout(pending.timeoutId);
|
|
163
|
+
pending.reject(new Error(message.error?.message || 'Bridge daemon registration failed.'));
|
|
164
|
+
}
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
|
|
127
168
|
if (message.type === 'agent.response') {
|
|
128
169
|
const pending = this.waiting.get(message.response.id);
|
|
129
170
|
if (pending) {
|
|
@@ -148,20 +189,31 @@ export class BridgeClient extends EventEmitter {
|
|
|
148
189
|
// 'close' fires after 'error'; reconnect is triggered there.
|
|
149
190
|
});
|
|
150
191
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
192
|
+
const authToken =
|
|
193
|
+
this.authToken === undefined
|
|
194
|
+
? this.transport.type === 'tcp'
|
|
195
|
+
? await readBridgeAuthToken()
|
|
196
|
+
: null
|
|
197
|
+
: normalizeBridgeAuthToken(this.authToken);
|
|
198
|
+
try {
|
|
199
|
+
await writeSocketLine(
|
|
200
|
+
socket,
|
|
201
|
+
`${JSON.stringify({
|
|
202
|
+
type: 'register',
|
|
203
|
+
role: 'agent',
|
|
204
|
+
clientId: this.clientId,
|
|
205
|
+
...(authToken ? { authToken } : {}),
|
|
206
|
+
})}\n`
|
|
207
|
+
);
|
|
208
|
+
} catch (error) {
|
|
209
|
+
const pending = this.waiting.get('registered');
|
|
210
|
+
if (pending) {
|
|
211
|
+
clearTimeout(pending.timeoutId);
|
|
156
212
|
this.waiting.delete('registered');
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
reject,
|
|
162
|
-
timeoutId,
|
|
163
|
-
});
|
|
164
|
-
});
|
|
213
|
+
}
|
|
214
|
+
throw error;
|
|
215
|
+
}
|
|
216
|
+
await registrationPromise;
|
|
165
217
|
|
|
166
218
|
this.protocolCompatibility = null;
|
|
167
219
|
this.protocolWarning = null;
|
|
@@ -244,13 +296,15 @@ export class BridgeClient extends EventEmitter {
|
|
|
244
296
|
});
|
|
245
297
|
});
|
|
246
298
|
|
|
247
|
-
|
|
248
|
-
await
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
299
|
+
try {
|
|
300
|
+
await writeSocketLine(this.socket, `${JSON.stringify({ type: 'agent.request', request })}\n`);
|
|
301
|
+
} catch (error) {
|
|
302
|
+
const pending = this.waiting.get(request.id);
|
|
303
|
+
if (pending) {
|
|
304
|
+
clearTimeout(pending.timeoutId);
|
|
305
|
+
this.waiting.delete(request.id);
|
|
306
|
+
}
|
|
307
|
+
throw error;
|
|
254
308
|
}
|
|
255
309
|
const response = /** @type {BridgeResponse} */ (await responsePromise);
|
|
256
310
|
return this.attachProtocolWarning(response);
|
|
@@ -237,11 +237,10 @@ export const CLI_HELP_SECTIONS = Object.freeze([
|
|
|
237
237
|
{
|
|
238
238
|
title: 'Setup',
|
|
239
239
|
lines: [
|
|
240
|
-
'bbx install [--browser chrome|edge|brave|chromium|arc] [
|
|
240
|
+
'bbx install [extension-id] [--browser chrome|edge|brave|chromium|arc] [--all] Install native messaging manifest',
|
|
241
241
|
'bbx uninstall Remove native host manifests, Browser Bridge runtime files, and managed MCP/skill installs',
|
|
242
|
-
'bbx install [--all] [--browser <name>] [extension-id] Install native host manifest (--all for all supported browsers)',
|
|
243
242
|
'bbx install-skill [targets|all] [--global] [--project <path>] Install/update the managed Browser Bridge CLI skill',
|
|
244
|
-
'bbx install-mcp [client|all] [--local] Write MCP config for codex|claude|cursor|copilot|opencode|antigravity|windsurf',
|
|
243
|
+
'bbx install-mcp [client|all] [--local] Write MCP config for codex|claude|cursor|copilot|opencode|antigravity|windsurf|agents',
|
|
245
244
|
'bbx status Check bridge connection',
|
|
246
245
|
'bbx doctor Diagnose install, daemon, extension, and access readiness',
|
|
247
246
|
'bbx restart Restart the local Browser Bridge daemon',
|
|
@@ -94,29 +94,19 @@ export function getMcpConfigShape(clientName) {
|
|
|
94
94
|
* }}
|
|
95
95
|
*/
|
|
96
96
|
function createBaseServerConfig(clientName) {
|
|
97
|
-
const
|
|
98
|
-
process.
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
env: {},
|
|
103
|
-
}
|
|
104
|
-
: {
|
|
105
|
-
command: 'bbx',
|
|
106
|
-
args: ['mcp', 'serve'],
|
|
107
|
-
env: {},
|
|
108
|
-
};
|
|
97
|
+
const serverConfig = {
|
|
98
|
+
command: process.execPath,
|
|
99
|
+
args: [mcpServerBinPath],
|
|
100
|
+
env: {},
|
|
101
|
+
};
|
|
109
102
|
|
|
110
103
|
if (clientName === 'opencode') {
|
|
111
104
|
return {
|
|
112
105
|
type: 'local',
|
|
113
|
-
command:
|
|
114
|
-
process.platform === 'win32'
|
|
115
|
-
? [process.execPath, mcpServerBinPath]
|
|
116
|
-
: ['bbx', 'mcp', 'serve'],
|
|
106
|
+
command: [process.execPath, mcpServerBinPath],
|
|
117
107
|
};
|
|
118
108
|
}
|
|
119
|
-
return
|
|
109
|
+
return serverConfig;
|
|
120
110
|
}
|
|
121
111
|
|
|
122
112
|
/** @type {Record<McpClientName, { key: string, includeType: boolean, legacyKeys?: string[], keepEmptyBlock?: boolean }>} */
|
|
@@ -142,13 +132,11 @@ const MCP_CONFIG_SHAPES = {
|
|
|
142
132
|
*/
|
|
143
133
|
export function buildMcpConfig(clientName) {
|
|
144
134
|
if (clientName === 'codex') {
|
|
145
|
-
const command = process.platform === 'win32' ? process.execPath : 'bbx';
|
|
146
|
-
const args = process.platform === 'win32' ? [mcpServerBinPath] : ['mcp', 'serve'];
|
|
147
135
|
return {
|
|
148
136
|
mcp_servers: {
|
|
149
137
|
[BROWSER_BRIDGE_SERVER_NAME]: {
|
|
150
|
-
command,
|
|
151
|
-
args,
|
|
138
|
+
command: process.execPath,
|
|
139
|
+
args: [mcpServerBinPath],
|
|
152
140
|
},
|
|
153
141
|
},
|
|
154
142
|
};
|
|
@@ -277,12 +265,10 @@ export async function getMcpConfigPaths(clientName, options) {
|
|
|
277
265
|
* @returns {string}
|
|
278
266
|
*/
|
|
279
267
|
function formatCodexServerBlock() {
|
|
280
|
-
const command = process.platform === 'win32' ? process.execPath : 'bbx';
|
|
281
|
-
const args = process.platform === 'win32' ? [mcpServerBinPath] : ['mcp', 'serve'];
|
|
282
268
|
return [
|
|
283
269
|
`[mcp_servers."${BROWSER_BRIDGE_SERVER_NAME}"]`,
|
|
284
|
-
`command = ${JSON.stringify(
|
|
285
|
-
`args = ${JSON.stringify(
|
|
270
|
+
`command = ${JSON.stringify(process.execPath)}`,
|
|
271
|
+
`args = ${JSON.stringify([mcpServerBinPath])}`,
|
|
286
272
|
'',
|
|
287
273
|
].join('\n');
|
|
288
274
|
}
|
|
@@ -523,8 +509,13 @@ async function installJsonMcpConfig(clientName, configPath, stdout) {
|
|
|
523
509
|
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
|
|
524
510
|
existing = parsed;
|
|
525
511
|
}
|
|
526
|
-
} catch {
|
|
527
|
-
|
|
512
|
+
} catch (error) {
|
|
513
|
+
if (!isMissingFileError(error)) {
|
|
514
|
+
throw new Error(
|
|
515
|
+
`Cannot update ${configPath}: existing MCP config is not valid JSON. Fix or remove it first.`
|
|
516
|
+
);
|
|
517
|
+
}
|
|
518
|
+
// File missing - start fresh.
|
|
528
519
|
}
|
|
529
520
|
|
|
530
521
|
const shape = getMcpConfigShape(clientName);
|
|
@@ -661,3 +652,15 @@ export function parseInstalledMcpConfig(clientName, raw) {
|
|
|
661
652
|
return { configured: false };
|
|
662
653
|
}
|
|
663
654
|
}
|
|
655
|
+
|
|
656
|
+
/**
|
|
657
|
+
* @param {unknown} error
|
|
658
|
+
* @returns {boolean}
|
|
659
|
+
*/
|
|
660
|
+
function isMissingFileError(error) {
|
|
661
|
+
return Boolean(
|
|
662
|
+
error &&
|
|
663
|
+
typeof error === 'object' &&
|
|
664
|
+
/** @type {{ code?: unknown }} */ (error).code === 'ENOENT'
|
|
665
|
+
);
|
|
666
|
+
}
|
|
@@ -160,10 +160,10 @@ export async function checkBrowserManifests() {
|
|
|
160
160
|
export async function getDoctorReport(options = {}) {
|
|
161
161
|
const manifest = await (options.loadManifest || loadInstalledManifest)();
|
|
162
162
|
const allowedOrigins = Array.isArray(manifest?.allowed_origins) ? manifest.allowed_origins : [];
|
|
163
|
-
const manifestInstalled = Boolean(manifest);
|
|
164
163
|
const defaultExtensionId = options.defaultExtensionIdInfo || resolveDefaultExtensionId();
|
|
165
164
|
|
|
166
|
-
const browserManifests = await checkBrowserManifests();
|
|
165
|
+
const browserManifests = await (options.checkBrowserManifests || checkBrowserManifests)();
|
|
166
|
+
const manifestInstalled = Boolean(manifest) || browserManifests.some((b) => b.installed);
|
|
167
167
|
|
|
168
168
|
/** @type {DoctorReport} */
|
|
169
169
|
const report = {
|
|
@@ -214,8 +214,6 @@ export async function getDoctorReport(options = {}) {
|
|
|
214
214
|
report.extensionConnected = false;
|
|
215
215
|
}
|
|
216
216
|
|
|
217
|
-
const browsersWithoutManifest = browserManifests.filter((b) => !b.installed);
|
|
218
|
-
|
|
219
217
|
if (!report.manifestInstalled) {
|
|
220
218
|
report.issues.push('native_host_manifest_missing');
|
|
221
219
|
report.nextSteps.push(
|
|
@@ -223,12 +221,6 @@ export async function getDoctorReport(options = {}) {
|
|
|
223
221
|
? 'Run `bbx install` (or `bbx install --all` for all browsers) to install the native host manifest.'
|
|
224
222
|
: 'Run `bbx install <extension-id>` (or `bbx install --all`) to install the native host manifest.'
|
|
225
223
|
);
|
|
226
|
-
} else if (browsersWithoutManifest.length > 0) {
|
|
227
|
-
report.issues.push('native_host_manifest_partial');
|
|
228
|
-
const missing = browsersWithoutManifest.map((b) => b.browser).join(', ');
|
|
229
|
-
report.nextSteps.push(
|
|
230
|
-
`Manifests missing for: ${missing}. Run \`bbx install --all\` to install for all supported browsers.`
|
|
231
|
-
);
|
|
232
224
|
}
|
|
233
225
|
if (!report.daemonReachable) {
|
|
234
226
|
report.issues.push('daemon_offline');
|
|
@@ -54,13 +54,20 @@ export type ClientMessage =
|
|
|
54
54
|
role: 'agent' | 'extension';
|
|
55
55
|
clientId?: string;
|
|
56
56
|
}
|
|
57
|
+
| {
|
|
58
|
+
type: 'registration_failed';
|
|
59
|
+
error?: {
|
|
60
|
+
code?: string;
|
|
61
|
+
message?: string;
|
|
62
|
+
};
|
|
63
|
+
}
|
|
57
64
|
| {
|
|
58
65
|
type: 'agent.response';
|
|
59
66
|
response: BridgeResponse;
|
|
60
67
|
};
|
|
61
68
|
|
|
62
69
|
export interface PendingRequest {
|
|
63
|
-
resolve: (value:
|
|
70
|
+
resolve: (value: unknown) => void;
|
|
64
71
|
reject: (error: Error) => void;
|
|
65
72
|
timeoutId: NodeJS.Timeout;
|
|
66
73
|
}
|
|
@@ -73,6 +80,7 @@ export interface BridgeClientOptions {
|
|
|
73
80
|
autoReconnect?: boolean;
|
|
74
81
|
restartDaemonOnVersionMismatch?: boolean;
|
|
75
82
|
restartDaemonFn?: typeof restartBridgeDaemon;
|
|
83
|
+
authToken?: string | null;
|
|
76
84
|
}
|
|
77
85
|
|
|
78
86
|
export interface ShortcutCommand {
|
|
@@ -110,6 +118,7 @@ export interface DoctorReport {
|
|
|
110
118
|
|
|
111
119
|
export interface DoctorReportOptions {
|
|
112
120
|
loadManifest?: () => Promise<{ allowed_origins?: string[] } | null>;
|
|
121
|
+
checkBrowserManifests?: () => Promise<BrowserManifestStatus[]>;
|
|
113
122
|
manifestPath?: string;
|
|
114
123
|
defaultExtensionIdInfo?: { extensionId: string | null; source: string };
|
|
115
124
|
bridgeClientRunner?: <T>(
|