better-dsh 0.2.2-c → 0.2.3-b

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 (27) hide show
  1. package/docs/50_test-reports/2026-09-06-write/345/267/245/345/205/267sandbox/345/215/207/347/272/247/351/200/217/344/274/240bug/345/244/215/345/217/221/345/217/212/346/214/202/350/265/267-/344/272/213/344/273/266/346/212/245/345/221/212.md +133 -0
  2. package/docs/50_test-reports/2026-09-08-hashline-edit-E_RANGE_UNVERIFIED/350/267/250/350/275/256/344/274/232/350/257/235/351/224/256/345/244/261/346/225/210-/350/257/212/346/226/255/346/212/245/345/221/212.md +226 -0
  3. package/docs/50_test-reports/upstream-dsh-0.1.3-alpha.2-local-test-report.md +44 -0
  4. package/docs/50_test-reports/upstream-dsh-0.1.3-alpha.2-report.md +110 -0
  5. package/docs/50_test-reports/v0.2.3b-hashline-content-locator/345/256/236/346/265/213/346/212/245/345/221/212.md +73 -0
  6. package/docs/60_exploration-and-research/01-cordis-runtime/cordis-engineering-feasibility.md +155 -0
  7. package/docs/60_exploration-and-research/01-cordis-runtime/cordis-research.md +385 -152
  8. package/docs/60_exploration-and-research/01-cordis-runtime/js-ts-language-fundamentals.md +628 -0
  9. package/docs/60_exploration-and-research/02-dsh/dash-research.md +592 -0
  10. package/docs/60_exploration-and-research/02-dsh/dsh-web-profile-package-map.md +186 -0
  11. package/docs/60_exploration-and-research/02-dsh/dsh-web-ui-slot-system-research.md +310 -0
  12. package/docs/60_exploration-and-research/02-dsh/dsh-webui-backend-data-inventory.md +633 -0
  13. package/docs/60_exploration-and-research/02-dsh/dsh-webui-strip-boundary-research.md +300 -0
  14. package/docs/60_exploration-and-research/02-dsh/dsh-webui-wire-appendix.md +3729 -0
  15. package/docs/60_exploration-and-research/02-dsh/web-frontend-composability-research.md +191 -0
  16. package/docs/60_exploration-and-research/02-dsh/webui-wire-data/capture-live-turn.json +1 -0
  17. package/docs/60_exploration-and-research/02-dsh/webui-wire-data/observed-endpoints.json +224 -0
  18. package/docs/60_exploration-and-research/02-dsh/webui-wire-data/remote-inventory.json +110954 -0
  19. package/docs/60_exploration-and-research/02-dsh/webui-wire-data/served-index-sample.html +47 -0
  20. package/docs/60_exploration-and-research/02-dsh/webui-wire-data/session-events.json +9411 -0
  21. package/docs/60_exploration-and-research/02-dsh/webui-wire-data/ws-frame-examples.json +20 -0
  22. package/docs/superd/00-blueprint.md +331 -0
  23. package/docs/superd/01-component-boundaries.md +261 -0
  24. package/docs/superd/02-dsh-embed-modes.md +151 -0
  25. package/docs/superd/image.png +0 -0
  26. package/lib/index.js +1214 -1293
  27. package/package.json +1 -1
@@ -0,0 +1,261 @@
1
+ # Super D 组件边界与 UI 组合层(v0.3)
2
+
3
+ > 2026-09-08 · 比蓝图细一层:组件级"复用 / 替换 / 排除 / 自研"边界,两幅 Mermaid + 包 roster
4
+ > 草案。蓝图见 `00-blueprint.md`(v0.2,§3.5 对偶裁决)。
5
+ > 上游包事实依据:`../60_exploration-and-research/02-dsh/dsh-web-profile-package-map.md`(220 包
6
+ > 四层分类)与 `dsh-web-ui-slot-system-research.md`(插槽机制一手核验);机制语言层依据:
7
+ > `../60_exploration-and-research/02-dsh/dash-research.md` §1「"Everything is plug-in" 的语言层
8
+ > 实现」。本文回答:DSH 现有包里哪些拿来做依赖、哪些明确不要(如 DSH Session Controller)、
9
+ > 自研 agent adapter 补在哪、前端组合层替换 DSH UI 的哪一段。
10
+ > 2026-09-08 v0.2 修订:BFF 定性为 routing mux(namespace 分流);ui-sidebar 移入 superD 自有
11
+ > shell;selector 位置与品牌 MVP 策略落位;DSH 认证链速查见 §2 要点(全文:蓝图 §3.6)。
12
+ > 2026-09-08 v0.3 修订:selector 移位侧栏底部(`sidebar.footer.action` list 槽 occupant,
13
+ > Settings 上方),superd-sidebar copy 撤回,ui-sidebar 回归中性底座(蓝图 §3.7 v0.4 / 决策 #24)。
14
+
15
+ ---
16
+
17
+ ## 1. 前后端对偶总则
18
+
19
+ - 后端 **Agent Adapter**(数据面):把 runtime 的字段/事件映射到 superD 标准字段(DSH
20
+ SessionEvent 词汇)。统一 `RuntimeProvider` 接口,一个 runtime 一个包。
21
+ - 前端 **Agent UI Adapter**(呈现面,机制 = 插槽组合):把标准字段渲染成该 runtime 的 UI
22
+ 形态。一个 runtime 一个包,从 agent-ui-dsh 蓝本 copy 起步。
23
+ - 两者都是 cordis 插件;**同一 selector 驱动、同组启停**——一个 runtime 落地为一对包:
24
+ `@pgmi-builds/agent-adapter-x` + `@pgmi-builds/agent-ui-x`。
25
+ - **切换 = 组合重算**:selector 变更 → 插件行集合(空间数据)一次变更 → 时间维机制(受影响
26
+ fiber 的 unload → reload 重放)落地 → 插槽树重渲染。这是 "everything is plug-in" 的机制内
27
+ 行为(epoch 签名驱动),不是 hack;语言层同构案例见 dash-research §1(5) 的 provider 切换。
28
+ - **互斥不变量**:同一时刻仅一个 composition 激活。conversation.* 槽带同 key 双注册会撞、
29
+ list 槽会叠渲染)——互斥由 host 侧 Composition Registry 保证(按 selector 算出互斥插件行集)。
30
+ - **DSH 面 = 全透传 + routing mux**:superD 自有 namespace(`machines.*` / `superd.*`)本地应答,
31
+ 其余按 active adapter 转发;DSH 三面(session/workspace/settings)逐帧透传、零翻译(蓝图 §3.6)。
32
+
33
+ ---
34
+
35
+ ## 2. Host 侧组件边界
36
+
37
+ ```mermaid
38
+ flowchart LR
39
+ subgraph UP["复用 · 上游原样(exact-pin)"]
40
+ COR["cordis 核心 + loader/include/group/timer/hmr<br/>(插件系统基座)"]
41
+ CARRY["载体:host-webserver · host-frontend-static<br/>client-modules(wire ①②:静态资产 + boot graph)"]
42
+ WIREH["wire:client-connection · api-gateway · typert-*<br/>(wire ③④:/api unary + WS mux,哑传输)"]
43
+ end
44
+
45
+ subgraph SD["自研 · superD host 核心"]
46
+ BOOT["superd 主插件(CLI 自举 · bundle 组装)"]
47
+ REG["Provider Registry<br/>RuntimeProvider 统一接口"]
48
+ BFF["superD Routing Mux / BFF<br/>machines.* / superd.* 本地应答 · 其余透传 active adapter"]
49
+ MM["Machine Manager<br/>local/remote 清单 · 连接池 · 探测"]
50
+ PAIR["Session Pairing(配对表 ~/.superd/pairings.json)"]
51
+ SELH["Selector + Composition Registry<br/>runtime 选择态 · 插件行集合重算"]
52
+ AUTH["AuthChain(空壳中间件链)"]
53
+ end
54
+
55
+ subgraph AD["自研 · Agent Adapter ×6(@pgmi-builds/agent-adapter-*)"]
56
+ A1["dsh(wire 透传 3080)"]
57
+ A2["omp(omp-web 桥接改造)"]
58
+ A3["pi(与 omp 同系共用源码)"]
59
+ A4["claude-code(SDK)"]
60
+ A5["codex(SDK)"]
61
+ A6["hermes(协议桥/源码 import)"]
62
+ end
63
+
64
+ CONSU["自研 · Consumers:TG messaging channel(M2+,orchestra 搬运)"]
65
+
66
+ subgraph X["排除 · 不进依赖树"]
67
+ X1["DSH BFF:api-session / api-workspace / api-settings-controller"]
68
+ X2["DSH 数据面:session / persistence / projection / query / title / telemetry…"]
69
+ X3["DSH agent 运行时:agent / agent-loop / llm-* / tools / tool-* / sandbox / fs / subprocess…"]
70
+ X4["DSH 编排原语:subagent / goal / workflow / schedule / agent-team…"]
71
+ end
72
+
73
+ REMOTE["远端 machine(superD-to-superD:DEV3…)"]
74
+ RT["上游 runtime 进程(dsh:3080 / omp / claude-code / codex / pi / hermes)"]
75
+
76
+ BOOT --> COR & CARRY & WIREH
77
+ BOOT --> REG & MM & PAIR & SELH & AUTH
78
+ BFF --> REG
79
+ SELH --> REG
80
+ MM --> REMOTE
81
+ REG --> A1 & A2 & A3 & A4 & A5 & A6
82
+ A1 & A2 & A3 & A4 & A5 & A6 --> RT
83
+ REMOTE -.->|"remote superD 起 adapter 桥本地 runtime"| RT
84
+ CONSU --> PAIR
85
+ BFF -.->|"替代其 RPC 面(不进树)"| X1
86
+ ```
87
+
88
+ 要点:
89
+
90
+ - **载体层原样**:webserver / frontend-static / client-modules / connection / gateway 保持上游
91
+ 依赖——wire 面 100% 兼容、升级跟随 upstream,锁版策略对冲无稳定性承诺的风险(蓝图 §2/§5)。
92
+ - **替换层 = thin routing mux,不是全量重实现**:DSH 三个 BFF controller(**含 DSH Session
93
+ Controller**)不进依赖树;superD 侧按 RPC namespace 分流——自有面本地应答,其余透传 active
94
+ adapter(DSH 面逐帧零翻译,UI 与上游锁死同版 ⇒ 契约逐字节一致)。
95
+ - **DSH 认证链速查**:machine token 一次性 mint `dsh-auth-*` cookie(HMAC 签名、持久 secret ⇒
96
+ cookie 跨上游重启有效,默认 30 天)→ adapter 服务端 cookie jar 附带转发;loopback Host 天然过
97
+ 信任栅栏。源码锚点与 mint 流程见蓝图 §3.6。
98
+ - **排除层**:DSH 的会话数据面、agent 运行时、编排原语整体不引入。superD 不跑 agent、不存原始
99
+ 会话(唯一持久状态是 pairing 表)。这些包不是"被 patch 掉",而是**根本不出现在 bundle 里**。
100
+ - **远端**:v1 仅 superD-to-superD;CLI 型 runtime 必须由远端 superD 本地桥接(蓝图决策 #17)。
101
+
102
+ ---
103
+
104
+ ## 3. 浏览器侧组件边界(Agent UI Adapter 替换带)
105
+
106
+ ```mermaid
107
+ flowchart TD
108
+ subgraph BASE["中性底座 · 上游原样(所有 composition 共享,exact-pin)"]
109
+ RUN["cordis-client-runner(浏览器 Cordis:fiber/registry/reflect/events)"]
110
+ CMOD["client-modules / client-locale / client-store / client-hmr"]
111
+ SLO["ui-slots(SlotCore · SlotMap 契约声明合并)"]
112
+ REN["ui-renderer(唯一 React 绑定 · renderSlot('root'))"]
113
+ LAY["ui-layout(root 布局树 · sidebar/会话区 区域槽)"]
114
+ SID["ui-sidebar(sidebar 壳,原样复用)<br/>含 sidebar.footer.action list 槽(selector 落位)"]
115
+ THM["ui-theme"]
116
+ SET["ui-settings(settings 页壳)"]
117
+ WS["ui-workspace(workspace 壳 · 数据由 superD BFF 聚合供给)"]
118
+ RPC["client-connection / api-remotes(RPC 桩 · 远程能力本地代理)"]
119
+ end
120
+
121
+ subgraph COMP["自研 · Agent UI Adapter ×6(@pgmi-builds/agent-ui-* · 同一时刻仅一个激活)"]
122
+ U1["agent-ui-dsh<br/>(蓝本:copy DSH 会话面全套)"]
123
+ U2["agent-ui-omp"]
124
+ U3["agent-ui-pi"]
125
+ U4["agent-ui-claude-code"]
126
+ U5["agent-ui-codex"]
127
+ U6["agent-ui-hermes"]
128
+ end
129
+
130
+ subgraph OWN["自研 · superD 自有 UI(occupant / 自有槽,不动底座)"]
131
+ SELUI["Runtime Selector(Agents 按钮)<br/>sidebar.footer.action occupant · Settings 上方"]
132
+ MSET["Machines 设置页(multi-machine 配置)"]
133
+ BRD["brand:MVP 不动(沿用上游品牌)"]
134
+ TAB["(后续)superD 专属全页 Tab + 专属插槽"]
135
+ end
136
+
137
+ subgraph CUT["被 composition 替换 · 不进 bundle(槽声明权随 copy 转移)"]
138
+ C1["ui-chat / ui-conversation / ui-session / ui-trajectory"]
139
+ C2["ui-model-selection / ui-agent-preset / ui-permission-presets / ui-settings-models"]
140
+ C3["ui-tool / ui-attachment / ui-input-trigger / ui-reference / ui-message-feedback / ui-approval / ui-user-questions"]
141
+ C4["ui-plan / ui-goal / ui-subagent / ui-workflow-run / ui-jobs / ui-deliverables / ui-skill / ui-commands"]
142
+ end
143
+
144
+ RPC --> RUN
145
+ RUN --> SLO
146
+ REN --> LAY
147
+ LAY --> SID
148
+ LAY --> SET
149
+ LAY --> WS
150
+ LAY -->|"会话区槽带(conversation.* / tool.call.* / composer.*)同一时刻仅一个激活"| U1 & U2 & U3 & U4 & U5 & U6
151
+ U1 -.->|"copy 蓝本(锁版 diff 基线)"| C1 & C2 & C3 & C4
152
+ SID --> SELUI
153
+ SET --> MSET
154
+ SID --> BRD
155
+ LAY -.-> TAB
156
+ SELUI -.->|"切换:整组换装 + 重渲染"| COMP
157
+ ```
158
+
159
+ 要点:
160
+
161
+ - **中性底座 = 上游原样依赖**:插槽机制(ui-slots)、渲染器(ui-renderer)、布局树(ui-layout)、
162
+ 主题、settings 页壳、sidebar 壳、wire 桩。换 runtime 不动这一层——对应 DSH 前端"框架中立
163
+ 内核 + 可替换渲染后端"的既有分层。
164
+ - **替换不是 patch,是不进 bundle**:CUT 组的 DSH 包(ui-chat / ui-conversation / ui-session /
165
+ …)直接不进依赖树;composition 包 copy 了它们的源码,**槽声明权(SlotMap 契约合并 + 槽
166
+ 注册)随 copy 转移**给 composition。底座的 layout 只认槽带,不认包。
167
+ - **superD 自有 UI 全部走 occupant**:selector(Agents 按钮)= `sidebar.footer.action` list 槽
168
+ occupant(Settings 上方;owner 只给 `wide` 标志,rail 态渲染单图标,样式对齐 Settings 行);
169
+ Machines 挂 settings 页;brand MVP 不动;未来全页 Tab 走 ui-layout 声明新区域槽或自有路由
170
+ 槽——中性底座零 copy 例外(v0.4:superd-sidebar 计划撤回)。
171
+ - **数据来源不变**:composition 消费的仍是 api-remotes RPC 桩暴露的标准面(session/workspace/
172
+ settings),服务端由 superD BFF + adapter 供给。前后端各自的"对偶替换带"互不越界。
173
+
174
+ ---
175
+
176
+ ## 4. 包 roster 草案(三档处置,M1 定稿——对齐蓝图开放问题 #6)
177
+
178
+ ### ① 中性底座:原样依赖(exact-pin)
179
+
180
+ | 组 | 包 | 备注 |
181
+ |---|---|---|
182
+ | Cordis 框架 | `cordis`、`plugin-loader`、`plugin-include`、`plugin-group`、`plugin-timer`、`plugin-hmr`、`cosmokit`、`schemastery` | 插件系统基座,全量需要 |
183
+ | 载体(host) | `host-webserver`、`host-frontend-static`、`client-modules` | wire ①②(静态资产 + boot graph) |
184
+ | wire 双端 | `client-connection`、`api-gateway`、`api-remotes`、`typert-protocol/-registry/-loader`、`cordis-host-runner`、`cordis-client-runner` | wire ③④ + 浏览器 Cordis,哑传输/哑运行时 |
185
+ | 前端基建 | `client-locale`、`client-store`、`client-hmr` | observable/locale/热更基建 |
186
+ | UI 壳 | `ui-slots`、`ui-renderer`、`ui-layout`、`ui-sidebar`、`ui-theme`、`ui-settings`(页壳)、`ui-cordis`(调试面板) | 组合机制与布局骨架,所有 composition 共享(v0.4:sidebar 回归) |
187
+ | UI 壳(复核位) | `ui-workspace` | 壳可复用;数据面改由 superD BFF 聚合(machine/runtime 分组),M1 复核是否需 patch |
188
+
189
+ > v0.4 修订:~~ui-sidebar 移出中性底座~~ **撤回**——selector 移位侧栏底部 footer 区,落位现成
190
+ > list 槽 `sidebar.footer.action`(occupant 注册,渲染于 Settings **上方**堆叠;与 Settings 同行
191
+ > 不可行:owner 的 `footArea` 是 column 布局,occupant 改不了 owner)。ui-sidebar 回归中性底座
192
+ > 原样依赖,copy 例外清零(蓝图 §3.7 v0.4 / 决策 #24)。
193
+
194
+ ### ② composition 蓝本:不直接依赖,copy 进 agent-ui-dsh 后派生 per-runtime 包
195
+
196
+ | 组 | DSH 源包(蓝本) | 说明 |
197
+ |---|---|---|
198
+ | 会话核心 | `ui-chat`、`ui-conversation`、`ui-session`、`ui-trajectory` | conversation.* 槽带的 owner,整组 copy |
199
+ | 模型/预设 | `ui-model-selection`、`ui-agent-preset`、`ui-permission-presets`、`ui-settings-models` | 模型列表/预设是 runtime 特有词汇 |
200
+ | 会话附属 | `ui-tool`、`ui-attachment`、`ui-input-trigger`、`ui-reference`、`ui-message-feedback`、`ui-approval`、`ui-user-questions` | 审批/提问面各 runtime 形态不一;缺字段 = 隐藏(降级容错是设计内行为) |
201
+ | DSH 深度面 | `ui-plan`、`ui-goal`、`ui-subagent`、`ui-workflow-run`、`ui-jobs`、`ui-deliverables`、`ui-skill`、`ui-commands` | DSH 特有编排/深度面;其他 runtime 按字段在场裁剪或整块不实现 |
202
+
203
+ 派生纪律:`agent-ui-omp/-pi/-claude-code/-codex/-hermes` 一律从 **agent-ui-dsh**(而非上游原包)
204
+ fork 起步——蓝本已经过一次"去 DSH 宿主耦合"的整理,派生成本更低。
205
+
206
+ ### ③ 排除:不进依赖树
207
+
208
+ | 组 | 包 | 理由 |
209
+ |---|---|---|
210
+ | DSH BFF | `api-session-controller`、`api-workspace-controller`、`api-settings-controller` | 替换层:RPC 面由 superD BFF + Provider Registry 接管 |
211
+ | DSH 数据面 | `session`、`session-persistence(-jsonl/sqlite)`、`session-projection(-cache)`、`session-query(-sqlite)`、`session-title*`、`session-stats`、`session-telemetry*`、`session-log-*`、`session-checkpoint-policy`、`session-reference` | superD 不存原始会话;唯一持久状态 = pairing 表 |
212
+ | DSH agent 运行时 | `agent`、`agent-loop`、`agent-presets`、`agent-default-model`、`agent-instructions`、`llm-*`、`tools`、`tool-*`、`shell*`、`bash-*`、`pwsh-*`、`sandbox*`、`fs*`、`subprocess*`、`code-runtime*`、`compaction*`、`spill*`、`credentials*`、`system-prompt`、`persona`、`mcp-client` | superD 不跑 agent、不含模型调用链(蓝图 §1) |
213
+ | DSH 编排原语 | `subagent*`、`goal*`、`workflow*`、`schedule`、`jobs*`、`experimental-agent-team`、`plan-mode` | 上游 runtime 自带编排;superD 只桥接 |
214
+ | 宿主杂项 | `plugin-inventory*`、`directory-picker-*`(暂缓)、`brand-official`(brand 槽由自有 occupant 替换)、`settings-plugins`/`settings-plugin-inventory`(superD v1 无插件管理面) | 按需再议;directory-picker 与 machine 语义耦合,M1 裁决 |
215
+ | 候补复核 | `dsh-settings`、`settings-file`、`storage*` | superD 自身设置面(machines/selector/TG bindings)的底座:复用或自研轻量 JSON,M1 定 |
216
+
217
+ ---
218
+
219
+ ## 5. 切换机制(两档实现)
220
+
221
+ **v1 简化档(M1 先用)**:
222
+
223
+ ```
224
+ UI selector 切 runtime(settings CAS 写入)
225
+ → host Composition Registry 重算互斥插件行集(disable 旧组 · enable 新组)
226
+ → boot graph 随之变更(该组 client 模块表换装)
227
+ → 前端整页 reload:client runner 按新组合重新拉模块、逐个 apply
228
+ → SlotRegistry 重算 → renderSlot('root') 整树重渲染
229
+ ```
230
+
231
+ 成本最低,正确性靠"组合是声明式数据"保证——页面刷新只是最粗粒度的 reload。
232
+
233
+ **v2 热切换档(M2+ 增强)**:client-hmr 式 fiber 级换装,不整页刷新——旧行组 fiber unload
234
+ (LIFO 撤销槽注册)、新行组 reload 重放,React 树保持挂载、仅会话区槽带重渲染。
235
+
236
+ **机制依据**:插件行集合是运行时数据(空间维被数据化);enable/disable 触发受影响 fiber 的
237
+ epoch 变化 → unload → reload 重放(时间维机制化);插槽注册随 disposer 撤销、重放时重登记。
238
+ 与 dash-research §1(5) 的 provider 切换案例完全同构,只是粒度从"单服务槽"换成"一组插件行"。
239
+
240
+ ---
241
+
242
+ ## 6. 维护策略(对偶架构的收益兑现)
243
+
244
+ - **各 composition 独立演进**:Codex 字段更新 → 只改 agent-ui-codex + codex adapter,回归测试
245
+ 只测这一对包;DSH Web UI 更新 → diff 锁定版本 → 选择性抄进 agent-ui-dsh,不跟则维持现状。
246
+ - **copy 纪律**:每个 composition 包记录其蓝本基线(上游精确版本号);跟进 = `diff 基线..新版`
247
+ → 人工挑选 → 更新基线记录。vendor verify/restore(蓝图 §5)防误改上游本体。
248
+ - **弃大全包的理由存档**:全量字段/全模板路线在 5+ 个更新步伐不一的上游之间同步一个全量表,
249
+ 复杂度按 O(runtime × 字段) 增长且任何一次上游 breaking 都波及全部用户;组合层把复杂度切回
250
+ O(runtime) 的独立通道——这正是用 Cordis 组合机制(换 provider = 换插件行)换来的维护性。
251
+
252
+ ---
253
+
254
+ ## 来源
255
+
256
+ - 蓝图:`00-blueprint.md` v0.4(§2 分界点、§3.5 对偶裁决、§3.6 透传与认证链、§3.7 selector 位置 v0.4、决策 #18–#24)
257
+ - DSH 包事实:`../60_exploration-and-research/02-dsh/dsh-web-profile-package-map.md`(220 包四层
258
+ 分类、类型双轨)、`dsh-web-ui-slot-system-research.md`(SlotCore/双半插件/RPC 边界)
259
+ - 机制语言层:`../60_exploration-and-research/02-dsh/dash-research.md` §1「"Everything is
260
+ plug-in" 的语言层实现」(3)(5)(名字耦合与 epoch、provider 切换生命周期)
261
+ - 官方文档蒸馏:dsh-dev-skill `skill/chapters/ch18`(slots/webServer/settings 契约)
@@ -0,0 +1,151 @@
1
+ # DSH 运行时呈现层嵌入模式研究(iframe / 反代透传)
2
+
3
+ > 2026-09-08 · 起因:DSH runtime 的呈现层能否绕开"自有 composition",直接复用上游 Web UI?
4
+ > 两个候选:①iframe 直嵌 3080 页面;②DSH adapter 假装 browser 向 3080 发请求、把 traffic
5
+ > 透传给 3090 前端。方法:上游源码一手核验(checkout `dsh-v0.1.2-alpha.5`,
6
+ > `~/workspaces/dashr/upstream/deepseek-harness`)。
7
+ > 结论先行:**两者都可行**;②其实就是蓝图 v0.2 已裁决的"DSH adapter wire 透传"形态的自然延伸,
8
+ > 真正的新选择是"前端也用上游的"——最干净的落法是 **iframe(或 iframe-over-反代)作为一个
9
+ > lite composition**,与终态集成 composition 并存,不冲突。
10
+
11
+ ---
12
+
13
+ ## 1. 四个变体速览
14
+
15
+ | 变体 | 前端是什么 | 代理代码 | 远程 machine | HTTPS 混合内容 | 集成度 | 定位 |
16
+ |---|---|---|---|---|---|---|
17
+ | **A 直连 iframe** | 上游真 UI(iframe src=3080) | 零 | ✗(浏览器须直达 3080) | 有风险 | 零 | 本地 DSH 的零代码捷径 |
18
+ | **B 反代 + iframe** | 上游真 UI(iframe src=镜像) | HTTP+WS 透明反代 | ✓(经 superD-to-superD) | ✓(镜像走 HTTPS) | 低(画布级) | **推荐的 M1 lite 形态** |
19
+ | **B′ 反代全页(无 iframe)** | 上游真 UI(整页) | 同 B | ✓ | ✓ | 零(无壳共存) | ✗ 主线;仅"远程访问网关"产品形态 |
20
+ | **C 集成 composition** | 自有 UI(agent-ui-dsh) | RPC 透传(蓝图既有) | ✓ | ✓ | 高(统一壳) | 蓝图 v0.2 终态 |
21
+
22
+ 判定:B′ 把代理代码和零集成两头的成本都占了却无收益——**整页 app 无法与我们的壳共挂**
23
+ (同一页面装两套 client 模块表 = 同 id 硬错,上游设计即禁止;DSH UI 是整页 root 槽应用,
24
+ 没有"挂在 div 里"的形态),且页面内没有我们的 selector。B′ 唯一合理用途是"superD 作为
25
+ DSH 远程访问网关"(独立产品卖点),不是统一入口的主线。
26
+
27
+ ---
28
+
29
+ ## 2. 上游事实核查(六条,均已一手核验)
30
+
31
+ 1. **无 frame-busting 头**:`packages/host/`、`packages/boot/` 全量 grep 无
32
+ `X-Frame-Options` / `Content-Security-Policy` / `frame-ancestors` → **DSH 页面可被 iframe**。
33
+ 2. **token 只存在于 DSH 进程内存**:`client/connection/src/browser-auth.ts` —— launch token
34
+ 为进程内随机数(`PROCESS_LAUNCH_TOKENS` WeakMap,L52–60),**无 env 覆盖、不落盘**;启动时
35
+ 由 `authenticatedUrl()` 拼成 `/?token=…` 随 URL 打印(`bundle/web-app/src/index.ts:287` 调用)。
36
+ → 获取路径只有一条:**读 DSH 进程的 stdout/journal/日志**。
37
+ 3. **token → cookie 交换**:`GET /?token=…`(Host authority 必须在)→ `303` + 签名 cookie
38
+ `dsh-auth-<authority>`,**24h 有效**(`authorizeIndex`,L236+);token 本身**随 DSH 进程存活**,
39
+ cookie 过期可用同一 token 重铸。401 响应体带指纹
40
+ `dsh web authentication required; reopen the URL printed by dsh web.`(蓝图开放问题 #4 已记)。
41
+ 4. **`/api` 信任栅栏按 Host 绑定,且"不是 auth 层"**(`api-request-trust.ts` 文件头注释):
42
+ loopback / 部署 LAN IP / 声明的 `trustedHosts` 过栅栏。→ 反代向上游发 `Host: 127.0.0.1:3080`
43
+ 即过;iframe 内页面对 3080 的调用是 same-origin(Host=loopback)也过。
44
+ 5. **客户端 API base = `window.location.origin`**(`client/connection/src/client/rpc.ts:109–110`,
45
+ 缺省回退内部 base)→ **经反代时客户端自动打向代理 origin**,无需改上游客户端。
46
+ 6. **资产/模块 URL 是 root-相对路径**(boot graph 里 `/plugins/??pkg/client.js&rev=…` 形态)→
47
+ 反代**必须挂在 origin 根**(独立端口或子域);挂路径前缀(`3090/_dsh/`)会打断这些引用,
48
+ 除非连 boot graph JSON 一起做路径重写(可行但增加脆弱面)。
49
+
50
+ ---
51
+
52
+ ## 3. 变体 A:直连 iframe(本地零代码)
53
+
54
+ 机制:superD 壳的会话区渲染 `<iframe src="http://127.0.0.1:3080/?token=…">`。iframe 文档的
55
+ origin 就是 3080,其自身的一切 `/api`+WS 调用都是 same-origin、Host=loopback → 栅栏直过;
56
+ token URL 首开后 303 换 cookie,此后 iframe 内自持会话。
57
+
58
+ - **前提 1(✓ 已核验)**:上游不设 frame 头。
59
+ - **前提 2(✓ 已核验)**:栅栏按 Host,iframe 内 same-origin 调用天然合法。
60
+ - **前提 3(工程项)**:token 注入——adapter 从 DSH stdout/journal 抓 URL(本地机器可做)。
61
+
62
+ 局限:远程 machine 的 3080 浏览器不可达(superD-to-superD 才可达);superD 壳若走 HTTPS
63
+ (Caddy),iframe 载入 plain HTTP 属混合内容,浏览器会拦;跨域 iframe 完全密封(壳不能
64
+ reach-in 折叠其自带 chrome)。
65
+
66
+ ## 4. 变体 B:透明反代 + iframe(推荐 lite 形态)
67
+
68
+ "DSH adapter 假装成 browser"的准确工程含义:**adapter 以 browser 的口音说 HTTP/WS**(正确的
69
+ Host 头、持 cookie、做 WS upgrade),**不是**跑一个真的 headless browser(重且脆,不需要)。
70
+ 实现就是标准反向代理:
71
+
72
+ ```mermaid
73
+ flowchart LR
74
+ BR["用户浏览器:superD 壳(3090)"] -->|"会话区 iframe src = 镜像 origin"| IF["iframe:真 DSH UI"]
75
+ IF -->|"same-origin /api + WS<br/>(rpc.ts 按 location.origin 打)"| MIR["superD DSH 镜像<br/>(透明反代,origin 根挂载)"]
76
+ MIR -->|"Host: 127.0.0.1:3080<br/>+ 代理持有 dsh-auth cookie"| DSH["目标机 DSH(3080)"]
77
+ BR -->|"列表/设置/新建会话等 BFF RPC(method 级)"| BFF["superD BFF + DSH adapter"]
78
+ BFF -->|"同上,另一条腿"| DSH
79
+ ```
80
+
81
+ 实现要点:
82
+
83
+ - **origin 根挂载**(事实 6):镜像监听独立端口/子域(本地如 3091+ 自动分配;远程经 Caddy
84
+ 路由到 remote superD 的镜像),纯透传、零重写。
85
+ - **认证归代理**:adapter 用抓到的 token 在镜像侧完成一次 `/?token=` 交换、持有并续期 cookie;
86
+ **浏览器全程不见 DSH token**——对下游只有 superD 自己的认证面(authChain)。
87
+ - **WS 1:1 桥**:每个下游 WS upgrade 对应一条独立上游 WS(不做扇出 hub,M1 单用户足够);
88
+ streaming 响应禁缓冲。
89
+ - **401 自愈**:镜像收到指纹 401 → 重读目标机 DSH 日志取新 token → 重交换(DSH 重启 token
90
+ 轮换的自动恢复)。
91
+ - **可选 same-origin 增强**:若镜像与壳同 origin(路径前缀 + boot graph 路径重写),壳可
92
+ reach-in 折叠 iframe 内 DSH 自带 sidebar(双 chrome 问题);这是 nice-to-have,不作前提。
93
+
94
+ ## 5. 与 v0.2 组合层架构的统一(关键:不二选一)
95
+
96
+ iframe **本身就是一种合法 composition**:`agent-ui-dsh-lite` 包往会话区槽 register 一个 iframe
97
+ 组件(URL 由 adapter 供给:machine URL + token),selector 切换 = 换 composition——v0.2 的
98
+ 机制原样适用,一行不用改。分工随之清晰:
99
+
100
+ - **会话画布**:lite 形态下 = 真 DSH UI(iframe);终态 = agent-ui-dsh(copy 版)。
101
+ - **会话列表 / workspace / settings / TG consumer**:永远走 superD BFF 的 method 级 RPC
102
+ (TG 本来就需要 method 级访问,adapter 的数据面两形态共用)——**统一入口不丢**:列表、
103
+ 选择器、机器管理都是我们的,iframe 只接管画布。
104
+ - **双 chrome**(iframe 内 DSH 自带 sidebar 叠在我们的壳里):M1 接受;缓解路径 = 同源
105
+ reach-in 或移动断点折叠。
106
+ - **红利**:镜像是 wire 层的"参考实现 + 永远新鲜的对照"——集成 composition 的字段映射
107
+ 可随时对着真实流量校准;上游 UI 更新零成本跟进(lite 形态下根本不维护 UI)。
108
+
109
+ ## 6. token 获取与生命周期(两形态共用)
110
+
111
+ | 事实 | 值 | 出处 |
112
+ |---|---|---|
113
+ | token 存储 | 仅进程内存(WeakMap),无 env、无文件 | browser-auth.ts L52–60 |
114
+ | 发放面 | 启动时随 `authenticatedUrl` 打印到 stdout | web-app/src/index.ts:287 |
115
+ | token 寿命 | DSH 进程生命周期(重启即轮换) | WeakMap 语义 |
116
+ | cookie 寿命 | 24h,可用同一 token 无限重铸 | authorizeIndex + DAY_MILLISECONDS |
117
+ | 抓取实现 | 本地:journal/日志文件;远程:remote superD 读它本机的 | 蓝图本地发现三步可复用 |
118
+
119
+ 长期项:向上游提 service-token / machine-token 配置需求(免除日志抓取);M1 不依赖。
120
+
121
+ ---
122
+
123
+ ## 7. 验证清单(M1 裁决 lite 形态前)
124
+
125
+ 1. [ ] 直连 iframe 本地 3080 实测:`?token=` 注入 → 303 → cookie 建立 → UI 全功能。
126
+ 2. [ ] HTTPS 壳(Caddy)iframe plain-HTTP 127.0.0.1 的混合内容行为(桌面 + 移动 Chrome)。
127
+ 3. [ ] 镜像反代全通:静态资产 + boot graph + `/api` unary + WS upgrade + streaming。
128
+ 4. [ ] WS 经代理的 connection generations 行为(断线重连、代理重启)。
129
+ 5. [ ] DSH 重启 token 轮换 → 401 自愈链路。
130
+ 6. [ ] 双 chrome 观感裁决(接受 / reach-in 折叠 / 移动断点)。
131
+ 7. [ ] 同源变体的 boot graph 路径重写成本评估(仅当需要 reach-in 时)。
132
+
133
+ ## 8. 建议
134
+
135
+ - **M1**:本地 DSH 用变体 A(零代码);远程 DSH 用变体 B(镜像反代)。`agent-ui-dsh-lite`
136
+ (iframe composition)先落地,`agent-ui-dsh`(集成 copy)延到 M2+。
137
+ - **终态**:蓝图 v0.2 的集成 composition 不变;lite 作为长尾保留("用最新原版 DSH UI"本身
138
+ 可以是一个用户选项)。
139
+ - **不做**:B′ 全页镜像作主线;headless browser 方案。
140
+
141
+ ---
142
+
143
+ ## 来源
144
+
145
+ - 上游 checkout(`~/workspaces/dashr/upstream/deepseek-harness`,tag `dsh-v0.1.2-alpha.5`):
146
+ `packages/client/connection/src/{browser-auth,api-request-trust,rpc-host}.ts`、
147
+ `packages/client/connection/src/client/rpc.ts`、`packages/bundle/web-app/src/index.ts`;
148
+ `packages/host/`+`packages/boot/` frame 头 grep(空)。
149
+ - `00-blueprint.md` v0.2(§3 adapter 体系、§3.5 composition、开放问题 #4/#7);
150
+ `01-component-boundaries.md`(呈现层替换带)。
151
+ - 运维证据:`docs/60_exploration-and-research/`(boot graph 模块 URL 形态、token 轮换实践)。
Binary file