@chatpanel/bridge 0.10.4 → 0.10.6

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chatpanel/bridge",
3
- "version": "0.10.4",
3
+ "version": "0.10.6",
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": [
@@ -15,7 +15,9 @@ import { findAgentBin } from '../env.js';
15
15
  function makeCliAgent(command, spec, notFoundHint) {
16
16
  let installed = false;
17
17
  let lastProbe = 0;
18
+ const resolvedSpec = { ...spec, command };
18
19
  return {
20
+ spec: resolvedSpec,
19
21
  async available() {
20
22
  // Cache a positive result; re-probe (throttled) while not found so it
21
23
  // self-heals once the CLI appears on PATH.
@@ -33,7 +35,7 @@ function makeCliAgent(command, spec, notFoundHint) {
33
35
  return listSpecModels(command, spec.listModelsArgs, options.workingDir);
34
36
  },
35
37
  chat(input, emit) {
36
- return runSpec({ ...spec, command }, input, emit);
38
+ return runSpec(resolvedSpec, input, emit);
37
39
  },
38
40
  };
39
41
  }
@@ -67,6 +69,7 @@ export const opencode = makeCliAgent(
67
69
  // with `opencode mcp add chatpanel --url http://127.0.0.1:4319/mcp` (opencode
68
70
  // only loads MCP from its global config, not a per-run file).
69
71
  requiresStableMcp: true,
72
+ stableMcpConfigCheck: 'opencode',
70
73
  listModelsArgs: 'models',
71
74
  label: 'OpenCode',
72
75
  },
@@ -76,9 +79,13 @@ export const opencode = makeCliAgent(
76
79
  export const kiro = makeCliAgent(
77
80
  'kiro-cli',
78
81
  {
79
- args: 'chat --no-interactive',
82
+ args: 'chat --no-interactive --require-mcp-startup',
80
83
  promptVia: 'arg',
81
84
  modelArg: '--model {model}',
85
+ trustToolsArg: '--trust-tools={tools}',
86
+ requiresStableMcp: true,
87
+ stableMcpConfigCheck: 'kiro',
88
+ stableMcpSetupCommand: 'kiro-cli mcp add --scope global --name chatpanel_browser --url http://127.0.0.1:4319/mcp --force',
82
89
  listModelsArgs: '--list-models',
83
90
  label: 'Kiro',
84
91
  },
@@ -66,6 +66,7 @@ const IDLE_MS = Number(process.env.CHATPANEL_CUSTOM_TIMEOUT_MS) || 180_000;
66
66
  const ANSI_RE = /\u001b\[[0-9;?]*[ -/]*[@-~]/g;
67
67
  const stripAnsi = (s) => s.replace(ANSI_RE, '');
68
68
  const OPENCODE_STABLE_MCP_URL = 'http://127.0.0.1:4319/mcp';
69
+ const CHATPANEL_STABLE_MCP_URL = 'http://127.0.0.1:4319/mcp';
69
70
 
70
71
  export async function available() {
71
72
  // The engine ships in every bridge; individual custom agents are user-defined
@@ -139,6 +140,21 @@ export function piToolArgs(extensionFile, mcp) {
139
140
  return ['--extension', extensionFile];
140
141
  }
141
142
 
143
+ export function trustToolArgs(template, mcp) {
144
+ const tmpl = String(template || '').trim();
145
+ const names = mcpToolSpecs(mcp).map((s) => s.name).filter(Boolean);
146
+ if (!tmpl || !names.length) return [];
147
+ const value = names.join(',');
148
+ return tmpl.includes('{tools}')
149
+ ? tmpl.replaceAll('{tools}', value).split(/\s+/).filter(Boolean)
150
+ : [...tmpl.split(/\s+/).filter(Boolean), value];
151
+ }
152
+
153
+ export function stableMcpSetupCommand(spec = {}) {
154
+ if (spec.stableMcpSetupCommand) return spec.stableMcpSetupCommand;
155
+ return `opencode mcp add chatpanel --url ${CHATPANEL_STABLE_MCP_URL}`;
156
+ }
157
+
142
158
  export function buildPiExtensionSource(mcp) {
143
159
  const specs = mcpToolSpecs(mcp);
144
160
  const declarations = specs.map((spec, index) => {
@@ -240,6 +256,44 @@ async function opencodeHasStableMcpConfig() {
240
256
  return false;
241
257
  }
242
258
 
259
+ async function commandStdout(command, args, cwd) {
260
+ const resolved = resolveCommand(command);
261
+ if (!resolved) return '';
262
+ const [bin, argv, opts] = buildSpawnSpec(resolved, args, cwd || null);
263
+ return new Promise((resolve) => {
264
+ const child = spawn(bin, argv, opts);
265
+ let out = '';
266
+ const timer = setTimeout(() => {
267
+ child.kill('SIGKILL');
268
+ resolve(out);
269
+ }, 4000);
270
+ child.stdout.on('data', (d) => (out += d.toString()));
271
+ child.on('error', () => {
272
+ clearTimeout(timer);
273
+ resolve('');
274
+ });
275
+ child.on('close', () => {
276
+ clearTimeout(timer);
277
+ resolve(out);
278
+ });
279
+ try { child.stdin.end(); } catch { /* ignore */ }
280
+ });
281
+ }
282
+
283
+ async function kiroHasStableMcpConfig(command, cwd) {
284
+ for (const scope of ['workspace', 'global', 'default']) {
285
+ const out = await commandStdout(command, ['mcp', 'list', scope], cwd);
286
+ if (out.includes(CHATPANEL_STABLE_MCP_URL) || /chatpanel_browser/i.test(out)) return true;
287
+ }
288
+ return false;
289
+ }
290
+
291
+ async function hasStableMcpConfig(spec, cwd) {
292
+ if (spec.stableMcpConfigCheck === 'kiro') return kiroHasStableMcpConfig(spec.command || 'kiro-cli', cwd);
293
+ if (spec.stableMcpConfigCheck === 'opencode') return opencodeHasStableMcpConfig();
294
+ return false;
295
+ }
296
+
243
297
  export async function chat({ messages, system, options, images }, emit) {
244
298
  // Pro gate — verified, not just UI. No valid signed entitlement → no run.
245
299
  if (!(await isProEntitled(options.entitlement))) {
@@ -319,14 +373,17 @@ export async function runSpec(spec, { messages, system, options = {}, images },
319
373
  : [...tmpl.split(/\s+/).filter(Boolean), cfgFile];
320
374
  args = [...tokens, ...args];
321
375
  }
376
+ if (options.mcp?.url && spec.trustToolsArg) {
377
+ args.push(...trustToolArgs(spec.trustToolsArg, options.mcp));
378
+ }
322
379
  // NOTE: opencode only loads MCP from its GLOBAL config (~/.config/opencode),
323
380
  // never a per-run/project file — so we can't inject it here. opencode reaches
324
381
  // the browser tools via the bridge's STABLE /mcp endpoint, registered once with
325
382
  // `opencode mcp add chatpanel --url http://127.0.0.1:4319/mcp`.
326
- if (options.mcp?.url && spec.requiresStableMcp && !(await opencodeHasStableMcpConfig())) {
383
+ if (options.mcp?.url && spec.requiresStableMcp && !(await hasStableMcpConfig(spec, cwd))) {
327
384
  emit({
328
385
  type: 'status',
329
- text: `OpenCode needs one-time browser-tool setup: opencode mcp add chatpanel --url ${OPENCODE_STABLE_MCP_URL}`,
386
+ text: `${label} needs one-time browser-tool setup: ${stableMcpSetupCommand(spec)}`,
330
387
  });
331
388
  }
332
389
 
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, ['-ilc', `command -v ${name} 2>/dev/null`], {
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, ['-ilc', 'command -p echo "$PATH"'], {
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.4';
33
+ const VERSION = '0.10.6';
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