在一台 Windows 机器上遇到一个与 #403 相关、但阶段不同的问题:#403 是「更新遗留真实目录插件直接失败」;这里是转换成功之后,一次无关操作把生成代链接删掉了。
环境
- DSH Desktop 0.8.2 / dsh 0.1.2-rc.1 / Windows 11 build 26200
- profile
web,pnpm nodeLinker: hoisted
背景
dsh-vscode-mode 0.3.3→0.4.6、@anionex/dsh-vision-toolkit 0.1.44→0.1.45 原先是普通目录安装(就是 #403 那种遗留形态),两个都带 dsh.bundle.patch。
转换做法(#403 里缺的就是这条路径):把已构建好的生成代 id 写入 desired.json → publishGenerationManifest() 同步 manifest → 重启 Harness 由启动投影建链接。重启日志 [desktop] projected generations: 6 linked, 0 unlinked,两个插件都跑在新版本上。
复现
- 完成上面的转换,确认
node_modules/<pkg> 是指向 .generations/live/... 的 junction;
- 在插件市场卸载一个完全无关的普通依赖(我这边是 432 字节的占位包
dsh-cowork);
- 观察上面那两个插件。
现象
市场日志里只有这一条操作:
{"at":"2026-09-18T05:44:04.322Z","level":"info","event":"toggle","detail":"dsh-cowork: no loader entry matched"}
{"at":"2026-09-18T05:44:04.324Z","level":"info","event":"uninstall","detail":"dsh-cowork exit=0 live-removed=false"}
结果(被动的两个插件与 dsh-cowork 毫无关系):
node_modules/dsh-vscode-mode、node_modules/@anionex/dsh-vision-toolkit 操作后 Test-Path = False(操作前是指向生成代的 junction)
pnpm-lock.yaml 39,770 → 30,232 字节,两者的记录消失
package.json 不一致:dependencies(0.4.6 / 0.1.45)、pnpm.overrides(两条 link:)、dsh.desktop.generationProjection.plugins(两条记录)都在,但 dsh.profile.bundles 少了它们(22 → 20)
desired.json 没变、生成代目录也完好,所以不是生成代丢了,是指向它的路径被删了。市场随后把这两个插件显示为「未安装 / not installed」。
原因(我读代码得到的过程,不保证准确)
dsh-desktop-market-installer/pnpm-runner.mjs:213 的 suspendGenerationProjectionForPnpm():每次 pnpm 操作前,把已投影插件从 dependencies 和 pnpm.overrides 中临时删除(257–266 行),结束后 restore() 只恢复这两项(294–304 行)—— 不碰 lockfile,也不碰 bundles。
- 把遗留插件转成生成代的流程(
out/main/index.js:9613 upgradePluginToGeneration())只做「安装生成代 + 发布链接」,从不触发一次 pnpm 协调,所以 lockfile 里一直留着该包迁移前的记录(dsh-vscode-mode: specifier: ^0.3.3 / version: 0.3.3)。也就是说 pnpm 始终认为这是自己的依赖,这与 generations/projection.mjs 头部注释里的 "pnpm therefore never owns the projected node_modules path" 对不上。
- 于是在第 1 步的窗口里,一次普通依赖重建就把这两个包当「manifest 里已不需要」删掉了;紧接着 CLI 的
reconcilePlugins()(@deepseek-ai/dsh/lib/plugin-*.js:46)发现这些名字解析不到声明 dsh.bundle 的包,就把它们从 bundles 里剪掉。第 3 步这个函数本身是按设计工作的,问题在第 1、2 步。
影响
下次冷启动的启动投影会自愈:我用同一个 projectGenerations() 手工跑过,链接重建、bundles 回到 22、再跑一次不改文件(幂等)。所以不是永久丢功能。
但两次冷启动之间,运行中的 harness 里该插件的懒加载 require 会失败,市场的判定和后续操作都基于「未安装」这个错误视图。
建议
suspendGenerationProjectionForPnpm() 摘除 dependencies/overrides 时,把对应包从 pnpm-lock.yaml 一并摘掉、restore() 时恢复;或者生成代发布成功后触发一次协调,清掉迁移前的旧记录。
- profile 级 pnpm 操作结束后校验一次
dsh.profile.bundles(= enabled generations ∪ 声明 dsh.bundle 的依赖),不一致就立即重建,别留到下次冷启动。现成的 projectGenerations() / exposeMissingGenerationLinks()(projection.mjs:336)可以直接复用。
- 删除类操作前后对已登记的生成代链接做一次存在性检查,异常时给出提示(目前完全静默)。
在一台 Windows 机器上遇到一个与 #403 相关、但阶段不同的问题:#403 是「更新遗留真实目录插件直接失败」;这里是转换成功之后,一次无关操作把生成代链接删掉了。
环境
web,pnpmnodeLinker: hoisted背景
dsh-vscode-mode0.3.3→0.4.6、@anionex/dsh-vision-toolkit0.1.44→0.1.45 原先是普通目录安装(就是 #403 那种遗留形态),两个都带dsh.bundle.patch。转换做法(#403 里缺的就是这条路径):把已构建好的生成代 id 写入
desired.json→publishGenerationManifest()同步 manifest → 重启 Harness 由启动投影建链接。重启日志[desktop] projected generations: 6 linked, 0 unlinked,两个插件都跑在新版本上。复现
node_modules/<pkg>是指向.generations/live/...的 junction;dsh-cowork);现象
市场日志里只有这一条操作:
结果(被动的两个插件与
dsh-cowork毫无关系):node_modules/dsh-vscode-mode、node_modules/@anionex/dsh-vision-toolkit操作后Test-Path= False(操作前是指向生成代的 junction)pnpm-lock.yaml39,770 → 30,232 字节,两者的记录消失package.json不一致:dependencies(0.4.6 / 0.1.45)、pnpm.overrides(两条link:)、dsh.desktop.generationProjection.plugins(两条记录)都在,但dsh.profile.bundles少了它们(22 → 20)desired.json没变、生成代目录也完好,所以不是生成代丢了,是指向它的路径被删了。市场随后把这两个插件显示为「未安装 / not installed」。原因(我读代码得到的过程,不保证准确)
dsh-desktop-market-installer/pnpm-runner.mjs:213的suspendGenerationProjectionForPnpm():每次 pnpm 操作前,把已投影插件从dependencies和pnpm.overrides中临时删除(257–266 行),结束后restore()只恢复这两项(294–304 行)—— 不碰 lockfile,也不碰 bundles。out/main/index.js:9613 upgradePluginToGeneration())只做「安装生成代 + 发布链接」,从不触发一次 pnpm 协调,所以 lockfile 里一直留着该包迁移前的记录(dsh-vscode-mode: specifier: ^0.3.3 / version: 0.3.3)。也就是说 pnpm 始终认为这是自己的依赖,这与generations/projection.mjs头部注释里的 "pnpm therefore never owns the projected node_modules path" 对不上。reconcilePlugins()(@deepseek-ai/dsh/lib/plugin-*.js:46)发现这些名字解析不到声明dsh.bundle的包,就把它们从bundles里剪掉。第 3 步这个函数本身是按设计工作的,问题在第 1、2 步。影响
下次冷启动的启动投影会自愈:我用同一个
projectGenerations()手工跑过,链接重建、bundles回到 22、再跑一次不改文件(幂等)。所以不是永久丢功能。但两次冷启动之间,运行中的 harness 里该插件的懒加载
require会失败,市场的判定和后续操作都基于「未安装」这个错误视图。建议
suspendGenerationProjectionForPnpm()摘除dependencies/overrides时,把对应包从pnpm-lock.yaml一并摘掉、restore()时恢复;或者生成代发布成功后触发一次协调,清掉迁移前的旧记录。dsh.profile.bundles(= enabled generations ∪ 声明dsh.bundle的依赖),不一致就立即重建,别留到下次冷启动。现成的projectGenerations()/exposeMissingGenerationLinks()(projection.mjs:336)可以直接复用。