一键升级 DSH 并增量同步官方文档,随时检索
33.6/100
综合分
5
GitHub 星标
216
npm 周下载
—/5
用户评分
数据截至 2026/9/19 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。
安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add dsh-updater-npmnpm 源 · dsh-updater-npm 的发布者账号已校验与本仓库一致
状态速览
判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态信任档位:已验证
是什么
dsh 原生插件 · dev
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
更新放缓:最近一次提交在 46 天前
档位由哪些信号合成
本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。
社区实装还没有人打卡 · 装过的第一个就是你
安装兼容性检查✓ 自动检查通过展开查看检查明细 ↓
以下结论由程序自动检查 npm 包、engines 声明与入口文件得出,未做人工实机验证——能装不等于用着没问题。
✓npm 包dsh-updater-npm @ 1.13.4
✓Node 引擎要求 >=20 · 基线 Node 22.19 满足
✓dsh CLI 依赖未声明 dsh 版本约束
✓入口文件main/exports/bin 已声明
验证方式:npm registry 存在性 + package.json 静态校验 · 最后检查 2026/10/28 17:42:28
安全扫描自动 · 静态分析源码v2.7.0🔵 扫描:含敏感能力尚无人工评估展开查看扫描说明与证据 ↓
安全扫描自动 · 静态分析源码
v2.7.0静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。
可执行外部命令读写/删除本地文件写入剪贴板(不计风险)发起外部网络请求
查看扫描证据(共 94 处,展示前 33 条)
- 读取本地文件lib/index.js:324
return JSON.parse(readFileSync(file, 'utf8').replace(/^ FEFF/, '')) - 写入本地文件lib/index.js:332
mkdirSync(dirname(file), { recursive: true }) - 写入本地文件lib/index.js:333
writeFileSync(file, JSON.stringify(value, null, 2), 'utf8') - 写入本地文件lib/index.js:338
mkdirSync(dirname(file), { recursive: true }) - 写入本地文件lib/index.js:339
writeFileSync(file, text, 'utf8') - 读取本地文件lib/index.js:406
const installedIn = (name) => existsSync(join(profilesRoot, name, 'node_modules', SELF_PACKAGE_NAME, 'package.json')) - 读取本地文件lib/index.js:410
const inShared = () => existsSync(join(profilesRoot, 'node_modules', SELF_PACKAGE_NAME, 'package.json')) - 读取本地文件lib/index.js:473
for (const name of readdirSync(dir)) { - 读取本地文件lib/index.js:475
const t = statSync(join(dir, name)).mtimeMs - 读取本地文件lib/index.js:551
try { text = readFileSync(file, 'utf8') } catch (e) { return null } - 发起网络请求lib/index.js:595
const res = await fetch(url, { ...opts, signal: AbortSignal.timeout(timeoutMs) }) - 删除本地文件lib/index.js:766
try { if (existsSync(target)) rmSync(target) } catch (e) { } - 发起网络请求lib/index.js:1170
const res = await fetch(registryBase() + '/@deepseek-ai/dsh/latest', { signal: AbortSignal.timeout(15000) }) - 写入本地文件lib/index.js:1226
try { mkdirSync(logDir, { recursive: true }) } catch (e) { } - 执行外部命令来自配置/变量lib/index.js:1235
child = spawn(cmd.argv[0], shellArgv(cmd.argv.slice(1), useShell), { - 执行外部命令固定命令lib/index.js:1383
child = spawn('git', args, { cwd: dir, stdio: ['ignore', 'pipe', 'pipe'], shell: false }) - 执行外部命令来自配置/变量lib/index.js:1487
const probe = spawn(process.platform === 'win32' ? 'where' : 'which', [name], { stdio: 'ignore' }) - 执行外部命令来自配置/变量lib/index.js:1600
child = spawn(argv[0], shellArgv(argv.slice(1), useShell), { - 发起网络请求lib/index.js:1693
const res = await fetch(url, { signal: AbortSignal.timeout(10 * 60 * 1000) }) - 写入本地文件lib/index.js:1696
writeFileSync(dest, buf) - 删除本地文件lib/index.js:1717
try { rmSync(tmp, { force: true }) } catch (e) { } - 删除本地文件递归lib/index.js:1873
try { rmSync(dir, { recursive: true, force: true }); count += 1 } catch (e) { } - 删除本地文件lib/index.js:1879
try { rmSync(full, { force: true }) } catch (e) { } - 删除本地文件递归lib/index.js:1898
rmSync(item.dir, { recursive: true, force: true }) - 删除本地文件递归lib/index.js:1972
try { rmSync(d.path, { recursive: true, force: true }); files += 1 } catch (e) { } - 执行外部命令来自配置/变量lib/index.js:2452
const child = spawn(process.execPath, [boot, script, logFile, token], { stdio: 'ignore', detached: true, shell: false }) - 发起网络请求client/client.js:385
return fetch("/dsh-updater-npm/check?uilang=" + uiLang(), { cache: "no-store", signal: AbortSignal.timeout(30000) }) - 发起网络请求client/client.js:389
return fetch("/dsh-updater-npm/update?uilang=" + uiLang(), { method: "POST", cache: "no-store", signal: AbortSignal.timeout(600000) }) - 发起网络请求client/client.js:393
return fetch("/dsh-updater-npm/progress", { cache: "no-store", signal: AbortSignal.timeout(10000) }) - 发起网络请求client/client.js:499
fetch("/dsh-updater-npm/update-source?uilang=" + uiLang(), { method: "POST", cache: "no-store", signal: AbortSignal.timeout(600000) }) - 发起网络请求client/client.js:523
fetch("/dsh-updater-npm/install-pwsh?uilang=" + uiLang(), { method: "POST", cache: "no-store", signal: AbortSignal.timeout(600000) }) - 发起网络请求client/client.js:537
fetch("/dsh-updater-npm/repair?uilang=" + uiLang(), { method: "POST", cache: "no-store", signal: AbortSignal.timeout(600000) }) - 写入剪贴板client/client.js:558
navigator.clipboard.writeText(text).then(mark, function () { })
扫描于 2026/11/3(1 天前) · 范围 入口闭包 + 全包扫描 · 来源 npm 发布包
静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。
给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个
界面预览
1 张 · 点击查看大图功能亮点
- 一键升级DSH
- 增量同步官方文档
- 检索与阅读文档
DSH 更新器加官方文档同步插件:一键 npm 更新并实时显示进度,增量同步文档并显示进度,提供 dsh_docs_search/read 工具。
最近更新
在 GitHub 查看全部 →v1.13.42026-09-19
· 跨 DSH 环境审计:修掉 7 处缺陷
针对「换一台 DSH 环境还能不能用」做了一轮审计,查出七处缺陷,全部修复。每一处都在真实部署上
复现并验证过,不是靠读代码推断的。
· 1. 设置页导航的小红点永远不刷新
locale.register(ns, dicts) 这个两参形式,会把第二个参数的键当作 locale id、按
dsh-client-locale 的 LOCALE_ID_PATTERN(/^[A-Za-z]{2,8}(-[A-Za-z0-9]{1,8})*$/u)校验。
插件传的是合成的 "~nav…" 键,过不了校验 → register() 在 publish() 提升 revision 之前
就抛错,而外层的 catch 把异常吞了。壳程序只在 slot 版本号或 locale revision 变化时才重算导航行,
所以红点既不出现也不消失。现改用三参形式 + 唯一命名空间。
· 2. locateInstall() 的兜底是死代码
旧实现按「preset 路径以 /agent-presets//agent.cordis.yml 结尾」反推安装目录,但真实布局是:
· 随包发布:/node_modules/@deepseek-ai/dsh-agent-presets/presets//agent.cordis.yml
· 用户自建:/.agent-presets//agent.cordis.yml
两者都不含 /agent-presets/,后缀判定恒为 false。也就是说只要 argv[1] 不是 bin.js
(打包 / 嵌入式 / 被包装启动),更新、修复、重启、文档工具就全部没有兜底路径。
现改为解析 @deepseek-ai/dsh/package.json 再 realpath。锚点必须是 profile 目录而不是
import.meta.url:link: 或 junction 安装时 Node 会把模块 URL 改写成包的真实路径,
那里根本没有 node_modules —— 这一版先写错过,是实机探针打出 locateInstall() = null 才发现。
· 3. buildRestartScript() 收 platform 参数却用宿主的 path.join
生产上只传 process.platform,所以一直看不出来。但在 Windows 上生成 POSIX 脚本时整条路径会被
翻成反斜杠(连开头的 / 都变成 \),脚本随即判 staging incomplete 并整个跳过交换。
现按参数选用 posix.join / win32.join。
· 4. 暂存包校验的 60 秒上限没生效
verifyStagedBinary() 把数字 60000 当第三个参数传,而该参数早已改成 limits 对象;
(60000).idleMs 是 undefined → 静默回落到 5 分钟 / 60 分钟默认值,暂存包半坏时这一步能挂满一小时。
· 5. 升级备份把凭据副本摊在默认可读权限下
快照会复制 .credentials.yaml 与整个 sessions/。备份目录用默认 umask 创建(POSIX 上通常
0755),同机其他账号可以直接读走凭据副本。备份根与新目录都收紧到 0700。
· 6. registry 被硬编码成 registry.npmjs.org
配了镜像站或企业内网 registry 的用户,插件去 npmjs 查版本、npm 自己却从镜像装包:轻则慢,
重则在墙内直接报「registry unreachable」,而更新本来能成功。现按
env → ./.npmrc → ~/.npmrc → 默认 解析,取到的值不是 http(s) URL 就继续往下走。
· 7. resolveProfileName() 在共享安装布局下判断错误
插件只落在共享的 profiles/node_modules(hoisted 或部分 pnpm 布局)时,逐 profile 探测全部为假,
函数会把 argv 里已确定的 profile 丢掉、退回硬编码的 'web',自更新命令就可能指向错误的 profile。
· 另外修掉的一处(不限于环境问题)
借 shell 启动时 Node 只把参数用空格拼接、不做转义,含空格的参数会被 cmd.exe 拆开。
--prefix 与 --logs-dir 都落在用户目录下,所以凡是用户名带空格的 Windows 环境
(C:\Users\John Smith\…)都会踩到:npm 报 Unknown command: "Smith\…"。已统一补上转义。
· 验证
v1.13.32026-09-18
· 仅文档 + 元数据改动(无代码变更)
· README 加入截图
设置页两张卡片的截图(取自正在运行的 v1.13.2 构建):可见当前卡片形态——没有运行日志面板、
没有激活地址行,并且带「插件版本 1.13.2」一行与「上次重启:已交换并启动 v0.1.5-rc.2」。
· 文件:docs/dsh-update-card-1.13.png(593×565);旧截图 docs/dsh-update-card.png
(图中还是已移除的日志面板)已删除。
· 文件名里带版本号是故意的:GitHub raw CDN 对同名文件有缓存,覆盖后一段时间仍会返回旧图。
· 中英两份 README 均在卡片列表下方引用(绝对 raw URL,npm 页面同样能显示)。
· 顺带修掉的元数据
package.json 的 description 还写着 v1.12.3 已移除的「live scrolling log panel」,
已改为如实描述:实时进度 / 空闲超时(慢链路不会被杀)/ 暂存交换与可清理的回滚点 / 文档增量同步。
· 一次自我纠错
v1.13.3 第一次打 tag 时漏了改版本号(提交里仍是 1.13.2),发布工作流以
E403 You cannot publish over the previously published versions: 1.13.2 失败;
已删除该 tag、补齐版本号与 description 后重新打 tag 发布。
v1.13.22026-09-18
· 仅文档改动(无代码变更)
修掉 npm 页面显示中文 README 的问题。
· 根因
npm 挑包 readme 的逻辑是 glob('{README,README.*}') 后取第一个「像 markdown」的命中项:
· 该 glob 在根目录同时命中 README.md 与 README.zh.md,且 README.zh.md 排在前面;
· 判定正则 /\.m?a?r?k?d?o?w?n?$/i 是非锚定的,结尾的 .zh.md 也能命中。
两者叠加 ⇒ 1.13.1 发布出去的包 readme 是中文那份(包内 README.md 本身是英文,只是没被选上)。
· 修法
中文文件改名 README_zh.md(下划线),不再匹配 {README,README.*};实测 glob 结果只剩
README.md,npm 现在发布英文 README。两个 README 顶部都留了 HTML 注释说明下划线不能改回点号。
仓库侧默认 README 与语言切换链接保持不变:README.md(英文) ⇄ README_zh.md(中文)。
数据来自仓库的 GitHub Release,可能与插件实际版本不一致。
README
由 DeepSeek 最新模型翻译生成dsh-updater-npm
英文 |
用于 DeepSeek Harness (dsh) 的 DSH 更新器 + 官方文档同步插件。
设置中有两张卡片:
- DSH Update (npm) — 检查 npm 上最新的 @deepseek-ai/dsh 并一键更新,带实时进度,以及一个执行分阶段替换的“Restart DSH”按钮。
- DSH Docs (official) — 增量同步 deepseek-ai/deepseek-harness 的 docs/ 到 $DSH_HOME/docs-sync/(按 GitHub blob sha 跳过未更改的文件),带进度条,并注册 dsh_docs_search / dsh_docs_read 模型工具。
设置中的 DSH Update 和 DSH Docs 卡片
安装 · 用法 · 运行模式 · 升级安全网 · 路由 · 许可证
安装
从 npm 安装(推荐)
dsh plugin --profile web add dsh-updater-npm
或从 GitHub 安装
dsh plugin --profile web add github:SiriusWJ/dsh-updater-npm
之后重启 dsh web;“DSH Update”和“DSH Docs”卡片会出现在设置中。
展开完整文档(README 剩余 1 节)
dsh-updater-npm
英文 |
用于 DeepSeek Harness (dsh) 的 DSH 更新器 + 官方文档同步插件。
设置中有两张卡片:
- DSH Update (npm) — 检查 npm 上最新的 @deepseek-ai/dsh 并一键更新,带实时进度,以及一个执行分阶段替换的“Restart DSH”按钮。
- DSH Docs (official) — 增量同步 deepseek-ai/deepseek-harness 的 docs/ 到 $DSH_HOME/docs-sync/(按 GitHub blob sha 跳过未更改的文件),带进度条,并注册 dsh_docs_search / dsh_docs_read 模型工具。
设置中的 DSH Update 和 DSH Docs 卡片
安装 · 用法 · 运行模式 · 升级安全网 · 路由 · 许可证
安装
从 npm 安装(推荐)
dsh plugin --profile web add dsh-updater-npm
或从 GitHub 安装
dsh plugin --profile web add github:SiriusWJ/dsh-updater-npm
之后重启 dsh web;“DSH Update”和“DSH Docs”卡片会出现在设置中。
i18n: UI 和每条宿主消息都支持 英文 / 中文,并自动跟随系统语言(或设置 → 通用 → 语言中的手动选择)。dsh_docs_search / dsh_docs_read 工具的描述和输出也跟随它。
用法
DSH Update
- 每 30 分钟检查一次(页面每 60 秒刷新一次缓存结果)。
- 当有新版本时,设置侧边栏中“DSH Update”旁边会出现一个红点(🔴)。
- “Update via npm”会运行 npm install -g @deepseek-ai/dsh@latest,并显示实时进度(进度条 + npm 输出的最后几行)。这里刻意没有滚动日志面板——完整的 npm 输出会在操作结束时写入 $DSH_HOME/plugin-data/dsh-updater-npm/last-run.log,这样卡片保持安静,而当你需要时日志仍然在那里。
- 更新完成后,会出现一个“Restart DSH”按钮:它会用原始命令行退出当前进程并重新启动它(Windows 上使用 PowerShell,macOS/Linux 上使用 /bin/sh)。
在源码树更新和部署修复后也会出现同样的按钮。
不需要新的激活 URL——浏览器会自行重新认证,并且不会弹出新窗口。
- 一旦替换成功(即新版本确实在运行),卡片会显示回滚点的磁盘占用和一个“Remove rollback point”按钮。之前的部署会保留为 /dsh.old-(真实案例中为 222 MB)。安全规则,在主机:只列出并删除版本与正在运行的版本不同的回滚点——实时部署永远不会被触碰。
- 版本比较采用 semver 风格:本地版本比远程版本更新(例如 rc.7 与 rc.6)不会被报告为更新。
超时策略与运行日志(自 v1.12 起)
旧版本对 npm 安装使用简单粗暴的 10 分钟硬超时。在慢速链路上,一次分阶段安装被测量到在 9 分 25 秒内下载了 247 个 tarball(222 MB 目录树中 239 个 @deepseek-ai 子包里的 231 个),随后被终止,仅报告 staging install failed: npm——不是卡死,不是网络故障,仅仅是一次硬超时。
现在看门狗采用双阈值,并可通过 $DSH_HOME/plugin-data/dsh-updater-npm/config.json 配置:
{
"docsEnabled": false,
"npmIdleMinutes": 10,
"npmTimeoutMinutes": 60
}
| 键 | 默认值 | 含义 |
| npmIdleMinutes | 10 | 零活动持续多少分钟后判定为卡住并终止(慢但在推进 ⇒ 永不终止) |
| npmTimeoutMinutes | 60 | 绝对墙钟时间上限,用于阻止真正无界的挂起 |
什么算作活动(在 v1.12.1 中修正): 仅监视子进程的 stdout 是不够的——当 npm 未连接到 TTY 时几乎不打印任何内容(一次实测 5 分钟的安装没有产生任何一行 stdout,而 npm 自身的调试日志记录了 525 个请求,看门狗却将其终止了)。现在合并三个信号,取最大值:
1. --loglevel=http——npm 将每个请求/阶段记录到 stdout(实测:1111 行而非 0 行);
2. --logs-dir——npm 的完整调试日志写入 plugin-data/dsh-updater-npm/npm-logs/;
3. 文件系统活动——在 Windows 上 npm 的调试日志可能只在退出时才刷新(在一次实测 14 秒的安装中,有 12 秒它一直停留在 0 字节),因此暂存目录及其 node_modules/ 也会被监视:解压会持续向其中写入文件。
只有当三者全部空闲时,安装才被视为卡住;在静默阶段,每分钟会追加一行 [heartbeat] …(写入 last-run.log)。
- 看门狗终止时,日志会带有 [watchdog] … 和 [exit] … 行,错误会说明是空闲超时还是总时限,并附上已用时间——不再有含糊的 : npm。
- 同一策略覆盖分阶段安装、非 Windows 的原地 npm install -g,以及源码树的 pnpm/npm install;msiexec /qn 本质上是静默的,获得 10 分钟空闲窗口。
- /dsh-updater-npm/progress 仅返回进度(无日志负载)。
插件自身版本与自更新门控
- 卡片显示此插件自身的版本,并与 npm 上最新的 dsh-updater-npm 进行比较(GET https://registry.npmjs.org/dsh-updater-npm/latest,缓存 10 分钟)。
- 如果插件自身已过时,卡片会说明这一点并显示一条可复制的命令 (dsh plugin --profile add dsh-updater-npm@)。"Update via npm" 按钮处于禁用状态,并且 /update 在服务端被拒绝,提示信息相同。
- 原因:DSH 的更新是由当前正在运行的插件代码执行的,而过时插件的升级路径本身可能就不安全(例如 1.9.0 在 Windows 上暂存时未使用 -g,交换后丢失了所有依赖)。
- 离线直通: 除非明确已知存在更新的插件版本,否则不会阻止任何操作,因此气隙或离线环境不会被锁定。
- 源码树模式(git pull)不受门控,仅提供提示。
DSH Docs
- 同步开关(默认关闭):"DSH Docs" 卡片有一个"Auto-sync official docs"开关。当它关闭时,不会同步任何内容,并且 dsh_docs_search / dsh_docs_read 工具不会被加载。当它打开时,插件会在首次启动时同步(约 217 个文件:英文 + 中文 .zh.md),之后每 24 小时同步一次,并注册这些工具。开关状态保存在 $DSH_HOME/plugin-data/dsh-updater-npm/config.json 中。
- "Sync official docs"会触发一次手动同步(开关必须打开),带有进度条(已下载 / 总数以及当前文件)和阶段(列出 → 下载 → 重建索引)。
- 从文档区搜索和阅读,或直接在对话中通过模型工具进行:
- dsh_docs_search — 搜索本地官方文档索引(中文查询优先使用中文文档)
- dsh_docs_read — 阅读单个文档(章节聚焦、80 KB 截断、路径遍历防护)
文档位于 $DSH_HOME/docs-sync/,索引位于 $DSH_HOME/docs-sync/.index.json。
运行模式
没有 PowerShell 7 的 Windows: DSH 的 shell 工具需要 pwsh。当缺少 pwsh 时,卡片会显示提示和一个 "Install PowerShell 7" 按钮(尝试 winget install Microsoft.PowerShell,失败则回退到官方 win-x64 MSI 并显示实时进度;重启 DSH 以应用)。
插件会检测运行模式并如实运行:
| 模式 | 检测方式 | 更新方式 | 备注 |
| npm-global | argv[1] 为 /lib/bin.js | Windows:暂存更新(将新版本放入单独的暂存目录 → "Restart DSH" 以原子方式将其换入,失败时回滚)。非 Windows:就地 npm install -g,然后"Restart DSH" | Windows 更新目标是正在运行的实例本身(包括原生依赖),就地安装会遇到 EBUSY 并留下半安装的目录树。暂存流程从不触碰正在运行的部署;旧目录被重命名为 dsh.old- 作为回滚点;重启脚本在执行任何操作前会验证暂存包;损坏的部署会被报告,并可通过一键修复 |
| 来源(源码树) | argv[1] 包含 bin.ts / tsx / apps/ | 源码树更新: git fetch → git pull --ff-only → 安装依赖(pnpm/npm) | 从源码树运行(pnpm dsh web)意味着 npm -g 不会影响正在运行的实例。卡片会显示分支 / 本地与远程提交 / 落后提交数,并提供一键更新;未提交的更改或缺少 git 会禁用按钮并给出明确提示 |
多副本保护: 一个环境可能包含多个 dsh 副本(多个 Node 安装的全局目录、DSH profiles 等)。该插件只会更新当前正在运行的那个,优先使用属于正在运行实例自身 Node 安装的 npm;/check 会列出它检测到的其他副本,而如果 npm 运行成功但正在运行的副本版本没有变化,则会被报告为失败,而不是误报成功。副本会按 realpath 去重,因此指向正在运行实例的 junctions/symlinks(例如 $DSH_HOME/profiles/node_modules 下的依赖镜像)不会被误认为独立副本。
版本漂移故障排查:如果更新报告成功,但重启后旧版本又回来了,那么你是在从源码树运行,而 npm 只更新了全局安装。以 npm-global 模式启动 dsh(例如指向 D:\tools\node22\dsh.cmd web 的快捷方式),更新就会生效。
升级安全网
以下每一项都是在一次真实的 0.1.2-rc.1 → 0.1.5-rc.1 跨版本升级出错后添加的,并且自 v1.10.0 起一直有效。
1. 分阶段安装必须传入 -g(布局一致性)。 不带 -g 的 npm install --prefix 会生成提升式树,而 npm-global 部署是嵌套式的(@deepseek-ai/dsh/node_modules/…,自包含)。替换只移动包目录,因此提升式分阶段树意味着丢失新部署的所有依赖。该命令现在始终传入 -g,并且在写入待替换标记之前,插件会验证嵌套式实时树是否具有嵌套式分阶段树,以及分阶段的 lib/bin.js 是否确实能在实例自身的 node 下运行——任何失败都会中止,而不会触碰部署。
2. 验证先于终止。 重启脚本会在停止当前进程之前验证分阶段包(存在性 + 布局);失败时它会 exit 1,让旧进程继续运行,而不是被半终止。
3. 旧部署会保留为回滚点。 替换后,先前的目录会被重命名为 dsh.old-(绝不立即删除),并记录结果。一旦新版本看起来稳定,卡片上的 “Remove rollback point” 按钮会释放空间。
4. 重命名重试。 刚终止的进程可能会短暂持有目录句柄,因此重命名最多重试 5 次,每次间隔 3 秒。
5. 自动升级前备份。 当版本确实会发生变化时,settings.yaml、 .credentials.yaml、.agent-presets/ 以及每个配置文件的 package.json / cordis.yml / pnpm-lock.yaml 都会快照到 $DSH_HOME/upgrade-backups/dsh--to--/;会话日志(最大 256 MB)也会一并复制,因为会话会迁移到一种旧版本无法读回的格式。
最多保留最新的 5 个快照。
6. 重启后无需激活 URL。 自 DSH 0.1.5 起,Web 认证 cookie 仅针对一次激活签名,因此重启会使其失效——但浏览器会自行重新认证。
(直到 v1.12.2,重启脚本还会抓取新的 ?token= URL,写入 activation-url.txt 并打开浏览器窗口;整条路径在 v1.12.3 中因不再需要而被移除。)
7. 残留检测与一键清理。 /check 会报告未被引用的 staging- / repair- 目录(一次未完成的更新曾留下 222 MB)以及 npm 中断安装产生的 .- 残留(真实案例中为 65 MB);卡片提供“清理残留”选项,超过 6 小时的 staging 目录会在启动时自动回收。
8. 跨版本破坏性变更提示。 跨越已知的破坏性范围(当前为 0.1.5: V3 会话格式、persona text → prefix/suffix、插件 API 与 slot 变更)时会显示带有发行说明链接的提示。
9. 重启启动器真正启动(v1.12.2)。 在 Windows 上
spawn('powershell.exe', …, { detached: true }) 等同于 DETACHED_PROCESS:
脚本一行都不会执行,而 spawn 却仍报告 exit=0——这是一种静默的假成功,导致交换未完成。启动器现在会启动一个分离的 node bootstrap,由它把平台脚本作为普通子进程运行,并将 node bootstrap started 写入 restart.log;插件只有在看到该 token 后才移交已暂存的包(≤ 5 秒),否则会保留 pending-swap.json 和暂存目录树,并报告明确的错误。
路由
- GET /dsh-updater-npm/check — 更新检查(10 分钟缓存;包含跨版本提示、上次重启结果、待处理交换、残留和回滚点)
- POST /dsh-updater-npm/update — 运行 npm 更新(仅限同源;先备份,再暂存)
- POST /dsh-updater-npm/restart — 重启此 DSH 实例(仅限同源;将待处理的暂存包原子交换到位,支持 Windows/macOS/Linux)
- POST /dsh-updater-npm/cleanup — 删除残留的 staging 目录和 npm 安装残留(仅限同源)
- POST /dsh-updater-npm/cleanup-rollback — 删除版本与当前运行版本不同的回滚点(仅限同源)
- GET /dsh-updater-npm/progress — 实时更新/同步进度(轮询)
- GET /dsh-updater-npm/docs/status — 文档同步状态
- POST /dsh-updater-npm/docs/sync — 触发文档同步(仅限同源)
- GET /dsh-updater-npm/docs/search?q=&lang=&limit= — 搜索本地文档索引
- GET /dsh-updater-npm/docs/read?path=§ion= — 读取一个文档
更新日志
v1.13.4
对该插件在其开发所用部署之外的 DSH 部署上的行为进行的一次审计发现了七个缺陷。七个缺陷均已修复,且每一个都已在真实部署上复现并验证。
- 设置导航红点从未刷新。 双参数形式 locale.register(ns, dicts) 会将第二个参数的每个键视为一个 locale id,并依据 dsh-client-locale 的 LOCALE_ID_PATTERN(/^[A-Za-z]{2,8}(-[A-Za-z0-9]{1,8})$/u)对其进行校验。该插件传入了一个合成的 "~nav…" 键,无法通过该校验,因此 register() 在 publish() 提升修订号之前就抛出了异常——而外围的 catch 吞掉了该错误。外壳仅在插槽版本或 locale 修订号变化时才重新计算导航行,因此红点从未出现或消失。现在它改用带唯一命名空间的三参数形式。
- locateInstall() 的回退逻辑是死代码。 它从一个以 /agent-presets//agent.cordis.yml 结尾的 agent 预设路径推导安装目录。该布局已不复存在:随附的预设位于 /node_modules/@deepseek-ai/dsh-agent-presets/presets//,用户预设位于 /.agent-presets//。两者都不包含 /agent-presets/,因此后缀判断始终为假,每当 argv[1] 不是 bin.js 时,更新 / 修复 / 重启 / 文档工具都没有恢复路径。现在它会解析 @deepseek-ai/dsh/package.json 并对其取真实路径。锚点是profile 目录,而不是 import.meta.url:在使用 link: 或 junction 安装时,Node 会将模块 URL 重写为该包的真实路径,而那里并不存在 node_modules。
- buildRestartScript() 接受一个 platform 参数,却用宿主机的 path.join 拼接路径。 生产环境始终只传入 process.platform,从而掩盖了这一不匹配——但在 Windows 上生成的 POSIX 脚本会完全使用反斜杠(开头的 / 变成了 \),随后脚本会以 staging incomplete 中止并完全跳过交换。现在它会根据该参数选择 posix.join / win32.join。
- 对暂存包校验的 60 秒上限没有生效。 verifyStagedBinary() 将数字 60000 作为 runInstallCmd 的第三个参数传入,但该参数此后已变为 limits 对象;(60000).idleMs 为 undefined,因此该调用静默回退到 5 分钟 / 60 分钟的默认值,一个半损坏的暂存包可能使这一步停滞一小时。
- 升级备份留下了可读的凭据副本。 快照会复制 .credentials.yaml 以及整个 sessions/ 目录树。备份目录以默认 umask(在 POSIX 上通常为 0755)创建,因此其他本地账户可以读取凭据副本。备份根目录和新的备份目录现在均为 0700。
- registry 被硬编码为 registry.npmjs.org。 在使用镜像或内部 registry 时,插件在查询 npmjs,而 npm 本身却是从镜像安装的——这往好了说是慢,而在防火墙后面,它会对一个本可以成功的更新报告“registry unreachable”。现在它按 env → ./.npmrc → ~/.npmrc → default 的顺序解析,跳过任何不是 http(s) URL 的值。
- resolveProfileName() 误判了共享安装。 当插件只存在于共享的 profiles/node_modules 中(被提升,或某些 pnpm 布局)时,每个按 profile 的探测都返回 false,于是该函数丢弃了从 argv 推导出的 profile,转而采用硬编码的 'web',因此打印出的自更新命令可能指向错误的 profile。现在它也会检查共享位置。
验证:本地冒烟测试套件新增了对平台路径拼接、registry 解析、 locale-bump 契约以及 locateInstall 回退的断言。test/smoke.mjs 仍然报告 57 通过 / 1 失败——与未改动的上游提交上显示的同一个既有 EPERM 失败。POSIX 重启 + 交换流程在 Git Bash sh 下真实执行过(交换、回滚点、暂存清理、重新启动新部署),这也正是暴露出上述 path.join 缺陷的原因。
v1.13.3
- 仅文档——无代码更改。
- 在 README 中添加了来自运行中的 v1.13.2 构建的两张 Settings 卡片截图:它展示了当前的卡片布局(没有运行日志面板,没有激活 URL 行)以及插件自身的版本行。
过时的截图(仍显示已移除的运行日志面板)已在 v1.13.1 中删除;其替代品是 docs/dsh-update-card-1.13.png。文件名中的版本号是刻意的——GitHub 的原始 CDN 在文件被覆盖后仍会继续提供旧字节一段时间。
v1.13.2
- 仅文档——无代码更改。
- 修复了 npm 页面显示中文 README 的问题。npm 用
glob('{README,README.}') 挑选包 readme,并取第一个类 markdown 的命中项;README.zh.md 匹配该
模式并且排在 README.md 之前(检查正则未锚定,因此结尾的 .zh.md 也算作 markdown),所以中文文件胜出。因此中文 README 被命名为 README_zh.md(下划线),glob 无法匹配它——npm 现在发布英文 README.md。两个文件中都有一条注释记录了为什么必须保留下划线。
v1.13.1
- 仅文档——无代码更改,运行时行为与 v1.13.0 完全相同。
- README 现在是双语的,默认英文(README.md)加上中文翻译(README_zh.md),顶部相互链接;中文文件随 npm 包一起发布。
- 若干章节已与代码脱节,并已更正:过时的卡片截图(它仍显示在 v1.12.3 中移除的运行日志面板)已被删除;超时章节不再提及日志面板或 /progress?since=;安全网不再描述激活 URL 抓取(浏览器会自行重新认证);路由列表新增了 cleanup-rollback;回滚点现在被描述为卡片上的按钮;而重启启动器的修复(node 引导)成为安全网第 9 项。
v1.13.0
- 新增: 成功交换后(新版本正在运行),卡片会显示回滚点的磁盘占用以及一个 “移除回滚点” 按钮(POST /dsh-updater-npm/cleanup-rollback)。安全规则在主机上强制执行:只有版本与正在运行的版本不同的 .old- 目录才会被列出并移除,因此线上部署永远不会受到影响(冒烟测试中有硬断言)。
- 优化: 卡片的说明性文字已移除——updNote / docsNote / pluginOutdatedBody 被完全删除,其余字符串被压缩为单行(srcDirty、pwshMissingHint、deployBrokenWarn、npmMismatch、stagingWasteFound、repairRunning、docs*Hint、……),只保留状态和操作。
- 测试:58 项(新增:回滚扫描、仅移除非当前版本、线上部署不受影响、清理路由的同源防护)。
v1.12.3
根据用户反馈做减法:
- 移除: 重启脚本中整套“抓取新的激活 URL + 打开浏览器”的逻辑(Windows 和 POSIX 分支均移除),以及它所需的、最长 2 分钟的轮询尾部。
- 移除: 滚动运行日志面板——操作结束时,完整的 npm 输出现在会写入 plugin-data/dsh-updater-npm/last-run.log(包含 [watchdog] / [exit] 行),卡片只保留进度条和最后几行。/progress 不再携带 log / limits。
- 保留:--loglevel=http(它是看门狗真正的心跳,不是装饰)、三信号活动探测,以及空闲/硬超时对。
- 测试:脚本断言现在检查不会抓取任何 token、不会打开浏览器,另加一项 last-run.log 断言(共 56 项)。
v1.12.2
修复了这条断裂的链路:“内置重启什么都不做,而外部重启后你仍然停留在旧版本”:暂存安装实际上已经成功(verbose exit 0 / info ok),但重启步骤静默失败,因此交换从未发生。
- 根本原因(已复现): launchRestartScript 使用了 spawn('powershell.exe', […], { detached: true })。在 Windows 上这意味着 DETACHED_PROCESS:控制台程序会立即以 exit=0 退出,脚本一行都不会执行,而 spawn 却不报告任何错误。A/B 测试:同一个脚本通过分离的 node 引导运行则执行正常。
- 修复(关键): 启动一个分离的 node 引导程序,将平台脚本作为普通子进程运行;该引导程序会把 node bootstrap started 写入 restart.log。
- 修复(关键): restartNow 现在会在交出暂存包之前等待该 token(最多 5 秒)。如果它始终没有出现,则返回明确的错误,并保留 pending-swap.json 和该暂存目录——旧代码在这里删除了标记,因此 222 MB 的暂存树后来被当作“残留垃圾”清理掉了,用户既无法升级也无法恢复。
- 新增: /check 返回 pendingSwap,卡片显示“已暂存,等待交换”,同时保留“重启 DSH”按钮,因此外部重启仍可一键完成;暂存目录已消失的标记会被自动清理。
- 测试:新增 3 个(共 54 个)——启动器被真正执行,waitForBootstrap 不会误报,启动器 + 交换脚本在模拟部署上执行真实的目录交换。旧代码必然无法通过这些测试,这正是它此前躲过 36 个测试的原因。
更早的版本
- v1.12.1——修复了 1.12.0 引入的误杀:在没有 TTY 时 npm 的 stdout 是静默的,因此仅基于 stdout 的空闲看门狗恰好在 5 分 00 秒时杀掉了一个正常运行的安装。新增了 --loglevel=http --progress=false、--logs-dir 以及三信号活动探测;默认 npmIdleMinutes 从 5 改为 10。端到端实测:1 分钟安装 520 个包,222.6 MB / 25474 个文件,退出码 0(同一命令的冷缓存运行在 9 分 25 秒内从未完成)。
- v1.12.0——粗暴的 10 分钟硬超时改为空闲超时 + 墙钟上限,可通过 npmIdleMinutes / npmTimeoutMinutes 配置;引入了看门狗和运行日志(日志面板在 1.12.3 中再次移除);源码树依赖安装复用同一看门狗。
- v1.11.0——卡片显示插件自身版本,并在插件本身过旧时禁用“通过 npm 更新”(/update 也会拒绝;离线时直接放行)。
- v1.10.0——上述升级安全网,在真实的 0.1.2-rc.1 → 0.1.5-rc.1 升级之后:-g + 布局/可执行性校验、先校验后终止、回滚点、重命名重试、升级前备份、输出重定向、残留清理、破坏性变更提示。
包含每项测量数据的完整中文更新日志:README_zh.md。
许可证
MIT预览分享卡片长什么样 ↓
分享 · DeepSeek Harness Hub
SiriusWJ/dsh-updater-npm
一键升级 DSH 并增量同步官方文档,随时检索
相关插件 · 开发 / 工程
隐私简历生成器reactive-resume/reactive-resume
Reactive Resume 已迁至 GitHub 上的…
★ 45,288已汉化MCP
71.7
长时任务执行闭环AMAP-ML/LongHorizon-Harness
面向 Computer-Use Agent 的 Loop Engineering
★ 1,764已汉化DSH
70.5
多代理编排运行时Enderfga/claw-orchestrator
面向编程智能体的运行时。把 Claude Code、Codex、Antigravity、Grok…
★ 586已汉化MCP
69.4
记忆进化插件csyangwen/dsh-memory-evolve
dsh-memory-evolve 使用场景指南
★ 461已汉化DSH
67.9
原生编程智能体paean-ai/deeptide
由 DeepSeek 打造,为 DeepSeek 而生。
★ 1,107已汉化DSH
67.7
多引擎接力台emo-xiaoyu/harness-mix
macOS/Linux 已加入源码构建与启动适配;目标系统的完整桌面验收仍需在对应机器执行。安装方式、Linux…
★ 419已汉化DSH
66.8
这是你的仓库?认领条目