@chatpanel/bridge 0.10.23 → 0.10.24
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 +59 -0
- package/src/server.js +3 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chatpanel/bridge",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.24",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Local bridge that exposes the AI coding agents installed on your machine \u2014 Claude Code (CLI), Codex (CLI), and Antigravity CLI (formerly Gemini CLI, which remains available for business/enterprise) \u2014 to the ChatPanel Chrome extension over a localhost SSE endpoint. Bring your own agent.",
|
|
6
6
|
"keywords": [
|
package/src/env.js
CHANGED
|
@@ -11,6 +11,7 @@ import { spawnSync } from 'node:child_process';
|
|
|
11
11
|
import { readdirSync, readFileSync, existsSync } from 'node:fs';
|
|
12
12
|
|
|
13
13
|
let enriched = false;
|
|
14
|
+
let envEnriched = false;
|
|
14
15
|
|
|
15
16
|
// The agent CLIs the bridge shells out to. Claude has its own richer resolution
|
|
16
17
|
// (resolveClaude: native / cli.js / WSL / SDK) below.
|
|
@@ -421,6 +422,64 @@ export function commandCandidateFiles(name, home = os.homedir(), platform = proc
|
|
|
421
422
|
return unique([...fromDirs, ...agentCandidateBins(name, home, platform, env)]);
|
|
422
423
|
}
|
|
423
424
|
|
|
425
|
+
// Agent credentials a LaunchAgent/systemd unit does NOT inherit.
|
|
426
|
+
//
|
|
427
|
+
// enrichPath() repairs the minimal PATH a service gets; this is the same problem
|
|
428
|
+
// one layer up. A CLI that authenticates from a FILE (claude, codex, copilot:
|
|
429
|
+
// ~/.copilot) works fine under the service, but one that reads an API key from
|
|
430
|
+
// the ENVIRONMENT (dsh -> DEEPSEEK_API_KEY) fails with a missing-credential
|
|
431
|
+
// error that looks like a ChatPanel bug — the launchd job's environment is just
|
|
432
|
+
// SSH_AUTH_SOCK and a bare PATH.
|
|
433
|
+
//
|
|
434
|
+
// So: ask the user's login shell for a NARROW allowlist of agent credential
|
|
435
|
+
// variables and fill in only the ones we don't already have. These are the
|
|
436
|
+
// user's own credentials, on their own machine, handed to CLIs the user
|
|
437
|
+
// configured ChatPanel to launch — exactly what would happen had they run the
|
|
438
|
+
// CLI from their terminal. Values are never logged, and /debug never dumps the
|
|
439
|
+
// environment (it exposes only PATH/home, and only under CHATPANEL_BRIDGE_DEBUG).
|
|
440
|
+
const AGENT_ENV_KEYS = [
|
|
441
|
+
'DEEPSEEK_API_KEY', 'DSH_HOME',
|
|
442
|
+
'ANTHROPIC_API_KEY', 'ANTHROPIC_AUTH_TOKEN', 'ANTHROPIC_BASE_URL',
|
|
443
|
+
'OPENAI_API_KEY', 'OPENAI_BASE_URL', 'OPENAI_API_BASE',
|
|
444
|
+
'GEMINI_API_KEY', 'GOOGLE_API_KEY',
|
|
445
|
+
'OPENROUTER_API_KEY', 'GROQ_API_KEY', 'XAI_API_KEY', 'MISTRAL_API_KEY',
|
|
446
|
+
];
|
|
447
|
+
|
|
448
|
+
export function enrichAgentEnv() {
|
|
449
|
+
if (envEnriched) return;
|
|
450
|
+
envEnriched = true;
|
|
451
|
+
if (process.platform === 'win32') return; // service env is inherited there
|
|
452
|
+
|
|
453
|
+
const missing = AGENT_ENV_KEYS.filter((k) => !process.env[k]);
|
|
454
|
+
if (!missing.length) return;
|
|
455
|
+
try {
|
|
456
|
+
const shell = process.env.SHELL || '/bin/zsh';
|
|
457
|
+
// Emit NAME\tVALUE for each var that is actually set; trailing `true` keeps
|
|
458
|
+
// the exit status clean when the last test fails.
|
|
459
|
+
const script = `${missing
|
|
460
|
+
.map((k) => `[ -n "\${${k}:-}" ] && printf '%s\\t%s\\n' ${k} "\$${k}"`)
|
|
461
|
+
.join('; ')}; true`;
|
|
462
|
+
// -lic, not -lc: these keys are typically exported from ~/.zshrc (or ~/.bashrc),
|
|
463
|
+
// which a LOGIN-only shell does NOT source — that's interactive-only. TERM=dumb
|
|
464
|
+
// keeps prompt/banner noise down, and the parser below ignores any line that
|
|
465
|
+
// isn't a clean NAME<TAB>VALUE, so rc-file chatter is harmless.
|
|
466
|
+
const r = spawnSync(shell, ['-lic', script], {
|
|
467
|
+
encoding: 'utf8',
|
|
468
|
+
timeout: 6000,
|
|
469
|
+
env: { ...process.env, TERM: 'dumb' },
|
|
470
|
+
});
|
|
471
|
+
for (const line of String(r.stdout || '').split('\n')) {
|
|
472
|
+
const i = line.indexOf('\t');
|
|
473
|
+
if (i <= 0) continue;
|
|
474
|
+
const key = line.slice(0, i).trim();
|
|
475
|
+
const value = line.slice(i + 1);
|
|
476
|
+
if (AGENT_ENV_KEYS.includes(key) && !process.env[key] && value) process.env[key] = value;
|
|
477
|
+
}
|
|
478
|
+
} catch {
|
|
479
|
+
/* no login shell / timeout — agents that need a file-based login still work */
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
|
|
424
483
|
export function enrichPath() {
|
|
425
484
|
if (enriched) return;
|
|
426
485
|
enriched = true;
|
package/src/server.js
CHANGED
|
@@ -29,7 +29,7 @@ import * as antigravity from './engines/antigravity.js';
|
|
|
29
29
|
import { pi, opencode, kiro, copilot, deepseek } from './engines/cli-agents.js';
|
|
30
30
|
import * as custom from './engines/custom.js';
|
|
31
31
|
import { installService, uninstallService, serviceStatus, restartService } from './service.js';
|
|
32
|
-
import { AGENT_CLIS, enrichPath, findAgentBin, resolveCommand } from './env.js';
|
|
32
|
+
import { AGENT_CLIS, enrichPath, enrichAgentEnv, findAgentBin, resolveCommand } from './env.js';
|
|
33
33
|
import { stripHidden } from './sanitize.js';
|
|
34
34
|
import { checkForUpdate, selfUpdate } from './update.js';
|
|
35
35
|
import { callLocalMcp } from './mcp-local.js';
|
|
@@ -38,7 +38,7 @@ import { assertPublicHttpUrl, assertPublicWebUrl } from './ssrf.js';
|
|
|
38
38
|
// Hardcoded (not read from package.json) so it survives Bun's single-file
|
|
39
39
|
// --compile, where package.json isn't on a readable FS. CI fails the publish if
|
|
40
40
|
// this drifts from package.json, so the two can't silently diverge.
|
|
41
|
-
const VERSION = '0.10.
|
|
41
|
+
const VERSION = '0.10.24';
|
|
42
42
|
const HOST = process.env.CHATPANEL_BRIDGE_HOST || '127.0.0.1';
|
|
43
43
|
const PORT = Number(process.env.CHATPANEL_BRIDGE_PORT) || 4319;
|
|
44
44
|
|
|
@@ -828,6 +828,7 @@ function runMcpStdioProxy(url) {
|
|
|
828
828
|
|
|
829
829
|
function startServer() {
|
|
830
830
|
enrichPath(); // so codex/agy (Antigravity) are found even under a minimal service PATH
|
|
831
|
+
enrichAgentEnv(); // and so env-authenticated CLIs (dsh) have their key under launchd
|
|
831
832
|
ensureToken(); // per-install bearer token for privileged routes (defense-in-depth)
|
|
832
833
|
// Fail LOUD on a port clash. The bridge binds a FIXED 4319 so the extension always
|
|
833
834
|
// finds it; if it's taken, say how to recover instead of dying on a raw stack trace.
|