@chatpanel/bridge 0.10.32 → 0.10.33
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 +15 -0
- package/package.json +1 -1
- package/scripts/install.ps1 +4 -0
- package/scripts/install.sh +33 -0
- package/src/engines/codex.js +59 -10
- package/src/server.js +1 -1
package/README.md
CHANGED
|
@@ -92,11 +92,26 @@ npm start # → http://127.0.0.1:4319
|
|
|
92
92
|
| `POST` | `/v1/completions` | OpenAI-compatible legacy text Completions (streaming and non-streaming) |
|
|
93
93
|
| `POST` | `/v1/responses` | OpenAI-compatible Responses API (streaming and non-streaming) |
|
|
94
94
|
| `POST` | `/v1/messages` | Anthropic-compatible Messages API (streaming and non-streaming) |
|
|
95
|
+
| `GET` | `/skills` | skills installed on this machine, across every agent harness + configured folders |
|
|
96
|
+
| `GET` | `/skills/<name>` · `/skills/<name>/file/<path>` | one skill's instructions · one reference file |
|
|
97
|
+
| `POST` | `/mcp` | MCP endpoint — `chatpanel_skill_*` tools any CLI can call (plus browser tools while a ChatPanel chat is driving) |
|
|
95
98
|
|
|
96
99
|
`/chat` streams Server-Sent Events: `{type:'delta',text}` as the answer is
|
|
97
100
|
generated, `{type:'tool',name,summary}` / `{type:'status'}` for activity, and a
|
|
98
101
|
final `{type:'done'}` (or `{type:'error',error}`).
|
|
99
102
|
|
|
103
|
+
### Use your skills from any CLI
|
|
104
|
+
|
|
105
|
+
The bridge discovers the skills installed on your machine — Claude Code, Codex, Copilot,
|
|
106
|
+
Gemini, Hermes, `~/.agents/skills`, and any folder you configure — and exposes them as MCP
|
|
107
|
+
tools (`chatpanel_skill_list` / `_open` / `_read`). Point any MCP client at
|
|
108
|
+
`http://127.0.0.1:4319/mcp` and it can list and load them with no browser open.
|
|
109
|
+
|
|
110
|
+
For **one** server that adds your local **history** (chats, meetings, notes — redacted)
|
|
111
|
+
*alongside* your skills, add the
|
|
112
|
+
[Privacy Gateway](https://github.com/chatpanel/chatpanel-gateway) instead and point your CLI
|
|
113
|
+
at `chatpanel-gateway mcp`. The gateway proxies these skill tools and puts redaction in front.
|
|
114
|
+
|
|
100
115
|
`options` per agent (set in ChatPanel Settings):
|
|
101
116
|
|
|
102
117
|
```jsonc
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chatpanel/bridge",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.33",
|
|
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/scripts/install.ps1
CHANGED
|
@@ -52,3 +52,7 @@ if ($installed) {
|
|
|
52
52
|
Write-Host " `"$bin`""
|
|
53
53
|
}
|
|
54
54
|
Write-Host "Manage it: `"$bin`" --status | --uninstall"
|
|
55
|
+
Write-Host ""
|
|
56
|
+
Write-Host "Optional upgrade - the Privacy Gateway adds PII redaction, model routing and voice," -ForegroundColor Gray
|
|
57
|
+
Write-Host "and lets any CLI reach your history + skills through one MCP server:" -ForegroundColor Gray
|
|
58
|
+
Write-Host " irm https://dl.chatpanel.net/gateway/install.ps1 | iex"
|
package/scripts/install.sh
CHANGED
|
@@ -4,10 +4,23 @@
|
|
|
4
4
|
#
|
|
5
5
|
# curl -fsSL https://raw.githubusercontent.com/chatpanel/chatpanel-bridge/main/scripts/install.sh | bash
|
|
6
6
|
#
|
|
7
|
+
# Add the optional privacy gateway (redaction, model routing, voice) in the same flow:
|
|
8
|
+
# curl -fsSL https://dl.chatpanel.net/bridge/install.sh | bash -s -- --gateway
|
|
9
|
+
#
|
|
7
10
|
# Downloading via curl means the file is NOT quarantined, so macOS won't show the
|
|
8
11
|
# "damaged / unidentified developer" prompt that browser downloads trigger.
|
|
9
12
|
set -euo pipefail
|
|
10
13
|
|
|
14
|
+
# --gateway also installs the ChatPanel Privacy Gateway — the optional upgrade that adds
|
|
15
|
+
# PII redaction, model routing and voice in front of everything the bridge does. The bridge
|
|
16
|
+
# is the common case and is always installed; the gateway is opt-in and heavier.
|
|
17
|
+
WITH_GATEWAY=0
|
|
18
|
+
for a in "$@"; do
|
|
19
|
+
case "$a" in
|
|
20
|
+
--gateway|--with-gateway) WITH_GATEWAY=1 ;;
|
|
21
|
+
esac
|
|
22
|
+
done
|
|
23
|
+
|
|
11
24
|
os="$(uname -s)"
|
|
12
25
|
arch="$(uname -m)"
|
|
13
26
|
asset=""
|
|
@@ -59,3 +72,23 @@ case ":${PATH}:" in
|
|
|
59
72
|
*":${dest}:"*) : ;;
|
|
60
73
|
*) echo "Tip: add it to your PATH -> export PATH=\"\$HOME/.local/bin:\$PATH\"" ;;
|
|
61
74
|
esac
|
|
75
|
+
|
|
76
|
+
if [ "$WITH_GATEWAY" = "1" ]; then
|
|
77
|
+
echo
|
|
78
|
+
echo "Adding the ChatPanel Privacy Gateway (optional upgrade)..."
|
|
79
|
+
# One download story: the gateway installer lives at the same dl host. A gateway failure
|
|
80
|
+
# must not fail the bridge install — the bridge is already up.
|
|
81
|
+
if curl -fsSL https://dl.chatpanel.net/gateway/install.sh | bash; then
|
|
82
|
+
echo "Gateway installed. One MCP config now gives any CLI your history + skills, redacted:"
|
|
83
|
+
echo " chatpanel-gateway mcp"
|
|
84
|
+
else
|
|
85
|
+
echo "Gateway install didn't complete — the bridge is unaffected. Retry any time:"
|
|
86
|
+
echo " curl -fsSL https://dl.chatpanel.net/gateway/install.sh | bash"
|
|
87
|
+
fi
|
|
88
|
+
else
|
|
89
|
+
echo
|
|
90
|
+
echo "Optional upgrade — the Privacy Gateway adds PII redaction, model routing and voice,"
|
|
91
|
+
echo "and lets any CLI (Codex, Claude Code) reach your history + skills through one MCP server:"
|
|
92
|
+
echo " curl -fsSL https://dl.chatpanel.net/gateway/install.sh | bash"
|
|
93
|
+
echo " (or re-run this with --gateway to add it now.)"
|
|
94
|
+
fi
|
package/src/engines/codex.js
CHANGED
|
@@ -151,6 +151,10 @@ export async function chat({ messages, system, options, images }, emit, { signal
|
|
|
151
151
|
args.push('-s', sandbox, '-c', 'approval_policy=never');
|
|
152
152
|
}
|
|
153
153
|
if (REASONING) args.push('-c', `model_reasoning_effort=${REASONING}`);
|
|
154
|
+
// Ask Codex to emit reasoning SUMMARIES so the panel can stream the model's thinking.
|
|
155
|
+
// Additive + safe: a no-op for models/providers that don't produce summaries (e.g. some
|
|
156
|
+
// hosted models expose none), and cheap at the default effort. forwardEvent renders them.
|
|
157
|
+
args.push('-c', 'model_reasoning_summary=auto');
|
|
154
158
|
// Browser tools: register the bridge's MCP server as a stdio MCP server (the
|
|
155
159
|
// bridge binary in --mcp-stdio mode), so Codex can call our page-action tools.
|
|
156
160
|
// `-c key=value` parses value as TOML; JSON.stringify yields valid TOML here.
|
|
@@ -182,6 +186,9 @@ export async function chat({ messages, system, options, images }, emit, { signal
|
|
|
182
186
|
|
|
183
187
|
let stdout = '';
|
|
184
188
|
let stderr = '';
|
|
189
|
+
// Per-run event state: correlate a command's started/completed events and emit each
|
|
190
|
+
// reasoning summary once (Codex sends the same item id across item.started/updated/completed).
|
|
191
|
+
const evState = { started: new Set(), reasoned: new Set(), n: 0 };
|
|
185
192
|
let idleTimer;
|
|
186
193
|
const armIdle = () => {
|
|
187
194
|
clearTimeout(idleTimer);
|
|
@@ -201,7 +208,7 @@ export async function chat({ messages, system, options, images }, emit, { signal
|
|
|
201
208
|
stdout = stdout.slice(nl + 1);
|
|
202
209
|
if (!line.startsWith('{')) continue;
|
|
203
210
|
try {
|
|
204
|
-
forwardEvent(JSON.parse(line), emit);
|
|
211
|
+
forwardEvent(JSON.parse(line), emit, evState);
|
|
205
212
|
} catch {
|
|
206
213
|
/* not a JSON event line */
|
|
207
214
|
}
|
|
@@ -240,16 +247,58 @@ export async function chat({ messages, system, options, images }, emit, { signal
|
|
|
240
247
|
});
|
|
241
248
|
}
|
|
242
249
|
|
|
243
|
-
|
|
250
|
+
// Translate Codex `exec --json` events into the bridge's streaming vocabulary the panel
|
|
251
|
+
// renders richly. Codex sends item.started (in_progress) then item.completed for each item,
|
|
252
|
+
// reusing the item id — so we correlate a tool's start/done by that id and show its command,
|
|
253
|
+
// its output, and its exit status, the way Codex's own CLI does. Schema (codex-cli 0.15x):
|
|
254
|
+
// command_execution: { id, command, aggregated_output, exit_code, status }
|
|
255
|
+
// reasoning: { id, text } (only some models/efforts emit it)
|
|
256
|
+
// file_change: { id, changes:[{path}] }
|
|
257
|
+
// agent_message: { id, text } (the answer — read from -o outFile at close)
|
|
258
|
+
export function forwardEvent(ev, emit, state = { started: new Set(), reasoned: new Set(), n: 0 }) {
|
|
244
259
|
const t = ev.type || '';
|
|
245
260
|
const item = ev.item || {};
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
261
|
+
const itype = item.type || '';
|
|
262
|
+
const completed = t === 'item.completed' || item.status === 'completed' || item.status === 'failed';
|
|
263
|
+
|
|
264
|
+
if (itype === 'command_execution' || (!itype && t.includes('command'))) {
|
|
265
|
+
const id = item.id || `cmd_${state.n++}`;
|
|
266
|
+
if (!state.started.has(id)) {
|
|
267
|
+
state.started.add(id);
|
|
268
|
+
emit({ type: 'tool', name: 'shell', phase: 'start', callId: id, input: { command: item.command || '' } });
|
|
269
|
+
}
|
|
270
|
+
if (completed) {
|
|
271
|
+
const ok = item.exit_code === 0 || item.exit_code == null;
|
|
272
|
+
emit({
|
|
273
|
+
type: 'tool', name: 'shell', phase: 'done', callId: id,
|
|
274
|
+
status: ok ? 'ok' : `exit ${item.exit_code}`,
|
|
275
|
+
result: String(item.aggregated_output || '').slice(0, 4000),
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
if (itype === 'file_change' || (!itype && t.includes('patch'))) {
|
|
282
|
+
const id = item.id || `edit_${state.n++}`;
|
|
283
|
+
const files = Array.isArray(item.changes) ? item.changes.map((c) => c.path || c.file).filter(Boolean) : [];
|
|
284
|
+
if (!state.started.has(id)) {
|
|
285
|
+
state.started.add(id);
|
|
286
|
+
emit({ type: 'tool', name: 'edit', phase: 'start', callId: id, input: { files } });
|
|
287
|
+
}
|
|
288
|
+
if (completed) emit({ type: 'tool', name: 'edit', phase: 'done', callId: id, status: 'ok' });
|
|
289
|
+
return;
|
|
254
290
|
}
|
|
291
|
+
|
|
292
|
+
if (itype === 'reasoning' || (!itype && t.includes('reasoning'))) {
|
|
293
|
+
// Emit each reasoning summary once (dedup the started/updated/completed repeats).
|
|
294
|
+
const id = item.id || `r_${state.n++}`;
|
|
295
|
+
const text = item.text || item.summary || '';
|
|
296
|
+
if (text && !state.reasoned.has(id)) {
|
|
297
|
+
state.reasoned.add(id);
|
|
298
|
+
emit({ type: 'reasoning', text: `${text}\n` });
|
|
299
|
+
}
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
if (t === 'turn.started' || t === 'thread.started') emit({ type: 'status', text: 'Codex working' });
|
|
255
304
|
}
|
package/src/server.js
CHANGED
|
@@ -67,7 +67,7 @@ import {
|
|
|
67
67
|
// Hardcoded (not read from package.json) so it survives Bun's single-file
|
|
68
68
|
// --compile, where package.json isn't on a readable FS. CI fails the publish if
|
|
69
69
|
// this drifts from package.json, so the two can't silently diverge.
|
|
70
|
-
const VERSION = '0.10.
|
|
70
|
+
const VERSION = '0.10.33';
|
|
71
71
|
const HOST = process.env.CHATPANEL_BRIDGE_HOST || '127.0.0.1';
|
|
72
72
|
const PORT = Number(process.env.CHATPANEL_BRIDGE_PORT) || 4319;
|
|
73
73
|
|