@chatpanel/bridge 0.10.5 → 0.10.7
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/package.json +1 -1
- package/scripts/install.sh +8 -3
- package/src/engines/cli-agents.js +2 -0
- package/src/engines/custom.js +69 -15
- package/src/env.js +2 -2
- package/src/server.js +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chatpanel/bridge",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.7",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Local bridge that exposes the AI coding agents installed on your machine — Claude Code (CLI), Codex (CLI), and Gemini CLI — to the ChatPanel Chrome extension over a localhost SSE endpoint. Bring your own agent.",
|
|
6
6
|
"keywords": [
|
package/scripts/install.sh
CHANGED
|
@@ -34,16 +34,21 @@ url="https://dl.chatpanel.net/${asset}"
|
|
|
34
34
|
dest="${HOME}/.local/bin"
|
|
35
35
|
bin="${dest}/chatpanel-bridge"
|
|
36
36
|
mkdir -p "$dest"
|
|
37
|
+
tmp="$(mktemp "${dest}/.chatpanel-bridge.XXXXXX")"
|
|
38
|
+
trap 'rm -f "$tmp"' EXIT
|
|
37
39
|
|
|
38
40
|
echo "Downloading ChatPanel Bridge (~60 MB)..."
|
|
39
|
-
curl -fL --progress-bar "$url" -o "$
|
|
40
|
-
chmod +x "$
|
|
41
|
-
xattr -c "$
|
|
41
|
+
curl -fL --progress-bar "$url" -o "$tmp" # show a progress bar (not silent)
|
|
42
|
+
chmod +x "$tmp"
|
|
43
|
+
xattr -c "$tmp" 2>/dev/null || true # belt-and-suspenders; curl files aren't quarantined
|
|
42
44
|
|
|
43
45
|
# Clean upgrade: stop any running bridge (incl. a stray npx one) so the new
|
|
44
46
|
# install replaces it in place — same path, same service, no duplicates.
|
|
45
47
|
pkill -f 'chatpanel-bridge' 2>/dev/null || true
|
|
46
48
|
sleep 1
|
|
49
|
+
rm -f "$bin"
|
|
50
|
+
mv "$tmp" "$bin"
|
|
51
|
+
trap - EXIT
|
|
47
52
|
|
|
48
53
|
echo "Installed to ${bin}"
|
|
49
54
|
"$bin" --install
|
|
@@ -84,7 +84,9 @@ export const kiro = makeCliAgent(
|
|
|
84
84
|
modelArg: '--model {model}',
|
|
85
85
|
trustToolsArg: '--trust-tools={tools}',
|
|
86
86
|
requiresStableMcp: true,
|
|
87
|
+
autoSetupStableMcp: true,
|
|
87
88
|
stableMcpConfigCheck: 'kiro',
|
|
89
|
+
stableMcpSetupArgs: ['mcp', 'add', '--scope', 'global', '--name', 'chatpanel_browser', '--url', 'http://127.0.0.1:4319/mcp', '--force'],
|
|
88
90
|
stableMcpSetupCommand: 'kiro-cli mcp add --scope global --name chatpanel_browser --url http://127.0.0.1:4319/mcp --force',
|
|
89
91
|
listModelsArgs: '--list-models',
|
|
90
92
|
label: 'Kiro',
|
package/src/engines/custom.js
CHANGED
|
@@ -155,6 +155,12 @@ export function stableMcpSetupCommand(spec = {}) {
|
|
|
155
155
|
return `opencode mcp add chatpanel --url ${CHATPANEL_STABLE_MCP_URL}`;
|
|
156
156
|
}
|
|
157
157
|
|
|
158
|
+
export function stableMcpSetupPlan(spec = {}) {
|
|
159
|
+
const args = Array.isArray(spec.stableMcpSetupArgs) ? spec.stableMcpSetupArgs.filter((a) => a != null).map(String) : null;
|
|
160
|
+
if (!args?.length) return null;
|
|
161
|
+
return { command: spec.stableMcpSetupCommandName || spec.command, args };
|
|
162
|
+
}
|
|
163
|
+
|
|
158
164
|
export function buildPiExtensionSource(mcp) {
|
|
159
165
|
const specs = mcpToolSpecs(mcp);
|
|
160
166
|
const declarations = specs.map((spec, index) => {
|
|
@@ -256,25 +262,27 @@ async function opencodeHasStableMcpConfig() {
|
|
|
256
262
|
return false;
|
|
257
263
|
}
|
|
258
264
|
|
|
259
|
-
async function
|
|
265
|
+
export async function commandOutput(command, args, cwd) {
|
|
260
266
|
const resolved = resolveCommand(command);
|
|
261
267
|
if (!resolved) return '';
|
|
262
268
|
const [bin, argv, opts] = buildSpawnSpec(resolved, args, cwd || null);
|
|
263
269
|
return new Promise((resolve) => {
|
|
264
270
|
const child = spawn(bin, argv, opts);
|
|
265
271
|
let out = '';
|
|
272
|
+
let err = '';
|
|
266
273
|
const timer = setTimeout(() => {
|
|
267
274
|
child.kill('SIGKILL');
|
|
268
|
-
resolve(out);
|
|
275
|
+
resolve(out + err);
|
|
269
276
|
}, 4000);
|
|
270
277
|
child.stdout.on('data', (d) => (out += d.toString()));
|
|
278
|
+
child.stderr.on('data', (d) => (err += d.toString()));
|
|
271
279
|
child.on('error', () => {
|
|
272
280
|
clearTimeout(timer);
|
|
273
|
-
resolve(
|
|
281
|
+
resolve(out + err);
|
|
274
282
|
});
|
|
275
283
|
child.on('close', () => {
|
|
276
284
|
clearTimeout(timer);
|
|
277
|
-
resolve(out);
|
|
285
|
+
resolve(out + err);
|
|
278
286
|
});
|
|
279
287
|
try { child.stdin.end(); } catch { /* ignore */ }
|
|
280
288
|
});
|
|
@@ -282,7 +290,7 @@ async function commandStdout(command, args, cwd) {
|
|
|
282
290
|
|
|
283
291
|
async function kiroHasStableMcpConfig(command, cwd) {
|
|
284
292
|
for (const scope of ['workspace', 'global', 'default']) {
|
|
285
|
-
const out = await
|
|
293
|
+
const out = await commandOutput(command, ['mcp', 'list', scope], cwd);
|
|
286
294
|
if (out.includes(CHATPANEL_STABLE_MCP_URL) || /chatpanel_browser/i.test(out)) return true;
|
|
287
295
|
}
|
|
288
296
|
return false;
|
|
@@ -294,6 +302,59 @@ async function hasStableMcpConfig(spec, cwd) {
|
|
|
294
302
|
return false;
|
|
295
303
|
}
|
|
296
304
|
|
|
305
|
+
async function runStableMcpSetup(plan, cwd) {
|
|
306
|
+
const resolved = resolveCommand(plan.command);
|
|
307
|
+
if (!resolved) throw new Error(`Couldn't find "${plan.command}" to set up browser tools.`);
|
|
308
|
+
const [bin, argv, opts] = buildSpawnSpec(resolved, plan.args, cwd || null);
|
|
309
|
+
opts.env = { ...(opts.env || process.env), NO_COLOR: '1', FORCE_COLOR: '0', CLICOLOR: '0', TERM: 'dumb' };
|
|
310
|
+
await new Promise((resolve, reject) => {
|
|
311
|
+
let child;
|
|
312
|
+
try { child = spawn(bin, argv, opts); } catch (e) { return reject(new Error(`Failed to start ${plan.command}: ${e.message}`)); }
|
|
313
|
+
let stderr = '';
|
|
314
|
+
const timer = setTimeout(() => {
|
|
315
|
+
child.kill('SIGKILL');
|
|
316
|
+
reject(new Error(`${plan.command} MCP setup timed out.`));
|
|
317
|
+
}, 20_000);
|
|
318
|
+
child.stderr.on('data', (d) => (stderr += d.toString()));
|
|
319
|
+
child.on('error', (e) => {
|
|
320
|
+
clearTimeout(timer);
|
|
321
|
+
reject(new Error(`Failed to start ${plan.command}: ${e.message}`));
|
|
322
|
+
});
|
|
323
|
+
child.on('close', (code) => {
|
|
324
|
+
clearTimeout(timer);
|
|
325
|
+
if (code === 0) return resolve();
|
|
326
|
+
reject(new Error(`${plan.command} MCP setup exited ${code}: ${stderr.trim().split('\n').pop() || 'failed'}`));
|
|
327
|
+
});
|
|
328
|
+
try { child.stdin.end(); } catch { /* ignore */ }
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
export async function ensureStableMcpConfig(spec, cwd, label, emit, deps = {}) {
|
|
333
|
+
if (!spec.requiresStableMcp) return true;
|
|
334
|
+
const hasConfig = deps.hasConfig || hasStableMcpConfig;
|
|
335
|
+
const runSetup = deps.runSetup || runStableMcpSetup;
|
|
336
|
+
if (await hasConfig(spec, cwd)) return true;
|
|
337
|
+
|
|
338
|
+
const setupCommand = stableMcpSetupCommand(spec);
|
|
339
|
+
if (!spec.autoSetupStableMcp) {
|
|
340
|
+
const text = `${label} needs one-time browser-tool setup: ${setupCommand}`;
|
|
341
|
+
emit({ type: 'status', text });
|
|
342
|
+
throw new Error(text);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
const plan = stableMcpSetupPlan(spec);
|
|
346
|
+
if (!plan?.command) {
|
|
347
|
+
const text = `${label} needs one-time browser-tool setup: ${setupCommand}`;
|
|
348
|
+
emit({ type: 'status', text });
|
|
349
|
+
throw new Error(text);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
emit({ type: 'status', text: `${label} is setting up one-time browser tools...` });
|
|
353
|
+
await runSetup(plan, cwd);
|
|
354
|
+
if (await hasConfig(spec, cwd)) return true;
|
|
355
|
+
throw new Error(`${label} browser-tool setup completed, but the MCP server is still not visible. Run: ${setupCommand}`);
|
|
356
|
+
}
|
|
357
|
+
|
|
297
358
|
export async function chat({ messages, system, options, images }, emit) {
|
|
298
359
|
// Pro gate — verified, not just UI. No valid signed entitlement → no run.
|
|
299
360
|
if (!(await isProEntitled(options.entitlement))) {
|
|
@@ -376,16 +437,9 @@ export async function runSpec(spec, { messages, system, options = {}, images },
|
|
|
376
437
|
if (options.mcp?.url && spec.trustToolsArg) {
|
|
377
438
|
args.push(...trustToolArgs(spec.trustToolsArg, options.mcp));
|
|
378
439
|
}
|
|
379
|
-
//
|
|
380
|
-
//
|
|
381
|
-
|
|
382
|
-
// `opencode mcp add chatpanel --url http://127.0.0.1:4319/mcp`.
|
|
383
|
-
if (options.mcp?.url && spec.requiresStableMcp && !(await hasStableMcpConfig(spec, cwd))) {
|
|
384
|
-
emit({
|
|
385
|
-
type: 'status',
|
|
386
|
-
text: `${label} needs one-time browser-tool setup: ${stableMcpSetupCommand(spec)}`,
|
|
387
|
-
});
|
|
388
|
-
}
|
|
440
|
+
// Some CLIs only load MCP from their persistent config, so ensure that stable
|
|
441
|
+
// /mcp endpoint is present before letting the agent answer with no tools.
|
|
442
|
+
if (options.mcp?.url) await ensureStableMcpConfig(spec, cwd, label, emit);
|
|
389
443
|
|
|
390
444
|
const imageTokens = imageTokensFor(spec.imageArg, imageFiles);
|
|
391
445
|
let placedImages = false;
|
package/src/env.js
CHANGED
|
@@ -67,7 +67,7 @@ function shellWhich(name) {
|
|
|
67
67
|
if (process.platform === 'win32') return '';
|
|
68
68
|
try {
|
|
69
69
|
const shell = process.env.SHELL || '/bin/zsh';
|
|
70
|
-
const r = spawnSync(shell, ['-
|
|
70
|
+
const r = spawnSync(shell, ['-lc', `command -v ${name} 2>/dev/null`], {
|
|
71
71
|
encoding: 'utf8',
|
|
72
72
|
timeout: 4000,
|
|
73
73
|
});
|
|
@@ -319,7 +319,7 @@ export function enrichPath() {
|
|
|
319
319
|
let shellPath = '';
|
|
320
320
|
try {
|
|
321
321
|
const shell = process.env.SHELL || '/bin/zsh';
|
|
322
|
-
const r = spawnSync(shell, ['-
|
|
322
|
+
const r = spawnSync(shell, ['-lc', 'command -p echo "$PATH"'], {
|
|
323
323
|
encoding: 'utf8',
|
|
324
324
|
timeout: 4000,
|
|
325
325
|
});
|
package/src/server.js
CHANGED
|
@@ -30,7 +30,7 @@ import { callLocalMcp } from './mcp-local.js';
|
|
|
30
30
|
// Hardcoded (not read from package.json) so it survives Bun's single-file
|
|
31
31
|
// --compile, where package.json isn't on a readable FS. CI fails the publish if
|
|
32
32
|
// this drifts from package.json, so the two can't silently diverge.
|
|
33
|
-
const VERSION = '0.10.
|
|
33
|
+
const VERSION = '0.10.7';
|
|
34
34
|
const HOST = process.env.CHATPANEL_BRIDGE_HOST || '127.0.0.1';
|
|
35
35
|
const PORT = Number(process.env.CHATPANEL_BRIDGE_PORT) || 4319;
|
|
36
36
|
|