@chatpanel/bridge 0.2.14 → 0.2.15
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/src/engines/claude.js +8 -3
- package/src/env.js +39 -11
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chatpanel/bridge",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.15",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Local bridge that exposes the AI coding agents installed on your machine — Claude Code (CLI), Codex (CLI), and Gemini CLI — to the ChatPanel Chrome extension over a localhost SSE endpoint. Bring your own agent.",
|
|
6
6
|
"keywords": [
|
package/src/engines/claude.js
CHANGED
|
@@ -88,9 +88,14 @@ function buildSpawn(spec, args, cwd) {
|
|
|
88
88
|
// Run cli.js with the interpreter already running the bridge (node/bun).
|
|
89
89
|
return [process.execPath, [spec.script, ...args], opts];
|
|
90
90
|
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
91
|
+
if (spec.kind === 'cmd') {
|
|
92
|
+
// Launch the .cmd/.bat shim via cmd.exe with a real argv (shell:false). Node
|
|
93
|
+
// applies cmd.exe-aware quoting here, so args are passed safely — unlike
|
|
94
|
+
// spawn(..., { shell: true }), which concatenates (DEP0190).
|
|
95
|
+
return ['cmd.exe', ['/d', '/s', '/c', spec.bin, ...args], opts];
|
|
96
|
+
}
|
|
97
|
+
// kind === 'native' — a directly executable file (mac/linux binary or .exe).
|
|
98
|
+
return [spec.bin, args, opts];
|
|
94
99
|
}
|
|
95
100
|
|
|
96
101
|
// Spawn claude (however it resolves) and stream its stream-json output via
|
package/src/env.js
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
import os from 'node:os';
|
|
9
9
|
import path from 'node:path';
|
|
10
10
|
import { spawnSync } from 'node:child_process';
|
|
11
|
-
import { readdirSync, existsSync } from 'node:fs';
|
|
11
|
+
import { readdirSync, readFileSync, existsSync } from 'node:fs';
|
|
12
12
|
|
|
13
13
|
let enriched = false;
|
|
14
14
|
|
|
@@ -81,16 +81,22 @@ function shellWhich(name) {
|
|
|
81
81
|
// native binary the old code spawned, so behavior there is unchanged.
|
|
82
82
|
//
|
|
83
83
|
// Returns one of:
|
|
84
|
-
// { kind: 'native', bin
|
|
84
|
+
// { kind: 'native', bin } → spawn(bin, args) (directly exec'able)
|
|
85
85
|
// { kind: 'script', script } → spawn(process.execPath, [script, ...args])
|
|
86
|
+
// { kind: 'cmd', bin } → spawn('cmd.exe', ['/c', bin, ...args])
|
|
86
87
|
// { kind: 'wsl' } → spawn('wsl.exe', [wsl prefix, ...args])
|
|
87
88
|
// null → not found (caller may fall back to SDK)
|
|
89
|
+
//
|
|
90
|
+
// We never use spawn's `shell: true` — with an args array it concatenates rather
|
|
91
|
+
// than escapes (Node DEP0190 / a real injection surface). The 'script' and 'cmd'
|
|
92
|
+
// kinds run the shim safely with a proper argv instead.
|
|
88
93
|
export function resolveClaude() {
|
|
89
94
|
const override = process.env.CHATPANEL_CLAUDE_PATH;
|
|
90
95
|
if (override) {
|
|
91
96
|
const ext = path.extname(override).toLowerCase();
|
|
92
|
-
if (!isCompiledBinary() &&
|
|
93
|
-
|
|
97
|
+
if (!isCompiledBinary() && /^\.(c?js|mjs)$/.test(ext)) return { kind: 'script', script: override };
|
|
98
|
+
if (process.platform === 'win32' && (ext === '.cmd' || ext === '.bat')) return { kind: 'cmd', bin: override };
|
|
99
|
+
return { kind: 'native', bin: override };
|
|
94
100
|
}
|
|
95
101
|
|
|
96
102
|
if (process.platform === 'win32') {
|
|
@@ -102,12 +108,12 @@ export function resolveClaude() {
|
|
|
102
108
|
|
|
103
109
|
// macOS / Linux / WSL-native: same resolution the engine used before.
|
|
104
110
|
const bin = findAgentBin('claude');
|
|
105
|
-
return bin ? { kind: 'native', bin
|
|
111
|
+
return bin ? { kind: 'native', bin } : null;
|
|
106
112
|
}
|
|
107
113
|
|
|
108
114
|
// Locate a runnable Claude Code on Windows. Prefer the package's cli.js (run
|
|
109
115
|
// with our own Node/Bun — clean arg passing, no cmd.exe quoting), then a real
|
|
110
|
-
// .exe, then a .cmd shim
|
|
116
|
+
// .exe, then a .cmd/.bat shim launched safely via cmd.exe.
|
|
111
117
|
function findClaudeWindows() {
|
|
112
118
|
const dirs = (process.env.PATH || '').split(path.delimiter);
|
|
113
119
|
for (const d of dirs) {
|
|
@@ -119,17 +125,18 @@ function findClaudeWindows() {
|
|
|
119
125
|
// Running cli.js with our own interpreter only works under a real Node/Bun,
|
|
120
126
|
// not inside a compiled single-file binary (which is not a JS interpreter).
|
|
121
127
|
if (!isCompiledBinary()) {
|
|
122
|
-
const js = claudeCliJs(d);
|
|
128
|
+
const js = claudeCliJs(d) || shimTarget(d);
|
|
123
129
|
if (js) return { kind: 'script', script: js };
|
|
124
130
|
}
|
|
125
|
-
if (existsSync(path.join(d, 'claude.exe'))) return { kind: 'native', bin: path.join(d, 'claude.exe')
|
|
126
|
-
if (existsSync(path.join(d, 'claude.cmd'))) return { kind: '
|
|
127
|
-
if (existsSync(path.join(d, 'claude.bat'))) return { kind: '
|
|
131
|
+
if (existsSync(path.join(d, 'claude.exe'))) return { kind: 'native', bin: path.join(d, 'claude.exe') };
|
|
132
|
+
if (existsSync(path.join(d, 'claude.cmd'))) return { kind: 'cmd', bin: path.join(d, 'claude.cmd') };
|
|
133
|
+
if (existsSync(path.join(d, 'claude.bat'))) return { kind: 'cmd', bin: path.join(d, 'claude.bat') };
|
|
128
134
|
}
|
|
129
135
|
return null;
|
|
130
136
|
}
|
|
131
137
|
|
|
132
|
-
// The npm shim sits next to (or one level up from) the claude-code
|
|
138
|
+
// The npm shim usually sits next to (or one level up from) the claude-code
|
|
139
|
+
// package — quick static guesses before parsing the shim itself.
|
|
133
140
|
function claudeCliJs(dir) {
|
|
134
141
|
const rels = [
|
|
135
142
|
['node_modules', '@anthropic-ai', 'claude-code', 'cli.js'],
|
|
@@ -143,6 +150,27 @@ function claudeCliJs(dir) {
|
|
|
143
150
|
return null;
|
|
144
151
|
}
|
|
145
152
|
|
|
153
|
+
// Robust fallback: every npm/pnpm/yarn/volta shim literally names the JS entry it
|
|
154
|
+
// runs, relative to the shim dir (`%dp0%\…\cli.js` in .cmd, `$basedir/…/cli.js`
|
|
155
|
+
// in the sh/.ps1 shims). Extract that so any install layout resolves to a real
|
|
156
|
+
// cli.js we can run with our own interpreter.
|
|
157
|
+
function shimTarget(dir) {
|
|
158
|
+
for (const shim of ['claude.cmd', 'claude', 'claude.ps1']) {
|
|
159
|
+
let txt;
|
|
160
|
+
try {
|
|
161
|
+
txt = readFileSync(path.join(dir, shim), 'utf8');
|
|
162
|
+
} catch {
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
const m = txt.match(/(?:%~?dp0%|\$basedir|\$\{basedir\})[\\/]+([^"'\s]+\.[cm]?js)/i);
|
|
166
|
+
if (m) {
|
|
167
|
+
const abs = path.join(dir, m[1].replace(/[\\/]+/g, path.sep));
|
|
168
|
+
if (existsSync(abs)) return abs;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return null;
|
|
172
|
+
}
|
|
173
|
+
|
|
146
174
|
// Is `claude` reachable inside the default WSL distro's login shell? Cached;
|
|
147
175
|
// re-probed (throttled) while not found so it self-heals once WSL/claude appear.
|
|
148
176
|
let wslClaude = null;
|