better-dsh 0.2.4-a → 0.2.4-c

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.
Files changed (49) hide show
  1. package/cordis.patch.yml +7 -0
  2. package/docs/10_plans/2026-09-23-remote-execution-framework.md +236 -0
  3. package/docs/10_plans/2026-09-23-remote-framework-impl.md +2083 -0
  4. package/docs/10_plans/2026-09-26-lsp-ast-reminder.md +64 -0
  5. package/docs/20_specs/agent/spec.md +54 -0
  6. package/docs/20_specs/ast/spec.md +34 -0
  7. package/docs/20_specs/compaction-recall/spec.md +46 -0
  8. package/docs/20_specs/ctx/spec.md +107 -0
  9. package/docs/20_specs/dsh/spec.md +47 -0
  10. package/docs/20_specs/dvc/spec.md +87 -0
  11. package/docs/20_specs/escalation-guidance/spec.md +44 -0
  12. package/docs/20_specs/fs-scheme-resolution/spec.md +37 -0
  13. package/docs/20_specs/hash-edit/spec.md +41 -0
  14. package/docs/20_specs/http-read/spec.md +73 -0
  15. package/docs/20_specs/kernel-provisioning/spec.md +53 -0
  16. package/docs/20_specs/lsp/spec.md +121 -0
  17. package/docs/20_specs/mobile-layout/spec.md +108 -0
  18. package/docs/20_specs/model-failover/spec.md +20 -0
  19. package/docs/20_specs/plugins-page-components/spec.md +79 -0
  20. package/docs/20_specs/preact-ui-shell/spec.md +22 -0
  21. package/docs/20_specs/repl-dispatch-resilience/spec.md +21 -0
  22. package/docs/20_specs/skill/spec.md +58 -0
  23. package/docs/20_specs/tool-surface/spec.md +222 -0
  24. package/docs/20_specs/url-schema/spec.md +148 -0
  25. package/docs/20_specs/web-trust-fence/spec.md +43 -0
  26. package/docs/50_test-reports/2026-09-22-better-dsh/347/273/204/344/273/266/347/203/255/346/217/222/346/213/224/344/270/216/344/276/235/350/265/226/347/272/247/350/201/224/345/256/236/346/265/213/346/212/245/345/221/212.md +18 -1
  27. package/docs/50_test-reports/2026-09-23-remote-framework/345/256/236/346/265/213/346/212/245/345/221/212.md +92 -0
  28. package/docs/50_test-reports/2026-09-23-remote-framework/347/254/254/344/270/200/344/272/272/347/247/260/345/244/215/346/265/213/346/212/245/345/221/212.md +125 -0
  29. package/docs/50_test-reports/2026-09-23-ssh/345/267/245/345/205/267assisted-pty/344/274/232/350/257/235framing/345/256/236/346/265/213/346/212/245/345/221/212.md +207 -0
  30. package/docs/50_test-reports/2026-09-24-v0.1.7-rc.1/345/257/271/351/275/220/350/275/256-compaction/344/270/216failover/350/256/276/347/275/256/351/235/242/344/277/256/345/244/215/345/256/236/346/265/213/346/212/245/345/221/212.md +65 -0
  31. package/docs/50_test-reports/2026-09-27-lsp-ast-reminder-4999/345/256/236/346/265/213/346/212/245/345/221/212.md +53 -0
  32. package/lib/client/index.js +18 -18
  33. package/lib/compaction/index.d.ts +20 -10
  34. package/lib/compaction/index.js +42 -56
  35. package/lib/failover/index.d.ts +30 -6
  36. package/lib/failover/index.js +32 -26
  37. package/lib/fs-aware/sandbox-plugin.js +1 -1
  38. package/lib/index.js +4 -7
  39. package/lib/mobile/plugin.d.ts +1 -1
  40. package/lib/remote/plugin.d.ts +24 -0
  41. package/lib/remote/plugin.js +1095 -0
  42. package/lib/url-schemes/index.d.ts +14 -14
  43. package/lib/url-schemes/index.js +119 -15
  44. package/lib/{web-trust-CgpBoFv3.d.ts → web-trust-BihHCelF.d.ts} +10 -10
  45. package/lib/web-trust.d.ts +1 -1
  46. package/package.json +12 -4
  47. package/docs/10_plans/dashr-remote-tool-design.md +0 -337
  48. package/docs/10_plans/self-exo-cot-draft.md +0 -322
  49. package/docs/10_plans/self-scheme-and-monolog-draft.md +0 -357
@@ -1,337 +0,0 @@
1
- # Remote 工具设计:SSH / Docker 的统一远端操作面(Dash 侧;teleport 的零部署载体)
2
-
3
- - **日期**: 2026-09-21
4
- - **定位**: 工具设计文档(从 superd 仓 teleport 方案 `docs/superpowers/plans/2026-09-19-teleport-design.md` §5.3 抽出,归位本仓)
5
- - **路径**: `docs/10_plans/dashr-remote-tool-design.md`
6
- - **归属**: **Dash**(URL scheme 与工具家族);Super D teleport 是其消费者与上层语义
7
- - **核心命题**: **`remote` 是一个工具,不是 teleport**。teleport 转移**整个环境**(Environment State 的热绑定);`remote` 只提供**操作远端环境的工具面**(同 harness 内的一个工具 + 一套统一语法)。SSH 只是 `remote` 的一个 backend。
8
- - **血缘**: superd 仓 `docs/superpowers/plans/2026-09-19-teleport-design.md` 的 §1.3(八项摩擦解剖)、§2.2 T-1(传输裁决)、§5.3(原节,已抽出)、§10.2 TB-6(bearer 层)、DP-1(结果面纪律)、DP-4(连接物流与语义分离,本文明细即其落地)
9
- - **修订**: 二轮(2026-09-21)——语法对齐本仓 `docs/specs/url-schema/spec.md`(remote-backed handler 类别、tool 层写通道、目标绑定);Docker 部分按「容器常驻 / 连接 / 会话」三层生命周期重写
10
-
11
- ---
12
-
13
- ## 一、边界与命题
14
-
15
- | 维度 | `remote` 工具(本文档) | Teleport(另一文档) |
16
- |---|---|---|
17
- | 做什么 | 给模型一套**操作远端环境**的工具 | 把远端**整个环境**虚拟成本地资源 |
18
- | 粒度 | 单次操作(exec / read / write / grep / glob) | 环境级(绑定 / 换绑 / Manifest) |
19
- | 远端需要装什么 | **零**(sshd / docker daemon 即 daemon) | teleportd 单二进制 |
20
- | 状态语义 | 会话级(cwd/env 粘住、连接池) | 环境级(Manifest、常驻服务、能力面) |
21
- | 关系 | teleport 的**零部署载体**;也可独立使用 | 在 `remote` 契约之上加环境语义 |
22
-
23
- **裁决 R1**:`remote` 独立成线、独立成文——它是「今天就能让 agent 舒服地用上远端」的交付物,不依赖瞬移的完整形态。
24
-
25
- ### 三案归二
26
-
27
- 远端环境基本三形态:SSH、Docker、LXD container。**LXD 经其自带 sshd 归入 SSH**(系统容器可装 sshd,经 bridge IP 或端口转发直达),于是只剩两个 backend:
28
-
29
- | backend | 覆盖 | target 形态 | 连接来源 |
30
- |---|---|---|---|
31
- | `ssh` | 裸机 / VPS(dev3、dev4)/ **LXD container(带 sshd)** / 远端 docker host | `ssh://dev3` | 既有 SSH 密钥(零新增) |
32
- | `docker` | 本机或可达 daemon 上的容器 | `remote://docker/runner` | Docker socket / `DOCKER_HOST` |
33
-
34
- LXD 无 sshd 时存在第三 backend 备选(LXD REST API,`/1.0/instances/<n>/exec`)——后置,见 O-R4。
35
-
36
- ---
37
-
38
- ## 二、统一寻址:`remote://`(对齐本仓 url-schema 规格)
39
-
40
- **裁决 R2(统一语法)**:一个 scheme、一套语法,backend 只体现在 authority 段:
41
-
42
- ```
43
- remote://<backend>/<target>/<path>
44
- remote://ssh/dev3/home/u1/workspaces/app/main.ts
45
- remote://docker/runner/app/main.py
46
-
47
- 别名(兼容直觉):ssh://dev3/path ≡ remote://ssh/dev3/path
48
- docker 的短别名待定(`docker://` 与 registry 引用语义相撞,见 O-R8)
49
- ```
50
-
51
- - **target 是逻辑名**,连接细节(主机、密钥、容器名、socket 路径)在 **target 注册表**(harness 配置,如 `remotes.yml`)里解析——**模型只见逻辑名,物流归 runtime**(DP-4)。
52
- - **形态约束**:**资源地址给文件,执行是工具参数**——不要把 `cmd` 塞进 `read`,两种语义别混。
53
-
54
- ### 2.1 目标绑定(裁决 R5):常见情形零路由决策
55
-
56
- session 绑定一个**当前 target**(默认 = 本地)。`bash` 不带 target 时就在该环境执行——于是日常调用**没有「这次去哪」的路由决策**,§1.3 第⑦项(双世界税)从「减轻」变成「消解」(对照 teleport T-4 单世界模型:这里是它在工具层的可达形态)。跨环境调用才写显式 target。
57
-
58
- ### 2.2 资源形态表(每条都对齐 dashr 既有先例)
59
-
60
- | URI 形态 | 语义 | 工具 | dashr 先例 |
61
- |---|---|---|---|
62
- | `ssh://`(裸 scheme) | 列已注册 targets + 健康状态 | read | 裸 `skill://` = 技能目录 |
63
- | `ssh://dev3` | **target 状态文档**(backend、host、连接/会话、cwd、延迟等级、能力) | read | `dsh://docs` = 文档索引 |
64
- | `ssh://dev3/path` | 文件内容(选择器 `:N-M` / `:raw` / `?q=` 统一生效) | read | fs 型 scheme |
65
- | `ssh://dev3/path` + content | 文件写入 | write(经 per-scheme 写通道) | 见 2.3 |
66
- | `ssh://dev3/src/**/*.ts` | 远端 glob | glob | spec 的元字符切分规则 |
67
- | `ssh://dev3/src` + pattern | 远端 grep(命中路径回写为 URL 形) | grep | spec 的 match-path 回写 |
68
- | `bash(target="ssh://dev3", cmd=…)` | 远端执行 | bash(target 可选,默认目标绑定) | — |
69
-
70
- `read ssh://dev3` 返回状态文档、`read ssh://` 列出 targets —— 这两条正是本仓 url-schema 既有惯例(裸 `skill://` 列目录、`dsh://docs` 出索引)在 remote 上的直接套用,不是新发明。
71
-
72
- ### 2.3 handler 类别:`remote` 是**第三类**(remote-backed)
73
-
74
- url-schema 规格现有两类 handler:
75
-
76
- - **path-backed**:实现 `resolvePath` 映射到真实磁盘路径,交给原生 ripgrep(如 `skill://`);
77
- - **content-backed**:解析出的文本物化到 `/dev/shm` 临时目录后交给原生搜索(如 `ctx://`)。
78
-
79
- `ssh://` / `docker://` 两者都不是:**无本地磁盘路径可映射,也不该把远端全树物化到本地**。它属于第三类 **remote-backed**——read / glob / grep / write 由 handler **在远端产出结果**,过网的只有结果(DP-1:结果过网,字节留在数据侧)。→ 需要在 url-schema 规格层承认这一 handler 类别(对应开放问题 O-R2)。
80
-
81
- ### 2.4 写通道:`remote` 是那条预留通道的第一个消费者
82
-
83
- fs-scheme-resolution 规格明确:FS seam 的虚拟目标是 **read-only**(`FS_VIRTUAL_READONLY`),真实路径的变更才走原生栅栏。而 url-schema 规格已经预留了 **tool 层结构化 per-scheme 写通道**(现状「all rejected this wave」)。
84
-
85
- **`remote` 就是这条通道的落地理由**:远端写(`write ssh://dev3/path`)与远端 `edit`(读-改-写 + CAS base rev)都必须经它,而不是 FS seam。也就是说——做 `remote` 顺带把「scheme 写」这条预留能力打通,其它只读 scheme(`ctx://`、`dsh://`)无需改动。
86
-
87
- ---
88
-
89
- ## 三、工具面
90
-
91
- ### 3.1 执行归 `bash`(裁决 R6)
92
-
93
- ```
94
- bash(cmd="pnpm test") # 目标绑定(默认本地)
95
- bash(target="ssh://dev3", cmd="pnpm test")
96
- bash(target="remote://docker/runner", cmd="pytest -q")
97
- ```
98
-
99
- **为什么不让 `remote` 当执行工具**:exec 语义本就属于 `bash`,模型对 `bash` 的直觉最强(§1.3 第①项的另一半);再造一个执行入口会把「命令在哪跑」变成每次都要回答的问题(第⑦项摩擦回潮)。
100
-
101
- **裁决 R6'(零新工具)**:整个 `remote` 能力**不新增任何 registry 工具**——它 = scheme handlers(read/write/grep/glob 的 `ssh://` 分支)+ bash wrapper 的可选 `target` 参数。此前草案里的 `remote(op=…)` 管理工具**删除**,逐项归位:
102
-
103
- | 原草案 op | 归位后 |
104
- |---|---|
105
- | `remote(op="list")` | **`read ssh://`**(裸 scheme = 列 targets,既有先例) |
106
- | `remote(op="status")` | **`read ssh://dev3`**(target 状态文档,既有先例) |
107
- | `remote(op="attach")` | **不是模型工具**。PTY 的消费者是**人**(Web UI 终端面板,骑同一条连接池);模型侧的 `session.mode="pty"`(R7)是 bash wrapper 的内部实现模式,模型不可见。模型真需要交互 IO 的场景(TUI/REPL)v0 用一次性命令 + `background` + 读日志覆盖;确需模型侧 attach 再立项 |
108
- | `remote(op="session.reset")` | **推迟**:TTL / 重连已覆盖绝大多数场景;确需时走 bare-target 的写通道 op |
109
-
110
- **模型可见面的全部增量 = `bash` 的一个可选参数 + 四个文件工具的 `ssh://` 分支。**
111
-
112
- 统一的关键在**参数**:target 与 cmd 分离 → 零 shell 嵌套、可审计、可审批(§七)。
113
-
114
- #### 3.1.1 落地形态:bash wrapper(native-capture 模式,零原生改动)
115
-
116
- 查证(2026-09-21):原生 dsh `bash` 的参数面 = `{command, description, timeoutMs, workdir, run_in_background}`(外加升级字段)——**没有 `target`**,唯一的「在哪」参数是本地 `workdir`;dashr 自身 src 也**未包装/改路由 bash**(`src/url-schemes/tools/` 只覆盖 glob/grep/materialize/read/write)。
117
-
118
- 所以 `bash` 的 `target` 不是改原生,而是复用本仓已成熟的 **native-capture + delegation shell** 模式(`src/url-schemes/native-capture.ts`):
119
-
120
- 1. `agent/session-start` 时先 `ctx.tools.get('bash', agent)` 捕获原生定义(**先捕获后注册**,避免自递归——native-capture.ts 的既有纪律);
121
- 2. 注册 bash wrapper,其 wire schema = 原生参数 + **可选 `target`**(additive-only,模型可见签名基本稳定);
122
- 3. 路由:无 `target` → 参数原样转交捕获的原生 bash(bit-identical 行为,策略门/沙箱照旧);有 `target` → 解析 target 注册表 → remote provider 执行;
123
- 4. host 未部署 bash 时 wrapper 报结构化 `NATIVE_BASH_UNAVAILABLE`(同既有 `NATIVE_*_UNAVAILABLE` 惯例)。
124
-
125
- **侵入性结论:零原生代码改动**——与 read/write/grep/glob 的 wrapper 同一套插件机制,只是多包一个工具、多加一个可选参数。
126
-
127
- ### 3.2 文件工具接受 URI(工具面恢复)
128
-
129
- `read` / `write` / `grep` / `glob` 接受 `ssh://` / `remote://` 地址,语义与本地一致(行号、offset/limit、ripgrep 语义、CAS)。这消掉 §1.3 第③项(工具面坍缩),是本设计最大的单项收益。`edit` 走 2.4 的写通道(读-改-写 + base rev)。
130
-
131
- ### 3.3 降级形态(若内建工具不能被扩展)
132
-
133
- `remote(op=read|write|edit|glob|grep, target=…, path=…)` 包装族——语义等价,但绕开了「模型的第一直觉」(本地工具形状)。**注意:这是唯一会出现新工具的形态**(与 R6' 冲突,故仅作最后退化);选择取决于 url-schema 的 delegation shell 机制能否容纳 remote-backed handler(与 teleport §5.2 同一未知数)。
134
-
135
- ---
136
-
137
- ## 四、连接物流(DP-4 落地)
138
-
139
- **裁决 R3(连接与语义分离)**:模型永远不管理连接——keepalive / 多路复用 / 重连 / 池化全是 runtime 职责。
140
-
141
- | 机制 | 设计 |
142
- |---|---|
143
- | 连接池 | 每 target **一条主连接**:SSH = 单 TCP 多 channel(`ssh2` 库)或 ControlMaster;Docker = 一条 API 客户端连接。握手一次,此后调用廉价 |
144
- | 会话状态 | 每 (target, session) 一份 **cwd/env 会话状态**,由 runtime 持有并**每次调用注入**:SSH = env/cwd 注入或常驻 PTY channel;Docker = exec 规格的 `WorkingDir`/`Env`(§5.2)——跨调用粘住,消 §1.3 第②项 |
145
- | 状态快照 | cwd / env diff 快照进 runtime state,重连后自动恢复(不依赖远端 shell 存活) |
146
- | 重连 | 指数退避 + 幂等读自动重放 + 写 journal(DP-1 同款幂等键) |
147
- | 空闲回收 | 空闲 TTL 关闭主连接;会话消亡但状态可从快照重建 |
148
- | 模型可见面 | **零连接句柄**——模型只看到逻辑 target 名 |
149
-
150
- ### 4.1 一条连接,多个消费者(SSH 连接面清单)
151
-
152
- SSH 连接(单 TCP、多 channel)是**共享基础设施**——连接池化后,下列消费者全部从同一条 keep-alive 连接取通道(DP-4 的真正收益):
153
-
154
- | SSH 能力 | channel/机制 | `remote` 里的消费者 |
155
- |---|---|---|
156
- | exec channel | 每命令一条 | `bash(target=…)` |
157
- | PTY channel | 交互会话 | **人**(Web UI 终端面板);模型侧无 attach 工具,`session.mode="pty"` 为 bash wrapper 内部模式 |
158
- | SFTP subsystem | 文件面 | `read`/`write`/`grep`/`glob` 的 `ssh://` handler |
159
- | 端口转发(direct-tcpip) | local/remote/dynamic | `forward`:摸到 dev3 上的 dev server / DB,无需开端口 |
160
- | Unix socket 转发(streamlocal) | streamlocal | 摸到**远端 docker.sock / LXD socket**(远端 daemon 场景的 bearer) |
161
- | 自定义 subsystem | sshd `Subsystem=` 配置 | teleport T-1 的 `teleportd --serve`(SFTP 同款形态) |
162
- | agent forwarding | 认证委托 | 跳板机场景(后置) |
163
-
164
- (app 级骑手——git/rsync/scp over SSH、`DOCKER_HOST=ssh://…`——都骑在 exec channel 上,不算独立连接面。)
165
-
166
- ### 4.2 会话双模(裁决 R7):exec 携带为默认,PTY 为可选
167
-
168
- 「远程登录 session」有三种成立方式,分层不同,必须分开指认(此前各轮表述有混用,此处为准):
169
-
170
- ```
171
- 第 1 层 连接(transport) TCP + 加密 + 认证 ← keep-alive 在这(每 target 一条,池化复用)
172
- 第 2 层 channel(应用会话) exec(一次性,跑完即关)/ shell+PTY(长驻)/ SFTP / forward
173
- 第 3 层 进程 bash(挂在 PTY 上)或 exec 的子进程
174
- 第 4 层 客户端状态簿 cwd/env(runtime 内存) ← 状态携带(R4)在这,不是 keep-alive
175
- ```
176
-
177
- - **exec 模式(默认)**:每次调用开 exec channel、跑完即关;cwd/env 由客户端状态簿注入。**这恰是本机 bash 工具的既有行为**——dsh 本地 bash 本就每次 fresh shell、无状态、官方指引「pass `workdir` 而非 `cd`」。所以状态携带不是妥协,是**与本地逐字对齐**:本地传 `workdir`,远端注入注入的 cwd,同一语义。
178
- - **PTY 模式(可选,`session.mode="pty"`)**:一条 shell+PTY channel 长驻(= 用户 `ssh dev3` 的等价物),调用把 `cmd` 当 stdin 喂进去,读输出直到完成哨兵(`printf '\x1e%s\x1e' "$?"` 或 OSC 133)。换来**完整 shell 连续性**(`source` / `export` / 别名 / 函数全活着),代价是哨兵判界、回显与 ANSI 噪声(`TERM=dumb` + `stty -echo` 缓解)、单会话串行、stdout/stderr 合并。
179
- - **attach**:把 PTY 的终端面暴露给**人**(Web UI 终端面板)或真交互;不把无界裸终端流直接灌给模型上下文(上下文安全),包成 op 才能限量读取。
180
-
181
- ---
182
-
183
- ## 五、Backend 实现
184
-
185
- ### 5.1 `ssh`
186
-
187
- - 库:`ssh2`(进程内连接池);通道:**exec channel**(命令)、**PTY channel**(常驻会话)、**SFTP subsystem**(文件)。
188
- - 文件面:read/write 经 SFTP(结构化、零转义);`edit` CAS = `stat(mtime+size)` 作 rev 条件写;`glob`/`grep` = exec channel 跑 `rg`/`find` 取 JSON。
189
- - LXD:容器内 sshd 起好后与本 backend 无差别。
190
-
191
- ### 5.2 `docker`
192
-
193
- #### 5.2.1 三层生命周期(先厘清)
194
-
195
- | 层 | 谁持有 | 需要 keep-alive 吗 |
196
- |---|---|---|
197
- | **容器** | dockerd / containerd(独立于任何客户端) | **否**——踢起来就一直在跑,除非显式停/删 |
198
- | **API 连接** | 客户端 ↔ dockerd(unix socket;远端 daemon 走 SSH/TCP) | **是**——shell-out `docker exec` 每次都是新客户端;进程内保持一条连接 |
199
- | **会话(shell 进程)** | 容器内那个进程 | **是**——每次 exec 都是新进程,cwd/env 不延续 |
200
-
201
- 所以「容器起来就一直跑」是对的,但 keep-alive 不是为容器准备的:要保鲜的是 **API 连接**(复用一条)与**会话状态**(cwd/env 跨调用)。两者都不是容器生命周期问题。
202
-
203
- #### 5.2.2 API 已经给了什么 / 没给什么
204
-
205
- **给了**(所以「exec 还能多做点什么」的答案是:exec 本身没什么可加的):
206
-
207
- - `POST /containers/{id}/exec` —— 创建 exec,可指定 **argv / Env / WorkingDir / User / Tty**
208
- - `POST /exec/{id}/start` —— 启动 + attach(hijacked 流)
209
- - `POST /exec/{id}/resize`(TTY 尺寸);`GET /exec/{id}/json`(退出码)
210
- - Archives API:`GET/PUT /containers/{id}/archive` —— tar 进/出 = **结构化文件面、零 shell**
211
- - inspect / logs / stats
212
-
213
- **没给**(→ 这才是桥要做的):
214
-
215
- 1. **文件原语**:行号读、行窗读、CAS 编辑;Archives 是 tar 粒度,没有 per-file stat(可 HEAD archive 取 size/mtime,或 exec `stat`)
216
- 2. **会话延续**(见 5.2.3)
217
- 3. **流不可重连**:客户端一断,exec 的流就没了;stdin 关闭通常让 shell 退出
218
- 4. **目标抽象**(容器名 / socket / docker context)、审计、策略
219
- 5. **命令完成判界**:`Tty:false` 时 API 用 8 字节头**多路复用 stdout/stderr**(可精确分帧——这是走 API 而非 CLI/PTY 的优势);`Tty:true` 是合并的原始流,需哨兵标记
220
-
221
- #### 5.2.3 会话模型(裁决 R4):状态携带优先,PTY 只给交互
222
-
223
- **「在容器里起一个 PTY 再桥到 harness」方向正确,但不是默认形态**。更干净的等价物:exec 规格自带 `WorkingDir` / `Env`——runtime 持有会话的 cwd/env,**每次调用注入**:
224
-
225
- ```
226
- docker exec(container, cmd, WorkingDir=session.cwd, Env=session.env)
227
- → 分帧 stdout/stderr + 精确退出码
228
- ```
229
-
230
- 好处:无需哨兵解析、无回显/ANSI 噪声、stdout/stderr 分离、退出码精确——而 cwd/env 依然跨调用粘住(消 §1.3 第②项)。**PTY 不是持久化的必需品,状态携带才是。**
231
-
232
- PTY 会话只在**真交互**时需要(TUI、REPL、供人观看的终端面):`exec(create, Tty=true)` 持有流;此时必须解决**完成判界**(写哨兵如 `printf '\x1e%s\x1e' $?`,或 shell 集成 OSC 133 提示符标记)并处理回显 / ANSI / `TERM` 约定。模型侧无 attach 工具(R6'):终端面的消费者是人(Web UI),模型的交互 IO v0 用一次性命令 + `background` + 读日志覆盖。
233
-
234
- #### 5.2.4 驻留、生命周期与安全(诚实边界)
235
-
236
- - 断线后 exec 流不可重连 → 真驻留需容器内 `tmux` / `setsid` 或写日志文件;否则「流丢了但进程可能还在」,靠 `logs` / 文件核对(O-R3)。
237
- - 默认假设容器**已在跑**;不在跑则 fail loud(不偷偷 `docker start`——那是用户的生命周期决策);生命周期控制(start/stop/rm)的模型侧形态推迟(O-R11)。
238
- - 远端 daemon(`DOCKER_HOST=ssh://dev3`):该 target 的 bearer 就是 SSH backend,§八 的分层自然成立。
239
- - **安全**:docker.sock = **宿主 root 等价**(把 socket 挂进容器是已知提权路径)——target 注册表须明示每个 target 的等价权限等级。
240
-
241
- ### 5.3 Backend 契约(统一接口)
242
-
243
- ```
244
- resolve(target) → {
245
- exec(cmd, {cwd, env, timeout, background}) → stream/result
246
- ptySession()? → 交互会话句柄(仅真交互需要)
247
- readFile/writeFile/stat/list/glob/grep
248
- forward(targetSocket)? → 端口/socket 转发(可选)
249
- close()
250
- }
251
- ```
252
-
253
- **关键观察:契约与 teleport 的 EnvSurface 原语同源**——`remote` = EnvSurface 契约的**零部署实现**;teleport 在同一契约上叠加 Manifest / hello 能力面 / 常驻服务 / jail / 审计。这让「轻量线 → 完整瞬移」成为同一接口的两次实现,而非两套东西(§八)。
254
-
255
- ### 5.4 为什么不让模型自己 `docker exec`
256
-
257
- 模型确实能自己做,但会原样复现 §1.3 的八项摩擦,且 Docker 特有几条:`docker exec -i … sh -c '…'` 再套一层 shell、`-t`/`-i` 语义微妙(忘了 `-i` 吞 stdin、加了 `-t` 混入 ANSI 与行尾变换)、容器名与 socket 细节泄漏进上下文、无文件面、无会话。抽象的价值 = 把**物流**收进 runtime,把**语法**统一给模型。
258
-
259
- ---
260
-
261
- ## 六、摩擦对账(对照 teleport §1.3 八项)
262
-
263
- | # | 摩擦 | `remote` 消解程度 |
264
- |---|---|---|
265
- | ① | 二阶 shell 转义 | **改善**:结构化参数不嵌套;文件操作经 SFTP/Archives 零转义;仅 `cmd` 保留一层(exec 语义固有) |
266
- | ② | 状态每步归零 | **解决**:状态携带(cwd/env 每次调用注入;SSH 侧可配常驻 PTY) |
267
- | ③ | 工具面坍缩 | **解决**:read/write/edit/glob/grep 接受 URI(3.2) |
268
- | ④ | 无流式无驻留 | **部分**:流式与 stdout/stderr 分帧有(API 优势);断线驻留弱(尤其 Docker,§5.2.4) |
269
- | ⑤ | 截断无纪律 | **可落**:复用 DP-1(服务端 cap + 续读) |
270
- | ⑥ | 政策盲区 | **改善**:target 与 cmd 分离,可审可审批 |
271
- | ⑦ | 双世界心智税 | **消解(有条件下)**:目标绑定使常规调用零路由决策(R5);显式跨环境调用仍有前缀 |
272
- | ⑧ | 每会话重新发现 | **部分**:`read ssh://dev3` 给出状态文档;完整能力面仍是 teleport hello 的增量 |
273
-
274
- **净收益:七项落地(其中⑦在目标绑定下真正消解)、一项(⑧)部分——完整能力面留给 teleport。**
275
-
276
- ---
277
-
278
- ## 七、安全 / 审计 / 审批
279
-
280
- - **可审计**:`{target, cmd, cwd, exit, duration}` 结构化落日志(远端语义首次有本地锚点)。
281
- - **jail 差异(如实)**:`ssh` backend 是 SSH 用户级 **all-or-nothing**(拿到 key = 该用户全部权限);`docker` backend 的边界是容器,但容器内常以 root 运行,且 **docker.sock 本身是宿主 root 等价**。真正的 jail(workspace 子树 + 专用低权用户)是 teleportd 的增量。
282
- - **审批**:危险操作按 **target 作用域**升级审批;策略仍在 harness tool 层生效(不经隧道黑箱)。
283
- - **凭据**:复用既有 SSH key / docker socket;**密钥永不进模型上下文**。
284
-
285
- ---
286
-
287
- ## 八、与 teleport 的分层关系
288
-
289
- ```
290
- Dash: remote 工具(统一寻址 + 连接物流 + backend 契约)
291
- ↑ 被消费
292
- Super D: teleport EnvSurface(+ Manifest / hello 能力面 / 常驻服务 / jail / 审计)
293
- ```
294
-
295
- - teleport 的 EnvSurface **可骑在 `remote` 连接设施上**(T-1 的 `exec teleportd` 通道改经 `remote` 建立)。
296
- - `remote` 单独用 = **轻量线**:今天就能给 agent 结构化的远端工具面,代价是 jail/审计/断线驻留弱化。
297
- - 两线共享 DP-1(结果面)与 DP-4(连接物流)与目标绑定(R5);teleport 另加环境语义。
298
-
299
- ---
300
-
301
- ## 九、MVP 里程碑
302
-
303
- | 里程碑 | 内容 | 验收(第一人称实测) |
304
- |---|---|---|
305
- | RM0 连接物流 | target 注册表 + 连接池(keep-alive)+ ssh exec + 会话状态携带 | 连续两次 `bash(target=…)` 共享 cwd;`ss` 确认全程**一条** TCP 连接;断网后自动重连并恢复 cwd |
306
- | RM1 工具面 | ssh 文件面:`read`/`write`/`grep`/`glob` 接受 `ssh://`(经 remote-backed handler + 写通道) | 在 dev3 上完成一次真实 TDD 循环,**全程无裸 `ssh` 字符串** |
307
- | RM2 Docker backend | Engine API exec(`WorkingDir`/`Env` 状态携带)+ Archives 文件面 | 同款 TDD 循环对本地容器成立;容器内 `cd` 跨调用粘住;无每调用新客户端 |
308
- | RM3 可选 | `forward`(sock/端口)+ LXD REST backend | 按需 |
309
-
310
- ---
311
-
312
- ## 十、开放问题
313
-
314
- - **O-R1 语法终态**:`remote://<backend>/<target>` 正典 + `ssh://` 别名(本文档建议),还是保留多 scheme?
315
- - **O-R2 remote-backed handler 类别**:需 url-schema 规格承认第三类 handler(不映射本地路径、不物化全树);同时决定 3.2 与 3.3 的形态。
316
- - **O-R3 Docker 长任务驻留**:容器内 tmux 约定 vs 写日志文件 vs 接受不可 re-attach。
317
- - **O-R4 LXD**:要求容器内 sshd(一次性配置)vs 实现 LXD REST backend(零配置但多一个 backend)。
318
- - **O-R5 归属与打包**:本工具是 Dash 插件还是 superd 插件;跨项目分发形态。
319
- - **O-R6 审批粒度**:per-target 策略表(allow/deny/ask + 命令模式)何时落地。
320
- - **O-R7 选择器 vs 远端路径冒号**:统一选择器语法按 `:` 切分,远端路径可能自带 `:`(Windows 风格名、扩展属性);需定「最后一个冒号段匹配选择器语法才算选择器」之类的判定规则,否则误判为 `URL_BAD_SELECTOR`。
321
- - **O-R8 docker 短别名**:`docker://` 与 OCI registry 引用语义相撞;是否只留 `remote://docker/<target>/…` 正典。
322
- - **O-R9 目标绑定的外溢**:session 绑定 target 后,本地工具(bash/read/grep)是否一律重定向(= teleport T-4 的工具层形态),还是仅 remote-aware 工具受影响。
323
- - **O-R10 Docker 文件面取舍**:Archives API(tar 粒度、无 stat)vs exec 内 `cat`/`stat`(有 quoting 但有粒度)的混合边界。
324
- - **O-R11 生命周期控制面**:容器 start/stop/rm 的模型侧形态——bare-target 写通道 op?独立管理面?还是永不给模型(仅人经 Web UI)?倾向最后者。
325
-
326
- ---
327
-
328
- ## 十一、词汇增补候选
329
-
330
- - **Remote 能力(`remote`,非 registry 工具)**:harness 内统一远端操作面——**零新工具**(R6'):scheme handlers(read/write/grep/glob 的 `ssh://` 分支)+ bash wrapper 可选 `target`;一套语法覆盖 ssh / docker backend,连接物流归 runtime。
331
- - **Target 注册表**:逻辑名 → 连接细节(主机/密钥/容器/socket)的解析表;模型只见逻辑名。
332
- - **目标绑定(Target Binding)**:session 级默认 target;常规调用零路由决策,跨环境才写显式 target(R5)。
333
- - **会话状态携带(State Carrying)**:cwd/env 由 runtime 持有并每次调用注入(Docker 走 exec 规格的 `WorkingDir`/`Env`),而非依赖远端常驻 shell(R4)。
334
- - **remote-backed handler**:url-schema 的第三类 scheme handler——不映射本地磁盘路径、不物化全树,结果在远端产出后过网(§2.3)。
335
- - **Backend 契约**:exec / ptySession? / 文件族 / forward? / close 的统一接口;与 teleport EnvSurface 原语同源。
336
- - **连接物流(Connection Logistics)**:keepalive / 多路复用 / 重连 / 池化由 runtime 承担(DP-4)。
337
- - **会话双模(R7)**:exec 状态携带为默认(与本机 bash 工具逐字对齐——本地 bash 本就是每次 fresh shell)、PTY 长驻为可选(完整 shell 连续性);attach 面向人与真交互。