@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 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 (macOS / Linux, no Node.js needed)
17
+ ### Option A — one-line install (no Node.js needed)
18
18
 
19
+ **macOS / Linux:**
19
20
  ```bash
20
- curl -fsSL https://raw.githubusercontent.com/chatpanel/chatpanel-bridge/main/scripts/install.sh | bash
21
+ curl -fsSL https://dl.chatpanel.net/bridge/install.sh | bash
21
22
  ```
22
23
 
23
- This downloads the standalone binary for your OS, installs it to `~/.local/bin`,
24
- and sets it to start at login. **Recommended** — installing via curl avoids the
25
- macOS "damaged / unidentified developer" prompt that browser downloads trigger.
26
- Then open the ChatPanel side panel and your agents appear.
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.7",
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"
@@ -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="chatpanel-bridge-macos-arm64"
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="chatpanel-bridge-linux-x64"
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://github.com/${REPO}/releases/latest/download/${asset}"
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 ${asset} ..."
40
- curl -fsSL "$url" -o "$bin"
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
@@ -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
- if (!process.env.ANTHROPIC_API_KEY) {
25
- // Not fatal the SDK can use your local Claude Code login.
26
- return { ok: true };
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 p = path.join(d, name);
33
- if (existsSync(p) || existsSync(p + '.exe')) return p;
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.7';
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
- const WIN_TASK = 'ChatPanelBridge';
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
- const tr = [`"${program}"`, ...args.map((a) => `"${a}"`)].join(' ');
87
- const r = run('schtasks', ['/Create', '/TN', WIN_TASK, '/TR', tr, '/SC', 'ONLOGON', '/RL', 'LIMITED', '/F']);
88
- if (r.status !== 0) throw new Error((r.stderr || '').trim() || 'schtasks create failed');
89
- run('schtasks', ['/Run', '/TN', WIN_TASK]); // start now
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('schtasks', ['/Delete', '/TN', WIN_TASK, '/F']);
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('schtasks', ['/Query', '/TN', WIN_TASK]).status === 0;
105
+ return run('reg', ['query', WIN_RUN_KEY, '/v', WIN_RUN_NAME]).status === 0;
96
106
  }
97
107
 
98
108
  // ---------------------------------------------------------------- Linux (systemd user)