zcode-acp-server 0.18.0 → 0.18.2
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 +27 -343
- package/README.zh-CN.md +24 -162
- package/dist/backend/sandbox.d.ts.map +1 -1
- package/dist/backend/sandbox.js +13 -1
- package/dist/backend/sandbox.js.map +1 -1
- package/dist/config/provider-registry.d.ts +6 -0
- package/dist/config/provider-registry.d.ts.map +1 -1
- package/dist/config/provider-registry.js +18 -2
- package/dist/config/provider-registry.js.map +1 -1
- package/dist/handlers/session.d.ts.map +1 -1
- package/dist/handlers/session.js +9 -0
- package/dist/handlers/session.js.map +1 -1
- package/docs/CLI.md +188 -0
- package/docs/REMOTE.md +115 -0
- package/docs/SANDBOX.md +56 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -124,355 +124,29 @@ most setups need no `ZCODE_BIN` at all — set it only for custom installs:
|
|
|
124
124
|
|
|
125
125
|
## Sandbox
|
|
126
126
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
-
|
|
130
|
-
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
env needed. Flipping it mid-run takes effect on the next prompt (the
|
|
134
|
-
backend restarts sandboxed); flipping back takes effect the next time the
|
|
135
|
-
backend restarts on its own.
|
|
136
|
-
|
|
137
|
-
Once armed, the zcode backend subprocess — and every Bash/Edit/Write it
|
|
138
|
-
performs, including all child processes — runs wrapped in a
|
|
139
|
-
Seatbelt (`sandbox-exec`) profile that denies file writes everywhere except:
|
|
140
|
-
|
|
141
|
-
- the workspace root(s) of your live sessions,
|
|
142
|
-
- `~/.zcode*` (the backend's own sessions/db/logs),
|
|
143
|
-
- the system temp directory and regenerable tool caches (`~/Library/Caches`,
|
|
144
|
-
`~/.cache`, `~/.npm`, `~/Library/pnpm`, `~/.node-gyp`),
|
|
145
|
-
- paths granted via the per-project config or the allow popup.
|
|
146
|
-
|
|
147
|
-
Reads and process execution stay open: deletion (`rm`, `mv`, truncation) is a
|
|
148
|
-
write-class syscall, so the write denial stops it regardless of which binary
|
|
149
|
-
performs it — including `/bin/rm`, `python shutil.rmtree`, or shell
|
|
150
|
-
redirections.
|
|
151
|
-
|
|
152
|
-
When a write outside the whitelist is attempted, the tool fails with
|
|
153
|
-
`Operation not permitted` and the bridge asks via the editor's permission
|
|
154
|
-
popup with four choices: allow **once**, allow **always**, reject **once**,
|
|
155
|
-
reject **always**. The "always" choices are persisted by the bridge into
|
|
156
|
-
`<workspace>/.zcode/acp/sandbox.json` (created on first run — allows to the
|
|
157
|
-
`allow` list, rejections to a `deny` list that suppresses future asks; edit
|
|
158
|
-
the file to undo either). "Once" choices and dismissed popups persist
|
|
159
|
-
nothing and will ask again. The agent itself cannot edit
|
|
160
|
-
that file — the sandbox denies
|
|
161
|
-
writes to `.zcode/acp/` inside the workspace while the bridge (outside the
|
|
162
|
-
sandbox) writes it on your behalf. After an allow, the backend restarts with
|
|
163
|
-
the widened profile (a few seconds; the bridge auto-continues the interrupted
|
|
164
|
-
task). Set `"strictGit": true` in the config to also put `.git` behind the
|
|
165
|
-
popup.
|
|
166
|
-
|
|
167
|
-
This targets accident prevention, not malice: indirect escapes (an
|
|
168
|
-
agent-edited `.bashrc`, build scripts, or git hooks that you later run
|
|
169
|
-
yourself outside the sandbox) are out of scope — treat its output like any
|
|
170
|
-
other code review. Verify a profile manually with
|
|
171
|
-
`bash scripts/verify-sandbox.sh` (macOS, after `pnpm build`).
|
|
127
|
+
Optional macOS Seatbelt confinement for everything the agent writes: dual
|
|
128
|
+
switch (`ZCODE_ACP_SANDBOX=1` globally, or `"enabled": true` in the
|
|
129
|
+
auto-created `<workspace>/.zcode/acp/sandbox.json` per project), allow/reject
|
|
130
|
+
popups for out-of-whitelist writes persisted visibly in that config, and
|
|
131
|
+
automatic backend restart + task continuation after an allow. Full manual:
|
|
132
|
+
[docs/SANDBOX.md](docs/SANDBOX.md).
|
|
172
133
|
|
|
173
134
|
## Remote Access
|
|
174
135
|
|
|
175
|
-
With `ZCODE_ACP_REMOTE=1` the bridge additionally
|
|
176
|
-
WebSocket
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
A ready-made client — Android APK plus a self-hostable web build — lives at
|
|
182
|
-
[william0wang/zcode-acp-remote](https://github.com/william0wang/zcode-acp-remote).
|
|
183
|
-
|
|
184
|
-
```text
|
|
185
|
-
phone / browser ──WS── tunnel ── hub (127.0.0.1:8377, single entry)
|
|
186
|
-
│ byte-level proxy
|
|
187
|
-
▼
|
|
188
|
-
bridge ACP endpoint (127.0.0.1:8378+n)
|
|
189
|
-
│ same AgentApp as stdio
|
|
190
|
-
Zed ──────── stdio ────────────────┘
|
|
191
|
-
```
|
|
192
|
-
|
|
193
|
-
Enable it per-agent in Zed's settings (Zed merges these into the agent's
|
|
194
|
-
environment):
|
|
195
|
-
|
|
196
|
-
```json
|
|
197
|
-
"agents": {
|
|
198
|
-
"ZCode": {
|
|
199
|
-
"command": "zcode-acp-server",
|
|
200
|
-
"env": {
|
|
201
|
-
"ZCODE_ACP_REMOTE": "1",
|
|
202
|
-
"ZCODE_ACP_REMOTE_TOKEN": "<a-long-random-secret>"
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
```
|
|
207
|
-
|
|
208
|
-
**Hub.** The first bridge with remote enabled spawns the hub daemon as a
|
|
209
|
-
detached, machine-level singleton on `ZCODE_ACP_HUB_PORT` (it can also be run
|
|
210
|
-
manually). It does three things only: token auth, instance discovery, and
|
|
211
|
-
byte-level proxying (ACP WebSocket plus read-only session files) — no session
|
|
212
|
-
state, no path semantics. It exits after ~10 idle minutes and is re-spawned
|
|
213
|
-
on demand. Each bridge registers every 10s as a heartbeat and drops out of
|
|
214
|
-
discovery ~30s after it stops.
|
|
215
|
-
|
|
216
|
-
**Discovery API** (for client authors; fields are additive-only):
|
|
217
|
-
|
|
218
|
-
```text
|
|
219
|
-
GET /api/instances → [{"id","port","pid","startedAt","workspace",
|
|
220
|
-
"origin","sessions":[{"sessionId","title?","updatedAt"}]}]
|
|
221
|
-
GET /api/instances?probe=1 → same list, but unreachable bridges are pruned first
|
|
222
|
-
WS /acp?instance=<id> → proxied to that bridge's endpoint
|
|
223
|
-
GET /api/instances/{id}/fs/… → read-only session files (list + raw bytes, ADR-0004)
|
|
224
|
-
```
|
|
225
|
-
|
|
226
|
-
`origin` labels how an instance was started: `"editor"` (a bridge an editor
|
|
227
|
-
spawned over stdio) or `"serve"` (a headless bridge created remotely, see
|
|
228
|
-
below).
|
|
229
|
-
|
|
230
|
-
**Remote session-create** (ADR-0014). A remote client can start a NEW agent
|
|
231
|
-
session in any of the machine's known projects — no editor required:
|
|
232
|
-
|
|
233
|
-
```text
|
|
234
|
-
GET /api/projects → [{"workspacePath","sessions","lastActive"}]
|
|
235
|
-
POST /api/instances {workspacePath} → {"id","reused"}
|
|
236
|
-
```
|
|
237
|
-
|
|
238
|
-
`/api/projects` aggregates the App's tasks index: every workspace that ever
|
|
239
|
-
ran a session (system temp trees, `~/.zcode` itself, and vanished
|
|
240
|
-
directories filtered out), newest activity first. The list gates the POST —
|
|
241
|
-
paths outside it get 403 (a convenience bound, not a security boundary: a
|
|
242
|
-
token holder can already drive an editor-bridge session in any cwd; the
|
|
243
|
-
trust boundary is the token). On create the hub
|
|
244
|
-
spawns `zcode-acp serve` — a headless bridge — in the project's cwd; it
|
|
245
|
-
registers back within seconds and is reachable like any other instance. A
|
|
246
|
-
live serve instance for the same workspace is reused instead of re-spawned
|
|
247
|
-
(`reused:true`). The serve bridge lives for remote interest only: it exits
|
|
248
|
-
~10 minutes after the last client detaches and the last turn finishes, and
|
|
249
|
-
its `session/new` always uses the project cwd regardless of what a client
|
|
250
|
-
sends.
|
|
251
|
-
|
|
252
|
-
`sessions` lists the project's **currently running** conversations (live
|
|
253
|
-
editor tabs and remote attachments) under the same ACP session ids the
|
|
254
|
-
editor uses — attaching by id joins the conversation's live notification
|
|
255
|
-
stream, and the hub dedupes sessions shared by several bridges of the same
|
|
256
|
-
project.
|
|
257
|
-
|
|
258
|
-
Auth is `Authorization: Bearer <token>` or `?token=` (browsers cannot set WS
|
|
259
|
-
headers); `/api/*` sends `Access-Control-Allow-Origin: *` — the token is the
|
|
260
|
-
security boundary. A proxied connection stays bound to one instance; switching
|
|
261
|
-
instances means reconnecting. Remote clients can also pull plan quota via the
|
|
262
|
-
non-standard `account/usage_stats` ACP method (no session required), and
|
|
263
|
-
browse/download the files of a session's project through the `/fs` routes
|
|
264
|
-
above. During replay, compaction summaries and rewritten tool calls arrive as
|
|
265
|
-
collapsed `tool_call` updates instead of walls of text
|
|
266
|
-
(`docs/REPLAY-GUIDE.md`).
|
|
267
|
-
|
|
268
|
-
Building a remote client — web, mobile, or CLI? The full integration contract
|
|
269
|
-
(endpoints, framing, lifecycle timings, failure recovery, platform notes)
|
|
270
|
-
lives in [docs/REMOTE-CLIENTS.md](docs/REMOTE-CLIENTS.md).
|
|
271
|
-
|
|
272
|
-
**Semantics.** All agent notifications are broadcast to every client.
|
|
273
|
-
Permission / elicitation requests go to every client and the **first answer
|
|
274
|
-
wins**; losing clients receive `$/cancel_request` so their dialogs close.
|
|
275
|
-
Concurrent prompts for one session are serialized exactly as they are for a
|
|
276
|
-
single editor. Capabilities declared by any client are OR-merged.
|
|
277
|
-
|
|
278
|
-
**Tunnels.** Designed for one-port tunnels (Cloudflare Tunnel, frp): map the
|
|
279
|
-
hub port only. frp's `tcp` mode passes WebSocket as-is; Cloudflare Tunnel
|
|
280
|
-
drops idle WebSocket connections, so the hub sends 30s keepalive pings on both
|
|
281
|
-
legs. The bridge endpoint itself is loopback-only and never exposed.
|
|
282
|
-
|
|
283
|
-
**Binding beyond loopback.** The hub speaks plain HTTP/WS — the token travels
|
|
284
|
-
and authorizes in cleartext, so `ZCODE_ACP_HUB_HOST=0.0.0.0` (needed only when
|
|
285
|
-
the tunnel agent runs in its own container) is exactly as safe as the network
|
|
286
|
-
it lands on. Keep the bind loopback unless that interface is private to the
|
|
287
|
-
tunnel agent, and put TLS in front before mapping it anywhere untrusted.
|
|
136
|
+
With `ZCODE_ACP_REMOTE=1` the bridge additionally serves the **same sessions**
|
|
137
|
+
over WebSocket through a machine-level hub daemon — a phone or browser can
|
|
138
|
+
watch, drive, and even create sessions in any known project while Zed stays
|
|
139
|
+
the primary client. Discovery API, tunnels, auth, and semantics:
|
|
140
|
+
[docs/REMOTE.md](docs/REMOTE.md); the client integration contract:
|
|
141
|
+
[docs/REMOTE-CLIENTS.md](docs/REMOTE-CLIENTS.md).
|
|
288
142
|
|
|
289
143
|
## Unified CLI (`zcode-acp`)
|
|
290
144
|
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
Bare `zcode-acp` opens an interactive terminal chat against this same bridge
|
|
297
|
-
(built with [Ink](https://github.com/vadimdemedes/ink), the same renderer
|
|
298
|
-
Claude Code and Gemini CLI use). Completed messages print once into the
|
|
299
|
-
terminal's **native scrollback** — smooth wheel scrolling, text selection,
|
|
300
|
-
search, and history that survives exit are all just your terminal, unchanged.
|
|
301
|
-
Only a compact dynamic footer ever repaints: the live-turn tail (capped at
|
|
302
|
-
half the screen), queued-prompt panel, completion menu, and the prompt box.
|
|
303
|
-
|
|
304
|
-
```bash
|
|
305
|
-
zcode-acp # chat in this directory
|
|
306
|
-
```
|
|
307
|
-
|
|
308
|
-
A startup welcome panel (version, session directory, seeded config, key
|
|
309
|
-
hints) lands in scrollback first. Streaming output has code-fence coloring,
|
|
310
|
-
dim thinking lines, and live tool rows. The prompt line wraps across rows
|
|
311
|
-
with a CJK-aware block cursor — `←`/`→` (or Ctrl-B/Ctrl-F) move inside the
|
|
312
|
-
typed text, Backspace/Delete edit at the caret, Ctrl-A/Ctrl-E jump to the
|
|
313
|
-
line's ends, Ctrl-U clears the line; completion keeps precedence while its
|
|
314
|
-
menu is open. The status row carries a compact plan-quota readout
|
|
315
|
-
(`5h 16% · wk 4%`) refreshed every 10 minutes — `/quota` prints the full
|
|
316
|
-
card. Pasted or dragged-in content (error logs, file drops) is sanitized and
|
|
317
|
-
size-capped before it reaches the editor, so long pastes batch cleanly.
|
|
318
|
-
`esc` interrupts a running turn (immediately — the bridge resolves the prompt
|
|
319
|
-
as cancelled without waiting for the backend); `Ctrl-C` also interrupts, and
|
|
320
|
-
while idle press it twice to quit. `/exit` leaves; the session itself persists
|
|
321
|
-
in the ZCode backend and is available to your editor.
|
|
322
|
-
|
|
323
|
-
Sending a follow-up right after an interrupt waits for the backend to finish
|
|
324
|
-
the cancelled generation first — a `[上一个回复仍在生成,等待结束后发送…]`
|
|
325
|
-
note explains the pause (the Aug-28 app-server accepts mid-generation sends
|
|
326
|
-
as steer input but drops them when the old turn ends; the bridge polls until
|
|
327
|
-
the session is idle, up to 90s, so the message actually runs).
|
|
328
|
-
|
|
329
|
-
Messages typed while a turn is running (or the session is still starting) are
|
|
330
|
-
queued, not lost: each shows up in the transcript immediately and a `⏸ queued`
|
|
331
|
-
panel above the prompt box lists everything waiting to run. When the current
|
|
332
|
-
turn ends the queue drains one prompt at a time — through the same command
|
|
333
|
-
parsing as direct submits, so queued `/help` and `/exit` still work.
|
|
334
|
-
|
|
335
|
-
`/sessions` lists this project's previous conversations in an 8-row sliding
|
|
336
|
-
window (position counter, plus "N newer above / N older below" hints); arrow
|
|
337
|
-
keys move over the full list even though only part of it renders. Resuming
|
|
338
|
-
loads the most recent tail of the conversation (last 50 messages, turn-aligned)
|
|
339
|
-
instead of dumping thousands of lines at once — when older history exists the
|
|
340
|
-
note says exactly what was loaded:
|
|
341
|
-
`resumed "<title>" — showing last 50 of 1234 messages`.
|
|
342
|
-
|
|
343
|
-
The full-width prompt box mirrors the editor's dropdowns: its bottom row shows
|
|
344
|
-
the current `model · mode · thought level`, and typing `/` opens an interactive
|
|
345
|
-
completion menu — `↑`/`↓` move, `enter` picks the highlighted entry (or `tab` /
|
|
346
|
-
`→`; typing the exact form already sends), `esc` dismisses. After picking
|
|
347
|
-
`/model`, `/mode`, or `/thought` the same menu lists the config options (the
|
|
348
|
-
current one marked `●`) and **enter on a row switches immediately** — no second
|
|
349
|
-
confirmation. Argument-free commands (`/exit`, `/help`, `/sessions`, `/new`,
|
|
350
|
-
`/compact`, `/mcp`, `/quota`) run on pick as well; every other completion
|
|
351
|
-
(skills, plugins) only fills the line, since those usually expect arguments.
|
|
352
|
-
The arg-less forms still print a static listing over the same slash-command
|
|
353
|
-
path the editor uses. `/help` lists every command the bridge advertises,
|
|
354
|
-
including plugin commands. `/new` swaps in a fresh session without leaving
|
|
355
|
-
the terminal (the old conversation stays in `/sessions` and in scrollback).
|
|
356
|
-
|
|
357
|
-
Submitted prompts are history: `↑`/`↓` (with the completion menu closed)
|
|
358
|
-
recall them per project across restarts — the first `↑` stashes the draft
|
|
359
|
-
and `↓` past the newest entry restores it. Pasted text folds to a single
|
|
360
|
-
line (newlines and tabs become spaces), so a multi-paragraph paste lands in
|
|
361
|
-
the box as one prompt instead of firing line-by-line. While a reply streams,
|
|
362
|
-
the footer shows a live status row — `⠋ working… (12s · esc to interrupt)` —
|
|
363
|
-
so stretches with no streamed output (long tool calls) still visibly tick.
|
|
364
|
-
|
|
365
|
-
Unexpected internal errors never take the REPL down silently: they print to
|
|
366
|
-
stderr and surface as an `-- error absorbed: …` note in the transcript while
|
|
367
|
-
everything keeps running. Only repeated failures within ten seconds trip a
|
|
368
|
-
breaker that shuts the UI down cleanly.
|
|
369
|
-
|
|
370
|
-
While remote access is enabled, turns started from other clients (the mobile
|
|
371
|
-
app, a second editor) render live in the REPL too, and questions or permission
|
|
372
|
-
requests answered elsewhere dismiss the local picker automatically.
|
|
373
|
-
|
|
374
|
-
Without a TTY (pipes, Windows editor shims — where the bin name is lost from
|
|
375
|
-
`argv`), bare `zcode-acp` falls back to the stdio server, so editor configs
|
|
376
|
-
pointing at either bin name keep working. Ask for the REPL explicitly with
|
|
377
|
-
`zcode-acp repl`; without a TTY that errors instead of falling back.
|
|
378
|
-
|
|
379
|
-
### Quota cards
|
|
380
|
-
|
|
381
|
-
Check plan usage from the terminal — no editor or running server needed. By
|
|
382
|
-
default it shows both **GLM Coding Plan** and **Opencode Go** in one card;
|
|
383
|
-
pass a provider to focus on one.
|
|
384
|
-
|
|
385
|
-
GLM credentials are read from `~/.zcode/v2/config.json`. Opencode Go
|
|
386
|
-
credentials come from environment variables (the dashboard needs a browser
|
|
387
|
-
cookie — see [Opencode Go setup](#opencode-go-setup) below).
|
|
388
|
-
|
|
389
|
-
```bash
|
|
390
|
-
# Both providers (default): GLM + Opencode Go in one card
|
|
391
|
-
zcode-acp quota
|
|
392
|
-
|
|
393
|
-
# Focus on one provider
|
|
394
|
-
zcode-acp quota glm # GLM Coding Plan only
|
|
395
|
-
zcode-acp quota go # Opencode Go only (rolling + weekly + monthly)
|
|
396
|
-
|
|
397
|
-
# Live monitor: clear the screen and refresh every 30s (default)
|
|
398
|
-
zcode-acp quota -w
|
|
399
|
-
zcode-acp quota go -w # watch Opencode Go only
|
|
400
|
-
|
|
401
|
-
# Refresh at a custom interval (seconds; minimum 10)
|
|
402
|
-
zcode-acp quota --watch --interval 60
|
|
403
|
-
|
|
404
|
-
# Plain monochrome bars (color is the default on a terminal)
|
|
405
|
-
zcode-acp quota --plain
|
|
406
|
-
```
|
|
407
|
-
|
|
408
|
-
By default the CLI renders heat-colored (green→yellow→red) progress bars with
|
|
409
|
-
the usage numbers overlaid inside the bar, so each line stays short. Pass
|
|
410
|
-
`--plain` (or `-p`) for the classic monochrome `█`/`░` layout. Color is also
|
|
411
|
-
disabled automatically when stdout is piped or redirected, so captured output
|
|
412
|
-
stays clean.
|
|
413
|
-
|
|
414
|
-
The watch mode clears and redraws the card in place, like `top`/`htop`. Press
|
|
415
|
-
`Ctrl-C` to exit. The 10s minimum exists because the quota API is cached for
|
|
416
|
-
10s internally — a shorter interval would just keep returning the stale cached
|
|
417
|
-
value.
|
|
418
|
-
|
|
419
|
-
When the package isn't globally installed, run the built file directly:
|
|
420
|
-
|
|
421
|
-
```bash
|
|
422
|
-
node dist/cli.js quota -w
|
|
423
|
-
```
|
|
424
|
-
|
|
425
|
-
### Opencode Go setup
|
|
426
|
-
|
|
427
|
-
Opencode Go has no JSON API for subscription usage — the CLI scrapes the
|
|
428
|
-
authenticated dashboard at `opencode.ai/workspace/<id>/go`, so it needs your
|
|
429
|
-
browser `auth` cookie. Credentials are read from two sources, **merged
|
|
430
|
-
field-by-field with environment variables taking precedence** over the config
|
|
431
|
-
file:
|
|
432
|
-
|
|
433
|
-
- **Config file**: `~/.pi/agent/opencode-go.json` — same convention as the
|
|
434
|
-
`@beyona/pi-zai-usage` Pi extension, so if you already configured it there
|
|
435
|
-
you're done.
|
|
436
|
-
```json
|
|
437
|
-
{ "workspaceId": "wrk_your_workspace_id", "authCookie": "Fe26.2**your_cookie_value" }
|
|
438
|
-
```
|
|
439
|
-
- **Environment variables** (override the matching file field):
|
|
440
|
-
```bash
|
|
441
|
-
export OPENCODE_GO_WORKSPACE_ID="wrk_your_workspace_id"
|
|
442
|
-
export OPENCODE_GO_AUTH_COOKIE="Fe26.2**your_cookie_value"
|
|
443
|
-
```
|
|
444
|
-
|
|
445
|
-
How to get the values:
|
|
446
|
-
|
|
447
|
-
1. **Workspace ID** — open `https://opencode.ai`, navigate to your Go
|
|
448
|
-
workspace, and copy the `wrk_…` id from the URL
|
|
449
|
-
(`https://opencode.ai/workspace/<wrk_…>/go`).
|
|
450
|
-
2. **Auth cookie** — open browser DevTools (F12) → Application → Cookies →
|
|
451
|
-
`opencode.ai` → copy the value of the cookie named `auth` (it starts with
|
|
452
|
-
`Fe26.2**`).
|
|
453
|
-
|
|
454
|
-
Without credentials, the default dual-provider mode silently shows GLM only
|
|
455
|
-
(no error). Running `zcode-acp quota go` without credentials prints a setup hint.
|
|
456
|
-
|
|
457
|
-
### Hub and server subcommands
|
|
458
|
-
|
|
459
|
-
`zcode-acp hub` runs the remote-access hub daemon manually (normally
|
|
460
|
-
auto-spawned by bridges — see [Remote Access](#remote-access)). `zcode-acp
|
|
461
|
-
server` speaks ACP on stdio — that is what editors invoke through the
|
|
462
|
-
`zcode-acp-server` bin; you rarely need it by hand.
|
|
463
|
-
|
|
464
|
-
### Upgrading from 0.11
|
|
465
|
-
|
|
466
|
-
0.12.0 folds the old standalone bins into the unified CLI (see
|
|
467
|
-
[ADR-0007](docs/adr/0007-unified-cli-entry-and-bin-pruning.md)):
|
|
468
|
-
|
|
469
|
-
| Old (≤0.11) | New (0.12) |
|
|
470
|
-
| -------------------- | ------------------------------------- |
|
|
471
|
-
| `zcode-acp-server` | unchanged (kept for editor configs) |
|
|
472
|
-
| `zcode-quota [args]` | `zcode-acp quota [args]` (same flags) |
|
|
473
|
-
| `zcode-acp-hub` | `zcode-acp hub` |
|
|
474
|
-
|
|
475
|
-
Editor configs referencing `zcode-acp-server` keep working unchanged.
|
|
145
|
+
One command for every surface: an interactive terminal chat REPL with native
|
|
146
|
+
scrollback (`zcode-acp`), plan quota cards (`zcode-acp quota`, GLM + Opencode
|
|
147
|
+
Go), the remote hub daemon (`zcode-acp hub`), and the stdio server editors
|
|
148
|
+
invoke (`zcode-acp server`). REPL keys, completion, history, and quota setup:
|
|
149
|
+
[docs/CLI.md](docs/CLI.md).
|
|
476
150
|
|
|
477
151
|
## ACP Registry
|
|
478
152
|
|
|
@@ -528,6 +202,9 @@ See [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) for the full architecture docum
|
|
|
528
202
|
|
|
529
203
|
- [Architecture](docs/ARCHITECTURE.md) — event stream, dual-path deduplication, module responsibilities
|
|
530
204
|
- [Protocol](docs/PROTOCOL.md) — ZCode JSON-RPC protocol details
|
|
205
|
+
- [Sandbox](docs/SANDBOX.md) — full sandbox manual (switches, whitelist, popups, verification)
|
|
206
|
+
- [Remote Access](docs/REMOTE.md) — hub, discovery API, tunnels, remote session-create
|
|
207
|
+
- [Unified CLI](docs/CLI.md) — REPL, quota cards, hub/server subcommands
|
|
531
208
|
- [Remote Clients](docs/REMOTE-CLIENTS.md) — remote access integration contract (discovery, transport, recovery)
|
|
532
209
|
- [Replay Guide](docs/REPLAY-GUIDE.md) — building a client UI on tail replay
|
|
533
210
|
- [Development](docs/DEVELOPMENT.md) — local development, debugging, adding extension methods
|
|
@@ -539,6 +216,13 @@ Contributions are welcome! See [CONTRIBUTING.md](CONTRIBUTING.md) for setup,
|
|
|
539
216
|
code style, commit conventions, and the PR checklist. Notable changes are
|
|
540
217
|
recorded in [CHANGELOG.md](CHANGELOG.md).
|
|
541
218
|
|
|
219
|
+
Thanks to everyone who has contributed (updated automatically from the
|
|
220
|
+
[contributors graph](https://github.com/william0wang/zcode-acp/graphs/contributors)):
|
|
221
|
+
|
|
222
|
+
<a href="https://github.com/william0wang/zcode-acp/graphs/contributors">
|
|
223
|
+
<img src="https://contrib.rocks/image?repo=william0wang/zcode-acp" alt="Contributors" />
|
|
224
|
+
</a>
|
|
225
|
+
|
|
542
226
|
## Related Projects
|
|
543
227
|
|
|
544
228
|
- [glm-acp-agent](https://github.com/stefandevo/glm-acp-agent) — a self-contained ACP agent that calls the GLM API directly; zcode-acp instead bridges the real `zcode app-server`, inheriting its full official harness.
|
package/README.zh-CN.md
CHANGED
|
@@ -117,174 +117,26 @@ ZCode CLI 内置于桌面应用中,默认不会加到 `PATH`。用 `ZCODE_BIN`
|
|
|
117
117
|
|
|
118
118
|
## 沙箱
|
|
119
119
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
`true` 即可单独启用本项目,无需全局环境变量。运行中途翻开关,下一次
|
|
126
|
-
prompt 生效(后端以沙箱重启);翻回 `false` 则在后端下次自然重启时停用。
|
|
127
|
-
|
|
128
|
-
启用后,zcode 后端子进程——连同它执行的每一次 Bash/Edit/Write 及所有子进
|
|
129
|
-
程——被包进 Seatbelt(`sandbox-exec`)沙箱:除以下位置外**一律禁止文件写入**:
|
|
130
|
-
|
|
131
|
-
- 各活跃会话的工作区根目录;
|
|
132
|
-
- `~/.zcode*`(后端自身的会话/数据库/日志);
|
|
133
|
-
- 系统临时目录与可再生的工具缓存(`~/Library/Caches`、`~/.cache`、
|
|
134
|
-
`~/.npm`、`~/Library/pnpm`、`~/.node-gyp`);
|
|
135
|
-
- 项目配置或放行弹窗授权的路径。
|
|
136
|
-
|
|
137
|
-
读取和进程执行不受限:删除(`rm`、`mv`、截断)属于写类系统调用,写禁令
|
|
138
|
-
会拦下它——无论用哪个二进制执行(`/bin/rm`、Python `shutil.rmtree`、shell
|
|
139
|
-
重定向都一样)。
|
|
140
|
-
|
|
141
|
-
当 Agent 尝试写白名单之外的位置时,工具报 `Operation not permitted`,桥
|
|
142
|
-
通过编辑器权限弹窗给出四个选项:**仅此一次**、**始终允许**、**拒绝一次**、
|
|
143
|
-
**始终拒绝**。两个“始终”由桥代写进 `<工作区>/.zcode/acp/sandbox.json`
|
|
144
|
-
(首次运行自动创建——放行记入 `allow` 列表,拒绝记入 `deny` 列表并不再
|
|
145
|
-
询问;编辑该文件即可撤销);两个“一次”及关闭弹窗不保存任何东西,下次
|
|
146
|
-
仍会询问。Agent 自己改不了这个文件——沙箱禁止写工作区内的
|
|
147
|
-
`.zcode/acp/`,而桥在沙箱外。放行
|
|
148
|
-
后后端以加宽的 profile 重启(几秒),桥自动继续被中断的任务。配置里设
|
|
149
|
-
`"strictGit": true` 可让 `.git` 也走弹窗。
|
|
150
|
-
|
|
151
|
-
沙箱目标是防误删误写,不是防恶意:间接逃逸(Agent 改了你的 `.bashrc`、构
|
|
152
|
-
建脚本或 git hook,随后由你自己在沙箱外执行)不在防护范围——请像代码评审
|
|
153
|
-
一样对待它的产出。可用 `bash scripts/verify-sandbox.sh` 手工验证
|
|
154
|
-
profile(macOS,先 `pnpm build`)。
|
|
120
|
+
可选的 macOS Seatbelt 写入隔离:双开关(全局 `ZCODE_ACP_SANDBOX=1`,或项目
|
|
121
|
+
级在自动创建的 `<工作区>/.zcode/acp/sandbox.json` 里设 `"enabled": true`),
|
|
122
|
+
白名单之外的写入会弹放行/拒绝确认,"始终"决定可见地持久化在该配置里;放行
|
|
123
|
+
后后端自动以加宽的 profile 重启并续接被中断的任务。完整手册(英文):
|
|
124
|
+
[docs/SANDBOX.md](docs/SANDBOX.md)。
|
|
155
125
|
|
|
156
126
|
## 远程访问
|
|
157
127
|
|
|
158
|
-
设置 `ZCODE_ACP_REMOTE=1` 后,bridge
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
```text
|
|
164
|
-
phone / browser ──WS── 隧道 ── hub (127.0.0.1:8377, 唯一入口)
|
|
165
|
-
│ 字节级代理
|
|
166
|
-
▼
|
|
167
|
-
bridge ACP 端点 (127.0.0.1:8378+n)
|
|
168
|
-
│ 与 stdio 同一个 AgentApp
|
|
169
|
-
Zed ──────── stdio ────────────┘
|
|
170
|
-
```
|
|
171
|
-
|
|
172
|
-
启用方式:在上文「配置 Zed」的 `env` 里追加(Zed 会把这些合并进 agent
|
|
173
|
-
的环境变量):
|
|
174
|
-
|
|
175
|
-
```json
|
|
176
|
-
"ZCODE_ACP_REMOTE": "1",
|
|
177
|
-
"ZCODE_ACP_REMOTE_TOKEN": "<一段足够长的随机密钥>"
|
|
178
|
-
```
|
|
179
|
-
|
|
180
|
-
相关环境变量(详见上文表格):`ZCODE_ACP_REMOTE`(开关)、
|
|
181
|
-
`ZCODE_ACP_REMOTE_TOKEN`(必填 token)、`ZCODE_ACP_HUB_PORT`(hub 端口,
|
|
182
|
-
默认 8377)、`ZCODE_ACP_HUB_HOST`(hub 绑定地址)、
|
|
183
|
-
`ZCODE_ACP_REMOTE_PORT`(bridge 端点起始端口,默认 8378)。
|
|
184
|
-
|
|
185
|
-
**Hub。** 第一个启用远程的 bridge 会以 detached 方式拉起机器级单例
|
|
186
|
-
`zcode-acp-hub`(监听 `ZCODE_ACP_HUB_PORT`,也可手动运行)。它只做三件事:
|
|
187
|
-
token 鉴权、实例发现、字节级 WebSocket 代理——不保存会话状态、不解析 ACP。
|
|
188
|
-
空闲约 10 分钟后退出,需要时再被拉起。每个 bridge 每 10 秒注册一次作为
|
|
189
|
-
心跳,心跳停止约 30 秒后从发现列表移除;客户端刷新时也可调用
|
|
190
|
-
`GET /api/instances?probe=1` 主动探测,立即清理不可达的实例。
|
|
191
|
-
|
|
192
|
-
**远程创建会话**(ADR-0014)。远程客户端可以在本机的已知项目里直接开一个
|
|
193
|
-
新会话——不需要任何编辑器在场:
|
|
194
|
-
|
|
195
|
-
```text
|
|
196
|
-
GET /api/projects → [{"workspacePath","sessions","lastActive"}]
|
|
197
|
-
POST /api/instances {workspacePath} → {"id","reused"}
|
|
198
|
-
```
|
|
199
|
-
|
|
200
|
-
`/api/projects` 聚合 App 的任务索引:所有跑过会话的项目(过滤系统临时
|
|
201
|
-
目录、`~/.zcode` 自身和已不存在的目录),按最近活跃排序。这份列表约束
|
|
202
|
-
POST——列表之外的路径一律 403(注意这是便利性约束而非安全边界:持有
|
|
203
|
-
token 者本就能以任意 cwd 驱动 editor bridge 会话,真正的信任边界是
|
|
204
|
-
token 本身)。创建时 hub 会在项目目录下拉起 `zcode-acp serve`(无头 bridge),
|
|
205
|
-
数秒内注册回 hub,之后像普通实例一样可连接;同项目已有存活的 serve 实例
|
|
206
|
-
则直接复用(`reused:true`)。serve bridge 只为远程连接而活:最后一个客户端
|
|
207
|
-
断开且最后一个 turn 结束约 10 分钟后自动退出;其 `session/new` 无论客户端
|
|
208
|
-
传什么都使用项目目录(cwd 端到端钉死在项目内)。
|
|
209
|
-
|
|
210
|
-
**语义。** 所有 agent 通知广播给每个已连接客户端;权限 / elicitation 请求
|
|
211
|
-
发给所有客户端,**先应答者生效**,其余客户端收到 `$/cancel_request` 关闭
|
|
212
|
-
对话框。同一会话的并发 prompt 与单编辑器一样串行化。任一客户端声明的能力
|
|
213
|
-
按 OR 合并。`/api/instances` 的每个实例带 `origin` 字段(`"editor"`=编辑器
|
|
214
|
-
stdio 桥,`"serve"`=远程创建的无头桥),客户端可据此标注。
|
|
215
|
-
|
|
216
|
-
**隧道。** 面向单端口隧道(Cloudflare Tunnel、frp)设计:只映射 hub 端口。
|
|
217
|
-
frp 的 `tcp` 模式原样透传 WebSocket;Cloudflare Tunnel 会断开空闲连接,
|
|
218
|
-
hub 因此在两段链路上每 30 秒发送 keepalive ping。bridge 端点本身只监听
|
|
219
|
-
回环地址,永不直接暴露。
|
|
220
|
-
|
|
221
|
-
要构建远程客户端(Web、移动端或 CLI)?完整的集成契约(端点、帧格式、
|
|
222
|
-
生命周期时序、故障恢复、平台注意事项)见
|
|
128
|
+
设置 `ZCODE_ACP_REMOTE=1` 后,bridge 会额外通过机器级 hub 守护进程把**相同
|
|
129
|
+
的会话**暴露到 WebSocket——手机或浏览器可以旁观、驱动、甚至在本机已知项目里
|
|
130
|
+
直接创建新会话,Zed 仍是主客户端并拥有进程。发现 API、隧道、鉴权与语义:
|
|
131
|
+
[docs/REMOTE.md](docs/REMOTE.md);客户端集成契约:
|
|
223
132
|
[docs/REMOTE-CLIENTS.md](docs/REMOTE-CLIENTS.md)。
|
|
224
133
|
|
|
225
|
-
##
|
|
226
|
-
|
|
227
|
-
除了 ACP server,本包还附带一个 `zcode-quota` 命令,可在**终端**里直接查询
|
|
228
|
-
用量——无需编辑器,也无需 server 运行。默认在一张卡片里同时显示
|
|
229
|
-
**GLM Coding Plan** 和 **Opencode Go**;传入 provider 参数可只看其中一个。
|
|
230
|
-
|
|
231
|
-
GLM 凭证读取自 `~/.zcode/v2/config.json`。Opencode Go 凭证来自环境变量
|
|
232
|
-
(dashboard 需要浏览器 cookie——见下方 [Opencode Go 配置](#opencode-go-配置))。
|
|
233
|
-
|
|
234
|
-
```bash
|
|
235
|
-
# 双平台(默认):GLM + Opencode Go 合并为一张卡片
|
|
236
|
-
zcode-quota
|
|
237
|
-
|
|
238
|
-
# 只看某一个 provider
|
|
239
|
-
zcode-quota glm # 仅 GLM Coding Plan
|
|
240
|
-
zcode-quota go # 仅 Opencode Go(rolling + weekly + monthly 三窗口)
|
|
241
|
-
|
|
242
|
-
# 常驻监控:清屏并每 30s 刷新(默认)
|
|
243
|
-
zcode-quota -w
|
|
244
|
-
zcode-quota go -w # 只监控 Opencode Go
|
|
245
|
-
|
|
246
|
-
# 自定义刷新间隔(秒,最小 10)
|
|
247
|
-
zcode-quota --watch --interval 60
|
|
134
|
+
## 统一 CLI(zcode-acp)
|
|
248
135
|
|
|
249
|
-
|
|
250
|
-
zcode-quota
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
默认情况下 CLI 会渲染热力配色(绿→黄→红)的进度条,并把用量数字叠在条内,
|
|
254
|
-
这样每行更紧凑。传 `--plain`(或 `-p`)切回经典的 `█`/`░` 单色布局。当 stdout
|
|
255
|
-
被管道或重定向时,彩色也会自动关闭,保证捕获到的输出干净。
|
|
256
|
-
|
|
257
|
-
watch 模式会原地清屏重绘卡片,效果类似 `top`/`htop`。按 `Ctrl-C` 退出。
|
|
258
|
-
之所以设最小间隔 10s,是因为配额 API 内部有 10s 缓存——更短的间隔只会一直
|
|
259
|
-
返回过期的缓存值,没有意义。
|
|
260
|
-
|
|
261
|
-
未全局安装时,可直接运行构建产物:
|
|
262
|
-
|
|
263
|
-
```bash
|
|
264
|
-
node dist/bin/quota.js -w
|
|
265
|
-
```
|
|
266
|
-
|
|
267
|
-
### Opencode Go 配置
|
|
268
|
-
|
|
269
|
-
Opencode Go 订阅用量没有 JSON API——CLI 抓取的是登录后的 dashboard 页面
|
|
270
|
-
`opencode.ai/workspace/<id>/go`,因此需要你的浏览器 `auth` cookie。设置两个
|
|
271
|
-
环境变量:
|
|
272
|
-
|
|
273
|
-
```bash
|
|
274
|
-
export OPENCODE_GO_WORKSPACE_ID="wrk_你的工作区id"
|
|
275
|
-
export OPENCODE_GO_AUTH_COOKIE="Fe26.2**你的cookie值"
|
|
276
|
-
```
|
|
277
|
-
|
|
278
|
-
获取方式:
|
|
279
|
-
|
|
280
|
-
1. **Workspace ID**——打开 `https://opencode.ai`,进入你的 Go 工作区,从 URL
|
|
281
|
-
里复制 `wrk_…` id(`https://opencode.ai/workspace/<wrk_…>/go`)。
|
|
282
|
-
2. **Auth cookie**——打开浏览器开发者工具(F12)→ Application → Cookies →
|
|
283
|
-
`opencode.ai` → 复制名为 `auth` 的 cookie 值(以 `Fe26.2**` 开头)。
|
|
284
|
-
|
|
285
|
-
未设置这两个变量时,默认的双平台模式会**静默退化为只显示 GLM**(不报错)。
|
|
286
|
-
若明确运行 `zcode-quota go` 但未配置,会打印一条配置提示。把它们加到 shell
|
|
287
|
-
配置文件(`~/.zshrc` / `~/.bashrc`)即可持久化。
|
|
136
|
+
本包所有能力收敛在一条命令下:交互式终端聊天 REPL(原生滚动回溯)、套餐
|
|
137
|
+
用量卡片(`zcode-acp quota`,GLM + Opencode Go)、远程 hub 守护进程
|
|
138
|
+
(`zcode-acp hub`)以及编辑器调用的 stdio server(`zcode-acp server`)。
|
|
139
|
+
REPL 按键、补全、历史与配额配置详见 [docs/CLI.md](docs/CLI.md)。
|
|
288
140
|
|
|
289
141
|
## ACP Registry
|
|
290
142
|
|
|
@@ -336,6 +188,9 @@ CI 会在每次 push 和 pull request 时运行 `typecheck`、`lint`、`build`
|
|
|
336
188
|
|
|
337
189
|
- [架构](docs/ARCHITECTURE.md) —— 事件流、双路径去重、模块职责
|
|
338
190
|
- [协议](docs/PROTOCOL.md) —— ZCode JSON-RPC 协议细节
|
|
191
|
+
- [沙箱](docs/SANDBOX.md) —— 沙箱完整手册(开关、白名单、弹窗、验证)
|
|
192
|
+
- [远程访问](docs/REMOTE.md) —— hub、发现 API、隧道、远程创建会话
|
|
193
|
+
- [统一 CLI](docs/CLI.md) —— REPL、配额卡片、hub/server 子命令
|
|
339
194
|
- [开发](docs/DEVELOPMENT.md) —— 本地开发、调试、新增扩展方法
|
|
340
195
|
- [故障排查](docs/TROUBLESHOOTING.md) —— 常见问题排查
|
|
341
196
|
|
|
@@ -344,6 +199,13 @@ CI 会在每次 push 和 pull request 时运行 `typecheck`、`lint`、`build`
|
|
|
344
199
|
欢迎贡献!请阅读 [CONTRIBUTING.md](CONTRIBUTING.md) 了解环境搭建、代码风格、
|
|
345
200
|
commit 约定和 PR 检查清单。重要变更记录在 [CHANGELOG.md](CHANGELOG.md)。
|
|
346
201
|
|
|
202
|
+
感谢每一位贡献者(由[贡献者图谱](https://github.com/william0wang/zcode-acp/graphs/contributors)
|
|
203
|
+
自动生成,覆盖全部历史贡献者):
|
|
204
|
+
|
|
205
|
+
<a href="https://github.com/william0wang/zcode-acp/graphs/contributors">
|
|
206
|
+
<img src="https://contrib.rocks/image?repo=william0wang/zcode-acp" alt="贡献者" />
|
|
207
|
+
</a>
|
|
208
|
+
|
|
347
209
|
## 相关项目
|
|
348
210
|
|
|
349
211
|
- [glm-acp-agent](https://github.com/stefandevo/glm-acp-agent) —— 自包含的 ACP agent,直接调用 GLM API;zcode-acp 则桥接真实的 `zcode app-server`,继承其完整的官方 harness。
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sandbox.d.ts","sourceRoot":"","sources":["../../src/backend/sandbox.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAiBH,uEAAuE;AACvE,eAAO,MAAM,WAAW,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"sandbox.d.ts","sourceRoot":"","sources":["../../src/backend/sandbox.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAiBH,uEAAuE;AACvE,eAAO,MAAM,WAAW,sBAAsB,CAAC;AA2B/C,MAAM,WAAW,aAAa;IAC5B;;;;OAIG;IACH,OAAO,EAAE,OAAO,CAAC;IACjB,wEAAwE;IACxE,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB;;;;OAIG;IACH,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,2EAA2E;IAC3E,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,sEAAsE;AACtE,wBAAgB,iBAAiB,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,CAE/D;AAoCD;;;;;;;;;;;;GAYG;AACH,wBAAgB,iBAAiB,CAAC,aAAa,EAAE,MAAM,GAAG,aAAa,CA+EtE;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,aAAa,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAyBtF;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,aAAa,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAyBpF;AAOD;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAsB7C;AAOD,MAAM,WAAW,eAAe;IAC9B,qEAAqE;IACrE,UAAU,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,aAAa,CAAA;KAAE,CAAC,CAAC;IAC3D,4EAA4E;IAC5E,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB;;;;;OAKG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,eAAe,GAAG,MAAM,CAqClE;AAED,8EAA8E;AAC9E,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,GAAG;IACpE,UAAU,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,aAAa,CAAA;KAAE,CAAC,CAAC;IAC3D,UAAU,EAAE,MAAM,EAAE,CAAC;CACtB,CAqBA;AAiBD;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAEpE;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,KAAK,GAAE,QAAQ,CAAC,MAAM,CAAmB,GAAG,OAAO,CAchF;AAED,4DAA4D;AAC5D,wBAAgB,2BAA2B,IAAI,IAAI,CAMlD;AAKD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,eAAe,GAAG,MAAM,EAAE,CAc/E"}
|
package/dist/backend/sandbox.js
CHANGED
|
@@ -49,6 +49,18 @@ const DEFAULT_CACHE_DIRS = [
|
|
|
49
49
|
"~/Library/pnpm",
|
|
50
50
|
"~/.node-gyp",
|
|
51
51
|
];
|
|
52
|
+
/**
|
|
53
|
+
* Well-known system temp trees, default-writable. Tools hardcode `/tmp`
|
|
54
|
+
* (a symlink to /private/tmp) or use /var/tmp, and $TMPDIR only names the
|
|
55
|
+
* process's own /var/folders leaf — without these, `mktemp` in a script or a
|
|
56
|
+
* compiler scratch file hits an EPERM popup for plain scratch space
|
|
57
|
+
* (observed: /private/tmp/adv_backup). /private/var/folders is the per-user
|
|
58
|
+
* temp+cache tree ($TMPDIR's parent, includes DARWIN_USER_CACHE_DIR); the
|
|
59
|
+
* specific $TMPDIR leaf stays allowed for tightness. Listed in RESOLVED
|
|
60
|
+
* form — SBPL subpath filters match REAL paths, and /tmp and /var/tmp
|
|
61
|
+
* resolve into the /private entries.
|
|
62
|
+
*/
|
|
63
|
+
const DEFAULT_TEMP_DIRS = ["/private/tmp", "/private/var/tmp", "/private/var/folders"];
|
|
52
64
|
/** Backend state roots that must stay writable for the bridge to function. */
|
|
53
65
|
const ZCODE_STATE_DIRS = ["~/.zcode", "~/.zcode-beta", "~/.zcode-plugin"];
|
|
54
66
|
/** Path of the per-project sandbox config inside a workspace root. */
|
|
@@ -294,7 +306,7 @@ export function buildSandboxProfile(input) {
|
|
|
294
306
|
allows.push(`(allow file-write* (subpath ${sb(resolveReal(allowed))}))`);
|
|
295
307
|
}
|
|
296
308
|
}
|
|
297
|
-
for (const p of [...ZCODE_STATE_DIRS, ...DEFAULT_CACHE_DIRS]) {
|
|
309
|
+
for (const p of [...ZCODE_STATE_DIRS, ...DEFAULT_CACHE_DIRS, ...DEFAULT_TEMP_DIRS]) {
|
|
298
310
|
allows.push(`(allow file-write* (subpath ${sb(resolveReal(p))}))`);
|
|
299
311
|
}
|
|
300
312
|
for (const allowed of input.extraAllow) {
|