DSH 核心升级与插件冲突排查排障记录

一次从”老版本跑不起来”到”极简环境彻底清净”的完整复盘。五个报错看着各不相干,最后都指向同一条主线:核心太旧、环境太乱、插件太多。

一、背景:一个旧环境,两个隐藏的雷

这次升级之前的机器,状态是这样的:

  • Node 用 fnm 管理,停在 v22.23.4;
  • DSH 全局装的是 0.1.2-alpha.1;
  • 系统里还留着一个指向 macOS 桌面版 App 的 dsh 软链接(/usr/local/bin/dsh)。

表面看着能用,实际埋了两个雷:一个是核心版本太旧,一个是终端里敲 dsh 执行的其实不是你装的那个新版。

触发问题的导火索很普通:想用 npx 跑最新版,或者想加载个第三方插件,结果模块丢失、版本冲突一起冒出来。

目标就一个:把 DSH 本体从 0.1.2-alpha.1 升到 0.2.2-rc.1,Node 升到 v24.22.2,把僵尸配置清干净,让 Web 端能稳定起来。

二、五个报错,一条主线

1. 模块缺失:failed to import loader entry ... missed the module table

原因:正在跑的 DSH 核心还是 0.1.2-alpha.1,而插件依赖的新模块(比如 @deepseek-ai/dsh-client-store)是更晚的版本才引入的。核心里压根没有这张模块表,插件自然 import 不到。

解决:根子上的办法只有一个——升级 DSH 核心。但这一升,就牵出了后面一连串环境问题。

2. 环境混乱:EBADENGINE 和指向桌面版的幽灵路径

原因有两个:

  • 全局旧 DSH 跑在 Node 22 下,新版 DSH 要求更高的 Node 引擎,引擎对不上,报 EBADENGINE;
  • 终端里敲 dsh,系统实际执行的是 /usr/local/bin/dsh(还指着旧的桌面版 App),而不是 npm 全局装的新版——你以为升级了,其实一直在跑旧的。

解决:

  • 用 fnm install 24 把 Node 升上去;
  • 找到新版的真实路径(~/.local/share/fnm/node-versions/v24.22.2/installation/bin/dsh);
  • 强制覆盖旧软链接:sudo ln -sf <新版真实路径> /usr/local/bin/dsh。

3. 依赖被拦:ERR_PNPM_MINIMUM_RELEASE_AGE_VIOLATION / ERR_PNPM_IGNORED_BUILDS

原因:pnpm v11 以后默认开了供应链安全保护。它不装”发布不足 24 小时”的包,也不许未经许可就跑底层原生编译脚本(比如 node-pty、cloudflared)。

解决:

  • 安装/修复依赖时,强制绕过发布时间限制,加 --config.minimum-release-age=0;
  • 放行原生编译脚本:编辑 ~/.dsh/profiles/web/pnpm-workspace.yaml,加 allowBuilds: 列表;或者 npm 全局安装时追加 --allow-scripts=... 参数。

4. 配置里的”幽灵包”(Zombie Bundles)

原因:反复用 npx 启动、或者网络波动中断,导致插件的依赖被删了,但 package.json 的 dsh.profile.bundles 或者 cordis.patch.yml 里还留着加载指令。启动时按配置去找包,找不到,直接崩。

解决:用社区脚本 dsh-fix-duplicate-loader-id 把重复的 insert 转成幂等的 id patch;或者直接手动编辑 cordis.patch.yml,删掉报错的那个包名。

5. 第三方全家桶冲突:duplicate loader entry id: dsh-market

原因:装了一堆第三方 UI 全家桶(比如 @linxin666/dsh-web-all)和实验性功能包,它们内部的依赖互相打架,或者调了已经废弃的旧版 API。

解决:做减法。 把引发崩溃的插件(比如 dsh-agents、pua、mnemon 还有第三方任务板)逐个或整体卸载:

1
dsh plugin --profile web remove <包名>

每卸载一个就立刻 dsh web 测一次,直到能正常起来为止。

三、从零到可用的完整步骤

第一步:升级 Node 环境(fnm)

1
2
3
fnm install 24
fnm use 24
fnm default 24

第二步:启用 pnpm,升级 DSH 核心

1
2
3
4
corepack enable pnpm

# 放行底层原生编译脚本,全局安装最新版
npm install -g @deepseek-ai/dsh@latest --allow-scripts=@deepseek-ai/dsh-subprocess-local,koffi,node-pty,@google/genai,protobufjs

第三步:修正系统软链接

1
2
# 强制覆盖指向旧 App 的软链接
sudo ln -sf ~/.local/share/fnm/node-versions/v24.22.2/installation/bin/dsh /usr/local/bin/dsh

第四步:修复 profile 依赖、清理僵尸插件

1
2
3
4
5
# 安装 profile 依赖(绕过发布冷却期)
dsh plugin --profile web install --config.minimum-release-age=0

# 卸载导致崩溃的冗余/过期插件
dsh plugin --profile web remove <报错的插件包名> --config.minimum-release-age=0

四、几条踩出来的经验

  1. 放弃 npx,统一用全局命令。 npx 会生成一堆临时缓存(~/.npm/_npx),还特别容易和常驻的 Web Server 撞端口、混版本。统一 npm install -g,省心。

  2. 警惕第三方”全家桶”。 dsh-web-all 这种聚合包装着省事,但只要里面某个子依赖和新版 DSH 不兼容,整个 DSH 就起不来。按需单独装单项插件,别图省事。

  3. 认识 DSH 的”双配置文件”。 package.json 里的 dsh.profile.bundles 控制装哪些包,cordis.patch.yml 控制插件树怎么插入。卸载插件时,两处都要清干净,只清一处就是埋雷。

  4. PNPM 的供应链策略要会绕。 开发调试阶段,只要来源可信,记得加 --config.minimum-release-age=0,否则刚发布的新版本插件永远装不上。

  5. 善用排障工具。 dsh-fix-duplicate-loader-id 能救命,遇到 loader 冲突先拿它 --dry-run 体检一遍。

五、最终能用的极简环境

  • Node:v24.22.2(fnm)
  • DSH 核心:v0.2.2-rc.1(npm 全局)
  • pnpm:Corepack 代理
  • 启动命令:dsh web

小注:如果之后终端偶尔还报”任务板拿不到 session”,Ctrl+C 停掉服务,执行 dsh plugin --profile web remove @linxin666/dsh-client-ui-task-board --config.minimum-release-age=0 就彻底清净了。


折腾归折腾,这一路的坑其实很有代表性:核心版本、Node 引擎、软链接、依赖缓存、插件配置,任何一个环节错位,都能让一个本来能用的东西说崩就崩。把它们串起来看,排查思路反而清晰了——先确认你跑的到底是不是你以为的那个版本,再做减法。