@chatpanel/bridge 0.10.8 → 0.10.10
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/env.js +160 -22
- package/src/server.js +3 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chatpanel/bridge",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.10",
|
|
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/env.js
CHANGED
|
@@ -14,11 +14,11 @@ let enriched = false;
|
|
|
14
14
|
|
|
15
15
|
// The agent CLIs the bridge shells out to. Claude has its own richer resolution
|
|
16
16
|
// (resolveClaude: native / cli.js / WSL / SDK) below.
|
|
17
|
-
const AGENT_CLIS = ['codex', 'claude', 'agy', 'pi', 'opencode', 'kiro-cli'];
|
|
17
|
+
export const AGENT_CLIS = ['codex', 'claude', 'agy', 'pi', 'opencode', 'kiro-cli'];
|
|
18
18
|
|
|
19
19
|
// Is `name` executable somewhere on the current PATH?
|
|
20
20
|
function onPath(name) {
|
|
21
|
-
const dirs = (process.env.PATH || ''
|
|
21
|
+
const dirs = splitPathList(process.env.PATH || '', process.platform);
|
|
22
22
|
return dirs.some((d) => d && (existsSync(path.join(d, name)) || existsSync(path.join(d, name + '.exe'))));
|
|
23
23
|
}
|
|
24
24
|
|
|
@@ -27,14 +27,9 @@ function onPath(name) {
|
|
|
27
27
|
// "is it installed/findable", NOT "does `--version` exit 0" (which can fail for
|
|
28
28
|
// reasons unrelated to installation, e.g. the CLI needs login).
|
|
29
29
|
export function findAgentBin(name) {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
for (const d of dirs) {
|
|
34
|
-
if (!d) continue;
|
|
35
|
-
for (const ext of exts) {
|
|
36
|
-
const p = path.join(d, name + ext);
|
|
37
|
-
if (existsSync(p)) return p;
|
|
30
|
+
for (const p of commandCandidateFiles(name)) {
|
|
31
|
+
if (existsSync(p)) {
|
|
32
|
+
return p;
|
|
38
33
|
}
|
|
39
34
|
}
|
|
40
35
|
return shellWhich(name) || null;
|
|
@@ -152,20 +147,20 @@ export function resolveCommand(command) {
|
|
|
152
147
|
// own Node/Bun — clean arg passing, no cmd.exe quoting), then a real .exe, then a
|
|
153
148
|
// .cmd/.bat shim launched safely via cmd.exe.
|
|
154
149
|
function findCommandWindows(name) {
|
|
155
|
-
const
|
|
156
|
-
for (const
|
|
157
|
-
if (!
|
|
158
|
-
const
|
|
159
|
-
|
|
150
|
+
const p = pathFor('win32');
|
|
151
|
+
for (const candidate of commandCandidateFiles(name, os.homedir(), 'win32', process.env)) {
|
|
152
|
+
if (!existsSync(candidate)) continue;
|
|
153
|
+
const d = p.dirname(candidate);
|
|
154
|
+
const ext = p.extname(candidate).toLowerCase();
|
|
160
155
|
// Running cli.js with our own interpreter only works under a real Node/Bun,
|
|
161
156
|
// not inside a compiled single-file binary (which is not a JS interpreter).
|
|
162
157
|
if (!isCompiledBinary()) {
|
|
163
158
|
const js = (name === 'claude' && claudeCliJs(d)) || shimTarget(d, name);
|
|
164
159
|
if (js) return { kind: 'script', script: js };
|
|
160
|
+
if (/^\.(c?js|mjs)$/.test(ext)) return { kind: 'script', script: candidate };
|
|
165
161
|
}
|
|
166
|
-
if (
|
|
167
|
-
if (
|
|
168
|
-
if (existsSync(path.join(d, name + '.bat'))) return { kind: 'cmd', bin: path.join(d, name + '.bat') };
|
|
162
|
+
if (ext === '.exe' || ext === '') return { kind: 'native', bin: candidate };
|
|
163
|
+
if (ext === '.cmd' || ext === '.bat') return { kind: 'cmd', bin: candidate };
|
|
169
164
|
}
|
|
170
165
|
return null;
|
|
171
166
|
}
|
|
@@ -290,14 +285,156 @@ function versionManagerBins(home) {
|
|
|
290
285
|
return bins;
|
|
291
286
|
}
|
|
292
287
|
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
288
|
+
function pathFor(platform) {
|
|
289
|
+
return platform === 'win32' ? path.win32 : path;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function pathDelimiterFor(platform) {
|
|
293
|
+
return platform === 'win32' ? ';' : path.delimiter;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
function splitPathList(value, platform) {
|
|
297
|
+
return (value || '').split(pathDelimiterFor(platform)).filter(Boolean);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
function unique(items) {
|
|
301
|
+
return [...new Set(items.filter(Boolean))];
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
export function agentInstallDirs(home, platform = process.platform, env = process.env) {
|
|
305
|
+
const p = pathFor(platform);
|
|
306
|
+
if (platform === 'win32') {
|
|
307
|
+
const appData = env.APPDATA || p.join(home, 'AppData', 'Roaming');
|
|
308
|
+
const localAppData = env.LOCALAPPDATA || p.join(home, 'AppData', 'Local');
|
|
309
|
+
const programFiles = env.ProgramFiles || 'C:\\Program Files';
|
|
310
|
+
const programFilesX86 = env['ProgramFiles(x86)'] || 'C:\\Program Files (x86)';
|
|
311
|
+
const programData = env.ProgramData || 'C:\\ProgramData';
|
|
312
|
+
return [
|
|
313
|
+
p.join(home, '.local', 'bin'),
|
|
314
|
+
p.join(home, '.codex', 'bin'),
|
|
315
|
+
p.join(home, '.claude', 'bin'),
|
|
316
|
+
p.join(home, '.claude', 'local'),
|
|
317
|
+
p.join(home, '.claude', 'local', 'bin'),
|
|
318
|
+
p.join(appData, 'npm'),
|
|
319
|
+
p.join(localAppData, 'pnpm'),
|
|
320
|
+
p.join(localAppData, 'Yarn', 'bin'),
|
|
321
|
+
p.join(localAppData, 'Volta', 'bin'),
|
|
322
|
+
p.join(home, '.yarn', 'bin'),
|
|
323
|
+
p.join(home, '.bun', 'bin'),
|
|
324
|
+
p.join(home, '.deno', 'bin'),
|
|
325
|
+
p.join(home, 'scoop', 'shims'),
|
|
326
|
+
p.join(programData, 'chocolatey', 'bin'),
|
|
327
|
+
p.join(localAppData, 'Microsoft', 'WindowsApps'),
|
|
328
|
+
p.join(localAppData, 'Programs', 'Claude Code'),
|
|
329
|
+
p.join(localAppData, 'Anthropic', 'Claude Code'),
|
|
330
|
+
p.join(programFiles, 'Claude Code'),
|
|
331
|
+
p.join(programFiles, 'Anthropic', 'Claude Code'),
|
|
332
|
+
p.join(programFilesX86, 'Claude Code'),
|
|
333
|
+
p.join(programFilesX86, 'Anthropic', 'Claude Code'),
|
|
334
|
+
];
|
|
297
335
|
}
|
|
336
|
+
return [
|
|
337
|
+
path.join(home, '.local', 'bin'),
|
|
338
|
+
path.join(home, '.opencode', 'bin'),
|
|
339
|
+
path.join(home, '.codex', 'bin'),
|
|
340
|
+
path.join(home, '.claude', 'bin'),
|
|
341
|
+
path.join(home, '.claude', 'local'),
|
|
342
|
+
path.join(home, '.claude', 'local', 'bin'),
|
|
343
|
+
];
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
function claudeNativeBins(home, platform = process.platform) {
|
|
347
|
+
const p = pathFor(platform);
|
|
348
|
+
const versionsDir = p.join(home, '.local', 'share', 'claude', 'versions');
|
|
349
|
+
let versions = [];
|
|
350
|
+
try {
|
|
351
|
+
versions = readdirSync(versionsDir)
|
|
352
|
+
.filter((v) => /^\d+\.\d+\.\d+/.test(v))
|
|
353
|
+
.sort((a, b) => b.localeCompare(a, undefined, { numeric: true }));
|
|
354
|
+
} catch {
|
|
355
|
+
/* native installer may not be present */
|
|
356
|
+
}
|
|
357
|
+
return versions.map((v) => p.join(versionsDir, v));
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
function withWindowsExts(file, platform) {
|
|
361
|
+
if (platform !== 'win32') return [file];
|
|
362
|
+
if (/\.(cmd|exe|bat|ps1)$/i.test(file)) return [file];
|
|
363
|
+
return [file, `${file}.cmd`, `${file}.exe`, `${file}.bat`, `${file}.ps1`];
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
export function agentCandidateBins(name, home = os.homedir(), platform = process.platform, env = process.env) {
|
|
367
|
+
const p = pathFor(platform);
|
|
368
|
+
const local = p.join(home, '.local', 'bin', name);
|
|
369
|
+
const localAppData = platform === 'win32' ? env.LOCALAPPDATA || p.join(home, 'AppData', 'Local') : null;
|
|
370
|
+
const programFiles = platform === 'win32' ? env.ProgramFiles || 'C:\\Program Files' : null;
|
|
371
|
+
const programFilesX86 = platform === 'win32' ? env['ProgramFiles(x86)'] || 'C:\\Program Files (x86)' : null;
|
|
372
|
+
const map = {
|
|
373
|
+
claude: [
|
|
374
|
+
local,
|
|
375
|
+
p.join(home, '.claude', 'bin', 'claude'),
|
|
376
|
+
p.join(home, '.claude', 'local', 'claude'),
|
|
377
|
+
p.join(home, '.claude', 'local', 'bin', 'claude'),
|
|
378
|
+
...(platform === 'win32'
|
|
379
|
+
? [
|
|
380
|
+
p.join(localAppData, 'Microsoft', 'WindowsApps', 'claude.exe'),
|
|
381
|
+
p.join(localAppData, 'Programs', 'Claude Code', 'claude.exe'),
|
|
382
|
+
p.join(localAppData, 'Anthropic', 'Claude Code', 'claude.exe'),
|
|
383
|
+
p.join(programFiles, 'Claude Code', 'claude.exe'),
|
|
384
|
+
p.join(programFiles, 'Anthropic', 'Claude Code', 'claude.exe'),
|
|
385
|
+
p.join(programFilesX86, 'Claude Code', 'claude.exe'),
|
|
386
|
+
p.join(programFilesX86, 'Anthropic', 'Claude Code', 'claude.exe'),
|
|
387
|
+
...claudeNativeBins(home, platform),
|
|
388
|
+
]
|
|
389
|
+
: claudeNativeBins(home, platform)),
|
|
390
|
+
],
|
|
391
|
+
codex: [
|
|
392
|
+
local,
|
|
393
|
+
p.join(home, '.codex', 'bin', 'codex'),
|
|
394
|
+
],
|
|
395
|
+
agy: [
|
|
396
|
+
local,
|
|
397
|
+
'/Applications/Antigravity.app/Contents/MacOS/agy',
|
|
398
|
+
'/Applications/Antigravity IDE.app/Contents/MacOS/agy',
|
|
399
|
+
],
|
|
400
|
+
pi: [
|
|
401
|
+
local,
|
|
402
|
+
],
|
|
403
|
+
opencode: [
|
|
404
|
+
p.join(home, '.opencode', 'bin', 'opencode'),
|
|
405
|
+
local,
|
|
406
|
+
],
|
|
407
|
+
'kiro-cli': [
|
|
408
|
+
local,
|
|
409
|
+
'/Applications/Kiro CLI.app/Contents/MacOS/kiro-cli',
|
|
410
|
+
],
|
|
411
|
+
};
|
|
412
|
+
return unique((map[name] || [local]).flatMap((file) => withWindowsExts(file, platform)));
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
export function commandCandidateFiles(name, home = os.homedir(), platform = process.platform, env = process.env) {
|
|
416
|
+
const p = pathFor(platform);
|
|
417
|
+
const fromDirs = unique([
|
|
418
|
+
...splitPathList(env.PATH || '', platform),
|
|
419
|
+
...agentInstallDirs(home, platform, env),
|
|
420
|
+
]).flatMap((dir) => withWindowsExts(p.join(dir, name), platform));
|
|
421
|
+
return unique([...fromDirs, ...agentCandidateBins(name, home, platform, env)]);
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
export function enrichPath() {
|
|
425
|
+
if (enriched) return;
|
|
298
426
|
enriched = true;
|
|
299
427
|
|
|
300
428
|
const home = os.homedir();
|
|
429
|
+
if (process.platform === 'win32') {
|
|
430
|
+
const merged = unique([
|
|
431
|
+
...splitPathList(process.env.PATH || '', 'win32'),
|
|
432
|
+
...agentInstallDirs(home, 'win32', process.env),
|
|
433
|
+
]);
|
|
434
|
+
process.env.PATH = merged.join(pathDelimiterFor('win32'));
|
|
435
|
+
return;
|
|
436
|
+
}
|
|
437
|
+
|
|
301
438
|
const common = [
|
|
302
439
|
'/opt/homebrew/bin',
|
|
303
440
|
'/opt/homebrew/sbin',
|
|
@@ -312,6 +449,7 @@ export function enrichPath() {
|
|
|
312
449
|
path.join(home, '.cargo', 'bin'),
|
|
313
450
|
path.join(home, '.deno', 'bin'),
|
|
314
451
|
path.join(home, '.bun', 'bin'),
|
|
452
|
+
...agentInstallDirs(home),
|
|
315
453
|
...versionManagerBins(home),
|
|
316
454
|
];
|
|
317
455
|
|
package/src/server.js
CHANGED
|
@@ -23,14 +23,14 @@ import * as antigravity from './engines/antigravity.js';
|
|
|
23
23
|
import { pi, opencode, kiro } from './engines/cli-agents.js';
|
|
24
24
|
import * as custom from './engines/custom.js';
|
|
25
25
|
import { installService, uninstallService, serviceStatus, restartService } from './service.js';
|
|
26
|
-
import { enrichPath, findAgentBin, resolveCommand } from './env.js';
|
|
26
|
+
import { AGENT_CLIS, enrichPath, findAgentBin, resolveCommand } from './env.js';
|
|
27
27
|
import { checkForUpdate, selfUpdate } from './update.js';
|
|
28
28
|
import { callLocalMcp } from './mcp-local.js';
|
|
29
29
|
|
|
30
30
|
// Hardcoded (not read from package.json) so it survives Bun's single-file
|
|
31
31
|
// --compile, where package.json isn't on a readable FS. CI fails the publish if
|
|
32
32
|
// this drifts from package.json, so the two can't silently diverge.
|
|
33
|
-
const VERSION = '0.10.
|
|
33
|
+
const VERSION = '0.10.10';
|
|
34
34
|
const HOST = process.env.CHATPANEL_BRIDGE_HOST || '127.0.0.1';
|
|
35
35
|
const PORT = Number(process.env.CHATPANEL_BRIDGE_PORT) || 4319;
|
|
36
36
|
|
|
@@ -438,8 +438,7 @@ const server = createServer(async (req, res) => {
|
|
|
438
438
|
return json(res, 200, {
|
|
439
439
|
version: VERSION,
|
|
440
440
|
home: os.homedir(),
|
|
441
|
-
|
|
442
|
-
agy: findAgentBin('agy') || null,
|
|
441
|
+
agents: Object.fromEntries(AGENT_CLIS.map((name) => [name, findAgentBin(name) || null])),
|
|
443
442
|
path: process.env.PATH,
|
|
444
443
|
});
|
|
445
444
|
}
|