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

侧栏重启按钮 FuriousKinght/dsh-restart-button

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

一键重启桌面应用并自动重连页面

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

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

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

状态速览

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

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

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

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

可执行外部命令读写/删除本地文件发起外部网络请求
查看扫描证据(共 31 处,展示前 31 条)
  • 读取本地文件
    entry.js:171
    entries = readdirSync(dir, { withFileTypes: true })
  • 读取本地文件
    entry.js:224
    if (statSync(candidate).isFile()) return candidate
  • 读取本地文件
    entry.js:248
    if (statSync(explicit).isFile()) return explicit
  • 读取本地文件
    entry.js:292
    if (existsSync(c)) return c
  • 读取本地文件
    entry.js:313
    if (!existsSync(dbPath)) return null
  • 写入本地文件
    entry.js:397
    mkdirSync(dir, { recursive: true })
  • 写入本地文件
    entry.js:413
    writeFileSync(configPath, JSON.stringify({
  • 执行外部命令来自配置/变量
    entry.js:486
    const child = spawn(shell, [
  • 发起网络请求
    lib/client.js:138
    fetch(`${ROUTE}/info`, { cache: 'no-store' })
  • 发起网络请求
    lib/client.js:177
    fetch(`${ROUTE}/info`, { cache: 'no-store' })
  • 发起网络请求
    lib/client.js:227
    fetch(`${ROUTE}/restart`, { method: 'POST', cache: 'no-store' })
  • 读取本地文件
    build-client.mjs:18
    const id = JSON.parse(readFileSync(join(root, 'package.json'), 'utf8')).name
  • 读取本地文件
    build-client.mjs:24
    const source = readFileSync(join(root, 'src', 'client.js'), 'utf8')
  • 写入本地文件
    build-client.mjs:29
    mkdirSync(join(root, 'lib'), { recursive: true })
  • 写入本地文件
    build-client.mjs:31
    writeFileSync(join(root, 'lib', 'client.js'), bundle)
  • 写入本地文件
    lib/relaunch-helper.cjs:51
    fs.appendFileSync(logPath, `${new Date().toISOString()} ${message}\n`, 'utf8')
  • 读取本地文件
    lib/relaunch-helper.cjs:101
    if (!fs.existsSync(dbPath)) return { skipped: 'cookies db not found' }
  • 写入本地文件
    lib/relaunch-helper.cjs:107
    fs.mkdirSync(dir, { recursive: true })
  • 写入本地文件
    lib/relaunch-helper.cjs:109
    fs.copyFileSync(dbPath, backupPath)
  • 读取本地文件
    lib/relaunch-helper.cjs:172
    if (fs.existsSync(j) && fs.statSync(j).size === 0) fs.unlinkSync(j)
  • 写入本地文件
    lib/relaunch-helper.cjs:200
    fs.mkdirSync(path.dirname(cfg.markerPath), { recursive: true })
  • 写入本地文件
    lib/relaunch-helper.cjs:201
    fs.writeFileSync(cfg.markerPath, JSON.stringify(snapshot, null, 2), 'utf8')
  • 读取本地文件
    lib/relaunch-helper.cjs:269
    if (fs.statSync(candidate).isFile()) return candidate
  • 执行外部命令来自配置/变量
    lib/relaunch-helper.cjs:309
    const result = spawnSync(TASKLIST, ['/FI', `PID eq ${String(pid)}`, '/NH', '/FO', 'CSV'], {
  • 执行外部命令来自配置/变量
    lib/relaunch-helper.cjs:387
    child = spawn(exePath, [], {
  • 读取本地文件
    lib/relaunch-helper.cjs:428
    config = JSON.parse(fs.readFileSync(configPath, 'utf8'))
  • 读取本地文件
    lib/relaunch-helper.cjs:462
    log(`dry run: launch target exists=${String(fs.existsSync(exePath))}`)
  • 执行外部命令来自配置/变量
    lib/relaunch-helper.cjs:526
    const r = spawnSync(TASKKILL, args, { windowsHide: true })
  • 发起网络请求
    src/client.js:136
    fetch(`${ROUTE}/info`, { cache: 'no-store' })
  • 发起网络请求
    src/client.js:175
    fetch(`${ROUTE}/info`, { cache: 'no-store' })
  • 发起网络请求
    src/client.js:225
    fetch(`${ROUTE}/restart`, { method: 'POST', cache: 'no-store' })
扫描于 2026/11/4(0 天前) · 范围 入口闭包 + 发布文件清单 · 来源 GitHub 仓库

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

依赖的 DSH / Cordis 模块
@deepseek-ai/dsh-host-webserver@deepseek-ai/dsh-client-ui-slots
给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个

功能亮点

  • 一键重启桌面应用
  • 自动刷新页面

侧边栏底部有个按钮,重启 DSH 桌面应用,应用回来后自动刷新页面。

README

由 DeepSeek 最新模型翻译生成
dsh-restart-button 侧边栏底部有个按钮,用来重启 DSH 桌面应用:它自己关掉,再自己回来,页面自动重连。不用再手动关窗口、重新双击图标了。 - 平台: 仅限 Windows 桌面端。 - 需要 DSH 0.2.0-rc.2 或更高版本 —— 这个插件就是在该版本上开发和验证的,也是声称能跑的最老构建。版本要求在 package.json 里声明为 peerDependencies,依赖 @deepseek-ai/dsh-host-webserver(路由注册需要它)和 @deepseek-ai/dsh-client-ui-slots(按钮挂载的页脚插槽需要它)。DSH 在导入插件前会先检查这些依赖,所以老版本构建会拒绝安装,并给出具体原因,而不是挂上一个永远画不出来的按钮。 - 适用范围: 打包好的桌面应用。在终端里跑 dsh web 时它会拒绝运行,因为那样杀掉父进程会连你的 shell 一起杀掉。 - 重启的代价: 这是强制杀进程(taskkill /T /F),所以正在跑的任务会被打断。这也是按钮要点两次的原因 —— 第一次是 5 秒内待命,第二次才确认。 - 无需配置: 插件没有声明任何设置命名空间,除了 Node 标准库之外没有别的依赖。 版本要求 范围是 ^0.2.0-rc.2,解析为 >=0.2.0-rc.2 <0.3.0。
展开完整文档(README 剩余 1 节)
dsh-restart-button

侧边栏底部有个按钮,用来重启 DSH 桌面应用:它自己关掉,再自己回来,页面自动重连。不用再手动关窗口、重新双击图标了。

- 平台: 仅限 Windows 桌面端。
- 需要 DSH 0.2.0-rc.2 或更高版本 —— 这个插件就是在该版本上开发和验证的,也是声称能跑的最老构建。版本要求在 package.json 里声明为 peerDependencies,依赖 @deepseek-ai/dsh-host-webserver(路由注册需要它)和 @deepseek-ai/dsh-client-ui-slots(按钮挂载的页脚插槽需要它)。DSH 在导入插件前会先检查这些依赖,所以老版本构建会拒绝安装,并给出具体原因,而不是挂上一个永远画不出来的按钮。
- 适用范围: 打包好的桌面应用。在终端里跑 dsh web 时它会拒绝运行,因为那样杀掉父进程会连你的 shell 一起杀掉。
- 重启的代价: 这是强制杀进程(taskkill /T /F),所以正在跑的任务会被打断。这也是按钮要点两次的原因 —— 第一次是 5 秒内待命,第二次才确认。
- 无需配置: 插件没有声明任何设置命名空间,除了 Node 标准库之外没有别的依赖。

版本要求

范围是 ^0.2.0-rc.2,解析为 >=0.2.0-rc.2 <0.3.0。DSH 拿 harness 自身的版本,按 semver.satisfies(runtimeVersion, range, { includePrerelease: true }) 求值,不匹配就拒绝安装。针对关键情形,这个调用已用真实的 semver 复现过:

| Harness 版本 | 能否安装 | |
| 0.1.7-rc.2 | ❌ | 低于下限 |
| 0.2.0-rc.1 | ❌ | 恰好是开发所依据版本的前一个发布 |
| 0.2.0-rc.2 | ✅ | 本插件开发和测试所用的版本 |
| 0.2.0-rc.3、0.2.0、0.2.1 | ✅ | 同一 minor 的后续预发布版和补丁版 |
| 0.3.0 | ❌ | 超出 ^ 的上界,新的 minor 需要重新检查 |

下限说明的是在哪里测过,而不是断言每个更早的构建恰好缺了哪个内部 API —— 那些构建拿不到,没法查。真正验证过的是范围本身:DSH 自己的调用已用 semver 7.8.5 复现,上表每一行都是跑出来的,不是读出来的。

拒绝不是崩溃:只有一行,写明插件名、版本号和不匹配的 peer,而 dsh plugin allow-version 就是用来刻意覆盖它的。

源码结构

| 路径 | 是什么 |
| entry.js | 宿主端:提供 /plugins/dsh-restart-button/{info,restart},并启动重启辅助程序。 |
| src/client.js | 浏览器端:在 sidebar.footer.action 插槽里注册一个操作。 |
| lib/client.js | 构建好的浏览器 bundle。已提交,所以仓库从源码安装时无需构建步骤。 |
| lib/relaunch-helper.cjs | 这个进程能活过 kill,再把应用重新拉起来。 |
| tools/ | 针对某个配置档的 install.mjs / uninstall.mjs。 |
| verify/ | 两套测试;都不碰正在运行的 DSH。 |

为什么这不是一行 IPC 调用就能搞定的事

Electron 主进程有 app.relaunch(),甚至还有一个 restartAppHostMenu 菜单项——但两者都待在 ...development ? [...] : [] 分支里,打包后的构建根本拿不到。Host → Electron 的 IPC 通道只传 shutdown-complete、quit-inspection 和更新回复,没有重启消息可发。所以重启只能从外部杀掉整棵进程树,再重新启动可执行文件。这带来两个问题,本插件就是为了解决它们:

1. helper 必须活过它杀掉的那棵树。 只杀主进程会让 Host 变成孤儿,端口 19387 一直被占,新应用就没法提供服务。taskkill /T 会带走整棵树——包括本插件正常启动的任何进程,因为 /T 沿父子链走,而 detached 的子进程在这条链里仍然是子进程。所以 helper 通过 WMI(Win32_Process.Create)创建,父进程是 WmiPrvSE.exe,在这棵树之外。
2. WMI 创建的 powershell.exe 在这台机器上起不来——它会立刻以「access denied」退出,不写任何日志,哪怕只是三行脚本;而 cmd.exe 和 where 在同样的上下文里却能跑。自带的 node.exe 在那里运行正常,哪怕路径里有 CJK 字符,所以 helper 用 Node 脚本。这样也把应用路径放在 UTF-8 的 config.json 附属文件里,而不是塞进 .ps1/.cmd——Windows PowerShell 5.1 会把后者按 ANSI 解码。

页面靠比较每个进程的 token 来恢复。从点击到 kill 之间大约有 1.2 s,这段时间旧服务器还在响应,所以「服务器有响应就 reload」会直接 reload 回那个正在死掉的进程。等一个不同的 token,就只有另一个进程才能满足。

安装

node tools/install.mjs --profile desktop # junction + one row in cordis.patch.yml
node tools/uninstall.mjs --profile desktop # reverses both

补丁层会热重载,所以这一行不用重启就能挂上——但正在运行的进程还留着旧的 entry.js(Node 的 ESM 缓存没有 cache-busting),所以第一次激活需要重启一次。

验证

node verify/verify-install.mjs # the browser half passes the client-module scan
node verify/verify-relaunch.mjs # the helper survives killing its own creator's tree

verify-relaunch.mjs 用一个替身进程复现生产环境的形态,不碰正在运行的 DSH:替身通过 WMI 创建 helper,helper 随后对替身所在的树执行 taskkill /T /F。如果 helper 是那棵树的普通子进程,它自己的 kill 会在半路把自己也带走,日志会在 launched 之前就断掉。
侧边栏脚下的一个按钮:点一下,DSH 自己关掉再起来,页面自己恢复。不用再手动关掉窗口、再手动双击图标。

侧边栏底部 ┌──────────────────────────┐ │ ⟳ 重启 DSH │ ← 第一下:进入确认态(5 秒后自动取消) └──────────────────────────┘ ┌──────────────────────────┐ │ ⟳ 再点一次确认 │ ← 第二下:真的重启 └──────────────────────────┘ ↓ 正在重启… → 等待 DSH 回来… → 页面自动刷新

它到底做了什么

DSH 桌面版本体是 Electron:DeepSeek Harness.exe(主进程)负责窗口,它派生一个 Host 子进程跑 Web 服务和所有工具调用。从插件里没有办法让应用自己重启:

- Electron 主进程确实有 app.relaunch(),甚至菜单里就有一项 restartAppHostMenu,但它被写死在开发者菜单分支里(...development ? [...] : []),打包版根本不会显示;
- Host 到 Electron 的 IPC 通道只有三种消息:shutdown-complete、退出检查、更新回复。没有「重启」这一种。

所以重启只能由外部完成:杀掉整棵进程树,再把可执行文件重新拉起来。这里有两个坑,都踩过并且都测过:

坑一:助手进程必须活过它自己要杀的那棵树

杀掉主进程是不够的——Host 子进程会变成孤儿,继续占着 19387 端口,新起来的应用绑不上端口。所以必须 taskkill /T(连整棵树一起杀)。但 /T 是按父子链走的,普通 spawn(..., {detached:true}) 出来的子进程仍然在这条链上,会跟着一起死。

解法:用 WMI(Win32_Process.Create)创建助手进程。WMI 创建出来的进程父进程是 WmiPrvSE.exe,不在被杀的树里。这一点在本机实测过,也有自动化测试守着(见下)。

坑二:WMI 创建出来的 powershell.exe 根本起不来

一开始助手是用 PowerShell 写的。实测结果是:WMI 返回成功、进程号也有了,但进程瞬间退出,「拒绝访问」,不写日志、不留输出——哪怕脚本只有三行、只是往文件里追加一行字。同样的环境里 cmd.exe、whoami、where 都正常。

换成本机自带的 node.exe 就正常了,即使它的路径里有中文。所以助手是一个 Node 脚本(lib/relaunch-helper.cjs),顺带也把编码问题一起解决了——应用路径是 D:\AI类型\deepseekharness\DeepSeek Harness.exe,而 .ps1 / .cmd 里塞中文是危险的:Windows PowerShell 5.1 会把无 BOM 的文件按系统 ANSI 代码页解码,中文可能被改坏、甚至提前结束字符串字面量。

(本来还有一条路是 8.3 短路径,可以绕开中文路径。本机这个卷没开 8.3:%~sI 原样返回长路径。)

页面是怎么自己回来的

点重启会把正在服务这个页面的进程杀掉,但页面本身不会被卸载,只是连接断了——所以它有机会自己恢复。难点在于:从「点了按钮」到「进程真的被杀」之间有大约 1.2 秒,这期间服务器还在正常应答,如果只是「轮询到服务器有响应就刷新」,会立刻刷新回旧进程,看起来像按钮没生效。

所以 Host 每一份响应都带一个只属于本进程的 token。页面记住当前 token,重启后等一个不一样的 token——不同的 token 只可能来自不同的进程,正好就是它在等的东西。不靠超时猜、不靠计时赌。

安装

node tools/install.mjs --profile desktop

做两件事:在 <profile>/node_modules/ 下建一个目录联接,以及在 <profile>/cordis.patch.yml 末尾追加一行 - insert:。

补丁层是热加载的,所以装完不用重启就会挂上;但浏览器页面要刷新一次才会出现按钮。

卸载:

node tools/uninstall.mjs --profile desktop

两个脚本都用 Node 而不是 PowerShell 写,是刻意的:补丁层是无 BOM 的 UTF-8 而且里面有中文(某个 provider 的 displayName),而 PowerShell 5.1 的两个默认行为恰好都会毁掉它(Get-Content 按 ANSI 解码、Set-Content -Encoding utf8 写出带 BOM)。用 Node 就是按它本来的样子读、按它本来的样子写。

验证

node verify/verify-install.mjs # 浏览器半边的打包链路能不能被扫描器认下来
node verify/verify-relaunch.mjs # 助手的"活过自己杀的那棵树"——用替身,不碰正在跑的 DSH

verify-relaunch.mjs 是这里最要紧的一个。它复刻了真实的形状:一个替身进程自己通过 WMI 创建助手,助手再 taskkill /T /F 杀掉替身那棵树。如果助手是那棵树里的普通子进程,它自己的这一刀就会把自己带走,日志会停在半路。测试不额外加任何强制手段——那一刀就是真的那一刀。

已知边界

- 需要 DSH 0.2.0-rc.2 或更新的版本 —— 这是本插件的开发与验证版本,也是它声称能用的最低版本。这个要求在 package.json 里以 peerDependencies 声明(@deepseek-ai/dsh-host-webserver 是注册路由所需,@deepseek-ai/dsh-client-ui-slots 是按钮挂载所需的底部槽位)。DSH 在导入插件前会核对——所以旧版本会明确拒绝安装并给出原因,而不是装上一个画不出来的按钮。

需要说明的是:这个下限是「在哪个版本上测过」的陈述,不是对每个更早构建内部缺了哪个 API 的断言——那些版本我手上没有,无法核对。真正被验证过的是范围本身:我把 DSH 自己那行判定逻辑拿真实的 semver 7.8.5 复现跑过,下表每一行都是跑出来的结果,不是读代码推断的。
范围写的是 ^0.2.0-rc.2(即 >=0.2.0-rc.2 <0.3.0),DSH 用 semver.satisfies(运行时版本, 范围, { includePrerelease: true }) 判定。实测结果:0.2.0-rc.1 装不上,0.2.0-rc.2 正常,后续的 0.2.0-rc.3 / 0.2.0 / 0.2.1 都可以,0.3.0 会因上界被挡下(新的次版本需要重新确认)。被挡下不是崩溃:只会有一行说明是哪个插件、哪个版本、哪个 peer 不匹配;确实想装可以用 dsh plugin allow-version 显式豁免。
- 只支持 Windows 桌面版。 判断依据是 Host 与 Electron 之间有没有 IPC 通道(process.connected)。命令行跑的 dsh web 没有这条通道,按钮会拒绝执行并说明原因——在那里「杀掉父进程」等于杀掉你的终端。
- 重启会中断正在跑的任务。 这是强杀,没有优雅退出:所以按钮要点两下,第二下才是真的。
- 第一次装完需要手动重启一次 DSH。 补丁层会热加载,但运行中的进程持有旧的 entry.js——Node 的 ESM 缓存不带 cache-busting,重新挂载同一个包名拿到的还是旧模块。这一次之后按钮才真正生效。
- 助手目录 %DSH_HOME%\restart-helper\ 会留下 config.json 和 relaunch.log。日志是重启出问题时唯一的现场记录,卸载脚本刻意不删它。
预览分享卡片长什么样 ↓FuriousKinght/dsh-restart-button
分享 · DeepSeek Harness Hub
FuriousKinght/dsh-restart-button

一键重启桌面应用并自动重连页面

这是你的仓库?认领条目

回到顶部