Replies: 3 comments 3 replies
背景:为什么会走到写脚本做兼容性测试这一步
而 DSH 目前没有提供任何隔离机制,脚本必须自己创建和销毁一次性 DSH_HOME。路径的生与灭都落在作者手里——这一步就是后面所有问题的入口。 事故脚本伪代码根因(可安全复现)事故脚本第 6 行是这样的:
于是整个用户目录被递归强删。PowerShell 其实报了错: 但脚本里没有 影响
为什么这不是个别失误同一环境下已发生三次同类误删,前两次都没有定位到根因,被当作环境问题或手滑处理。现场不留痕迹,后果又静默延迟,这种失效模式靠文档提醒和代码规范是压不住的。事故脚本本身还是 AI 写的,且作者读过"不要给 具体建议
可安全运行的最小复现下面这段脚本不删除任何文件,只证明那个变量解析到了哪里,任何人都能在自己机器上运行核对: 本机实测输出 |
|
两件事想分享,都和你这份提案直接相关: 1. 你们的误删事故我们原样踩过。 我们也在 PowerShell 里用
你们 P3(临时目录标记文件 + 清理前校验)等于把第 2、3 条做进 CLI,方向完全支持。 2. 隔离配方我们已经在用,可以先跑起来。 在官方 P1 落地之前:
以上基于我们本机实测,环境不同可能有差异。 |
[Bug] 桌面端「禁用所有插件」恢复流程会静默丢失
|
| 项 | 值 |
|---|---|
| 桌面端 | DeepSeek Harness 0.2.0-rc.2(win-x64,nightly 通道) |
| 宿主 | DSH 0.2.0-rc.2 |
| Profile | desktop(~/.dsh/profiles/desktop) |
| 系统 | Windows |
现象
在桌面端触发一次 profile 恢复(disableAllPlugins,即「禁用所有插件 / 安全模式」)之后:
- 第三方插件的 bundles 被正确恢复并重新启用,功能正常;
- 但写在
<profile>/cordis.patch.yml里的手写配置全部消失,且没有任何提示。
我这台机器上丢失的内容包括:三个自定义 LLM 供应商定义(provider A / B / C,各含 baseURL 与模型列表,此处已匿名化)、一个模型覆盖(某模型的自定义上下文窗口与输入模态)、以及一个插件的 enabled: false 开关。
配置文件从 2622 B 缩到 837 B,只剩应用自己托管的几个 UI 设置项。
外部表现是模型选择器里找不到原来的模型——我这边的表现是主模型静默回落到内置的 deepseek-official/deepseek-flash。但界面上没有任何错误或提示说明「你的配置已被移除」。
密钥本身没有丢失(~/.dsh/.credentials.yaml 未被触碰,其中的密钥引用仍在)。丢的只是引用密钥的供应商定义。
复现步骤
- 在
<profile>/cordis.patch.yml里手写几个条目(自定义 provider、模型覆盖或插件开关均可)。 - 触发「禁用所有插件 / 安全模式恢复」,或任何会调用
disableAllPlugins的路径。 - 观察
<profile>/cordis.patch.yml:手写条目消失,仅剩应用托管项;同目录下多出一个cordis.patch.yml.bak-<时间戳>文件。
根因定位
恢复逻辑在 Electron 侧(app.asar 内):
lib/main.js → DesktopProjectManager.disableAllPlugins():
async disableAllPlugins() {
return this.withLock(() => sanitizeProfile("dsh", this.paths.profile, WEB_PROFILE.bundles));
}sanitizeProfile()(dsh-app-boot 与 main.js 各有一份)只做两件事:
const backupBase = `${patchPath}.bak-${Date.now()}`
// ...
renameSync(patchPath, backupPath) // ① 把 cordis.patch.yml 整体改名备份走
if (manifest !== void 0) writeProfileBundles(profileDir, manifest, bundles) // ② 只重建 bundles
return backupPath它的 doc comment 也写明了这个设计意图:
Back up the profile patch and retain only the caller's recovery bundles.
The caller must stop the profile and exclude concurrent profile writes.
Installed packages and other manifest fields are preserved; patches are never parsed.
关键证据
时间线(本机实测,.bak 文件名中的时间戳即 rename 发生时刻):
| 时间 | 事件 |
|---|---|
| 15:17:43 | cordis.patch.yml 最后一次正常写入,手写条目都在(2622 B) |
| 17:10:09 | disableAllPlugins 执行:patch 被 rename 为 cordis.patch.yml.bak-<时间戳>,bundles 重置为内置 web profile |
| 17:17:24 | 应用重写出新的 patch(837 B),仅剩它自己托管的设置,手写条目不复存在 |
插件定义走 package.json 的 dsh.profile.bundles,恢复时被重建;而供应商定义走 cordis.patch.yml,恢复时被 rename 移走且此后从不读取回填。 这就是「插件好好的、只有自定义配置没了」的原因。
为什么这是个问题
我理解「不解析 patch」是有意为之的安全设计——如果用户的 cordis.patch.yml 本身已损坏,解析它会让恢复流程失败,而恢复流程的本意是「保证 app 一定能起来」。这一点我认为是对的。
问题出在代价与告知不对称:
- 静默。恢复完成后没有任何 UI 提示、日志提示或文档说明告知用户配置已被移走。唯一线索是目录里那个
.bak-<时间戳>文件,一般用户不会去翻。 - 不可逆性取决于用户是否会翻目录。patch 层没有版本控制,恢复流程除了
rename之外不做任何保留。 - 受损的不只是「偏好」。自建 / 第三方 LLM 供应商、模型参数覆盖这类配置往往是用户投入最多、最难重建的内容(要在多个渠道重新查
baseURL、模型 ID、上下文窗口),却和 UI 开关一起被静默丢弃。
建议
按成本从低到高:
- 至少给出提示(零行为风险):恢复完成后在 UI 或日志里告知「profile 用户配置已备份到
<path>.bak-<ts>」。这是最小且最容易被接受的改动。 - 提供显式恢复路径:在恢复流程结束后,或在插件管理器里提供一个「从备份恢复用户配置」的入口,把
.bak里的条目合并回 profile 层。 - 合并而非丢弃:在不解析 patch 的前提下,把备份的 patch 作为独立一层保留(例如恢复后生成一个独立的 user-layer 文件),避免既破坏「能启动」的保证,又丢掉配置。
文档层面的一点建议
profile-boot 中 $DSH_HOME/cordis.patch.yml(home 级用户层)的注释说明它 "outranks the per-profile layer",但 profile 级的 cordis.patch.yml 会被上述恢复流程移走,而 home 级不会。建议在文档里点明这一点——想避免被恢复丢弃,请把手写配置放在 home 级 ~/.dsh/cordis.patch.yml。
我的临时规避(供对照)
在等待修复期间,我把供应商定义移到了 home 级 ~/.dsh/cordis.patch.yml(sanitizeProfile 不会动这一层,且它 outranks profile 层),而把会被应用改写的选择项(agent-default-model)留在 profile 层——否则 home 层的定义会永久压住我在 UI 里手动切换模型的选择。
这只是自救措施,不替代上游修复。如果你也遇到了这个问题,可以按同样的方式先保住配置。
Uh oh!
There was an error while loading. Please reload this page.
背景
我在为 dsh-worktree-space 插件跑 DSH 兼容性矩阵验收。这类验收需要在一套一次性的 DSH_HOME 上完成「装得上 → 起得来 → 卸得干净 → 回滚得干净」全流程,但 DSH 目前没有提供任何对应的隔离命令,只能由插件作者自己写脚本实现。
经过
我写了一个临时 PowerShell 脚本,用一个名为 $home 的局部变量保存隔离目录。$HOME 在 PowerShell 中是只读自动变量,且变量名不区分大小写——这行赋值静默失败,$home 仍然指向真实的 C:\Users<user>。脚本下一行是对这个变量的 Remove-Item -Recurse -Force,于是整个用户目录被递归强删,.ssh、.dsh、.gitconfig、.npmrc 等点号开头的文件与目录全部丢失。所幸有事故前约 50 分钟的备份,已逐项恢复。
更值得警惕的是:同一环境下已发生三次同类误删,前两次都没有定位到根因。一个大多数时候诊断不出来的失效模式,靠文档提醒和代码规范是压不住的。
诉求
我据此写了一份提案,核心主张是:当隔离的创建与销毁都由 CLI 内部完成时,「忘记校验路径」这一整类事故就不再可表达。 具体建议:
P1:新增 dsh --profile --ephemeral,由 DSH 在系统 temp 下自建目录、打印路径、跑完自动清理;
P2:新增一等公民的验收命令 dsh plugin verify ,一站式完成安装 → 组合校验 → 可见性探测 → 卸载 → 回滚;
P3:DSH 自建的临时目录带标记文件,清理时校验标记,缺失即拒绝。
完整事故分析、根因复现(安全版脚本,不删任何文件)和提案全文见下方文档。[
DSH插件兼容性验收隔离提案-2026-10-01.md
](url)
All reactions