@chatpanel/bridge 0.10.6 → 0.10.8
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 +6 -1
- package/src/engines/custom.js +75 -18
- 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.8",
|
|
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
|
|
@@ -82,9 +82,14 @@ export const kiro = makeCliAgent(
|
|
|
82
82
|
args: 'chat --no-interactive --require-mcp-startup',
|
|
83
83
|
promptVia: 'arg',
|
|
84
84
|
modelArg: '--model {model}',
|
|
85
|
-
|
|
85
|
+
// Kiro can see MCP tools with --trust-tools, but in --no-interactive mode it
|
|
86
|
+
// only executes MCP calls when this broader trust flag is present.
|
|
87
|
+
trustAllToolsForMcp: true,
|
|
88
|
+
trustAllToolsArg: '--trust-all-tools',
|
|
86
89
|
requiresStableMcp: true,
|
|
90
|
+
autoSetupStableMcp: true,
|
|
87
91
|
stableMcpConfigCheck: 'kiro',
|
|
92
|
+
stableMcpSetupArgs: ['mcp', 'add', '--scope', 'global', '--name', 'chatpanel_browser', '--url', 'http://127.0.0.1:4319/mcp', '--force'],
|
|
88
93
|
stableMcpSetupCommand: 'kiro-cli mcp add --scope global --name chatpanel_browser --url http://127.0.0.1:4319/mcp --force',
|
|
89
94
|
listModelsArgs: '--list-models',
|
|
90
95
|
label: 'Kiro',
|
package/src/engines/custom.js
CHANGED
|
@@ -150,11 +150,22 @@ export function trustToolArgs(template, mcp) {
|
|
|
150
150
|
: [...tmpl.split(/\s+/).filter(Boolean), value];
|
|
151
151
|
}
|
|
152
152
|
|
|
153
|
+
export function mcpTrustArgs(spec = {}, mcp) {
|
|
154
|
+
if (spec.trustAllToolsForMcp) return [spec.trustAllToolsArg || '--trust-all-tools'];
|
|
155
|
+
return spec.trustToolsArg ? trustToolArgs(spec.trustToolsArg, mcp) : [];
|
|
156
|
+
}
|
|
157
|
+
|
|
153
158
|
export function stableMcpSetupCommand(spec = {}) {
|
|
154
159
|
if (spec.stableMcpSetupCommand) return spec.stableMcpSetupCommand;
|
|
155
160
|
return `opencode mcp add chatpanel --url ${CHATPANEL_STABLE_MCP_URL}`;
|
|
156
161
|
}
|
|
157
162
|
|
|
163
|
+
export function stableMcpSetupPlan(spec = {}) {
|
|
164
|
+
const args = Array.isArray(spec.stableMcpSetupArgs) ? spec.stableMcpSetupArgs.filter((a) => a != null).map(String) : null;
|
|
165
|
+
if (!args?.length) return null;
|
|
166
|
+
return { command: spec.stableMcpSetupCommandName || spec.command, args };
|
|
167
|
+
}
|
|
168
|
+
|
|
158
169
|
export function buildPiExtensionSource(mcp) {
|
|
159
170
|
const specs = mcpToolSpecs(mcp);
|
|
160
171
|
const declarations = specs.map((spec, index) => {
|
|
@@ -256,25 +267,27 @@ async function opencodeHasStableMcpConfig() {
|
|
|
256
267
|
return false;
|
|
257
268
|
}
|
|
258
269
|
|
|
259
|
-
async function
|
|
270
|
+
export async function commandOutput(command, args, cwd) {
|
|
260
271
|
const resolved = resolveCommand(command);
|
|
261
272
|
if (!resolved) return '';
|
|
262
273
|
const [bin, argv, opts] = buildSpawnSpec(resolved, args, cwd || null);
|
|
263
274
|
return new Promise((resolve) => {
|
|
264
275
|
const child = spawn(bin, argv, opts);
|
|
265
276
|
let out = '';
|
|
277
|
+
let err = '';
|
|
266
278
|
const timer = setTimeout(() => {
|
|
267
279
|
child.kill('SIGKILL');
|
|
268
|
-
resolve(out);
|
|
280
|
+
resolve(out + err);
|
|
269
281
|
}, 4000);
|
|
270
282
|
child.stdout.on('data', (d) => (out += d.toString()));
|
|
283
|
+
child.stderr.on('data', (d) => (err += d.toString()));
|
|
271
284
|
child.on('error', () => {
|
|
272
285
|
clearTimeout(timer);
|
|
273
|
-
resolve(
|
|
286
|
+
resolve(out + err);
|
|
274
287
|
});
|
|
275
288
|
child.on('close', () => {
|
|
276
289
|
clearTimeout(timer);
|
|
277
|
-
resolve(out);
|
|
290
|
+
resolve(out + err);
|
|
278
291
|
});
|
|
279
292
|
try { child.stdin.end(); } catch { /* ignore */ }
|
|
280
293
|
});
|
|
@@ -282,7 +295,7 @@ async function commandStdout(command, args, cwd) {
|
|
|
282
295
|
|
|
283
296
|
async function kiroHasStableMcpConfig(command, cwd) {
|
|
284
297
|
for (const scope of ['workspace', 'global', 'default']) {
|
|
285
|
-
const out = await
|
|
298
|
+
const out = await commandOutput(command, ['mcp', 'list', scope], cwd);
|
|
286
299
|
if (out.includes(CHATPANEL_STABLE_MCP_URL) || /chatpanel_browser/i.test(out)) return true;
|
|
287
300
|
}
|
|
288
301
|
return false;
|
|
@@ -294,6 +307,59 @@ async function hasStableMcpConfig(spec, cwd) {
|
|
|
294
307
|
return false;
|
|
295
308
|
}
|
|
296
309
|
|
|
310
|
+
async function runStableMcpSetup(plan, cwd) {
|
|
311
|
+
const resolved = resolveCommand(plan.command);
|
|
312
|
+
if (!resolved) throw new Error(`Couldn't find "${plan.command}" to set up browser tools.`);
|
|
313
|
+
const [bin, argv, opts] = buildSpawnSpec(resolved, plan.args, cwd || null);
|
|
314
|
+
opts.env = { ...(opts.env || process.env), NO_COLOR: '1', FORCE_COLOR: '0', CLICOLOR: '0', TERM: 'dumb' };
|
|
315
|
+
await new Promise((resolve, reject) => {
|
|
316
|
+
let child;
|
|
317
|
+
try { child = spawn(bin, argv, opts); } catch (e) { return reject(new Error(`Failed to start ${plan.command}: ${e.message}`)); }
|
|
318
|
+
let stderr = '';
|
|
319
|
+
const timer = setTimeout(() => {
|
|
320
|
+
child.kill('SIGKILL');
|
|
321
|
+
reject(new Error(`${plan.command} MCP setup timed out.`));
|
|
322
|
+
}, 20_000);
|
|
323
|
+
child.stderr.on('data', (d) => (stderr += d.toString()));
|
|
324
|
+
child.on('error', (e) => {
|
|
325
|
+
clearTimeout(timer);
|
|
326
|
+
reject(new Error(`Failed to start ${plan.command}: ${e.message}`));
|
|
327
|
+
});
|
|
328
|
+
child.on('close', (code) => {
|
|
329
|
+
clearTimeout(timer);
|
|
330
|
+
if (code === 0) return resolve();
|
|
331
|
+
reject(new Error(`${plan.command} MCP setup exited ${code}: ${stderr.trim().split('\n').pop() || 'failed'}`));
|
|
332
|
+
});
|
|
333
|
+
try { child.stdin.end(); } catch { /* ignore */ }
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
export async function ensureStableMcpConfig(spec, cwd, label, emit, deps = {}) {
|
|
338
|
+
if (!spec.requiresStableMcp) return true;
|
|
339
|
+
const hasConfig = deps.hasConfig || hasStableMcpConfig;
|
|
340
|
+
const runSetup = deps.runSetup || runStableMcpSetup;
|
|
341
|
+
if (await hasConfig(spec, cwd)) return true;
|
|
342
|
+
|
|
343
|
+
const setupCommand = stableMcpSetupCommand(spec);
|
|
344
|
+
if (!spec.autoSetupStableMcp) {
|
|
345
|
+
const text = `${label} needs one-time browser-tool setup: ${setupCommand}`;
|
|
346
|
+
emit({ type: 'status', text });
|
|
347
|
+
throw new Error(text);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
const plan = stableMcpSetupPlan(spec);
|
|
351
|
+
if (!plan?.command) {
|
|
352
|
+
const text = `${label} needs one-time browser-tool setup: ${setupCommand}`;
|
|
353
|
+
emit({ type: 'status', text });
|
|
354
|
+
throw new Error(text);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
emit({ type: 'status', text: `${label} is setting up one-time browser tools...` });
|
|
358
|
+
await runSetup(plan, cwd);
|
|
359
|
+
if (await hasConfig(spec, cwd)) return true;
|
|
360
|
+
throw new Error(`${label} browser-tool setup completed, but the MCP server is still not visible. Run: ${setupCommand}`);
|
|
361
|
+
}
|
|
362
|
+
|
|
297
363
|
export async function chat({ messages, system, options, images }, emit) {
|
|
298
364
|
// Pro gate — verified, not just UI. No valid signed entitlement → no run.
|
|
299
365
|
if (!(await isProEntitled(options.entitlement))) {
|
|
@@ -373,19 +439,10 @@ export async function runSpec(spec, { messages, system, options = {}, images },
|
|
|
373
439
|
: [...tmpl.split(/\s+/).filter(Boolean), cfgFile];
|
|
374
440
|
args = [...tokens, ...args];
|
|
375
441
|
}
|
|
376
|
-
if (options.mcp?.url
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
// never a per-run/project file — so we can't inject it here. opencode reaches
|
|
381
|
-
// the browser tools via the bridge's STABLE /mcp endpoint, registered once with
|
|
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
|
-
}
|
|
442
|
+
if (options.mcp?.url) args.push(...mcpTrustArgs(spec, options.mcp));
|
|
443
|
+
// Some CLIs only load MCP from their persistent config, so ensure that stable
|
|
444
|
+
// /mcp endpoint is present before letting the agent answer with no tools.
|
|
445
|
+
if (options.mcp?.url) await ensureStableMcpConfig(spec, cwd, label, emit);
|
|
389
446
|
|
|
390
447
|
const imageTokens = imageTokensFor(spec.imageArg, imageFiles);
|
|
391
448
|
let placedImages = false;
|
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.8';
|
|
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
|
|