zcode-acp-server 0.14.3 → 0.17.1
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 +109 -9
- package/README.zh-CN.md +91 -9
- package/dist/backend/index.d.ts +1 -0
- package/dist/backend/index.d.ts.map +1 -1
- package/dist/backend/index.js +1 -0
- package/dist/backend/index.js.map +1 -1
- package/dist/backend/sandbox.d.ts +92 -23
- package/dist/backend/sandbox.d.ts.map +1 -1
- package/dist/backend/sandbox.js +265 -70
- package/dist/backend/sandbox.js.map +1 -1
- package/dist/cli.d.ts +2 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +10 -1
- package/dist/cli.js.map +1 -1
- package/dist/config/auto-compact.d.ts.map +1 -1
- package/dist/config/auto-compact.js +6 -4
- package/dist/config/auto-compact.js.map +1 -1
- package/dist/config/mcp-discovery.d.ts.map +1 -1
- package/dist/config/mcp-discovery.js +7 -5
- package/dist/config/mcp-discovery.js.map +1 -1
- package/dist/handlers/background-tasks.d.ts.map +1 -1
- package/dist/handlers/background-tasks.js +2 -2
- package/dist/handlers/background-tasks.js.map +1 -1
- package/dist/handlers/dispatch.d.ts.map +1 -1
- package/dist/handlers/dispatch.js +4 -4
- package/dist/handlers/dispatch.js.map +1 -1
- package/dist/handlers/replay.d.ts.map +1 -1
- package/dist/handlers/replay.js +11 -5
- package/dist/handlers/replay.js.map +1 -1
- package/dist/handlers/sandbox-allow.d.ts +71 -0
- package/dist/handlers/sandbox-allow.d.ts.map +1 -0
- package/dist/handlers/sandbox-allow.js +299 -0
- package/dist/handlers/sandbox-allow.js.map +1 -0
- package/dist/handlers/server-requests.d.ts.map +1 -1
- package/dist/handlers/server-requests.js +7 -5
- package/dist/handlers/server-requests.js.map +1 -1
- package/dist/handlers/session.d.ts +14 -1
- package/dist/handlers/session.d.ts.map +1 -1
- package/dist/handlers/session.js +193 -20
- package/dist/handlers/session.js.map +1 -1
- package/dist/handlers/slash.d.ts.map +1 -1
- package/dist/handlers/slash.js +15 -13
- package/dist/handlers/slash.js.map +1 -1
- package/dist/i18n.d.ts +111 -0
- package/dist/i18n.d.ts.map +1 -0
- package/dist/i18n.js +222 -0
- package/dist/i18n.js.map +1 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +114 -15
- package/dist/index.js.map +1 -1
- package/dist/interaction/adapter.d.ts.map +1 -1
- package/dist/interaction/adapter.js +10 -7
- package/dist/interaction/adapter.js.map +1 -1
- package/dist/remote/config.d.ts +7 -0
- package/dist/remote/config.d.ts.map +1 -1
- package/dist/remote/config.js +2 -0
- package/dist/remote/config.js.map +1 -1
- package/dist/remote/endpoint.d.ts.map +1 -1
- package/dist/remote/endpoint.js +66 -11
- package/dist/remote/endpoint.js.map +1 -1
- package/dist/remote/hub-server.d.ts +15 -0
- package/dist/remote/hub-server.d.ts.map +1 -1
- package/dist/remote/hub-server.js +205 -1
- package/dist/remote/hub-server.js.map +1 -1
- package/dist/repl/App.d.ts.map +1 -1
- package/dist/repl/App.js +3 -2
- package/dist/repl/App.js.map +1 -1
- package/dist/repl/model.d.ts +2 -0
- package/dist/repl/model.d.ts.map +1 -1
- package/dist/repl/model.js.map +1 -1
- package/dist/repl/run.d.ts.map +1 -1
- package/dist/repl/run.js +5 -0
- package/dist/repl/run.js.map +1 -1
- package/dist/server.d.ts +78 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +114 -3
- package/dist/server.js.map +1 -1
- package/dist/tasks-index.d.ts +27 -0
- package/dist/tasks-index.d.ts.map +1 -1
- package/dist/tasks-index.js +72 -3
- package/dist/tasks-index.js.map +1 -1
- package/docs/REMOTE-CLIENTS.md +43 -0
- package/docs/TROUBLESHOOTING.md +9 -3
- package/package.json +13 -12
- package/dist/remote/session-liveness.d.ts +0 -19
- package/dist/remote/session-liveness.d.ts.map +0 -1
- package/dist/remote/session-liveness.js +0 -70
- package/dist/remote/session-liveness.js.map +0 -1
- package/dist/repl/mouse.d.ts +0 -49
- package/dist/repl/mouse.d.ts.map +0 -1
- package/dist/repl/mouse.js +0 -165
- package/dist/repl/mouse.js.map +0 -1
package/README.md
CHANGED
|
@@ -9,6 +9,14 @@ A standalone [Agent Client Protocol](https://agentclientprotocol.com/) (ACP) ser
|
|
|
9
9
|
|
|
10
10
|
The server launches the ZCode headless app-server (`zcode app-server --stdio`) as a subprocess, translates its internal event stream into ACP `session/update` notifications, and bridges ZCode's interaction channel to ACP — preferring `elicitation/create` when the client supports it, and falling back to `session/request_permission` otherwise — so an editor gets a first-class, native coding-agent experience.
|
|
11
11
|
|
|
12
|
+
## Why zcode-acp
|
|
13
|
+
|
|
14
|
+
- **Native editor experience** — streaming diffs, permission prompts and plan mode in Zed / JetBrains' own agent panel. No side-by-side terminal.
|
|
15
|
+
- **The official harness, not a reimplementation** — drives the real `zcode app-server`: native tools, skills, MCP and slash commands, auto-compaction, session resume/fork.
|
|
16
|
+
- **Beyond the editor** — a full bilingual terminal REPL (`zcode-acp`, English/中文, works over SSH), phone/web access to the same sessions (`zcode-acp-remote`), opt-in writes-only sandbox. Credentials stay in `~/.zcode`.
|
|
17
|
+
|
|
18
|
+
Because it drives the real ZCode client, your GLM Coding Plan comes along untouched — current perks (the 150% quota bonus, priority routing over raw API) and the plan's flat-rate economics apply exactly as in the official app. No API keys in editor settings.
|
|
19
|
+
|
|
12
20
|
## Status
|
|
13
21
|
|
|
14
22
|
In active development. Core bridging, slash commands and ZCode extensions,
|
|
@@ -26,15 +34,27 @@ in place; see the project board for what's next.
|
|
|
26
34
|
## Install
|
|
27
35
|
|
|
28
36
|
```bash
|
|
29
|
-
|
|
37
|
+
npm install -g zcode-acp-server
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
This installs both bins: `zcode-acp-server` (what your editor launches) and
|
|
41
|
+
`zcode-acp` (the unified CLI). Configure your ACP client to launch it — see
|
|
42
|
+
**Configure Zed** below or your editor's ACP docs.
|
|
43
|
+
|
|
44
|
+
<details>
|
|
45
|
+
<summary>Install from source instead</summary>
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
git clone https://github.com/william0wang/zcode-acp.git
|
|
30
49
|
cd zcode-acp-server
|
|
31
50
|
pnpm install
|
|
32
51
|
pnpm build
|
|
33
52
|
```
|
|
34
53
|
|
|
35
54
|
The compiled entry point is `dist/index.js` (also exposed as the
|
|
36
|
-
`zcode-acp-server` bin).
|
|
37
|
-
|
|
55
|
+
`zcode-acp-server` bin).
|
|
56
|
+
|
|
57
|
+
</details>
|
|
38
58
|
|
|
39
59
|
## Configure Zed
|
|
40
60
|
|
|
@@ -46,11 +66,10 @@ Add the server to Zed as a custom agent server. In `~/.config/zed/settings.json`
|
|
|
46
66
|
"agent_servers": {
|
|
47
67
|
"ZCode": {
|
|
48
68
|
"type": "custom",
|
|
49
|
-
"command": "
|
|
50
|
-
"args": ["/absolute/path/to/zcode-acp-server/dist/index.js"],
|
|
69
|
+
"command": "zcode-acp-server",
|
|
51
70
|
"env": {
|
|
52
|
-
//
|
|
53
|
-
//
|
|
71
|
+
// Only needed for custom installs — the CLI is auto-discovered from
|
|
72
|
+
// the desktop app bundle or PATH (see the table below).
|
|
54
73
|
"ZCODE_BIN": "/Applications/ZCode.app/Contents/Resources/glm/zcode.cjs",
|
|
55
74
|
},
|
|
56
75
|
},
|
|
@@ -58,6 +77,9 @@ Add the server to Zed as a custom agent server. In `~/.config/zed/settings.json`
|
|
|
58
77
|
}
|
|
59
78
|
```
|
|
60
79
|
|
|
80
|
+
Running from source instead? Use `"command": "node"` with
|
|
81
|
+
`"args": ["/absolute/path/to/zcode-acp-server/dist/index.js"]`.
|
|
82
|
+
|
|
61
83
|
Restart Zed and pick **ZCode** from the agent dropdown.
|
|
62
84
|
|
|
63
85
|
### `ZCODE_BIN` per platform
|
|
@@ -97,6 +119,56 @@ most setups need no `ZCODE_BIN` at all — set it only for custom installs:
|
|
|
97
119
|
| `ZCODE_ACP_HUB_PORT` | `8377` | Port of the machine-level hub daemon. Map exactly this one port in your tunnel. |
|
|
98
120
|
| `ZCODE_ACP_HUB_HOST` | `127.0.0.1` | Hub bind address. `0.0.0.0` exposes a token-only, unencrypted surface — only for a containerized tunnel agent on a private interface (see [Remote Access](#remote-access)). |
|
|
99
121
|
| `ZCODE_ACP_REMOTE_PORT` | `8378` | First loopback port for the bridge's ACP endpoint. Each bridge (each editor window) auto-increments to the next free port. |
|
|
122
|
+
| `ZCODE_ACP_SANDBOX` | _(unset)_ | Set to `1` to confine the agent's file writes with a macOS Seatbelt sandbox globally; per-project, set `"enabled": true` in `<workspace>/.zcode/acp/sandbox.json` instead (see [Sandbox](#sandbox)). |
|
|
123
|
+
| `ZCODE_ACP_LANG` | _(inherited)_ | Language of the bridge's user-facing strings (popups, status/hint lines, command menu descriptions): `zh` or `en`. When unset, the bridge inherits the ZCode app's language (`localePreference`/`locale` in `~/.zcode/v2/setting.json`), then falls back to the `LC_ALL`/`LC_MESSAGES`/`LANG` locale, defaulting to English. |
|
|
124
|
+
|
|
125
|
+
## Sandbox
|
|
126
|
+
|
|
127
|
+
Two switches arm the sandbox (macOS only), whichever comes first:
|
|
128
|
+
|
|
129
|
+
- globally: `ZCODE_ACP_SANDBOX=1`, or
|
|
130
|
+
- per project: `"enabled": true` in `<workspace>/.zcode/acp/sandbox.json`.
|
|
131
|
+
The bridge auto-creates that file with `"enabled": false` the first time
|
|
132
|
+
you open the workspace — flip the flag to opt this project in, no global
|
|
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`).
|
|
100
172
|
|
|
101
173
|
## Remote Access
|
|
102
174
|
|
|
@@ -145,12 +217,38 @@ discovery ~30s after it stops.
|
|
|
145
217
|
|
|
146
218
|
```text
|
|
147
219
|
GET /api/instances → [{"id","port","pid","startedAt","workspace",
|
|
148
|
-
"sessions":[{"sessionId","title?","updatedAt"}]}]
|
|
220
|
+
"origin","sessions":[{"sessionId","title?","updatedAt"}]}]
|
|
149
221
|
GET /api/instances?probe=1 → same list, but unreachable bridges are pruned first
|
|
150
222
|
WS /acp?instance=<id> → proxied to that bridge's endpoint
|
|
151
223
|
GET /api/instances/{id}/fs/… → read-only session files (list + raw bytes, ADR-0004)
|
|
152
224
|
```
|
|
153
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
|
+
|
|
154
252
|
`sessions` lists the project's **currently running** conversations (live
|
|
155
253
|
editor tabs and remote attachments) under the same ACP session ids the
|
|
156
254
|
editor uses — attaching by id joins the conversation's live notification
|
|
@@ -380,7 +478,7 @@ Editor configs referencing `zcode-acp-server` keep working unchanged.
|
|
|
380
478
|
|
|
381
479
|
This server is compatible with the [ACP Registry](https://agentclientprotocol.com/get-started/registry). It advertises a single `agent`-type auth method at `initialize` time — the GLM API key is read from `~/.zcode/v2/config.json` by the ZCode backend, so **no editor-side credentials are required**.
|
|
382
480
|
|
|
383
|
-
The registry submission assets live under [`registry/zcode-acp
|
|
481
|
+
The registry submission assets live under [`registry/zcode-acp/`](registry/zcode-acp/) (`agent.json` + `icon.svg`). Once the package is published to npm, copy that directory into a fork of [`agentclientprotocol/registry`](https://github.com/agentclientprotocol/registry) and open a PR — the CI validates the `agent.json` schema, icon, and that `initialize` returns a non-empty `authMethods`.
|
|
384
482
|
|
|
385
483
|
## Develop
|
|
386
484
|
|
|
@@ -443,6 +541,8 @@ recorded in [CHANGELOG.md](CHANGELOG.md).
|
|
|
443
541
|
|
|
444
542
|
## Related Projects
|
|
445
543
|
|
|
544
|
+
- [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.
|
|
545
|
+
- [claude-agent-acp](https://github.com/agentclientprotocol/claude-agent-acp) / [codex-acp](https://github.com/agentclientprotocol/codex-acp) — official ACP adapters for the Claude and Codex CLIs; zcode-acp is the same idea for the ZCode CLI.
|
|
446
546
|
- [zcode-open-bridge](https://github.com/tizerluo/zcode-open-bridge) — a community Python implementation that bridges ZCode to the MCP/ACP ecosystem. The design of this server references its bridge architecture and several handling strategies.
|
|
447
547
|
|
|
448
548
|
## Acknowledgements
|
package/README.zh-CN.md
CHANGED
|
@@ -9,6 +9,14 @@
|
|
|
9
9
|
|
|
10
10
|
本服务端以子进程方式启动 ZCode 无头 app-server(`zcode app-server --stdio`),将其内部事件流翻译为 ACP `session/update` 通知,并把 ZCode 的交互通道桥接到 ACP —— 当客户端支持时优先使用 `elicitation/create`,否则回退到 `session/request_permission` —— 从而让编辑器获得原生的、一流的编码助手体验。
|
|
11
11
|
|
|
12
|
+
## 为什么选 zcode-acp
|
|
13
|
+
|
|
14
|
+
- **编辑器原生体验** —— 流式改动以真实 diff 呈现,权限确认、计划模式都走 Zed / JetBrains 自己的 agent 面板,无需并排终端。
|
|
15
|
+
- **官方 harness,而非重新实现** —— 驱动真实的 `zcode app-server`:原生工具、skills、MCP 与斜杠命令、自动压缩、会话恢复/分叉。
|
|
16
|
+
- **不止于编辑器** —— 完整的双语终端 REPL(`zcode-acp`,中/英可切换,SSH 下可用),手机/网页访问相同会话(`zcode-acp-remote`),可选的只限写入沙箱。凭据留在 `~/.zcode`。
|
|
17
|
+
|
|
18
|
+
由于驱动的是真实 ZCode 客户端,你现有的 GLM Coding Plan 原样生效——当前的套餐权益(150% 额度加成、高于直连 API 的请求优先级)和包月计费方式都和在官方 App 中一致。编辑器设置无需任何 API key。
|
|
19
|
+
|
|
12
20
|
## 状态
|
|
13
21
|
|
|
14
22
|
早期开发中。核心框架已就绪,功能正在陆续加入。进度请看项目看板。
|
|
@@ -23,14 +31,25 @@
|
|
|
23
31
|
## 安装
|
|
24
32
|
|
|
25
33
|
```bash
|
|
26
|
-
|
|
34
|
+
npm install -g zcode-acp-server
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
会同时安装两个 bin:`zcode-acp-server`(编辑器调用)和 `zcode-acp`(统一 CLI)。
|
|
38
|
+
在你的 ACP 客户端里配置启动它 —— 见下方的 **在 Zed 中配置** 或你的编辑器的 ACP 文档。
|
|
39
|
+
|
|
40
|
+
<details>
|
|
41
|
+
<summary>改为从源码安装</summary>
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
git clone https://github.com/william0wang/zcode-acp.git
|
|
27
45
|
cd zcode-acp-server
|
|
28
46
|
pnpm install
|
|
29
47
|
pnpm build
|
|
30
48
|
```
|
|
31
49
|
|
|
32
|
-
编译产物入口为 `dist/index.js`(同时作为 `zcode-acp-server` bin
|
|
33
|
-
|
|
50
|
+
编译产物入口为 `dist/index.js`(同时作为 `zcode-acp-server` bin 暴露)。
|
|
51
|
+
|
|
52
|
+
</details>
|
|
34
53
|
|
|
35
54
|
## 在 Zed 中配置
|
|
36
55
|
|
|
@@ -42,11 +61,10 @@ ACP 客户端里配置启动它 —— 见下方的 **在 Zed 中配置** 或你
|
|
|
42
61
|
"agent_servers": {
|
|
43
62
|
"ZCode": {
|
|
44
63
|
"type": "custom",
|
|
45
|
-
"command": "
|
|
46
|
-
"args": ["/absolute/path/to/zcode-acp-server/dist/index.js"],
|
|
64
|
+
"command": "zcode-acp-server",
|
|
47
65
|
"env": {
|
|
48
|
-
//
|
|
49
|
-
//
|
|
66
|
+
// 仅自定义安装时需要——CLI 会从桌面应用内置路径或 PATH 自动发现
|
|
67
|
+
// (见下方表格)。
|
|
50
68
|
"ZCODE_BIN": "/Applications/ZCode.app/Contents/Resources/glm/zcode.cjs",
|
|
51
69
|
},
|
|
52
70
|
},
|
|
@@ -54,6 +72,9 @@ ACP 客户端里配置启动它 —— 见下方的 **在 Zed 中配置** 或你
|
|
|
54
72
|
}
|
|
55
73
|
```
|
|
56
74
|
|
|
75
|
+
从源码运行?改用 `"command": "node"` 与
|
|
76
|
+
`"args": ["/absolute/path/to/zcode-acp-server/dist/index.js"]`。
|
|
77
|
+
|
|
57
78
|
重启 Zed,然后从 agent 下拉菜单中选择 **ZCode**。
|
|
58
79
|
|
|
59
80
|
### 各平台的 `ZCODE_BIN` 路径
|
|
@@ -91,6 +112,46 @@ ZCode CLI 内置于桌面应用中,默认不会加到 `PATH`。用 `ZCODE_BIN`
|
|
|
91
112
|
| `ZCODE_ACP_HUB_PORT` | `8377` | 机器级 `zcode-acp-hub` 的端口。隧道只映射这一个端口。 |
|
|
92
113
|
| `ZCODE_ACP_HUB_HOST` | `127.0.0.1` | hub 绑定地址。`0.0.0.0` 会暴露仅 token 保护的明文面——只用于容器化隧道 agent 所在的私网接口(见[远程访问](#远程访问))。 |
|
|
93
114
|
| `ZCODE_ACP_REMOTE_PORT` | `8378` | bridge ACP 端点的起始回环端口。每个 bridge(每个编辑器窗口)自动递增取下一个空闲端口。 |
|
|
115
|
+
| `ZCODE_ACP_SANDBOX` | _(未设置)_ | 设为 `1` 全局启用 macOS Seatbelt 沙箱限制 Agent 的文件写入;项目级则在 `<工作区>/.zcode/acp/sandbox.json` 里设 `"enabled": true`(见[沙箱](#沙箱))。 |
|
|
116
|
+
| `ZCODE_ACP_LANG` | _(继承)_ | 桥的用户可见文案(弹窗、状态/提示行、命令菜单描述)语言:`zh` 或 `en`。未设置时继承 ZCode APP 的语言设置(`~/.zcode/v2/setting.json` 的 `localePreference`/`locale`),再退回 `LC_ALL`/`LC_MESSAGES`/`LANG` 区域设置,默认英文。 |
|
|
117
|
+
|
|
118
|
+
## 沙箱
|
|
119
|
+
|
|
120
|
+
两个开关任一生效(仅 macOS):
|
|
121
|
+
|
|
122
|
+
- 全局:`ZCODE_ACP_SANDBOX=1`;
|
|
123
|
+
- 项目级:`<工作区>/.zcode/acp/sandbox.json` 里设 `"enabled": true`。该文件
|
|
124
|
+
在首次打开工作区时由桥自动创建(默认 `"enabled": false`)——把开关翻成
|
|
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`)。
|
|
94
155
|
|
|
95
156
|
## 远程访问
|
|
96
157
|
|
|
@@ -128,10 +189,29 @@ token 鉴权、实例发现、字节级 WebSocket 代理——不保存会话状
|
|
|
128
189
|
心跳,心跳停止约 30 秒后从发现列表移除;客户端刷新时也可调用
|
|
129
190
|
`GET /api/instances?probe=1` 主动探测,立即清理不可达的实例。
|
|
130
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
|
+
|
|
131
210
|
**语义。** 所有 agent 通知广播给每个已连接客户端;权限 / elicitation 请求
|
|
132
211
|
发给所有客户端,**先应答者生效**,其余客户端收到 `$/cancel_request` 关闭
|
|
133
212
|
对话框。同一会话的并发 prompt 与单编辑器一样串行化。任一客户端声明的能力
|
|
134
|
-
按 OR
|
|
213
|
+
按 OR 合并。`/api/instances` 的每个实例带 `origin` 字段(`"editor"`=编辑器
|
|
214
|
+
stdio 桥,`"serve"`=远程创建的无头桥),客户端可据此标注。
|
|
135
215
|
|
|
136
216
|
**隧道。** 面向单端口隧道(Cloudflare Tunnel、frp)设计:只映射 hub 端口。
|
|
137
217
|
frp 的 `tcp` 模式原样透传 WebSocket;Cloudflare Tunnel 会断开空闲连接,
|
|
@@ -210,7 +290,7 @@ export OPENCODE_GO_AUTH_COOKIE="Fe26.2**你的cookie值"
|
|
|
210
290
|
|
|
211
291
|
本服务端兼容 [ACP Registry](https://agentclientprotocol.com/get-started/registry)。它在 `initialize` 时声明一个 `agent` 类型的认证方法——GLM API key 由 ZCode 后端从 `~/.zcode/v2/config.json` 读取,**编辑器侧无需配置任何凭据**。
|
|
212
292
|
|
|
213
|
-
Registry 提交资产位于 [`registry/zcode-acp
|
|
293
|
+
Registry 提交资产位于 [`registry/zcode-acp/`](registry/zcode-acp/)(`agent.json` + `icon.svg`)。包发布到 npm 后,将该目录复制到 [`agentclientprotocol/registry`](https://github.com/agentclientprotocol/registry) 的 fork 中并提 PR——CI 会校验 `agent.json` schema、图标,以及 `initialize` 返回的 `authMethods` 非空。
|
|
214
294
|
|
|
215
295
|
## 开发
|
|
216
296
|
|
|
@@ -266,6 +346,8 @@ commit 约定和 PR 检查清单。重要变更记录在 [CHANGELOG.md](CHANGELO
|
|
|
266
346
|
|
|
267
347
|
## 相关项目
|
|
268
348
|
|
|
349
|
+
- [glm-acp-agent](https://github.com/stefandevo/glm-acp-agent) —— 自包含的 ACP agent,直接调用 GLM API;zcode-acp 则桥接真实的 `zcode app-server`,继承其完整的官方 harness。
|
|
350
|
+
- [claude-agent-acp](https://github.com/agentclientprotocol/claude-agent-acp) / [codex-acp](https://github.com/agentclientprotocol/codex-acp) —— Claude 与 Codex CLI 的官方 ACP 适配器;zcode-acp 是同一思路在 ZCode CLI 上的实现。
|
|
269
351
|
- [zcode-open-bridge](https://github.com/tizerluo/zcode-open-bridge) —— 一个社区 Python 实现,将 ZCode 接入 MCP/ACP 生态。本项目参考了它的桥接架构和若干处理策略。
|
|
270
352
|
|
|
271
353
|
## 致谢
|
package/dist/backend/index.d.ts
CHANGED
|
@@ -3,5 +3,6 @@ export { ZcodeBackend, type ServerRequest, type EventListener } from "./client.j
|
|
|
3
3
|
export { EventStreamListener, TurnMonitor, type NextId } from "./listener.js";
|
|
4
4
|
export { resolveZcodeCommand } from "./resolve.js";
|
|
5
5
|
export { loadZcodeCredentials, mergeEnvWithCreds, type ZcodeCredentials } from "./credentials.js";
|
|
6
|
+
export { SANDBOX_ENV, appendSandboxAllow, armSandboxArgv, buildSandboxProfile, collectSandboxWorkspaces, projectSandboxEnabled, readSandboxConfig, resetSandboxDecisionForTest, sandboxActive, sandboxConfigPath, type SandboxConfig, } from "./sandbox.js";
|
|
6
7
|
export type { ZcodeRequest, ZcodeNotification, ZcodeResponse, ZcodeInbound, ZcodeSessionInfo, ZcodeCreateResult, ZcodeSessionListItem, ZcodeListResult, ZcodeEvent, ZcodeEventType, ZcodeSubscribeResult, ZcodeSnapshot, ZcodeProjection, ZcodeMessage, ZcodeMessagePart, ZcodeMessagesResult, ZcodeReadResult, ZcodeInteractionPermissionParams, ZcodeInteractionUserInputParams, ZcodeInteractionResponse, } from "./types.js";
|
|
7
8
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/backend/index.ts"],"names":[],"mappings":"AAAA,wFAAwF;AAExF,OAAO,EAAE,YAAY,EAAE,KAAK,aAAa,EAAE,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AACnF,OAAO,EAAE,mBAAmB,EAAE,WAAW,EAAE,KAAK,MAAM,EAAE,MAAM,eAAe,CAAC;AAC9E,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,KAAK,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAClG,YAAY,EACV,YAAY,EACZ,iBAAiB,EACjB,aAAa,EACb,YAAY,EACZ,gBAAgB,EAChB,iBAAiB,EACjB,oBAAoB,EACpB,eAAe,EACf,UAAU,EACV,cAAc,EACd,oBAAoB,EACpB,aAAa,EACb,eAAe,EACf,YAAY,EACZ,gBAAgB,EAChB,mBAAmB,EACnB,eAAe,EACf,gCAAgC,EAChC,+BAA+B,EAC/B,wBAAwB,GACzB,MAAM,YAAY,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/backend/index.ts"],"names":[],"mappings":"AAAA,wFAAwF;AAExF,OAAO,EAAE,YAAY,EAAE,KAAK,aAAa,EAAE,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AACnF,OAAO,EAAE,mBAAmB,EAAE,WAAW,EAAE,KAAK,MAAM,EAAE,MAAM,eAAe,CAAC;AAC9E,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,KAAK,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAClG,OAAO,EACL,WAAW,EACX,kBAAkB,EAClB,cAAc,EACd,mBAAmB,EACnB,wBAAwB,EACxB,qBAAqB,EACrB,iBAAiB,EACjB,2BAA2B,EAC3B,aAAa,EACb,iBAAiB,EACjB,KAAK,aAAa,GACnB,MAAM,cAAc,CAAC;AACtB,YAAY,EACV,YAAY,EACZ,iBAAiB,EACjB,aAAa,EACb,YAAY,EACZ,gBAAgB,EAChB,iBAAiB,EACjB,oBAAoB,EACpB,eAAe,EACf,UAAU,EACV,cAAc,EACd,oBAAoB,EACpB,aAAa,EACb,eAAe,EACf,YAAY,EACZ,gBAAgB,EAChB,mBAAmB,EACnB,eAAe,EACf,gCAAgC,EAChC,+BAA+B,EAC/B,wBAAwB,GACzB,MAAM,YAAY,CAAC"}
|
package/dist/backend/index.js
CHANGED
|
@@ -3,4 +3,5 @@ export { ZcodeBackend } from "./client.js";
|
|
|
3
3
|
export { EventStreamListener, TurnMonitor } from "./listener.js";
|
|
4
4
|
export { resolveZcodeCommand } from "./resolve.js";
|
|
5
5
|
export { loadZcodeCredentials, mergeEnvWithCreds } from "./credentials.js";
|
|
6
|
+
export { SANDBOX_ENV, appendSandboxAllow, armSandboxArgv, buildSandboxProfile, collectSandboxWorkspaces, projectSandboxEnabled, readSandboxConfig, resetSandboxDecisionForTest, sandboxActive, sandboxConfigPath, } from "./sandbox.js";
|
|
6
7
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/backend/index.ts"],"names":[],"mappings":"AAAA,wFAAwF;AAExF,OAAO,EAAE,YAAY,EAA0C,MAAM,aAAa,CAAC;AACnF,OAAO,EAAE,mBAAmB,EAAE,WAAW,EAAe,MAAM,eAAe,CAAC;AAC9E,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,EAAE,oBAAoB,EAAE,iBAAiB,EAAyB,MAAM,kBAAkB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/backend/index.ts"],"names":[],"mappings":"AAAA,wFAAwF;AAExF,OAAO,EAAE,YAAY,EAA0C,MAAM,aAAa,CAAC;AACnF,OAAO,EAAE,mBAAmB,EAAE,WAAW,EAAe,MAAM,eAAe,CAAC;AAC9E,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,EAAE,oBAAoB,EAAE,iBAAiB,EAAyB,MAAM,kBAAkB,CAAC;AAClG,OAAO,EACL,WAAW,EACX,kBAAkB,EAClB,cAAc,EACd,mBAAmB,EACnB,wBAAwB,EACxB,qBAAqB,EACrB,iBAAiB,EACjB,2BAA2B,EAC3B,aAAa,EACb,iBAAiB,GAElB,MAAM,cAAc,CAAC"}
|
|
@@ -2,12 +2,20 @@
|
|
|
2
2
|
* Seatbelt sandbox for the zcode backend subprocess (ADR-0011).
|
|
3
3
|
*
|
|
4
4
|
* The backend runs every Bash/Edit/Write tool with the user's full
|
|
5
|
-
* privileges; one careless command destroys real data.
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
5
|
+
* privileges; one careless command destroys real data. The bridge wraps the
|
|
6
|
+
* single backend spawn point with a generated `sandbox-exec` profile using a
|
|
7
|
+
* writes-only restriction model: reads and process execution stay open, file
|
|
8
|
+
* writes are denied everywhere except an explicit whitelist. Children inherit
|
|
9
|
+
* the sandbox, so one wrap covers the backend, its tool subprocesses, and
|
|
10
|
+
* model workers — deletion (rm/mv/truncate) is a write-class syscall, so it
|
|
11
|
+
* is stopped by the write denial regardless of which binary performs it
|
|
12
|
+
* (name-banning executables would be trivially bypassed and is deliberately
|
|
13
|
+
* not done).
|
|
14
|
+
*
|
|
15
|
+
* Arming is dual-switch (see sandboxActive): ZCODE_ACP_SANDBOX=1 forces it
|
|
16
|
+
* globally, or a workspace opts in via `enabled: true` in its own
|
|
17
|
+
* .zcode/acp/sandbox.json — the template is auto-created with enabled:false,
|
|
18
|
+
* so opting in is always an explicit user edit.
|
|
11
19
|
*
|
|
12
20
|
* Whitelist (frozen at spawn, rebuilt on backend restart):
|
|
13
21
|
* - workspace roots of all live sessions (union of server.sessionCwds)
|
|
@@ -16,6 +24,7 @@
|
|
|
16
24
|
* - system temp + regenerable cache dirs (zero-value targets, constant
|
|
17
25
|
* toolchain traffic)
|
|
18
26
|
* - each project's `.zcode/acp/sandbox.json` `allow` list
|
|
27
|
+
* - bridge-lifetime once-allows granted via the dynamic allow flow
|
|
19
28
|
*
|
|
20
29
|
* `<workspace>/.zcode/acp/` is a DENY island inside every allowed workspace:
|
|
21
30
|
* the sandbox forbids writes there while the bridge — outside the sandbox —
|
|
@@ -28,24 +37,54 @@
|
|
|
28
37
|
/** The one and only env switch. Every other knob is project config. */
|
|
29
38
|
export declare const SANDBOX_ENV = "ZCODE_ACP_SANDBOX";
|
|
30
39
|
export interface SandboxConfig {
|
|
40
|
+
/**
|
|
41
|
+
* Project-level switch: true arms the sandbox for this workspace without
|
|
42
|
+
* the global env. The auto-created template ships false — opting in is an
|
|
43
|
+
* explicit user edit.
|
|
44
|
+
*/
|
|
45
|
+
enabled: boolean;
|
|
31
46
|
/** Absolute realpaths OUTSIDE the workspace granted permanent write. */
|
|
32
47
|
allow: string[];
|
|
48
|
+
/**
|
|
49
|
+
* Absolute realpaths the user chose NEVER to grant ("永不放行") — the
|
|
50
|
+
* popup is suppressed for these. Visible config, not hidden memory: the
|
|
51
|
+
* user can review or undo a denial by editing this file.
|
|
52
|
+
*/
|
|
53
|
+
deny: string[];
|
|
33
54
|
/** true = .git sits behind the allow popup instead of default-writable. */
|
|
34
55
|
strictGit: boolean;
|
|
35
56
|
}
|
|
36
57
|
/** Path of the per-project sandbox config inside a workspace root. */
|
|
37
58
|
export declare function sandboxConfigPath(workspaceRoot: string): string;
|
|
38
59
|
/**
|
|
39
|
-
* Read the project sandbox config, auto-creating
|
|
40
|
-
*
|
|
41
|
-
*
|
|
60
|
+
* Read the project sandbox config, auto-creating the template (enabled:
|
|
61
|
+
* false) on first touch so the user finds the file and can flip the switch —
|
|
62
|
+
* the PRESENCE of the file is never the switch, only `enabled` is, so the
|
|
63
|
+
* auto-create cannot arm anything by itself. A malformed or non-object file
|
|
64
|
+
* falls back to enabled:true WITHOUT rewriting it: corruption must fail
|
|
65
|
+
* CLOSED (the user opted in; losing that to a half-saved file would silently
|
|
66
|
+
* disarm), and clobbering the user's mid-edit bytes with a template would be
|
|
67
|
+
* worse than the transient read. A symlinked/hardlinked config is treated
|
|
68
|
+
* the same way (armed, persistence disabled) — see configIntegrityOk. So is
|
|
69
|
+
* a config that was armed and then became unreadable or disappeared — only
|
|
70
|
+
* the agent could do that from inside the sandbox.
|
|
42
71
|
*/
|
|
43
72
|
export declare function readSandboxConfig(workspaceRoot: string): SandboxConfig;
|
|
44
73
|
/**
|
|
45
74
|
* Bridge-side persistence for "always allow" (the deny island keeps the
|
|
46
|
-
* agent from writing this file itself).
|
|
75
|
+
* agent from writing this file itself). Round-trips the whole config so the
|
|
76
|
+
* enabled flag survives the write. Dedupes by exact string. Returns false
|
|
77
|
+
* when persistence is impossible (symlinked config, unwritable path) — the
|
|
78
|
+
* caller then downgrades to a bridge-lifetime once-allow.
|
|
79
|
+
*/
|
|
80
|
+
export declare function appendSandboxAllow(workspaceRoot: string, allowedPath: string): boolean;
|
|
81
|
+
/**
|
|
82
|
+
* Bridge-side persistence for the popup's "永不放行" choice — the visible
|
|
83
|
+
* counterpart of appendSandboxAllow: a denied path is RECORDED in the
|
|
84
|
+
* config (never hidden in bridge memory), so the ask never resurfaces for
|
|
85
|
+
* it and the user can review or undo the decision by editing the file.
|
|
47
86
|
*/
|
|
48
|
-
export declare function
|
|
87
|
+
export declare function appendSandboxDeny(workspaceRoot: string, deniedPath: string): boolean;
|
|
49
88
|
/**
|
|
50
89
|
* Resolve a path to its filesystem truth. Seatbelt matches real paths, so a
|
|
51
90
|
* symlinked prefix (/tmp → /private/tmp) would silently fail to match —
|
|
@@ -58,13 +97,21 @@ export interface SandboxArmInput {
|
|
|
58
97
|
root: string;
|
|
59
98
|
config: SandboxConfig;
|
|
60
99
|
}>;
|
|
61
|
-
/** Extra writable roots from
|
|
100
|
+
/** Extra writable roots: allowlists from other workspaces + once-allows. */
|
|
62
101
|
extraAllow: string[];
|
|
102
|
+
/**
|
|
103
|
+
* Directory the profile file itself lives in — self-denied LAST so the
|
|
104
|
+
* sandboxed agent cannot race/symlink/occupy the next respawn's profile
|
|
105
|
+
* (see armSandboxArgv). $TMPDIR is agent-writable, so without this the
|
|
106
|
+
* profile path would be attacker-reachable.
|
|
107
|
+
*/
|
|
108
|
+
profileDir?: string;
|
|
63
109
|
}
|
|
64
110
|
/**
|
|
65
|
-
* Build the SBPL profile text.
|
|
66
|
-
* (verified empirically
|
|
67
|
-
*
|
|
111
|
+
* Build the SBPL profile text. SBPL resolves overlapping rules by LAST
|
|
112
|
+
* match (verified empirically: an allow emitted after a deny re-permits the
|
|
113
|
+
* write), so the layout is: base deny-all, then every allow, then the deny
|
|
114
|
+
* carve-outs (island, strictGit) LAST so nothing can override them.
|
|
68
115
|
*/
|
|
69
116
|
export declare function buildSandboxProfile(input: SandboxArmInput): string;
|
|
70
117
|
/** Resolved arm input for the CURRENT spawn: union of all live workspaces. */
|
|
@@ -76,16 +123,38 @@ export declare function collectSandboxWorkspaces(cwdRoots: Iterable<string>): {
|
|
|
76
123
|
extraAllow: string[];
|
|
77
124
|
};
|
|
78
125
|
/**
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
*
|
|
126
|
+
* The project-level switch: `enabled: true` inside the workspace's sandbox
|
|
127
|
+
* config (auto-created on first touch, template ships false). Reading also
|
|
128
|
+
* materializes the template for discovery. Once the sandbox is armed the
|
|
129
|
+
* deny island keeps the agent from flipping the switch back off.
|
|
130
|
+
*/
|
|
131
|
+
export declare function projectSandboxEnabled(workspaceRoot: string): boolean;
|
|
132
|
+
/**
|
|
133
|
+
* Whether the sandbox should arm for this bridge: ZCODE_ACP_SANDBOX=1
|
|
134
|
+
* (global, cached) OR any given workspace root opted in via
|
|
135
|
+
* sandbox.json `enabled` (project switch, re-checked per call so a flip
|
|
136
|
+
* mid-run is seen). macOS-only: elsewhere a requested sandbox warns once and
|
|
137
|
+
* runs unsandboxed.
|
|
82
138
|
*/
|
|
83
|
-
export declare function sandboxActive(): boolean;
|
|
84
|
-
/** Test hook: reset
|
|
139
|
+
export declare function sandboxActive(roots?: Iterable<string>): boolean;
|
|
140
|
+
/** Test hook: reset cached decisions and warn-once sets. */
|
|
85
141
|
export declare function resetSandboxDecisionForTest(): void;
|
|
86
142
|
/**
|
|
87
|
-
*
|
|
88
|
-
*
|
|
143
|
+
* Arm a backend argv: build the profile into a FRESH unpredictable dir and
|
|
144
|
+
* wrap with sandbox-exec. The dir sits DIRECTLY under $HOME with a
|
|
145
|
+
* `.zcode-acp-sbx-` prefix — OUTSIDE every whitelisted path (it is a SIBLING
|
|
146
|
+
* of `~/.zcode`, not inside it). That location is the load-bearing defense:
|
|
147
|
+
* $TMPDIR and the cache dirs are agent-writable, and a PRIOR sandboxed
|
|
148
|
+
* generation (a setsid survivor of the old process group) keeps its own
|
|
149
|
+
* profile's allows — so a profile placed there could be raced, symlinked,
|
|
150
|
+
* FIFO'd, or occupied no matter how fresh its name (reproduced across
|
|
151
|
+
* generations even with mkdtemp + O_EXCL + a self-deny, which each generation
|
|
152
|
+
* only applies to its own dir). The agent can also kill the backend at will
|
|
153
|
+
* (signals are not file-writes) to control respawn timing; with the profile
|
|
154
|
+
* unreachable from ANY generation, that primitive buys nothing. O_EXCL ("wx")
|
|
155
|
+
* additionally refuses pre-placed symlinks from a pre-arming process, and
|
|
156
|
+
* the profile still self-denies its own dir last (defense in depth for the
|
|
157
|
+
* workspace-root-is-$HOME edge, where home itself is writable).
|
|
89
158
|
*/
|
|
90
|
-
export declare function
|
|
159
|
+
export declare function armSandboxArgv(argv: string[], input: SandboxArmInput): string[];
|
|
91
160
|
//# sourceMappingURL=sandbox.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sandbox.d.ts","sourceRoot":"","sources":["../../src/backend/sandbox.ts"],"names":[],"mappings":"AAAA
|
|
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;AAc/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"}
|