@chatpanel/bridge 0.2.7 → 0.2.9
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/README.md +13 -6
- package/package.json +1 -1
- package/scripts/install.ps1 +54 -0
- package/scripts/install.sh +10 -6
- package/src/engines/claude.js +30 -3
- package/src/env.js +14 -2
- package/src/server.js +1 -1
- package/src/service.js +17 -7
package/README.md
CHANGED
|
@@ -14,16 +14,23 @@ the ones the bridge reports as available.
|
|
|
14
14
|
|
|
15
15
|
## Run it
|
|
16
16
|
|
|
17
|
-
### Option A — one-line install (
|
|
17
|
+
### Option A — one-line install (no Node.js needed)
|
|
18
18
|
|
|
19
|
+
**macOS / Linux:**
|
|
19
20
|
```bash
|
|
20
|
-
curl -fsSL https://
|
|
21
|
+
curl -fsSL https://dl.chatpanel.net/bridge/install.sh | bash
|
|
21
22
|
```
|
|
22
23
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
**Windows (PowerShell):**
|
|
25
|
+
```powershell
|
|
26
|
+
irm https://dl.chatpanel.net/bridge/install.ps1 | iex
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
This downloads the standalone binary for your OS, installs it, and sets it to
|
|
30
|
+
start at login. **Recommended** — installing this way avoids the macOS "damaged"
|
|
31
|
+
and Windows SmartScreen prompts that browser downloads trigger. Re-running it is a
|
|
32
|
+
clean in-place upgrade (no duplicate installs). Then open the ChatPanel side panel
|
|
33
|
+
and your agents appear.
|
|
27
34
|
|
|
28
35
|
Manage it: `chatpanel-bridge --status` · `--uninstall` · run with no flags to start
|
|
29
36
|
once in the foreground.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chatpanel/bridge",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.9",
|
|
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": [
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# ChatPanel Bridge installer (Windows) - no Node.js required.
|
|
2
|
+
#
|
|
3
|
+
# irm https://dl.chatpanel.net/bridge/install.ps1 | iex
|
|
4
|
+
#
|
|
5
|
+
# Note: no `$ErrorActionPreference = 'Stop'` here, because native tools (schtasks,
|
|
6
|
+
# curl) write progress/notices to stderr and that would be treated as fatal. We
|
|
7
|
+
# check exit codes explicitly instead.
|
|
8
|
+
$ProgressPreference = 'SilentlyContinue'
|
|
9
|
+
|
|
10
|
+
$url = 'https://dl.chatpanel.net/bridge/windows-x64.exe'
|
|
11
|
+
$dir = Join-Path $env:LOCALAPPDATA 'ChatPanel'
|
|
12
|
+
$bin = Join-Path $dir 'chatpanel-bridge.exe'
|
|
13
|
+
$tmp = "$bin.new"
|
|
14
|
+
|
|
15
|
+
Write-Host ""
|
|
16
|
+
Write-Host "Installing ChatPanel Bridge" -ForegroundColor Cyan
|
|
17
|
+
|
|
18
|
+
# Stop any running bridge + its scheduled task for a clean in-place upgrade.
|
|
19
|
+
# Run schtasks inside cmd so its "not found" stderr never reaches PowerShell.
|
|
20
|
+
cmd /c "schtasks /End /TN ChatPanelBridge >nul 2>&1"
|
|
21
|
+
Get-Process -Name 'chatpanel-bridge' -ErrorAction SilentlyContinue | Stop-Process -Force -ErrorAction SilentlyContinue
|
|
22
|
+
Start-Sleep -Milliseconds 600
|
|
23
|
+
|
|
24
|
+
New-Item -ItemType Directory -Force -Path $dir | Out-Null
|
|
25
|
+
|
|
26
|
+
Write-Host " Downloading the bridge (~60 MB)..." -ForegroundColor Gray
|
|
27
|
+
$ok = $false
|
|
28
|
+
if (Get-Command curl.exe -ErrorAction SilentlyContinue) {
|
|
29
|
+
& curl.exe -fL --progress-bar -o "$tmp" "$url" # fast, real progress bar
|
|
30
|
+
$ok = ($LASTEXITCODE -eq 0)
|
|
31
|
+
} else {
|
|
32
|
+
try { Invoke-WebRequest -Uri $url -OutFile $tmp -UseBasicParsing; $ok = $true } catch { $ok = $false }
|
|
33
|
+
}
|
|
34
|
+
if (-not $ok -or -not (Test-Path "$tmp")) {
|
|
35
|
+
Write-Host "Download failed - check your connection and re-run." -ForegroundColor Red
|
|
36
|
+
return
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
Unblock-File -Path "$tmp" -ErrorAction SilentlyContinue # no SmartScreen mark-of-the-web
|
|
40
|
+
Move-Item -Force "$tmp" "$bin"
|
|
41
|
+
|
|
42
|
+
Write-Host " Setting it to start at login..." -ForegroundColor Gray
|
|
43
|
+
& "$bin" --install
|
|
44
|
+
$installed = ($LASTEXITCODE -eq 0)
|
|
45
|
+
|
|
46
|
+
Write-Host ""
|
|
47
|
+
if ($installed) {
|
|
48
|
+
Write-Host "Done. ChatPanel Bridge is running and starts at login." -ForegroundColor Green
|
|
49
|
+
Write-Host "Open the ChatPanel side panel - your agents appear automatically."
|
|
50
|
+
} else {
|
|
51
|
+
Write-Host "Installed, but auto-start setup hit an issue. Start it manually with:" -ForegroundColor Yellow
|
|
52
|
+
Write-Host " `"$bin`""
|
|
53
|
+
}
|
|
54
|
+
Write-Host "Manage it: `"$bin`" --status | --uninstall"
|
package/scripts/install.sh
CHANGED
|
@@ -8,7 +8,6 @@
|
|
|
8
8
|
# "damaged / unidentified developer" prompt that browser downloads trigger.
|
|
9
9
|
set -euo pipefail
|
|
10
10
|
|
|
11
|
-
REPO="chatpanel/chatpanel-bridge"
|
|
12
11
|
os="$(uname -s)"
|
|
13
12
|
arch="$(uname -m)"
|
|
14
13
|
asset=""
|
|
@@ -16,14 +15,14 @@ asset=""
|
|
|
16
15
|
case "$os" in
|
|
17
16
|
Darwin)
|
|
18
17
|
if [ "$arch" = "arm64" ]; then
|
|
19
|
-
asset="
|
|
18
|
+
asset="bridge/macos-arm64"
|
|
20
19
|
else
|
|
21
20
|
echo "Intel Mac detected - no x64 binary yet. Use: npx @chatpanel/bridge (needs Node.js 18+)"
|
|
22
21
|
exit 1
|
|
23
22
|
fi
|
|
24
23
|
;;
|
|
25
24
|
Linux)
|
|
26
|
-
asset="
|
|
25
|
+
asset="bridge/linux-x64"
|
|
27
26
|
;;
|
|
28
27
|
*)
|
|
29
28
|
echo "Unsupported OS ($os). Use: npx @chatpanel/bridge (needs Node.js 18+)"
|
|
@@ -31,16 +30,21 @@ case "$os" in
|
|
|
31
30
|
;;
|
|
32
31
|
esac
|
|
33
32
|
|
|
34
|
-
url="https://
|
|
33
|
+
url="https://dl.chatpanel.net/${asset}"
|
|
35
34
|
dest="${HOME}/.local/bin"
|
|
36
35
|
bin="${dest}/chatpanel-bridge"
|
|
37
36
|
mkdir -p "$dest"
|
|
38
37
|
|
|
39
|
-
echo "Downloading
|
|
40
|
-
curl -
|
|
38
|
+
echo "Downloading ChatPanel Bridge (~60 MB)..."
|
|
39
|
+
curl -fL --progress-bar "$url" -o "$bin" # show a progress bar (not silent)
|
|
41
40
|
chmod +x "$bin"
|
|
42
41
|
xattr -c "$bin" 2>/dev/null || true # belt-and-suspenders; curl files aren't quarantined
|
|
43
42
|
|
|
43
|
+
# Clean upgrade: stop any running bridge (incl. a stray npx one) so the new
|
|
44
|
+
# install replaces it in place — same path, same service, no duplicates.
|
|
45
|
+
pkill -f 'chatpanel-bridge' 2>/dev/null || true
|
|
46
|
+
sleep 1
|
|
47
|
+
|
|
44
48
|
echo "Installed to ${bin}"
|
|
45
49
|
"$bin" --install
|
|
46
50
|
echo
|
package/src/engines/claude.js
CHANGED
|
@@ -9,6 +9,8 @@
|
|
|
9
9
|
|
|
10
10
|
import path from 'node:path';
|
|
11
11
|
import os from 'node:os';
|
|
12
|
+
import { existsSync } from 'node:fs';
|
|
13
|
+
import { findAgentBin, isCompiledBinary } from '../env.js';
|
|
12
14
|
|
|
13
15
|
let sdkPromise = null;
|
|
14
16
|
function loadSdk() {
|
|
@@ -16,14 +18,37 @@ function loadSdk() {
|
|
|
16
18
|
return sdkPromise;
|
|
17
19
|
}
|
|
18
20
|
|
|
21
|
+
// Where the Claude Code CLI lives. The SDK ships a bundled cli.js, but inside a
|
|
22
|
+
// compiled binary that file is on a virtual FS that child processes can't reach
|
|
23
|
+
// (it fails on Windows as "B:\~BUN\cli.js"). So in a binary we point the SDK at
|
|
24
|
+
// the user's INSTALLED Claude Code instead — preferring the real cli.js next to
|
|
25
|
+
// the npm shim (modern Node won't spawn a .cmd directly).
|
|
26
|
+
function claudeExecutable() {
|
|
27
|
+
if (process.env.CHATPANEL_CLAUDE_PATH) return process.env.CHATPANEL_CLAUDE_PATH;
|
|
28
|
+
if (!isCompiledBinary()) return undefined; // under node/bun the bundled cli.js works
|
|
29
|
+
const bin = findAgentBin('claude');
|
|
30
|
+
if (!bin) return undefined;
|
|
31
|
+
const dir = path.dirname(bin);
|
|
32
|
+
const candidates = [
|
|
33
|
+
path.join(dir, 'node_modules', '@anthropic-ai', 'claude-code', 'cli.js'),
|
|
34
|
+
path.join(dir, '..', 'lib', 'node_modules', '@anthropic-ai', 'claude-code', 'cli.js'),
|
|
35
|
+
];
|
|
36
|
+
for (const c of candidates) if (existsSync(c)) return c;
|
|
37
|
+
return bin;
|
|
38
|
+
}
|
|
39
|
+
|
|
19
40
|
export async function available() {
|
|
20
41
|
const sdk = await loadSdk();
|
|
21
42
|
if (!sdk) {
|
|
22
43
|
return { ok: false, reason: 'Agent SDK not installed (npm i in bridge/)' };
|
|
23
44
|
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
45
|
+
// In a compiled binary the bundled CLI is unreachable, so Claude Code must be
|
|
46
|
+
// installed locally. (Under node/bun the bundled CLI works, so this is skipped.)
|
|
47
|
+
if (isCompiledBinary() && !process.env.CHATPANEL_CLAUDE_PATH && !findAgentBin('claude')) {
|
|
48
|
+
return {
|
|
49
|
+
ok: false,
|
|
50
|
+
reason: 'Claude Code not found. Install it (npm i -g @anthropic-ai/claude-code), or run the bridge with `npx @chatpanel/bridge`.',
|
|
51
|
+
};
|
|
27
52
|
}
|
|
28
53
|
return { ok: true };
|
|
29
54
|
}
|
|
@@ -85,6 +110,7 @@ export async function chat({ messages, system, options }, emit) {
|
|
|
85
110
|
? { type: 'preset', preset: 'claude_code', append: system }
|
|
86
111
|
: { type: 'preset', preset: 'claude_code' },
|
|
87
112
|
...(options.model ? { model: options.model } : {}),
|
|
113
|
+
...(claudeExecutable() ? { pathToClaudeCodeExecutable: claudeExecutable() } : {}),
|
|
88
114
|
...(process.env.CHATPANEL_MAX_TURNS ? { maxTurns: Number(process.env.CHATPANEL_MAX_TURNS) } : {}),
|
|
89
115
|
},
|
|
90
116
|
});
|
|
@@ -140,6 +166,7 @@ export async function complete({ prompt, system, model }) {
|
|
|
140
166
|
settingSources: [], // skip CLAUDE.md / MCP for a tiny completion
|
|
141
167
|
systemPrompt: system || "Continue the user's text briefly. Reply with only the continuation.",
|
|
142
168
|
model: model || 'haiku',
|
|
169
|
+
...(claudeExecutable() ? { pathToClaudeCodeExecutable: claudeExecutable() } : {}),
|
|
143
170
|
},
|
|
144
171
|
});
|
|
145
172
|
for await (const message of iterator) {
|
package/src/env.js
CHANGED
|
@@ -26,15 +26,27 @@ function onPath(name) {
|
|
|
26
26
|
// "is it installed/findable", NOT "does `--version` exit 0" (which can fail for
|
|
27
27
|
// reasons unrelated to installation, e.g. the CLI needs login).
|
|
28
28
|
export function findAgentBin(name) {
|
|
29
|
+
// On Windows, CLIs are usually <name>.cmd / .exe / .bat (npm shims).
|
|
30
|
+
const exts = process.platform === 'win32' ? ['', '.cmd', '.exe', '.bat', '.ps1'] : [''];
|
|
29
31
|
const dirs = (process.env.PATH || '').split(path.delimiter);
|
|
30
32
|
for (const d of dirs) {
|
|
31
33
|
if (!d) continue;
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
+
for (const ext of exts) {
|
|
35
|
+
const p = path.join(d, name + ext);
|
|
36
|
+
if (existsSync(p)) return p;
|
|
37
|
+
}
|
|
34
38
|
}
|
|
35
39
|
return shellWhich(name) || null;
|
|
36
40
|
}
|
|
37
41
|
|
|
42
|
+
// True when running as a Bun/Node single-file compiled binary (not under a
|
|
43
|
+
// node/bun interpreter). Inside such a binary, bundled JS files live on a virtual
|
|
44
|
+
// FS that child processes can't reach — notably the Claude SDK's CLI on Windows.
|
|
45
|
+
export function isCompiledBinary() {
|
|
46
|
+
const base = path.basename(process.execPath).toLowerCase();
|
|
47
|
+
return !(base.startsWith('node') || base.startsWith('bun'));
|
|
48
|
+
}
|
|
49
|
+
|
|
38
50
|
// Ask the user's login shell to locate a command — no hardcoded locations, works
|
|
39
51
|
// wherever the user actually installed it.
|
|
40
52
|
function shellWhich(name) {
|
package/src/server.js
CHANGED
|
@@ -22,7 +22,7 @@ import * as gemini from './engines/gemini.js';
|
|
|
22
22
|
import { installService, uninstallService, serviceStatus } from './service.js';
|
|
23
23
|
import { enrichPath, findAgentBin } from './env.js';
|
|
24
24
|
|
|
25
|
-
const VERSION = '0.2.
|
|
25
|
+
const VERSION = '0.2.9';
|
|
26
26
|
const HOST = process.env.CHATPANEL_BRIDGE_HOST || '127.0.0.1';
|
|
27
27
|
const PORT = Number(process.env.CHATPANEL_BRIDGE_PORT) || 4319;
|
|
28
28
|
|
package/src/service.js
CHANGED
|
@@ -79,20 +79,30 @@ function macStatus() {
|
|
|
79
79
|
}
|
|
80
80
|
|
|
81
81
|
// ---------------------------------------------------------------- Windows
|
|
82
|
-
|
|
82
|
+
// Per-user auto-start via the HKCU Run key — works without admin (a scheduled
|
|
83
|
+
// task can be blocked by policy: "Access is denied"). A tiny .vbs launcher runs
|
|
84
|
+
// the bridge with NO console window.
|
|
85
|
+
const WIN_RUN_KEY = 'HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Run';
|
|
86
|
+
const WIN_RUN_NAME = 'ChatPanelBridge';
|
|
87
|
+
const winVbs = () => path.join(path.dirname(resolveLaunch().program), 'launch.vbs');
|
|
83
88
|
|
|
84
89
|
function winInstall() {
|
|
85
90
|
const { program, args } = resolveLaunch();
|
|
86
|
-
|
|
87
|
-
const
|
|
88
|
-
|
|
89
|
-
|
|
91
|
+
// VBS quoting: "" is a literal quote inside a string. Run(cmd, 0=hidden, False).
|
|
92
|
+
const parts = [program, ...args].map((p) => `""${p}""`).join(' ');
|
|
93
|
+
const vbs = winVbs();
|
|
94
|
+
mkdirSync(path.dirname(vbs), { recursive: true });
|
|
95
|
+
writeFileSync(vbs, `CreateObject("WScript.Shell").Run "${parts}", 0, False\r\n`);
|
|
96
|
+
const r = run('reg', ['add', WIN_RUN_KEY, '/v', WIN_RUN_NAME, '/t', 'REG_SZ', '/d', `wscript.exe "${vbs}"`, '/f']);
|
|
97
|
+
if (r.status !== 0) throw new Error((r.stderr || '').trim() || 'reg add failed');
|
|
98
|
+
run('wscript.exe', [vbs]); // start now, hidden
|
|
90
99
|
}
|
|
91
100
|
function winUninstall() {
|
|
92
|
-
run('
|
|
101
|
+
run('reg', ['delete', WIN_RUN_KEY, '/v', WIN_RUN_NAME, '/f']);
|
|
102
|
+
run('taskkill', ['/IM', 'chatpanel-bridge.exe', '/F']);
|
|
93
103
|
}
|
|
94
104
|
function winStatus() {
|
|
95
|
-
return run('
|
|
105
|
+
return run('reg', ['query', WIN_RUN_KEY, '/v', WIN_RUN_NAME]).status === 0;
|
|
96
106
|
}
|
|
97
107
|
|
|
98
108
|
// ---------------------------------------------------------------- Linux (systemd user)
|