# 已删除的机制说明(v2 → v1.2.0) 这份文档记录 **v2 里加过、随后按你要求全部删掉** 的机制。留档的原因有两个: 1. 那几个 API 是从 DSH 客户端包里逆出来的,**知识本身有价值**; 2. 写清楚**为什么它们算越界**,免得以后我又顺手加回去。 插件现在的边界是:**只加一个菜单项 + 发一条 kickoff 消息**。不开宿主端点、不动会话权限、不往聊天里灌全文。 --- ## 一、宿主 HTTP 路由(node 半边) **v2 写的代码** ```js ctx.inject(["webServer"], (scope) => { scope.effect(() => scope.webServer.register({ kind: "exact", // 或 "prefix" path: "/ppt-maker/prompt.md", handler: (_req, res) => { // 原始 node:http 的 req / res res.writeHead(200, { "content-type": "text/markdown; charset=utf-8", "cache-control": "no-store", "x-ppt-prompt-sha256": sha256 }); res.end(promptText); } }), "ppt-maker: prompt route"); }); ``` 同时注册了 `/ppt-maker/health` 返回 `{ ok, version, promptPath, promptBytes, promptSha256 }`。 **当时的动机**:v1 把 `C:\Users\ASUS\...` 三个绝对路径写死在 bundle 里,插件一搬走或文档一改名就失效。想让前端完全不依赖磁盘路径,就用 `import.meta.url` 算出包目录、把正文从宿主路由发给前端。 **为什么删**:插件本职是"加个菜单",却在你的 GUI 服务上开了两个**未认证端点**。`dsh-host-webserver` 文档写得很直白:*"不提供服务器级 TLS、认证或来源策略"*,安全策略由 route 注册方自己实施(`dsh-better-sidebar` 就带 `fence(req)` 做 trustedHosts 校验,我当时没做)。现在 host 是 `127.0.0.1` 只在本机可读,但一旦改成 `0.0.0.0`,提示词全文就对网络公开了。为省一个路径而开端口——不成比例。 **如果哪天真要用**:`kind` 取 `"exact"` / `"prefix"`;`registerUpgrade` 走 WebSocket;`registerFallback` 只有一个席位(已被 SPA dist 占用)。匹配顺序是「精确 route → 最长前缀 → fallback」。**务必自己加来源校验。** --- ## 二、自动切换会话权限(客户端半边) **v2 写的代码** ```js const remote = scope.get("remote"); await remote.commands.execute(sessionId, "/permission danger-full-access", []); // → { ok, value: { result: { kind, text } } } ``` 权限预设 id 只有三个:`read-only` / `workspace-write` / `danger-full-access`(源码里叫 `FULL_ACCESS_PRESET`)。 **当时的动机**:这次跑流程里,无头 Chromium 要命名管道、arkcli 要写工作区外,沙箱下几乎每一步都要批一次权限,你被弹了几十次。我想"插件替你切一次就一劳永逸"。 **为什么删**:会话权限是**整个会话的安全边界**(能写任意路径、免审批执行命令)。插件在你不点同意的情况下把它放开,等于"你点了个 PPT 制作,顺带把整个会话的门打开了"。而且 `dsh-client-ui-permission-presets` 自己在 UI 上对 `danger-full-access` 是**带风险确认勾选**的——宿主的设计意图就是"这件事必须由人明确确认",我绕过的正是这个设计。 **正确做法**(已写进文档 A0):由你手动 `/permission`,插件只在消息里提醒一句。 **如果哪天真要用**:就是上面那行 `remote.commands.execute(id, "/permission danger-full-access", [])`;需要 `inject` 里加 `remote` 与 `remote.commands`。 --- ## 三、提示词全文内联(客户端半边) **v2 写的代码**:`fetch("/ppt-maker/prompt.md")` 拿回 58,621 B 正文,拼进 kickoff 一起发出去。 **当时的动机**:工作目录是空的也能跑,彻底摆脱路径。 **为什么删**:① 聊天里会出现一整篇文档(约 1.4 万 token)——一大坨字;② 它抢走了文档的版本管理权:你改完 `PPT全流程_一键编排提示词.md` **必须重新部署插件**才生效,比"读一个文件"更死。 --- ## 四、composer 通知(客户端半边) **v2 写的代码**:`conversation.input.for(actx).notify(level, text)`,在输入框上方弹一条"已内联 N 字节 · 权限已切 · 已发送"。 **当时的动机**:v1 失败时可能静默,我想让每条链路都汇报。 **为什么删**:干净版里没有会静默降级的链路了——消息发没发出去,**看聊天框就知道**,不需要额外通道。出错时 `onSelect` 会抛错,ui-commands 的原生弹层会把错误显示在弹层里。 --- ## 五、装载顺序改动(很可能就是"客户端左侧 bug"的元凶) v2 我把 `@deepseek-ai/dsh-api-remotes` 加进了 `package.json` 的 `dsh.client.inject`。那份清单是**客户端 entry 的先行依赖**,动它等于改动客户端 bundle 的装载顺序。 你反馈"客户端左侧突然出现 bug、我改完后没变化、重启后恢复正常" —— 症状(改文件无效、重启即好)符合 **HMR 重建期的一次客户端装载故障**,而不是功能代码写错。 现在 `dsh.client.inject` 只保留最初那两个(`ui-commands`、`ui-conversation`),装载顺序回到原样;`_verify` 里加了一条断言把这份清单钉死,防止以后无意再动它。 --- ## 现在的边界(回归断言守着) | 不该有的 | 验证断言 | |---|---| | 改会话权限 | 源码不含 `danger-full-access` / `/permission` / `remote` | | 开宿主路由、发网络请求 | 源码不含 `/ppt-maker/` / `fetch(` | | 机器相关绝对路径 | 源码不含 `ASUS` / `C:\Users`,且必须出现 `%USERPROFILE%` | | 装载顺序漂移 | `package.json` 的 `dsh.client.inject` 必须恰好是两个包 | | 聊天被灌满 | kickoff 长度断言 < 3000 字符 | | 连点发两条 | 连点两次断言只发一条;失败后仍可重试 | | 自带副本与正本脱节 | `prompts/` 副本与 `PPT全流程_一键编排提示词.md` 逐字节比对 |