DeepSeek Harness 插件装不上与卸载事故:add 挂起、旧版不兼容与 junction 删源码
DeepSeek Harness(DSH)的插件装卸流程里,有三类事故最伤时间:dsh plugin add 在 Windows 上打完 Done in … 就挂住不退出、插件其实已经装好却报「安装失败」;用裸包名安装时被 pnpm 的发布时长门限静默降级到上一版,再被兼容性检查判为不兼容并回滚;以及卸载 link: 方式安装的插件时,删除逻辑跟随 junction 递归穿进源码仓库,把 .git 一起掏空。 前两类本质是「宿主内置的 pnpm 与 CLI 路径缺了兜底」,第三类是一条危险的真实删除路径——本批讨论里既有完整的事故现场,也有可操作的恢复方法(#9261、#9342、#9178、#9835、#8898)。
DeepSeek Harness 安装挂起:打印完成后进程不退出
一句话结论:卡的不是「没装完」,而是「收尾没做完」——宿主内置的 pnpm 打印完成后进程不退出,而 CLI 这条路径既没传空闲超时、stdio 又是继承模式,所以兜底也够不着,插件于是「装上了但不加载」。 症状是 Windows 上 dsh plugin --profile X add Pkg 打印 Done in … using pnpm v11.x 之后挂住 11 分钟以上,bundles 永不 reconcile(#9261)。 反过来,「装上了但不加载」也可能是激活阶段的问题,而不只是收尾没跑完——要确认卡在哪一头,可以对照 DeepSeek Harness 插件装上了不生效:重名注册、realm pending 与重载报错。
DeepSeek Harness 机制:两处缺口叠加
据社区核对源码与多环境复现(Windows / macOS 均复现)(#9261):
- pnpm 自身缺陷:宿主内置的 pnpm(讨论中为 11.7.0)存在「任务完成后进程不退出」的问题,上游已在 11.23.0 起修复(对应上游 issue
pnpm#13226); - CLI 路径没有兜底:调用处未传空闲超时,stdio 设为继承,于是「等一会儿就放弃」这件事在结构上不可达。
DeepSeek Harness 五步处置
- 确认插件其实已经装上:检查 profile 的
node_modules里有没有该包目录,以及 profile 的package.json是否已写入依赖;有即说明安装阶段完成(#9261)。 - 结束卡住的进程链:不要等它自己退出——
# 找到卡住的 pnpm / node 进程链并结束(保留宿主进程本身)
Get-Process node,pnpm -ErrorAction SilentlyContinue |
Where-Object { $_.StartTime -gt (Get-Date).AddMinutes(-30) } |
Select-Object Id, ProcessName, StartTime
# 确认目标后:Stop-Process -Id <Id> -Force
- 手动触发一次 reconcile:重启 DSH 会让 profile 与磁盘状态重新对齐,这一步做完插件才会真正加载(#9261)。
- 根治:把宿主内置的 pnpm 升到 ≥ 11.23.0,或直接用系统里较新的 pnpm 完成安装(#9261)。
- 旁支问题:讨论里还提到 Windows 上偶发
os error 5导致无法写 manifest,需要手工改package.json后重跑一次安装(#9261)。
DeepSeek Harness 裸包名装了旧版:发布时长门限 + 两把尺子
一句话结论:pnpm 11 默认只接受发布满 24 小时的版本,而 DSH 的兼容性预检用 pnpm view(不受该门限影响)、实际安装用 pnpm add(受门限影响)——两把尺子不一致,于是「预检通过、装到旧版、再被判不兼容」。 报错还会把人引向 allow-version,那是错误的方向(#9178)。
DeepSeek Harness 四步处置
- 确认判据:同一插件用裸包名装报「不兼容」,而显式指定最新版本号就能装上——即命中本条(#9178)。
- 规避一(最简单):安装时显式带版本号:
dsh plugin --profile <profile> add <package>@<version>
- 规避二:等该版本发布满 24 小时后再用裸包名安装(#9178)。
- 注意副作用:如果你用
minimumReleaseAgeExclude排除某些包,要留意旧版 pnpm 只取同包多条排除项里的第一条,其余会被静默忽略(较新版本才合并)(#9178)。
DeepSeek Harness 相关:整库被锁死(lockfile 级校验)
还有一条更狠的形态:门限校验作用于整个 lockfile,所以只要里面有任何一条比门限更新的记录,该 profile 的所有安装与更新都会硬失败,而且报错点名的往往是你没在装的那个包(#9342)。
- 确认:报错是
ERR_PNPM_MINIMUM_RELEASE_AGE_VIOLATION,点名的包与你正在装的包不一致(#9342)。 - 规避一:把最小发布时长临时设为 0:
dsh plugin --profile <profile> add <package> --config.minimum-release-age=0
DeepSeek Harness 卸载事故:link: 插件的 junction 被递归删除
一句话结论:卸载路径把 junction 当成普通目录递归删除,于是穿进链接目标、把插件源码仓库连 .git 一起删掉——这是本族里唯一会造成数据损失的形态。 报告者给出了完整现场取证:删除按目录项的字母序推进、止于 objects 之前(#9835)。
DeepSeek Harness 先做损害评估
- 先别动磁盘(尤其别在仓库里执行
git init/git clean等写操作),把现场留给恢复。 - 检查
.git/objects是否还在:这是能否完整恢复的关键。若objects目录仍在,绝大多数对象都能找回(#9835)。 - 确认被删范围:列出仓库根目录,看除
.git之外还剩什么(例如.deepseek-harness/是否也消失)。
DeepSeek Harness 四步恢复(.git/objects 完好时)
cd <你的仓库>
# 1) 重建最小 HEAD 与 config
"ref: refs/heads/main" | Out-File -Encoding ascii .git/HEAD
git config core.repositoryformatversion 0
git config core.bare false
# 2) 找回悬空对象
git fsck --lost-found
# 预期:输出一批 dangling commit / blob
# 3) 把最近的提交接回分支
git reset --hard <上面输出里最新的 commit id>
# 预期:工作区文件回来
# 4) 校验完整性
git status
git log -n 5 --oneline
据讨论中给的恢复法:.git/objects 完好时,重建 HEAD/config + git fsck --lost-found 即可把内容捞回来(#9835)。
DeepSeek Harness 一层归属要如实说明
讨论里对「删除究竟发生在哪一层」有分歧:报告者认为在卸载路径,社区复核后倾向于认为删除发生在 pnpm 子进程内,并质疑 lstat 判定那一环的实现细节。这一点在讨论中未被定论,所以本文只把「junction 被跟随删除」这一确证事实与恢复步骤写出来,不对层归属下结论(#9835)。
DeepSeek Harness 预防
- 卸载前先确认目标是不是重解析点:
Get-ChildItem -Force -Recurse -Attributes ReparsePoint "<profile>\node_modules" |
Select-Object FullName, LinkType, Target
# 预期:列出所有 junction / 符号链接及其目标
- 只删链接、不删目标:手工清理时删除链接本身即可,
.git目录绝不要进删除范围(#9835)。 - 开发期优先用真目录安装:把插件复制成真目录再装,可绕开 junction 相关的一整类问题(#9835)。
- 重要仓库提前备份:源码仓库在开发机上被链接进来是常态,唯一稳妥的保险是备份(#9835)。
DeepSeek Harness 附带一条:带查询参数的 tarball 地址装不上
一句话结论:识别 tarball 地址的规则要求扩展名出现在结尾,而带 ?token= 的地址结尾是查询串;另一条规则又会把这种两段路径当成 git 仓库地址,于是要么在安装前被拒,要么被误判成 git 源。(#8898)
- 确认:报错是
InvalidInstallSpecError,或明明给的是 tarball 直链却被当成 git 仓库去解析(#8898)。 - 规避一:先把文件下载成本地 tarball(保留查询参数完成鉴权),再用本地文件路径安装。
- 规避二:若直链无需鉴权,去掉查询参数后重试。
- 上游方向:讨论中给出的修法是在 URL 的路径部分识别扩展名、把查询串原样传给 pnpm(报告者的补丁通过了 120 个测试与 Web e2e)(#8898)。
DSH插件 排查注意事项
装卸类事故的第一原则是:先分清「装是装了没有」与「加载了没有」——这两件事在 DSH 里是分开的两步。 九条要点:
- 卡住时先查
node_modules与 profilepackage.json,确认安装阶段是否真的完成(#9261)。 - 进程不退出 ≠ 安装失败:区分「装完了没收尾」与「装到一半失败」(#9261)。
- 让 reconcile 跑一次:重启宿主是最省事且最有效的一步(#9261)。
- 「不兼容」先怀疑装到了旧版:检查实际安装的版本号,而不是照着错误去改
allow-version(#9178)。 - 同一把尺子两个标准:预检与安装走的是不同命令,这个不一致是本条根因(#9178)。
- lockfile 级校验会锁死整个 profile:报错点名的包可能根本不是你要装的(#9342)。
- 卸载前务必看一眼重解析点:这条命令花两秒,能避免一次仓库级事故(#9835)。
- 恢复要趁早且不要写盘:
.git/objects是恢复的命根子(#9835)。 - 修复状态如实:pnpm 不退出与发布时长门限的合并缺陷都已在上游新版本修复,但宿主捆绑的是旧版,所以当下仍需手工规避;
junction递归删除的层归属在讨论中未定论,官方未确认修复。
插件装卸是这套生态里最容易出状况的一环,所以更值得用一个专门的界面来降低变量。与其在各处手敲命令、事后靠 node_modules 反推状态,不如用 DSH Plugin Hub 这类可视化插件市场:安装前有确认弹窗展示来源与将执行的命令、已安装列表带来源标记与版本、更新与卸载各有确认步骤,出问题还能直接看系统日志。

来源:Discussion #9261、Discussion #9342、Discussion #9178、Discussion #9835、Discussion #8898。
常见问题
在 DeepSeek Harness 里,dsh plugin add 打完 Done in 卡住时,多数情况下依赖已经装好了,卡的是收尾。原因是宿主内置的 pnpm 版本存在「打印完成后进程不退出」的缺陷(上游已在较新版本修复),而 CLI 这条路径又没有传空闲超时、stdio 还是继承模式,所以超时兜底也够不着。处置办法是升级宿主内置 pnpm,或超时后结束该进程链并手动触发一次 profile reconcile。
这是 DeepSeek Harness 裸包名安装踩到 pnpm 发布时长门限的典型表现。pnpm 11 默认只接受发布满 24 小时的版本,于是裸包名解析到了上一版,而上一版往往已经不兼容当前 DSH,兼容检查就把这次安装拒掉并回滚。规避很简单:安装时显式指定版本(包名加 @ 版本号),或等满 24 小时后再装。
这条报错在 DeepSeek Harness 里意味着校验是对整个 lockfile 做的,而不只是你这次要装的包。只要 lockfile 里存在任何一条比门限更新的记录,该 profile 的所有安装与更新都会被锁死。规避是把最小发布时长配置成 0(命令行参数形式),或者把同一包重复写了多条排除项合并成一条(较新版本才支持合并,旧版本只取第一条生效)。
多数情况可以恢复,DeepSeek Harness 卸载这类插件时若跟随 junction 递归删除,.git 下的 objects 通常还在。恢复的做法是先在 .git 里重建 HEAD 与 config,再执行 git fsck --lost-found 找回对象并重置到分支。以后卸载这类插件前,先确认目标是不是重解析点,必要时只删链接本身、不删目标。
带查询参数的 tarball 地址装不上 DeepSeek Harness 插件,是因为插件管理器识别 tarball 地址时要求扩展名出现在结尾,而带查询参数的地址结尾是查询串。另一条匹配规则又会把这种两段路径当成 git 仓库地址,于是要么在安装前被拒,要么被当成 git 源去解析。规避是把带查询参数的文件先下载成本地 tarball 再安装,或手动把查询参数去掉后使用直链。
相关术语
- dsh plugin add
- dsh plugin add 是 DeepSeek Harness 的命令行插件安装入口,可指定 profile 与包名或来源。它在内部调用 pnpm 完成依赖解析与安装,因此 pnpm 的版本、配置与已知缺陷都会直接影响它的行为。— https://github.com/deepseek-ai/deepseek-harness/discussions/9261
- minimumReleaseAge
- minimumReleaseAge 是 pnpm 的一项供应链防护配置,表示只接受发布时长超过该阈值的版本(默认 24 小时),用来降低抢注与投毒风险。它作用于整个 lockfile,而不是仅作用于本次要安装的包。— https://github.com/deepseek-ai/deepseek-harness/discussions/9342
- profile reconcile
- profile reconcile 是 DeepSeek Harness 把 profile 声明与磁盘上实际安装状态对齐的过程,安装或卸载完成后由它把新的插件纳入运行时。安装进程异常退出时这一步不会执行,表现为「装上了但不加载」。— https://github.com/deepseek-ai/deepseek-harness/discussions/9261
- junction(目录联接)
- junction 是 Windows 上的目录重解析点,让一个目录路径指向另一个本地目录,创建时不需要特殊特权。link: 方式安装插件会在 node_modules 下产生这类链接,若卸载逻辑按普通目录递归删除,就会穿进链接目标,删掉插件源码仓库。— https://github.com/deepseek-ai/deepseek-harness/discussions/9835
来源
- #9261 — dsh plugin add 在 Windows 上挂起不退出,profile bundles 永不 reconcile· deepseek-ai(GitHub Discussions)
- #9342 — dsh plugin add 因 lockfile 里存在比 minimumReleaseAge 更新的条目而硬失败· deepseek-ai(GitHub Discussions)
- #9178 — 插件管理器裸包名安装受 pnpm 11 minimumReleaseAge 影响,静默降级到上一版并被判不兼容· deepseek-ai(GitHub Discussions)
- #9835 — Windows:卸载 link: 安装的插件会跟随 junction 递归删除,掏空插件源码仓库· deepseek-ai(GitHub Discussions)
- #8898 — Plugin Manager rejects or misclassifies HTTP tarball URLs with query parameters· deepseek-ai(GitHub Discussions)