@chatpanel/bridge 0.10.14 → 0.10.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/README.md CHANGED
@@ -7,7 +7,9 @@ so this bridges the gap.
7
7
  - **Claude Code** — embedded via `@anthropic-ai/claude-agent-sdk`, using your
8
8
  existing Claude Code login (or `ANTHROPIC_API_KEY`).
9
9
  - **Codex** — driven via the `codex exec` CLI, using your `codex login`.
10
- - **Gemini CLI** — driven via the `gemini -p` CLI, using your `gemini` login.
10
+ - **Antigravity CLI** — driven via the `agy -p` CLI, using your Antigravity login.
11
+ This is Google's successor to Gemini CLI; **Gemini CLI** itself remains available
12
+ for business/enterprise (paid API keys) and can be added as a custom agent.
11
13
 
12
14
  Bring whichever agent you already have installed — the extension auto-detects
13
15
  the ones the bridge reports as available.
@@ -27,7 +29,7 @@ in-place upgrade.
27
29
  ### Windows — via Node (recommended)
28
30
 
29
31
  Windows SmartScreen flags unsigned downloads, so on Windows run the bridge through
30
- Node — you already have it if you use Claude Code / Codex / Gemini (all npm CLIs),
32
+ Node — you already have it if you use Claude Code / Codex (both npm CLIs),
31
33
  and there's no security prompt:
32
34
 
33
35
  ```powershell
@@ -64,7 +66,7 @@ The agents you want to use must already be set up:
64
66
 
65
67
  - **Claude Code**: installed and signed in (`claude`), or set `ANTHROPIC_API_KEY`.
66
68
  - **Codex**: `codex` on your `PATH` and `codex login` done.
67
- - **Gemini CLI**: `gemini` on your `PATH` and signed in.
69
+ - **Antigravity CLI**: `agy` on your `PATH` and signed in (install the Antigravity app, then run `agy` once to sign in). Replaces Gemini CLI; business/enterprise users can still run `gemini` as a custom agent.
68
70
 
69
71
  The extension polls `/health` and shows each agent as available/unavailable.
70
72
 
package/package.json CHANGED
@@ -1,12 +1,13 @@
1
1
  {
2
2
  "name": "@chatpanel/bridge",
3
- "version": "0.10.14",
3
+ "version": "0.10.15",
4
4
  "type": "module",
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.",
5
+ "description": "Local bridge that exposes the AI coding agents installed on your machine — Claude Code (CLI), Codex (CLI), and Antigravity CLI (formerly Gemini CLI, which remains available for business/enterprise) — to the ChatPanel Chrome extension over a localhost SSE endpoint. Bring your own agent.",
6
6
  "keywords": [
7
7
  "chatpanel",
8
8
  "claude-code",
9
9
  "codex",
10
+ "antigravity-cli",
10
11
  "gemini-cli",
11
12
  "chrome-extension",
12
13
  "ai-agents",
@@ -11,7 +11,7 @@
11
11
  // path so the model opens it.
12
12
 
13
13
  import { spawn, spawnSync } from 'node:child_process';
14
- import { mkdirSync, writeFileSync, unlinkSync } from 'node:fs';
14
+ import { mkdirSync, writeFileSync, unlinkSync, existsSync, readFileSync, rmSync } from 'node:fs';
15
15
  import os from 'node:os';
16
16
  import path from 'node:path';
17
17
  import { findAgentBin } from '../env.js';
@@ -78,6 +78,57 @@ function writeImages(images, dir) {
78
78
  return files;
79
79
  }
80
80
 
81
+ // Antigravity has no per-run MCP flag (`agy --help` shows none). The CLI only
82
+ // discovers MCP servers from config files: <cwd>/.agents/mcp_config.json
83
+ // (workspace) or ~/.gemini/config/mcp_config.json (global). To give a headless
84
+ // `agy -p` run our page/MCP tools, write the workspace file pointing at the
85
+ // bridge's per-session HTTP MCP server. Remote servers MUST use `serverUrl`
86
+ // (Antigravity rejects the legacy `url`/`httpUrl` fields). This is the agy
87
+ // equivalent of what claude.js/codex.js do via --mcp-config / -c mcp_servers.
88
+ //
89
+ // Non-destructive: an existing .agents/mcp_config.json is parsed, our one server
90
+ // merged in, and the original restored on cleanup. If the file exists but isn't
91
+ // JSON we recognize, we leave it untouched (and tools simply won't attach) rather
92
+ // than risk corrupting the user's config. Returns a cleanup fn (always callable).
93
+ function setupMcpConfig(mcp, cwd) {
94
+ if (!mcp?.url) return () => {};
95
+ const dir = path.join(cwd, '.agents');
96
+ const file = path.join(dir, 'mcp_config.json');
97
+ const serverName = mcp.serverName || 'chatpanel_browser';
98
+ const hadDir = existsSync(dir);
99
+ let prev = null; // original file contents (null = file did not exist)
100
+
101
+ let config = { mcpServers: {} };
102
+ if (existsSync(file)) {
103
+ try {
104
+ prev = readFileSync(file, 'utf8');
105
+ const parsed = JSON.parse(prev);
106
+ if (!parsed || typeof parsed !== 'object') return () => {};
107
+ config = parsed;
108
+ if (!config.mcpServers || typeof config.mcpServers !== 'object') config.mcpServers = {};
109
+ } catch {
110
+ return () => {}; // unreadable / not JSON — don't clobber it
111
+ }
112
+ }
113
+ config.mcpServers[serverName] = { serverUrl: mcp.url };
114
+
115
+ try {
116
+ mkdirSync(dir, { recursive: true });
117
+ writeFileSync(file, JSON.stringify(config, null, 2));
118
+ } catch {
119
+ return () => {};
120
+ }
121
+ return () => {
122
+ try {
123
+ if (prev !== null) writeFileSync(file, prev); // restore original contents
124
+ else if (hadDir) rmSync(file, { force: true }); // remove just the file we added
125
+ else rmSync(dir, { recursive: true, force: true }); // remove the dir we created
126
+ } catch {
127
+ /* best effort */
128
+ }
129
+ };
130
+ }
131
+
81
132
  export async function chat({ messages, system, options, images }, emit) {
82
133
  try {
83
134
  mkdirSync(SCRATCH, { recursive: true });
@@ -90,18 +141,29 @@ export async function chat({ messages, system, options, images }, emit) {
90
141
  // reads @-referenced files (incl. images) inline as multimodal input, so no
91
142
  // read-tool approval is needed in headless `-p` mode. (Confirmed working.)
92
143
  const imageFiles = writeImages(images, cwd);
93
- const cleanup = () => imageFiles.forEach((f) => { try { unlinkSync(f); } catch { /* gone */ } });
144
+ // Wire our page/MCP tools into agy via a workspace mcp_config.json (it has no
145
+ // per-run MCP flag). Tools then route: agy → bridge /mcp/<session> → extension.
146
+ const cleanupMcp = setupMcpConfig(options.mcp, cwd);
147
+ const cleanup = () => {
148
+ imageFiles.forEach((f) => { try { unlinkSync(f); } catch { /* gone */ } });
149
+ cleanupMcp();
150
+ };
94
151
  let prompt = buildCliPrompt(messages, system);
95
152
  if (imageFiles.length) {
96
153
  prompt += `\n\nThe user attached image(s): ${imageFiles.map((f) => '@' + path.basename(f)).join(' ')}`;
97
154
  }
98
155
 
99
156
  // `-p` runs one prompt non-interactively. --model picks the model.
100
- // --dangerously-skip-permissions auto-approves tool use (headless has no human
101
- // approver) only when the user opted into bypassPermissions.
102
157
  const args = ['-p', prompt];
103
158
  if (options.model) args.push('--model', options.model);
104
- if (options.permissionMode === 'bypassPermissions') args.push('--dangerously-skip-permissions');
159
+ // Headless `agy -p` cannot prompt for tool approval — without this flag any MCP
160
+ // tool call just times out (it never reaches the server). So skip agy's own
161
+ // approval whenever the user opted into bypass OR we've attached page/MCP tools.
162
+ // The real gate stays on the ChatPanel side: each relayed call goes back to the
163
+ // extension, which applies its per-action confirmation for risky page actions.
164
+ if (options.permissionMode === 'bypassPermissions' || options.mcp?.url) {
165
+ args.push('--dangerously-skip-permissions');
166
+ }
105
167
  if (options.extraArgs) args.push(...String(options.extraArgs).split(/\s+/).filter(Boolean));
106
168
 
107
169
  await new Promise((resolve, reject) => {
package/src/env.js CHANGED
@@ -76,7 +76,7 @@ function shellWhich(name) {
76
76
  // ---------------------------------------------------------------------------
77
77
  // Claude Code launcher resolution.
78
78
  //
79
- // The Codex/Gemini engines can assume `spawn('codex', …)` runs a directly
79
+ // The Codex/Antigravity engines can assume `spawn('codex', …)` runs a directly
80
80
  // executable file on the current OS's PATH. Claude needs more care:
81
81
  // • On Windows, npm installs `claude.cmd` / `claude.ps1` / an extensionless
82
82
  // bash shim — NONE of which Node's spawn() can execute directly (that's the
package/src/server.js CHANGED
@@ -37,7 +37,7 @@ import { assertPublicHttpUrl } from './ssrf.js';
37
37
  // Hardcoded (not read from package.json) so it survives Bun's single-file
38
38
  // --compile, where package.json isn't on a readable FS. CI fails the publish if
39
39
  // this drifts from package.json, so the two can't silently diverge.
40
- const VERSION = '0.10.14';
40
+ const VERSION = '0.10.15';
41
41
  const HOST = process.env.CHATPANEL_BRIDGE_HOST || '127.0.0.1';
42
42
  const PORT = Number(process.env.CHATPANEL_BRIDGE_PORT) || 4319;
43
43
 
@@ -714,7 +714,7 @@ function runMcpStdioProxy(url) {
714
714
  }
715
715
 
716
716
  function startServer() {
717
- enrichPath(); // so codex/gemini are found even under a minimal service PATH
717
+ enrichPath(); // so codex/agy (Antigravity) are found even under a minimal service PATH
718
718
  ensureToken(); // per-install bearer token for privileged routes (defense-in-depth)
719
719
  server.listen(PORT, HOST, async () => {
720
720
  log('info', `listening on http://${HOST}:${PORT}`);