@chatpanel/bridge 0.2.6 → 0.2.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chatpanel/bridge",
3
- "version": "0.2.6",
3
+ "version": "0.2.7",
4
4
  "type": "module",
5
5
  "description": "Local bridge that exposes the AI coding agents installed on your machine — Claude Code (Agent SDK), Codex (CLI), and Gemini CLI — to the ChatPanel Chrome extension over a localhost SSE endpoint. Bring your own agent.",
6
6
  "keywords": [
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env bash
2
- # ChatPanel Bridge installer downloads the standalone binary for your OS and
2
+ # ChatPanel Bridge installer - downloads the standalone binary for your OS and
3
3
  # sets it to start at login. No Node.js required.
4
4
  #
5
5
  # curl -fsSL https://raw.githubusercontent.com/chatpanel/chatpanel-bridge/main/scripts/install.sh | bash
@@ -11,35 +11,42 @@ set -euo pipefail
11
11
  REPO="chatpanel/chatpanel-bridge"
12
12
  os="$(uname -s)"
13
13
  arch="$(uname -m)"
14
+ asset=""
14
15
 
15
16
  case "$os" in
16
17
  Darwin)
17
- if [ "$arch" = "arm64" ]; then asset="chatpanel-bridge-macos-arm64"
18
+ if [ "$arch" = "arm64" ]; then
19
+ asset="chatpanel-bridge-macos-arm64"
18
20
  else
19
- echo "Intel Mac detected no x64 binary yet. Use: npx @chatpanel/bridge (needs Node.js 18+)"
21
+ echo "Intel Mac detected - no x64 binary yet. Use: npx @chatpanel/bridge (needs Node.js 18+)"
20
22
  exit 1
21
- fi ;;
22
- Linux) asset="chatpanel-bridge-linux-x64" ;;
23
+ fi
24
+ ;;
25
+ Linux)
26
+ asset="chatpanel-bridge-linux-x64"
27
+ ;;
23
28
  *)
24
29
  echo "Unsupported OS ($os). Use: npx @chatpanel/bridge (needs Node.js 18+)"
25
- exit 1 ;;
30
+ exit 1
31
+ ;;
26
32
  esac
27
33
 
28
- url="https://github.com/$REPO/releases/latest/download/$asset"
29
- dest="$HOME/.local/bin"
30
- bin="$dest/chatpanel-bridge"
34
+ url="https://github.com/${REPO}/releases/latest/download/${asset}"
35
+ dest="${HOME}/.local/bin"
36
+ bin="${dest}/chatpanel-bridge"
31
37
  mkdir -p "$dest"
32
38
 
33
- echo "Downloading $asset"
39
+ echo "Downloading ${asset} ..."
34
40
  curl -fsSL "$url" -o "$bin"
35
41
  chmod +x "$bin"
36
- xattr -c "$bin" 2>/dev/null || true # belt-and-suspenders; curl files aren't quarantined
42
+ xattr -c "$bin" 2>/dev/null || true # belt-and-suspenders; curl files aren't quarantined
37
43
 
38
- echo "Installed to $bin"
44
+ echo "Installed to ${bin}"
39
45
  "$bin" --install
40
46
  echo
41
47
  echo "ChatPanel Bridge is running and will start at login."
42
- case ":$PATH:" in
43
- *":$dest:"*) : ;;
44
- *) echo "Tip: add it to your PATH → export PATH=\"\$HOME/.local/bin:\$PATH\"" ;;
48
+
49
+ case ":${PATH}:" in
50
+ *":${dest}:"*) : ;;
51
+ *) echo "Tip: add it to your PATH -> export PATH=\"\$HOME/.local/bin:\$PATH\"" ;;
45
52
  esac
@@ -19,6 +19,7 @@ import { readFile, unlink } from 'node:fs/promises';
19
19
  import { existsSync, mkdirSync, symlinkSync } from 'node:fs';
20
20
  import os from 'node:os';
21
21
  import path from 'node:path';
22
+ import { findAgentBin } from '../env.js';
22
23
 
23
24
  const TIMEOUT_MS = Number(process.env.CHATPANEL_CODEX_TIMEOUT_MS) || 180_000;
24
25
  const REASONING = process.env.CHATPANEL_CODEX_EFFORT ?? 'low'; // '' → respect config
@@ -61,13 +62,13 @@ function ensureIsolatedHome() {
61
62
  let installed = false;
62
63
  let lastProbe = 0;
63
64
  export async function available() {
64
- // Cache a positive result, but keep re-probing (throttled) while not found, so
65
- // it self-heals once codex appears on PATH never cache a negative forever.
65
+ // Availability = "is codex findable on PATH", not "does `codex --version` exit
66
+ // 0" (which fails when the CLI just needs login). Cache positives; re-probe
67
+ // (throttled) while not found so it self-heals once codex appears on PATH.
66
68
  if (!installed && Date.now() - lastProbe > 4000) {
67
69
  lastProbe = Date.now();
68
70
  try {
69
- const r = spawnSync('codex', ['--version'], { stdio: 'ignore', timeout: 8000 });
70
- installed = r.status === 0 || (r.status === null && !r.error);
71
+ installed = !!findAgentBin('codex');
71
72
  } catch {
72
73
  installed = false;
73
74
  }
@@ -12,6 +12,7 @@ import { spawn, spawnSync } from 'node:child_process';
12
12
  import { mkdirSync } from 'node:fs';
13
13
  import os from 'node:os';
14
14
  import path from 'node:path';
15
+ import { findAgentBin } from '../env.js';
15
16
 
16
17
  const TIMEOUT_MS = Number(process.env.CHATPANEL_GEMINI_TIMEOUT_MS) || 180_000;
17
18
  const SCRATCH = path.join(os.tmpdir(), 'chatpanel-gemini-scratch');
@@ -24,8 +25,7 @@ export async function available() {
24
25
  if (!installed && Date.now() - lastProbe > 4000) {
25
26
  lastProbe = Date.now();
26
27
  try {
27
- const r = spawnSync('gemini', ['--version'], { stdio: 'ignore', timeout: 8000 });
28
- installed = r.status === 0 || (r.status === null && !r.error);
28
+ installed = !!findAgentBin('gemini');
29
29
  } catch {
30
30
  installed = false;
31
31
  }
package/src/env.js CHANGED
@@ -21,6 +21,20 @@ function onPath(name) {
21
21
  return dirs.some((d) => d && (existsSync(path.join(d, name)) || existsSync(path.join(d, name + '.exe'))));
22
22
  }
23
23
 
24
+ // Resolve an agent CLI to its absolute path: first on the (enriched) PATH, then
25
+ // by asking the login shell. Returns the path, or null. Used for availability —
26
+ // "is it installed/findable", NOT "does `--version` exit 0" (which can fail for
27
+ // reasons unrelated to installation, e.g. the CLI needs login).
28
+ export function findAgentBin(name) {
29
+ const dirs = (process.env.PATH || '').split(path.delimiter);
30
+ for (const d of dirs) {
31
+ if (!d) continue;
32
+ const p = path.join(d, name);
33
+ if (existsSync(p) || existsSync(p + '.exe')) return p;
34
+ }
35
+ return shellWhich(name) || null;
36
+ }
37
+
24
38
  // Ask the user's login shell to locate a command — no hardcoded locations, works
25
39
  // wherever the user actually installed it.
26
40
  function shellWhich(name) {
package/src/server.js CHANGED
@@ -15,13 +15,14 @@
15
15
  // Binds to 127.0.0.1 only and accepts requests from the extension origin.
16
16
 
17
17
  import { createServer } from 'node:http';
18
+ import os from 'node:os';
18
19
  import * as claude from './engines/claude.js';
19
20
  import * as codex from './engines/codex.js';
20
21
  import * as gemini from './engines/gemini.js';
21
22
  import { installService, uninstallService, serviceStatus } from './service.js';
22
- import { enrichPath } from './env.js';
23
+ import { enrichPath, findAgentBin } from './env.js';
23
24
 
24
- const VERSION = '0.2.6';
25
+ const VERSION = '0.2.7';
25
26
  const HOST = process.env.CHATPANEL_BRIDGE_HOST || '127.0.0.1';
26
27
  const PORT = Number(process.env.CHATPANEL_BRIDGE_PORT) || 4319;
27
28
 
@@ -175,6 +176,15 @@ const server = createServer(async (req, res) => {
175
176
  const url = new URL(req.url, `http://${req.headers.host}`);
176
177
  try {
177
178
  if (req.method === 'GET' && url.pathname === '/health') return handleHealth(res);
179
+ if (req.method === 'GET' && url.pathname === '/debug') {
180
+ return json(res, 200, {
181
+ version: VERSION,
182
+ home: os.homedir(),
183
+ codex: findAgentBin('codex') || null,
184
+ gemini: findAgentBin('gemini') || null,
185
+ path: process.env.PATH,
186
+ });
187
+ }
178
188
  if (req.method === 'POST' && url.pathname === '/chat') return handleChat(req, res);
179
189
  if (req.method === 'POST' && url.pathname === '/complete') return handleComplete(req, res);
180
190
  json(res, 404, { error: 'Not found' });