@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 +1 -1
- package/scripts/install.sh +22 -15
- package/src/engines/codex.js +5 -4
- package/src/engines/gemini.js +2 -2
- package/src/env.js +14 -0
- package/src/server.js +12 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chatpanel/bridge",
|
|
3
|
-
"version": "0.2.
|
|
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": [
|
package/scripts/install.sh
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
|
-
# ChatPanel Bridge installer
|
|
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
|
|
18
|
+
if [ "$arch" = "arm64" ]; then
|
|
19
|
+
asset="chatpanel-bridge-macos-arm64"
|
|
18
20
|
else
|
|
19
|
-
echo "Intel Mac detected
|
|
21
|
+
echo "Intel Mac detected - no x64 binary yet. Use: npx @chatpanel/bridge (needs Node.js 18+)"
|
|
20
22
|
exit 1
|
|
21
|
-
fi
|
|
22
|
-
|
|
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 "
|
|
39
|
+
echo "Downloading ${asset} ..."
|
|
34
40
|
curl -fsSL "$url" -o "$bin"
|
|
35
41
|
chmod +x "$bin"
|
|
36
|
-
xattr -c "$bin" 2>/dev/null || true
|
|
42
|
+
xattr -c "$bin" 2>/dev/null || true # belt-and-suspenders; curl files aren't quarantined
|
|
37
43
|
|
|
38
|
-
echo "
|
|
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
|
-
|
|
43
|
-
|
|
44
|
-
*)
|
|
48
|
+
|
|
49
|
+
case ":${PATH}:" in
|
|
50
|
+
*":${dest}:"*) : ;;
|
|
51
|
+
*) echo "Tip: add it to your PATH -> export PATH=\"\$HOME/.local/bin:\$PATH\"" ;;
|
|
45
52
|
esac
|
package/src/engines/codex.js
CHANGED
|
@@ -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
|
-
//
|
|
65
|
-
//
|
|
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
|
-
|
|
70
|
-
installed = r.status === 0 || (r.status === null && !r.error);
|
|
71
|
+
installed = !!findAgentBin('codex');
|
|
71
72
|
} catch {
|
|
72
73
|
installed = false;
|
|
73
74
|
}
|
package/src/engines/gemini.js
CHANGED
|
@@ -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
|
-
|
|
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.
|
|
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' });
|