🎁 福利专区全网大模型免费应用 + 新用户福利 + 注册活动入口,低成本玩转 AI

更新与版本 Yara2090/dsh-update-center

DeepSeek Harness
Yara209030 天前有提交已提供中文文档扫描:含敏感能力

检测并一键安装新版,自检插件完整性并修复

30/100
综合分
0
GitHub 星标
—
npm 周下载
—/5
用户评分

数据截至 2026/10/3 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。

安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add Yara2090/dsh-update-center
该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)

状态速览

判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态
信任档位:待复验
是什么
dsh 原生插件 · dev
装得上吗
曾在 dsh 0.2.0-rc.2 下真实安装成功,但当前 dsh 已更新至 0.2.1-rc.5,本站尚未在新版下复验
L4 · 真实安装等级说明 →
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 30 天前
档位由哪些信号合成

本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。

验证判据与等级说明 →
社区实装还没有人打卡 · 装过的第一个就是你
安全扫描自动 · 静态分析源码
v2.7.0
🔵 扫描:含敏感能力尚无人工评估
展开查看扫描说明与证据 ↓

静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。

可执行外部命令读写/删除本地文件发起外部网络请求
查看扫描证据(共 39 处,展示前 27 条)
  • 发起网络请求
    client.js:61
    const response = await fetch(`${base}${pathname}`, init);
  • 读取本地文件
    lib/center.js:263
    if (!existsSync(statePath)) return;
  • 读取本地文件
    lib/center.js:265
    const parsed = JSON.parse(readFileSync(statePath, 'utf8'));
  • 写入本地文件
    lib/center.js:308
    await writeFile(statePath, payload, 'utf8');
  • 发起网络请求
    lib/center.js:345
    const response = await fetch(url, {
  • 执行外部命令来自配置/变量
    lib/center.js:512
    child = spawn(command, args, {
  • 读取本地文件
    lib/installation.js:31
    const parsed = JSON.parse(readFileSync(manifestPath, 'utf8'));
  • 读取本地文件
    lib/installation.js:90
    return existsSync(path.join(dir, name));
  • 读取本地文件
    lib/integrity.js:124
    const entries = await readdir(path.join(home, 'profiles'), { withFileTypes: true });
  • 读取本地文件
    lib/integrity.js:137
    const canonicalRoot = await realpath(pluginRoot).catch(() => undefined);
  • 读取本地文件
    lib/integrity.js:143
    const resolved = await realpath(path.join(candidate, 'node_modules', pluginName)).catch(() => undefined);
  • 读取本地文件
    lib/integrity.js:233
    const text = await readFile(target, 'utf8');
  • 读取本地文件
    lib/integrity.js:257
    source = await readFile(absolute, 'utf8');
  • 读取本地文件
    lib/integrity.js:267
    if (!existsSync(path.join(root, resolved))) missing.push(resolved);
  • 写入本地文件
    lib/integrity.js:281
    await writeFile(backup, await readFile(target), 'utf8');
  • 删除本地文件
    lib/integrity.js:291
    await unlink(target);
  • 删除本地文件
    lib/integrity.js:297
    await rmdir(target);
  • 写入本地文件
    lib/integrity.js:484
    await mkdir(home, { recursive: true });
  • 写入本地文件
    lib/integrity.js:579
    await writeFile(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`, 'utf8');
  • 写入本地文件
    lib/integrity.js:588
    await mkdir(path.dirname(linkPath), { recursive: true });
  • 写入本地文件
    lib/integrity.js:612
    await writeFile(statePath, `${JSON.stringify(options.preferences ?? {}, null, 2)}\n`, 'utf8');
  • 写入本地文件
    lib/integrity.js:619
    await mkdir(home, { recursive: true });
  • 读取本地文件
    lib/lifecycle.js:48
    return { home, stopper, canStop: existsSync(stopper) };
  • 读取本地文件
    lib/lifecycle.js:61
    const parsed = JSON.parse(readFileSync(statePath, 'utf8'));
  • 读取本地文件
    lib/progress.js:75
    entries = await readdir(dir, { withFileTypes: true });
  • 读取本地文件
    lib/progress.js:89
    total += (await stat(full)).size;
  • 读取本地文件
    lib/progress.js:104
    const info = await stat(target);
扫描于 2026/10/31(0 天前) · 范围 入口闭包 + 发布文件清单 · 来源 GitHub 仓库

静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。

给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个

功能亮点

  • 检测新版本
  • 一键安装更新
  • 自检并修复插件

README

由 DeepSeek 最新模型翻译生成
DSH 更新与版本(dsh-update-center) 给 DeepSeek Harness Web 设置面板加一个「更新与版本」页面:显示当前安装的 @deepseek-ai/dsh 版本、自动检测注册表上的新版本、一键装到同一个全局目录,并且能 自检插件的完整性(文件、依赖、配置)并在缺东西时一键修复。 一个 DSH Cordis 插件(Host + Client 双半边),纯 JavaScript,无构建步骤、无运行时依赖。 English summary: an "Updates & Version" settings page for the DeepSeek Harness Web GUI. It reads the running @deepseek-ai/dsh version, checks the npm registry for a newer release on a selectable channel, and installs it into the same global prefix. It also self-checks its own installation (files, links, settings) and repairs the parts that can
展开完整文档(README 剩余 1 节)
DSH 更新与版本(dsh-update-center)

给 DeepSeek Harness Web 设置面板加一个「更新与版本」页面:显示当前安装的 @deepseek-ai/dsh 版本、自动检测注册表上的新版本、一键装到同一个全局目录,并且能 自检插件的完整性(文件、依赖、配置)并在缺东西时一键修复。

一个 DSH Cordis 插件(Host + Client 双半边),纯 JavaScript,无构建步骤、无运行时依赖。

English summary: an "Updates & Version" settings page for the DeepSeek Harness Web GUI.
It reads the running @deepseek-ai/dsh version, checks the npm registry for a newer
release on a selectable channel, and installs it into the same global prefix. It also
self-checks its own installation (files, links, settings) and repairs the parts that can
be repaired safely. Plain ESM, no bundler, no runtime dependencies; the browser half only
imports react.

更新与版本设置页

设置 → 更新与版本:已安装与最新版本、更新通道、上次检测,以及完整性自检逐项结果。

亮点

- 版本一眼看清 —— 同时显示「已安装」与「最新」两个版本,可切「稳定版 / 预览版」通道;装到磁盘但还没重启时会并排列出「已安装」和「运行中」,不让人误以为已经生效。
- 一键更新,进度是真的 —— 不编造百分比(npm 不告诉调用方总量),给的是真实数字:已用时长、已取包数,以及 npm 下的下载字节与速率(pnpm 缓存布局不同,宁可不显示也不显示一个错的数字);静默时另有说明,不假装一切正常。
- 完整性自检 + 一键修复 —— 10 项检查覆盖插件文件、源码依赖、profile 注册与链接、偏好文件、运行环境。每项修复先写 .bak 备份,只补缺失、不删用户内容,反复点也不会越改越乱。
- 不用再去桌面找快捷方式 —— 「停止 Harness」做进设置页,点两次确认。顺带把「关浏览器 ≠ 停服务」这件事在页面上说清楚。
- 只对本机开放 —— 那条能替换全局 npm 包的路由只接受回环地址,非回环一律 403;每个会写磁盘的动作都有明确边界。
- 零依赖、无构建 —— 纯 ESM,插件自身不装任何运行时依赖,也没有打包步骤;浏览器半边只用宿主提供的 react。

安装

两种方式,任选一种。前提:已经装好 Node ≥ 20 和 DeepSeek Harness。

方式一:纯手动

1. 下载
git clone https://github.com/Yara2090/dsh-update-center.git C:\dsh\dsh-update-center

2. 装进 web profile(装依赖)
dsh plugin --profile web add "file:C:\dsh\dsh-update-center"

3. 让插件真正启用:打开 ~/.dsh/profiles/web/package.json,在 dsh.profile.bundles 里加上一行 "@local/dsh-update-center"。

这一步别省:dsh plugin add 只装依赖,不写 bundle 列表。少了它,插件不会被加载,设置里也就不会出现「更新与版本」。

4. 重启 Harness,打开 设置 → 更新与版本。

方式二:把 GitHub 地址交给 DSH,让它装

在 Harness 的对话框里发一句:

把 https://github.com/Yara2090/dsh-update-center 装成插件

DSH 会自己克隆、装依赖、并把插件写进 profile 的 bundle 列表;按它说的重启一次即可。

装好之后基本不用管:默认每 6 小时自动查一次新版本,有新版就在这个页面里提示。

出问题时

| 现象 | 怎么办 |
| 设置里找不到「更新与版本」 | 多半是 bundle 列表没写进去;按方式一第 3 步检查 dsh.profile.bundles |
| 页面在,但卡片里报问题 | 点卡片里的「一键修复」,能补的它会补,补不了的会说明要你做什么 |
| 浏览器打不开这个页面 | 它只对本机回环地址开放,在跑 Harness 的那台机器上打开 |
| 提示找不到 dsh 命令 | 先 npm i -g @deepseek-ai/dsh |

卸载

三步,都不影响你装的其它插件:

1. 从 profile 的 bundle 列表里删掉 "@local/dsh-update-center" 那一行(~/.dsh/profiles/web/package.json 的 dsh.profile.bundles)。
2. 卸掉依赖:dsh plugin --profile web remove @local/dsh-update-center
3. 重启 Harness。

偏好文件 <DSH_HOME>/dsh-update-center.json 可以留着(以后装回来设置还在),也可以直接删。
没有别的残留:它不改 Harness 的核心文件,也不往别处写东西。唯一的例外是你点过「一键修复」——那只动 profile 清单、profile 里的插件链接和上面那个偏好文件,而且每项都留了同名 .bak 备份。

目录

- 亮点
- 安装
- 卸载
- 界面与功能
- 完整性检查与一键修复
- 运行控制:停止
- 工作原理
- HTTP 接口
- 配置
- 开发与测试
- 目录结构
- 安全边界
- 已知限制
- 更新日志
- 许可

界面与功能

页面注册在设置面板的 settings.section 插槽,导航位置 order: 12(「模型」之后、「插件」之前)。

| 卡片 | 内容 |
| 版本信息 | 已安装版本、最新版本(按通道)、更新通道切换(稳定版 / 预览版)、状态行、上次检测时间、「立即检查」、「立即更新」 |
| 完整性检查 | 10 项自检结果(文件 / 依赖 / 配置 / 运行环境)、「检查完整性」、「一键修复」 |
| 自动检测 | 自动检测开关、检测频率(1 / 6 / 12 / 24 小时)、自动安装开关(默认关闭,开启时给出风险提示) |
| 安装 | 将执行的完整命令、安装进度(不确定进度条 + 已用时长 / 已下载字节 / 速率 / 已取包数)、安装输出实时回显、安装结果与总耗时、以及「需要重启才生效」的提示 |
| 运行控制 | 「停止 Harness」(点两次确认);停止后弹出浮层并自动尝试关闭标签页,浏览器不允许时提示按 Ctrl+W |

自动检测、安装与运行控制

同一页往下:自动检测频率与自动安装开关、将执行的完整安装命令、「停止 Harness」。

几个刻意的行为:

- 不把「读不懂」当成「已是最新」:定位不到安装版本、或注册表没返回所选标签时,页面显示明确的状态/错误,而不是绿色的「已是最新」。
- 不自动降级:本机跑预发布版、注册表上的 latest 还是更旧的稳定版时,判定为「无更新」。
- 更新完成但未重启时,页面同时列出「已安装」和「运行中」两个版本,说明磁盘上已经是新版、进程里仍是旧代码。
- 空闲时零请求:只有检测或安装进行中才以 1s 轮询状态,其余时间页面完全不访问后端。
- 不编造百分比:npm 不告诉调用者「总共要下多少」,所以进度条是不确定态,旁边给的是真实数字(时间、字节、速率、包数),而不是一个看起来精确、实际瞎猜的百分数。

安装期间看得见什么

npm install --global 的进度条只在 TTY 下画;输出被父进程用管道接走之后它一个字都不吐,调试日志也要等进程结束才落盘。结果是「正在下载 100 MB」和「已经卡死」在页面上长得一模一样。

这个面板用四条互相独立的信号把它拆开:

| 信号 | 来源 | 说明 |
| 已用时长 | 1s 心跳(Host 侧) | 只要进程活着就一直在走 |
| 已下载 / 速率 | 直接量 npm 下载缓存 _cacache/content-v2 的体积 | 静默下载大压缩包时,唯一能证明「在下载」而不只是「进程还活着」的信号 |
| 已取包数 | --loglevel=http 打出的 npm http fetch 行 | 只在真的拿到应答时增加 |
| 静默提示 | 上述活动时间距现在超过 90 秒 | 给一句「npm 在下载/解包阶段本来就不输出」的说明,而不是假装一切正常 |

安装命令因此带上了 --loglevel=http(pnpm 用 --reporter=append-only),让安装器在管道里也开口说话。

完整性检查与一键修复

插件由三部分组成:插件目录里的文件、profile 里的注册与链接、运行环境的配置。
任何一处缺了,表现出来往往就是「页面不见了」或「点了没反应」,很难从现象反推原因。
这一页把三部分逐项查一遍,并把其中能安全修的修掉。

十项检查(进入页面时自动跑一次,纯磁盘只读):

| 检查项 | 查什么 | 一键修复 |
| 插件文件 | 清单声明的入口、图标、语言包等是否都在 | — |
| 插件清单 | package.json 能否解析,name / version / dsh.bundle.patch / dsh.client 是否齐全 | — |
| 源码依赖 | 顺着相对导入走一遍宿主半边,用到的文件是否都存在;有没有依赖没随插件交付的外部包 | — |
| 浏览器半边 | client.js 是否完整(含模块加载标记) | — |
| profile 注册 | profile 清单里的依赖项与 dsh.profile.bundles 是否都指向本插件 | ✅ 补回缺失项(先备份) |
| profile 链接 | node_modules/@local/dsh-update-center 是否存在且指向插件目录 | ✅ 重建链接(只删链接,绝不碰它指向的目录) |
| 偏好文件 | <DSH_HOME>/dsh-update-center.json 是否还是合法 JSON | ✅ 用当前设置重写(先备份) |
| DSH 主目录可写 | 状态与偏好需要落盘的地方是否可写 | ✅ 创建目录 |
| Node 版本 | 是否 ≥ 20 | — |
| Harness 安装 | 能否定位全局安装的 @deepseek-ai/dsh | — |

几条刻意的规矩:

- 检查永远只读,只有点「一键修复」才写磁盘,而且每一项都先备份(同名 .bak)。
- 修复只补不删:往 profile 清单里补缺失的依赖项与 bundle 条目,绝不动用户已有的其它内容,因此反复点也不会越改越乱(幂等)。
- 不确定就不动手:链接位置如果是普通目录,脚本不会去删它——那可能是别人手工拷贝的一份插件(提示「是拷贝而不是链接」),也可能是别的东西占了名字(报错并让人工处理)。
- 修完把新的自检结果一并返回,页面直接显示修好之后的样子,而不是让用户再点一次检查。

运行控制:停止

关闭浏览器并不会停止 Harness。 关标签页只是断开连接,服务仍在后台跑着——这个误会会让人以为「改了代码页面却没变」,所以「停止」直接做进设置页。
这里只有一个动作:停止。曾经还有一个「重启」,但两个按钮带来的是「我到底点的是哪个、接下来会发生什么」的持续困惑;而「停止 + 用桌面快捷方式启动」本来就是同一条路径,少一个概念就少一处误解。

- 「停止 Harness」需要点两次确认,第二次才真的执行。
- 动作延后约 1.5 秒执行,并且做成独立子进程——要被停掉的正是这个进程,HTTP 回包必须先发出去。
- 不自己实现杀进程,而是调用你机器上原有的 stop-deepseek-harness.ps1(由安装器放在 <DSH_HOME> 下)。认端口、清状态这些细节只有一份实现,行为与桌面快捷方式完全一致。
- 脚本缺失时按钮禁用,并在页面上写明缺的是哪个文件,而不是让按钮点了没反应。
- Windows 上不能给子进程加 detached:它会带来 DETACHED_PROCESS(新进程没有控制台),而 Windows PowerShell 在这种状态下会以退出码 0 静默退出、一行脚本都不执行。Windows 本来就不会因为父进程退出而杀掉子进程,所以这里不需要它。
- 卡片上常驻一行「上次动作」:子进程是否真的起来了由 spawn / error 事件上报,并写进偏好文件跨重启保留(动作会把进程换掉,只有落盘才答得了「刚点的按钮生效没有」)。
- 安装进行中拒绝停止(409),避免把正在跑的安装器一起带走。

点完之后:关掉这个标签页

停止一旦执行,这个页面的连接就注定回不来(服务已经不在了,没有任何东西能再服务它)。所以动作排定后会盖一层浮层,说明「服务即将关闭、这个标签页可以关掉了」,并自动尝试关闭标签页。

但浏览器通常不允许页面关闭自己。 实测(Chromium/Edge):

history.length = 2, window.opener = false → window.close() 被拒绝,标签页仍在

Chromium 只允许「由脚本打开的」或「没有历史记录的」标签页被脚本关闭;而这里的页面是启动器从外部打开的(带令牌的地址 303 之后还留下了两条历史)。既然做不到,页面就不能假装做到了:调用之后等一小段时间,如果代码还在跑就说明被拒绝,于是如实提示按 Ctrl+W 关闭,而不是留一个转圈的界面。

工作原理

┌──────────────────────── 浏览器 ────────────────────────┐ │ client.js → settings.section 页面 │ │ fetch('/dsh-update-center/state' | '/check' | ...) │ └───────────────────────────┬────────────────────────────┘ │ 同源 HTTP,仅回环 ┌───────────────────────────▼────────────────────────────┐ │ index.js → ctx.webServer.register(prefix) │ │ lib/center.js 状态机 / 路由 / 自动检测 / 安装子进程 │ │ lib/integrity.js 自检与修复:文件、链接、注册、环境 │ │ lib/lifecycle.js 停止服务:调用本机停止脚本并安排脱离本进程的执行 │ │ lib/progress.js 进度信号:缓存体积、抓取计数、静默判定 │ │ lib/semver.js 版本解析与 semver 优先级比较 │ │ lib/installation.js 定位安装目录、探测包管理器 │ └────────────────────────────────────────────────────────┘

- Host 半边(index.js + lib/)持有三件浏览器拿不到的事实:本机安装的版本、注册表发布的版本、以及「把新版装上去」的能力。它注册一条前缀路由 /dsh-update-center,并把状态、检查、偏好、安装、自检、修复六个动作暴露成 JSON。
- Client 半边(client.js)只从浏览器模块表取 react,不 import 任何 Harness Client 包——那些包会随版本变化,而这个页面崩溃会让整个 slot entry 变空。
- 偏好落盘到 <DSH_HOME>/dsh-update-center.json(通道、自动检测、自动安装、频率、上次检测时间)。运行期数据(安装输出、错误)只留在内存里。

HTTP 接口

所有路由都在前缀 /dsh-update-center 之下,返回 application/json; charset=utf-8,并且只接受来自本机回环地址的请求,非回环一律 403。

| 方法 | 路径 | 作用 |
| GET | /dsh-update-center/state | 读取完整状态快照 |
| POST | /dsh-update-center/check | 查注册表;可带 {"channel":"latest"\|"next"};等检测完成再返回 |
| POST | /dsh-update-center/settings | 写偏好,字段 channel / autoCheck / autoInstall / checkIntervalHours |
| POST | /dsh-update-center/update | 启动安装;立即返回,进度靠轮询 /state |
| GET | /dsh-update-center/integrity | 跑一次完整性自检(只读),返回检查报告 |
| POST | /dsh-update-center/repair | 修复可自动处理的项,返回动作清单与修复后的新报告 |
| POST | /dsh-update-center/stop | 安排停止服务;约 1.5 秒后执行,因此回包会先返回 |

没有 /restart:重启服务请用桌面快捷方式(见「运行控制:停止」一节)。

状态对象的主要字段:

| 字段 | 含义 |
| currentVersion | 磁盘上已安装的版本(安装成功后会刷新) |
| runningVersion | 本进程启动时加载的版本 |
| latestVersion | 所选通道在注册表上的版本 |
| updateAvailable | 是否确实有更新(false 也包含「无法判定」) |
| checking / checkedAt / checkError | 检测中 / 上次检测时间 / 检测错误(无错误为 null) |
| updating / updateOutput / updateResult | 安装中 / 安装输出 / 安装结果 |
| updateStartedAt / updateElapsedMs / updateFinishedAt | 安装开始时间 / 已用毫秒(心跳刷新,结束即冻结) / 结束时间 |
| updateCacheBytes / updateCacheRate | 已下载字节(npm 缓存实量,估算值) / 字节每秒 |
| updateFetchCount / updatePackageCount | 已完成的注册表抓取次数 / 其中的压缩包数 |
| updateSilentMs / updateStalled | 距上次活动的毫秒数 / 是否已静默超过 90 秒 |
| installCommand | 将执行(或已执行)的完整命令 |
| restartRequired | 是否已装上磁盘但还没重启 |
| channels / statePath | 允许的通道列表 / 偏好文件路径 |
| lifecycle | { canStop, stopper }:本机能不能停止,以及停止脚本的路径 |

上表是状态对象的完整字段。其中只有「无错误」槽位(checkError / updateResult / currentVersionError)始终以 null 出现;其余字段在尚未发生时会整个不出现在 JSON 里——例如 latestVersion 要检测过一次才有,updateCacheBytes 要等安装开始之后才有。取值前先判一下。

自检报告(/integrity 与 /repair 共用同一形状):

| 字段 | 含义 |
| pluginRoot / pluginName / profileDir / statePath | 本次自检认定的插件目录 / 包名 / profile 目录(判定不出为 null)/ 偏好文件 |
| summary | errors / warnings / repairable / total 计数 |
| checks[] | 每项为 { id, status, repairable, detail };status 取 ok / warn / error |
| /repair 额外返回 | repaired[](含每项的 ok 与说明)、repairedCount、failedCount、restartRequired、以及修复后的 report |

配置

写在 profile 的 cordis.patch.yml 覆盖行里,字段全部可选:

- id: dsh-update-center
name: "@local/dsh-update-center"
config:
channel: latest # latest | next,默认 latest registry: "https://registry.npmjs.org/" # 检测与安装都用的源,可换成内网镜像 autoCheck: true # 后台自动检测,默认 true autoInstall: false # 检测到就自动安装,默认 false checkIntervalHours: 6 # 自动检测频率,默认 6

界面上能改的只有 channel / autoCheck / autoInstall / checkIntervalHours,它们会落到偏好文件里并覆盖这里的默认值;registry 只能在配置里改,并且检测与安装用的是同一个源(安装命令会带上 --registry=<地址>)。这样换成内网镜像后,检测到的新版本也真的能从镜像装下来,不会出现「检测到有新版本、安装却从另一个源拉不到」。

开发与测试

无需安装依赖、无需构建:

node --test # 100 个用例:版本比较 + 进度信号 + 自检与修复 + 停止计划 + 路由与拒绝分支 + 接口与文档一致性
node --check index.js # 语法检查(client.js / lib/*.js 同理)

如果运行环境禁止 Node 测试运行器 fork 子进程(沙箱会报 spawn EPERM),改用单进程模式:

npm run test:single # 等价于 node --test --test-isolation=none

测试不访问外网:注册表由本地假 HTTP 服务提供,偏好文件写在临时目录里,也绝不会真的执行 npm install。

其中 test/endpoints.test.js 是接口一致性检查,不是功能测试:前端(client.js)与后端(lib/center.js)靠字符串约定动作名,两边各改一半,写代码时不会有任何报错,只会在运行时 404。
它断言三件事:前端调的每个动作后端都认、后端认的每个动作前端都在用、两边路由前缀一致;检查自身也带非空断言,避免「什么都抽不到于是永远通过」。

推上去会自动跑同一套用例(见顶部徽章):Ubuntu 上跑 Node 20 与 22,Windows 上跑 Node 22。
Windows 那条会让 test/lifecycle.test.js 里的 PowerShell 冒烟测试真的执行——那条用例是「detached 让 PowerShell 静默退出」的产物,只在 Windows 上跑才有意义,其余平台自动跳过。
index.js 与 client.js 不被任何用例导入,因此 CI 里另外单独对它们跑 node --check。

改动 client.js 后,运行中的 Harness 会通过 bundle 探测自动热加载新的浏览器半边;改动 Host 半边(index.js / lib/)通常需要重启 Harness 才会载入新的模块代。

目录结构

├── index.js Host 半边入口:inject + apply,只做挂载 ├── client.js 浏览器半边:单文件,settings.section 页面 ├── lib/ │ ├── center.js 状态机、HTTP 路由、自动检测、安装子进程与进度心跳 │ ├── integrity.js 自检与一键修复:文件、源码依赖、profile 注册与链接、偏好文件 │ ├── lifecycle.js 停止:探测本机停止脚本并安排脱离本进程的执行 │ ├── progress.js 进度信号:缓存体积、抓取计数、静默判定(纯函数) │ ├── semver.js 版本解析与 semver 优先级比较(纯函数) │ └── installation.js 定位安装目录、探测包管理器、拼装安装命令 ├── test/ │ ├── semver.test.js 版本比较的边界用例 │ ├── progress.test.js 进度信号与安装命令的边界用例 │ ├── integrity.test.js 自检判定与修复安全性(含「不误删真实目录」) │ ├── lifecycle.test.js 停止的命令拼装与能力判定(含唯一真的拉起进程的 Windows 冒烟测试) │ ├── center.test.js 路由 / 状态 / 拒绝分支 / 安装出口的版本号校验 │ ├── endpoints.test.js 前后端接口一致性:动作名与路由前缀不许两边各改一半 │ └── docs.test.js 文档一致性:目录锚点、目录结构树、文中用例数 ├── locale/ │ ├── zh.json 插件卡片的中文显示名与描述 │ └── en.json 英文显示名与描述 ├── docs/ │ ├── screenshot.png README 顶部:版本信息与完整性检查 │ └── screenshot-2.png 「界面与功能」:自动检测、安装与运行控制 ├── .github/workflows/ │ └── test.yml CI:Ubuntu(Node 20/22)与 Windows(Node 22)跑 npm test ├── cordis.patch.yml bundle 补丁:插入 Host 插件行 ├── icon.svg 插件卡片图标 ├── CHANGELOG.md 逐版本的功能与取舍记录 └── package.json 清单:dsh.bundle.patch + dsh.client

安全边界

这条路由能替换机器上的全局 npm 包,因此:

- 仅回环:req.socket.remoteAddress 不是 ::1 / 127.0.0.0/8 / ::ffff:127.0.0.1
就直接 403,并且不返回任何状态;空地址也按拒绝处理。
- 不自动安装:autoInstall 默认关闭,开启后界面会常驻一条风险提示。
- 同时只跑一个安装:检测与安装互相排斥,重复请求返回 409。
- 插件卸载即收尾:路由、定时器、正在运行的安装子进程都由 ctx.effect 的清理函数一起释放。
- 更新仍需重启:安装只换磁盘上的文件,新版本要重启 Harness 才真正生效。
- 修复的写权限:只有「一键修复」会写磁盘,范围仅限 profile 清单(补依赖项与 bundle 条目)、 profile 里的插件链接、以及偏好文件;每项都先写同名 .bak 备份,且只补缺失、不删用户内容。
检查(GET /integrity)永远只读。
重建链接是唯一会删东西的动作,但它只删链接本身:链接指向的真实目录、以及「这里并不是链接」的情况,都绝不触碰。
- 停止不接受页面传来的任何命令:它只会调用 <DSH_HOME> 下那个固定名字的脚本,页面能选的只有「停止」这一个已定义动作,不存在把任意命令拼进参数的可能。
- 进命令行的外部输入都先校验:安装命令在 Windows 上经由 shell 执行(shell: true,因为
npm / pnpm 是 .cmd 包装脚本),参数不会被转义。因此注册表返回的 dist-tag 必须能按
semver 解析,配置里的 registry 必须是 http/https 的合法 URL——前者不合法就报错,后者不合法就退回官方源。这道校验有两层:读取注册表时一层,安装出口 runUpdate 自己再一层(安全闸不能只建在调用方)。

已知限制

- 需要 Node ≥ 20(用到全局 fetch 与 AbortSignal.timeout)。
- 安装用的是「同一个全局前缀 + 探测到的包管理器(npm / pnpm)」。如果这个包当初是用其它方式(例如手工拷贝)放进去的,探测会退回 npm,可能不符合你的环境;此时请手工执行界面上展示的那条命令。
- Windows 上正在运行的进程可能占住原生模块文件,导致全局安装失败;界面会把安装器的原始输出原样显示出来,按提示手动执行即可。
- 安装期间不要让本插件被卸载或重载:插件被卸载时会一并终止正在运行的安装子进程(这是「不留孤儿 npm」的代价)。界面在安装中会常驻这条提示。
- npm 在解包阶段会连续几分钟不输出任何东西,此时进度条只靠时间与缓存体积证明进程还活着;真正「卡住」与「正在解包」在外部无法彻底区分,超过 90 秒静默时界面会给出说明而不是结论。
- 桌面(Electron)版的更新走 Harness 自带通道,与本插件无关。
- 一键修复只覆盖「能安全补回来」的部分:插件源码文件丢了、Node 版本太低、Harness 没装,这些只能重新克隆仓库或自行升级,页面会如实说明而不是假装修好。
- 修复改的是 profile 清单与链接,因此改完之后需要重启 Harness 才生效(页面会提示)。
- 自检报告的文案由浏览器半边渲染,Host 只回 id 与细节字符串;因此新增检查项时忘记补文案,页面会退回显示检查项 id,而不是空白。
- 没有浏览器控制的环境下无法验证视觉呈现;本项目的验证覆盖语法、清单、Host 路由与实时 Client 插槽注册。

更新日志

完整历史与每一版的取舍理由见 CHANGELOG.md; Releases 里有对应的版本标签。

许可

MIT
预览分享卡片长什么样 ↓Yara2090/dsh-update-center
分享 · DeepSeek Harness Hub
Yara2090/dsh-update-center

检测并一键安装新版,自检插件完整性并修复

回到顶部