@chatpanel/bridge 0.10.38 → 0.10.40

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.38",
3
+ "version": "0.10.40",
4
4
  "type": "module",
5
5
  "description": "Local bridge that exposes the AI coding agents installed on your machine \u2014 Claude Code (CLI), Codex (CLI), and Antigravity CLI (formerly Gemini CLI, which remains available for business/enterprise) \u2014 to the ChatPanel Chrome extension over a localhost SSE endpoint. Bring your own agent.",
6
6
  "keywords": [
@@ -50,7 +50,9 @@ let lastProbe = 0;
50
50
  export async function available() {
51
51
  // Cache a positive result; keep re-probing (throttled) while not found so it
52
52
  // self-heals once agy appears on PATH — never cache a negative forever.
53
- if (!installed && Date.now() - lastProbe > 4000) {
53
+ // Re-probe in both directions (see codex.js): an uninstalled CLI must stop reporting itself
54
+ // available without waiting for a bridge restart.
55
+ if (Date.now() - lastProbe > (installed ? 30_000 : 4000)) {
54
56
  lastProbe = Date.now();
55
57
  try {
56
58
  installed = !!findAgentBin('agy');
@@ -54,7 +54,9 @@ let cachedOk = false;
54
54
  export async function available() {
55
55
  // Availability = "can we launch claude somehow" (native / cli.js / WSL / SDK),
56
56
  // NOT "does `claude --version` exit 0" (which fails when it just needs login).
57
- if (!cachedOk && Date.now() - lastProbe > 4000) {
57
+ // Re-probe in both directions (see codex.js): caching success forever meant an uninstalled
58
+ // CLI still reported itself available.
59
+ if (Date.now() - lastProbe > (cachedOk ? 30_000 : 4000)) {
58
60
  lastProbe = Date.now();
59
61
  try {
60
62
  const spec = resolveClaude();
@@ -21,9 +21,13 @@ function makeCliAgent(command, spec, notFoundHint, overrides = {}) {
21
21
  return {
22
22
  spec: resolvedSpec,
23
23
  async available() {
24
- // Cache a positive result; re-probe (throttled) while not found so it
25
- // self-heals once the CLI appears on PATH.
26
- if (!installed && Date.now() - lastProbe > 4000) {
24
+ // Re-probe in BOTH directions. Caching a positive result forever meant an UNINSTALLED
25
+ // CLI kept reporting itself available until the bridge restarted the picker showed a
26
+ // green dot for an agent that could no longer run, and the turn failed with "couldn't
27
+ // find it on your PATH". A found CLI is re-checked on a slow interval (it rarely
28
+ // disappears, and `which` is cheap but not free); a missing one keeps the fast interval
29
+ // so it still self-heals the moment it is installed.
30
+ if (Date.now() - lastProbe > (installed ? 30_000 : 4000)) {
27
31
  lastProbe = Date.now();
28
32
  try {
29
33
  installed = !!findAgentBin(command);
@@ -77,10 +77,10 @@ function ensureIsolatedHome() {
77
77
  let installed = false;
78
78
  let lastProbe = 0;
79
79
  export async function available() {
80
- // Availability = "is codex findable on PATH", not "does `codex --version` exit
81
- // 0" (which fails when the CLI just needs login). Cache positives; re-probe
82
- // (throttled) while not found so it self-heals once codex appears on PATH.
83
- if (!installed && Date.now() - lastProbe > 4000) {
80
+ // Availability = "is codex findable on PATH", not "does `codex --version` exit 0" (which
81
+ // fails when the CLI just needs login). Re-probed in BOTH directions: caching a positive
82
+ // forever kept an uninstalled CLI reporting itself available until the bridge restarted.
83
+ if (Date.now() - lastProbe > (installed ? 30_000 : 4000)) {
84
84
  lastProbe = Date.now();
85
85
  try {
86
86
  installed = !!findAgentBin('codex');
@@ -333,18 +333,23 @@ async function runStableMcpSetup(plan, cwd) {
333
333
  let child;
334
334
  try { child = spawn(bin, argv, opts); } catch (e) { return reject(new Error(`Failed to start ${plan.command}: ${e.message}`)); }
335
335
  let stderr = '';
336
+ // The CLI often reports WHY it couldn't register on stdout while still exiting 0 (Hermes
337
+ // prints "requires the 'mcp' Python SDK" and saves nothing). Capturing it is what lets
338
+ // the caller turn a silent no-op into an actionable message.
339
+ let stdout = '';
336
340
  const timer = setTimeout(() => {
337
341
  child.kill('SIGKILL');
338
342
  reject(new Error(`${plan.command} MCP setup timed out.`));
339
343
  }, 20_000);
340
344
  child.stderr.on('data', (d) => (stderr += d.toString()));
345
+ child.stdout?.on('data', (d) => (stdout += d.toString()));
341
346
  child.on('error', (e) => {
342
347
  clearTimeout(timer);
343
348
  reject(new Error(`Failed to start ${plan.command}: ${e.message}`));
344
349
  });
345
350
  child.on('close', (code) => {
346
351
  clearTimeout(timer);
347
- if (code === 0) return resolve();
352
+ if (code === 0) return resolve(`${stdout}\n${stderr}`.trim());
348
353
  reject(new Error(`${plan.command} MCP setup exited ${code}: ${stderr.trim().split('\n').pop() || 'failed'}`));
349
354
  });
350
355
  try { child.stdin.end(); } catch { /* ignore */ }
@@ -386,9 +391,15 @@ export async function ensureStableMcpConfig(spec, cwd, label, emit, deps = {}) {
386
391
  }
387
392
 
388
393
  emit({ type: 'status', text: `${label} is setting up one-time browser tools...` });
389
- await runSetup(plan, cwd);
394
+ // Keep what the CLI said: it commonly exits 0 while refusing to register (no MCP SDK, no
395
+ // HTTP transport), and that sentence is the only accurate explanation available.
396
+ const setupOutput = String((await runSetup(plan, cwd)) || '').trim();
390
397
  if (await hasConfig(spec, cwd)) return true;
391
- throw new Error(`${label} browser-tool setup completed, but the MCP server is still not visible. Run: ${setupCommand}`);
398
+ throw new Error(
399
+ `${label} browser-tool setup did not register the server.`
400
+ + (setupOutput ? ` ${setupOutput.split('\n').filter(Boolean).slice(-3).join(' ')}` : '')
401
+ + ` Run: ${setupCommand}`,
402
+ );
392
403
  }
393
404
 
394
405
  export async function chat({ messages, system, options, images }, emit, { signal } = {}) {
package/src/server.js CHANGED
@@ -67,7 +67,7 @@ import {
67
67
  // Hardcoded (not read from package.json) so it survives Bun's single-file
68
68
  // --compile, where package.json isn't on a readable FS. CI fails the publish if
69
69
  // this drifts from package.json, so the two can't silently diverge.
70
- const VERSION = '0.10.38';
70
+ const VERSION = '0.10.40';
71
71
  const HOST = process.env.CHATPANEL_BRIDGE_HOST || '127.0.0.1';
72
72
  const PORT = Number(process.env.CHATPANEL_BRIDGE_PORT) || 4319;
73
73