中文 | English
一站式安装、升级与维护 DeepSeek Harness(DSH)——桌面壳与 web 共用同一个 ~/.dsh/runtime 引擎,一次升级两端同时生效,桌面打开即是当前 runtime 的最新插件。本工具把这套「单引擎双端」模型固化成可复用脚本,并解决升级后常见的破坏性问题(壳覆盖 runtime、pnpm 被安全删除守卫拦截、原生模块缺失与半安装状态)。
Runtime 安装默认禁止任意依赖执行 lifecycle script,仅在严格的名称、版本和脚本三重白名单下构建 DSH 启动必需的 native addon,并用当前 Node/CPU ABI 实际加载验证。升级前会建立可恢复事务;安装失败、版本不符、native 验证失败、Ctrl-C 或终端中断都会恢复旧 Runtime。
本工具保证的理想状态
- 双端单引擎:桌面壳与 web 都软链到同一个
~/.dsh/runtime,不存在两份独立副本。- 一次升级、两端同步:
dsm update升的是 runtime,桌面与 web 同时生效。- 桌面即最新:插件 profile 两端共用
~/.dsh/profiles,打开桌面就是当前 runtime 的最新插件。- 壳更新不破防:
dsm doctor检验,出问题dsm pin一条命令修回。
DSH Desktop 的打包形态换过一次:≤ 2.0.5 是「共享安装」的壳(App 包不内嵌完整的 @deepseek-ai/dsh*,依赖 ~/.dsh/runtime 提供上游 Harness);≥ 2.0.7 改为「自包含」(整棵 node_modules 打进 app.asar,壳跑自己的 dsh 副本)。两种形态各有长期痛点:
- 壳与 runtime 的关系是隐式的 —— ≤2.0.5:桌面启动时
healProfilesModuleFallback()会按 App 包的依赖闭包把~/.dsh/profiles/node_modules/@deepseek-ai/*重新软链接,一旦壳把 dsh 又塞回 App 包,你的 runtime 升级与补丁就被静默覆盖;≥2.0.7:壳自带副本与 CLI runtime 是两份独立副本,两者的版本偏差不会自己暴露(壳不会崩,但两端行为与插件 API 面可能不同),而且升级 runtime 不再影响壳。 - pnpm 升级/装插件被拦截 —— 如果你在 WorkBuddy / CodeBuddy 之类宿主的终端里启动
dsh web,宿主注入的CODEBUDDY_SAFE_DELETE_*环境变量会让 pnpm 清理临时目录时触发批量删除确认(SAFE_DELETE_BULK_CONFIRM_REQUIRED),非交互环境直接失败。
本工具包把上述问题的可靠解法固化成可复用脚本。
升级 DSH 前先看这一节:哪些插件在哪个 DSH 版本上会"不适配"。标注 scan/pin 提前发现或等上游修复。
| 插件(包名) | 不适配的 DSH 版本 | 现象 | 根因 | 状态 / 解法 |
|---|---|---|---|---|
@liustack/modlens |
≤ 3.23.0 |
rc.2 强制 prepareCall 时旧版本会崩 |
rc.2 引入 adapter API 变更,旧 modlens 未实现 prepareCall |
✅ modlens ≥ 3.23.x 已原生修复,直接升级即可,无需补丁 |
| 任意第三方(极老旧)LLM adapter 插件 | 未跟进 rc.2 接口契约 | 可能报 adapter.<method> is not a function |
rc.2 统一了 adapter 接口契约,极老旧插件未跟进 | bin/scan-adapters.mjs 扫描已装 adapter 的 dsh 版本范围;缺方法就升级该插件到支持 rc.2 的版本 |
@deepseek-ai/dsh 本体 |
0.1.1-rc.2(配合旧壳) |
壳更新后 runtime 被静默覆盖、补丁丢失 | 壳内重新捆绑 dsh,heal 闭包把 profiles 软链指回壳 | ✅ 用 pin-runtime.sh 钉死 runtime 权威 |
fs-ext@2.1.1 |
0.1.3-alpha.1 源码安装 |
启动时报 Cannot find module './build/Release/fs_ext.node' |
安装器为供应链安全禁用了 lifecycle script,却未单独构建必需 native addon | ✅ 自动白名单构建、加载验证与失败回滚;既有异常安装可运行 dsm repair-native |
桌面壳(DSH Desktop.app) |
任意 runtime 升级后 | 打包形态随版本变化(≤2.0.5 共享软链 / ≥2.0.7 自包含),错位表现为壳启动崩溃或两端版本偏差 | ≤2.0.5 壳经软链共用 runtime,清单缺口 / API 代差都会让壳启动即崩;≥2.0.7 壳自带 dsh 副本,升级 runtime 不改变壳行为 | ✅ dsh-manage.sh shell 单独升级壳;dsm check 按模式报告(清单缺口 / API 冲突 / 版本偏差) |
读表要点:
- DSH 版本 指
~/.dsh/runtime里@deepseek-ai/dsh的版本(dsm status可查);壳 App 版本是另一回事,两者已解耦。 - rc.2 曾是一次破坏性 adapter 接口变更(每个 LLM adapter 须实现
prepareCall等新方法),但主流插件@liustack/modlens已在 ≥ 3.23.x 原生修复;其余老旧插件用bin/scan-adapters.mjs扫描即可提前发现兼容风险。
DSH Desktop 的打包形态换过一次(≤ 2.0.5「共享软链」→ ≥ 2.0.7「自包含」),本工具包按模式区别处理。
模式 A:共享软链(Desktop ≤ 2.0.5)
┌─────────────────────────────────────────────────────────────┐
│ DSH Desktop.app(壳) │
│ app.asar.unpacked/node_modules/@deepseek-ai/* ──软链──┐ │
└───────────────────────────────────────────────────────────┼──┘
│ (pin-runtime.sh 钉死)
▼
┌─────────────────────────────────────────────────────────────┐
│ ~/.dsh/runtime/node_modules/@deepseek-ai/* ◄── 权威来源 │
│ (你用 pnpm 升级、打补丁的地方) │
└─────────────────────────────────────────────────────────────┘
│ heal 自愈顺链而下
▼
┌─────────────────────────────────────────────────────────────┐
│ ~/.dsh/profiles/node_modules/@deepseek-ai/* ──软链──► runtime│
│ (桌面启动时由 dsh-app-boot heal 解析到这里) │
└─────────────────────────────────────────────────────────────┘
pin-runtime.sh 把 App 包 / profiles 的 @deepseek-ai/* 都软链接到 runtime,使 heal 的解析永远落到 runtime —— runtime 始终权威,壳更新盖不到。
模式 B:自包含(Desktop ≥ 2.0.7)
┌──────────────────────────────────────────────────────────────┐
│ DSH Desktop.app(壳) │
│ app.asar 正文内自带 node_modules/@deepseek-ai/*(约 167 MB) │
│ → 壳跑自己的 dsh 副本(2.0.7 实测 = 0.1.5-rc.1) │
└──────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────┐
│ ~/.dsh/runtime(CLI / Web 侧) ◄── 仍由 pin-runtime 钉死 │
└──────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────┐
│ ~/.dsh/profiles/node_modules/@deepseek-ai/* ──软链──► runtime │
│ (Web / CLI 启动时解析到这里) │
└──────────────────────────────────────────────────────────────┘
壳不再经软链共享 runtime,于是:
pin-runtime.sh跳过改造 .app —— 打包条目优先,改软链无效;而且改.app资源会破坏代码签名与更新校验。它只钉 profiles。- 关注点从「清单缺口」变成版本偏差:壳自带 dsh 版本 vs CLI runtime 版本。
dsm check会同时报出两者,不一致时给出告警与对齐建议(升级壳,或把 runtime 退到壳的基线版本)。 - 壳与 CLI 是两份独立副本:升级 CLI runtime 不会改变壳的行为,回退 runtime 也无法「修好」壳——版本偏差只能靠升级壳解决。
dsh-setup-manager/
├── README.md / README.en.md
├── LICENSE # MIT
├── .gitignore
├── bin/
│ ├── dsh-manage.sh # 统一管理:runtime/壳升级 · web · status · doctor · rollback · scan · check
│ ├── pin-runtime.sh # 钉死 runtime 权威(共享软链模式下改写壳/Profile 软链 → runtime)
│ ├── app-mode.sh # 判定壳的打包模式(shared 共享软链 / packed 自包含),被上面两个脚本共用
│ ├── check-desktop.mjs # 静态预检壳与 runtime 的兼容性(asar 清单差集 + 应用代码 API 导入 + 版本偏差)
│ ├── verify-heal.mjs # 校验 heal 后关键包是否仍解析到 runtime
│ ├── check-native-addons.mjs # 检查/白名单修复启动必需的 native addon
│ ├── scan-adapters.mjs # 扫描已装 LLM adapter 与 runtime dsh 版本的兼容性
│ └── scan-plugin-api.mjs # 静态比对插件对 runtime 的 API 导入,预检启动会崩的冲突
└── tests/ # 回归测试(bash 驱动,含 mock DSH 树与 asar 夹具)
两种方式都会得到一个 dsh-setup-manager/ 目录(内含 bin/dsh-manage.sh)。clone 或解压到任意位置均可。
# 方式一:git clone(推荐)
git clone https://github.com/TOBYCAI/dsh-setup-manager.git
cd dsh-setup-manager
chmod +x bin/*.sh
# 方式二:从 Releases 下载源码包(解压后同样是 dsh-setup-manager/ 目录)
# 在 https://github.com/TOBYCAI/dsh-setup-manager/releases 下载 dsh-setup-manager-src.zip
unzip dsh-setup-manager-src.zip
cd dsh-setup-manager
chmod +x bin/*.sh脚本通过 DSH_HOME 等环境变量适配你的实际路径,无需硬编码;下文别名把路径换成你实际 clone / 解压的位置即可。
把下面一行加进你的 shell rc(~/.zshrc 或 ~/.bashrc),之后所有命令都能用 dsm 代替 bin/dsh-manage.sh:
# 把路径换成你实际 clone / 解压 toolkit 的位置
echo 'alias dsm="bash $HOME/dsh-setup-manager/bin/dsh-manage.sh"' >> ~/.zshrc
source ~/.zshrc挂上后,下文示例都可简写为 dsm status / dsm update / dsm web / dsm doctor …。
上面"安装"指的是安装这套工具包。如果你要在一台还没装过 DSH 的机器上从零装好 DSH(桌面壳 + web runtime),直接用 install 子命令,它会:
- 按平台从 DSH Desktop 的 GitHub Releases 下载并安装桌面壳(macOS
.app/ LinuxAppImage·tar/ Windows.exe,Linux/Windows 壳安装标注未经真机验证); - 引导 web runtime:在
~/.dsh/runtime用 pnpm/npm 安装@deepseek-ai/dsh(与update-runtime同一机制,只是从零建目录),并在~/.dsh/bin/dsh建好软链; - 自动
pin(钉死 runtime 权威)+doctor(自检接管)。
# 一键双端安装(壳 + runtime + pin + doctor)
dsm install
# 指定 runtime 版本 / 只装一端 / 先预览
dsm install --runtime 0.1.1-rc.2
dsm install --no-shell # 只引导 runtime(壳已手动装好)
dsm install --no-runtime # 只装桌面壳
dsm install --dry-run # 只报告将做什么,不改动
⚠️ runtime 引导的目录布局依赖 DSH 上游约定,macOS 上已验证可用;换机器若doctor报 FAIL,按输出手动修正后重跑pin即可。dsh web前请把export PATH="$HOME/.dsh/bin:$PATH"加入你的 shell rc(脚本安装完会提示)。
安装完即进入维护模式:之后所有升级与自检都复用同一套命令——dsm install(已装则跳过)/ dsm update(升 runtime)/ dsm shell(升壳)/ dsm web(启动)/ dsm doctor(自检)/ dsm rollback(回滚)/ dsm cleanup(清理备份)/ dsm scan(升级前查兼容)/ dsm check(定时报告)。一台机器只需 dsm install 一次。
健壮性说明:status / check / scan / doctor 等只读命令在 DSH 尚未安装、dsh 不在 PATH 或更新服务暂时不可达时也不会静默中断——版本探测失败只降级为「未知」/ 优雅报告。doctor 除了版本无关的软链检查,还会真实加载启动必需的 native addon,发现缺失或 Node ABI 不匹配时给出 repair-native 修复入口。已装版本优先读 ~/.dsh/runtime/.../dsh/package.json,缺失时回退 dsh --version。
# 1) 首次 / 壳更新后:把 runtime 钉死为权威
dsm pin
# 2) 升级 runtime(交互确认 @next / @latest 各自版本)
dsm update
# 或单步非交互升级到指定版本:
dsm update-runtime 0.1.1-rc.2
# 或从官方 GitHub 源码构建安装(npm 尚未发布的版本,如 alpha;缺省探测最新 dsh-v* tag):
dsm update-src
dsm update-src 0.1.2-alpha.1
# ⚠ 升级前会先显示本次的空间预估:npm 渠道报包本体大小、官方依赖数量、
# 本机现有 runtime 实际占用参照与 pnpm store 提示;源码渠道报源码缓存
# (已缓存则复用,不再克隆)或构建后可达 1-2 GB 的提示;壳渠道报 dmg
# 下载量与备份占用。磁盘余量不足(预估 2 倍)时提示先 dsm cleanup。
# 3) 升级桌面壳(dsh-manage.sh 自动从 DSH Desktop 的 GitHub Releases 下载 universal dmg,备份后替换)
dsm shell
# 4) 启动 web(自动卸载 safe-delete 守卫,避免 pnpm 被拦截)
dsm web
# 5) 校验 heal 后关键包仍指向 runtime
node bin/verify-heal.mjs
# 查看当前版本与可用更新
dsm status
# 升级前先扫描已装 adapter 与 runtime 的兼容性(含插件对 runtime API 导入的静态预检,预测启动会崩的冲突)
dsm scan
# 仅报告模式(可挂 crontab 每天自检,不改动任何东西);含插件与 Desktop 两类兼容性检查:
# - 插件 API:插件 import 的命名导出在 runtime 中是否存在(缺失则 dsh web 启动会崩)
# - Desktop 兼容性:Desktop 的 asar 清单与 runtime 包集合差集 + 应用代码 import 的
# 命名导出是否仍存在(runtime 升级后 Desktop 启动即崩的两类故障都能提前暴露)
dsm check --cron
# 自检当前环境(软链 / 备份 / 守卫 / 版本)
dsm doctor
# 只重建经过审计的必需 native addon,并立即验证可加载性
dsm repair-native
# 从最近的备份回滚:runtime / shell / all
dsm rollback runtime
# 交互式清理备份(列出 bundle-bak-*/shell-bak-*,选择删除 / 保留;--dry-run 仅查看)
dsm cleanup
dsm cleanup --dry-run
# 升级前预览将变更的依赖树,不实际执行
dsm update --dry-run| 子命令 | 作用 | 是否改文件 |
|---|---|---|
install |
首次安装:下载桌面壳 + 引导 runtime + 自动 pin + doctor | 写(首次安装) |
status |
显示 runtime / 壳 / 守卫变量 / 已装 adapter 版本 | 只读 |
update [--dry-run] |
升级 runtime(--dry-run 仅预览依赖树变更) |
写(dry-run 只读) |
update-runtime <ver> |
单步非交互升级 runtime 到指定版本 | 写 |
update-src [<ver>] |
从官方 GitHub 源码构建安装(npm 未发布时可用;缺省探测最新 dsh-v* tag) | 写 |
shell |
升级桌面壳(下载备份替换;Linux/Windows 框架已就位,标注未验证) | 写 |
web |
启动 web(自动卸载 safe-delete 守卫;启动前静态预检插件↔runtime API 冲突,命中则阻止启动,--force 可绕过) |
启动进程 |
scan |
扫描已装 LLM adapter 与 runtime dsh 版本的 semver 兼容范围;并静态比对插件对 runtime 的 API 导入,预检可能导致启动崩溃的冲突 | 只读 |
check [--cron] |
仅报告模式自检(可挂定时任务),含插件与 Desktop 兼容性 | 只读 |
doctor |
自检软链指向 / 备份目录 / 守卫 / 版本(备份列出真实路径与大小) | 只读 |
repair-native |
白名单重建并加载验证 Runtime 启动必需的 native addon;拒绝未知版本或被篡改的安装脚本 | 写 |
rollback [runtime|shell|all] |
从 bundle-bak-* / shell-bak-* 还原 |
写 |
cleanup [--dry-run] |
交互式清理备份:bundle-bak-* / shell-bak-* / runtime-src 源码缓存(在用版本受保护,不可删) |
写(dry-run 只读) |
| 变量 | 默认 | 说明 |
|---|---|---|
DSH_HOME |
$HOME/.dsh |
DSH 数据根目录 |
DSH_APP |
/Applications/DSH Desktop.app |
壳 App 路径(macOS) |
DSH_APP_PKG |
自动探测 | 壳内 @deepseek-ai 目录(pin-runtime 用) |
DSH_PATCH_YML |
$DSH_HOME/patches/enable-skills.yml |
web 启动的 --patch 文件 |
DSH_PNPM |
自动探测 | 指定 pnpm 可执行文件 |
你在 WorkBuddy / CodeBuddy 终端里启动了 dsh web,宿主注入的 CODEBUDDY_SAFE_DELETE_* 让 pnpm 清理临时目录(>50 文件)需确认但无法确认。解法:用 dsm web 启动,它会 env -u 卸载这些守卫变量(详见脚本注释)。
重跑 dsm pin 重新钉死。bundle-bak-<时间戳>/ 保留被替换的真实目录,可据此回滚到「壳自带版本」。
- macOS:全部功能(壳升级走
.app+hdiutil)。 - Linux:
update/pin/web/verify-heal/scan/doctor/check全可用;壳升级框架已就位(下载 tarball 备份替换),未经真机验证,建议先手动确认 Release asset 命名。 - Windows:核心逻辑同 Linux(
shell走.exe安装包框架),未经真机验证。
MIT © TOBYCAI