DeepSeek Harness 桌面版重启插件:会话标题栏按钮 / 斜杠命令 / 设置页入口,经分离的 helper…
36.5/100
综合分
5
GitHub 星标
621
npm 周下载
—/5
用户评分
数据截至 2026/11/3 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。
安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add @luoshuizhiwei/dsh-desktop-restartnpm 源 · @luoshuizhiwei/dsh-desktop-restart 的发布者账号已校验与本仓库一致
状态速览
判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态信任档位:已验证
是什么
dsh 原生插件 · desktop
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 0 天前
档位由哪些信号合成
本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。
社区实装还没有人打卡 · 装过的第一个就是你
安装兼容性检查✓ 自动检查通过展开查看检查明细 ↓
以下结论由程序自动检查 npm 包、engines 声明与入口文件得出,未做人工实机验证——能装不等于用着没问题。
✓npm 包@luoshuizhiwei/dsh-desktop-restart @ 0.4.5
✓Node 引擎要求 >=20 · 基线 Node 22.19 满足
✓dsh CLI 依赖未声明 dsh 版本约束
✓入口文件main/exports/bin 已声明
验证方式:npm registry 存在性 + package.json 静态校验 · 最后检查 2026/10/29 08:40:58
安全扫描自动 · 静态分析源码v2.7.0🔵 扫描:含敏感能力尚无人工评估展开查看扫描说明与证据 ↓
安全扫描自动 · 静态分析源码
v2.7.0静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。
可执行外部命令读写/删除本地文件发起外部网络请求
查看扫描证据(共 78 处,展示前 44 条)
- 读取本地文件lib/index.js:160
const parsed = JSON.parse(readFileSync(file, 'utf8')) - 写入本地文件lib/index.js:179
mkdirSync(dirname(file), { recursive: true }) - 写入本地文件lib/index.js:181
writeFileSync(tmp, `${JSON.stringify(next, null, 2)}\n`, 'utf8') - 写入本地文件lib/index.js:182
renameSync(tmp, file) - 写入本地文件lib/index.js:223
mkdirSync(dirname(file), { recursive: true }) - 写入本地文件lib/index.js:224
writeFileSync(temporary, `${JSON.stringify(value, null, 2)}\n`, 'utf8') - 写入本地文件lib/index.js:225
renameSync(temporary, file) - 删除本地文件lib/index.js:228
try { unlinkSync(temporary) } catch { } - 读取本地文件lib/index.js:274
const parsed = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8')) - 读取本地文件lib/index.js:525
if (!statSync(candidate).isDirectory()) return null - 读取本地文件lib/index.js:526
if (!existsSync(join(candidate, 'package.json'))) return null - 读取本地文件lib/index.js:527
if (!existsSync(join(candidate, 'node_modules'))) return null - 读取本地文件lib/index.js:592
if (!existsSync(record)) return absent - 删除本地文件lib/index.js:703
unlinkSync(join(dir, name)) - 删除本地文件lib/index.js:723
if (Date.now() - Date.parse(op.updatedAt) > 7 * 86400000) unlinkSync(join(dir,name)) - 删除本地文件lib/index.js:731
for (const record of records.slice(STATE_KEEP)) unlinkSync(join(dir, record.name)) - 删除本地文件lib/index.js:850
if (record.token === token) unlinkSync(RESTART_LOCK) - 删除本地文件lib/index.js:867
unlinkSync(RESTART_LOCK) - 执行外部命令来自配置/变量lib/index.js:904
const helper = spawn(process.execPath, [HELPER_PATH, handoffPath], { - 发起网络请求lib/client.js:1326
const response = await fetch(url, Object.assign({}, options, controller ? {signal:controller.signal} : {})); - 发起网络请求lib/client.js:2094
const response = await fetch(PREFS_ROUTE + "?readNotice=" + encodeURIComponent(id), {method:"POST"}); - 发起网络请求lib/client.js:3189
fetch((query === "?cancelSweep=1" ? PREFS_ROUTE : SWEEP_ROUTE) + query, { method: "POST" }) - 执行外部命令来自配置/变量lib/helper.cjs:288
out = execFileSync(shell, ['-NoProfile', '-NonInteractive', '-EncodedCommand', encoded], { - 执行外部命令固定命令lib/helper.cjs:398
const child = spawn('powershell.exe', ['-NoProfile', '-NonInteractive', '-WindowStyle', 'Hidden', '-EncodedCommand', encoded], { detached: true, stdio - 写入本地文件lib/helper.cjs:400
try { fs.writeFileSync(paths.delivery, JSON.stringify({ status: 'failed', code: 'toast-process-failed' }), 'utf8') } - 读取本地文件lib/helper.cjs:539
try { previous = JSON.parse(fs.readFileSync(options.statePath, 'utf8')) } - 读取本地文件lib/helper.cjs:547
if (!options.databasePath || !fs.existsSync(options.databasePath)) { stage = 'database-missing'; throw new Error(stage) } - 写入本地文件lib/helper.cjs:555
fs.mkdirSync(options.backupDir, { recursive: true }) - 写入本地文件lib/helper.cjs:559
fs.renameSync(pendingBackup, path.join(options.backupDir, backupFile)) - 读取本地文件lib/helper.cjs:562
const backups = fs.readdirSync(options.backupDir).filter(name => /^auth-cookie-\d+-\d+\.sqlite$/u.test(name)) - 删除本地文件lib/helper.cjs:564
for (const file of backups.slice(3)) fs.unlinkSync(path.join(options.backupDir, file)) - 写入本地文件lib/helper.cjs:586
fs.mkdirSync(path.dirname(options.statePath), { recursive: true }) - 写入本地文件lib/helper.cjs:588
fs.writeFileSync(temporary, JSON.stringify(result), 'utf8'); fs.renameSync(temporary, options.statePath) - 读取本地文件lib/helper.cjs:620
try { previous = JSON.parse(fs.readFileSync(options.statePath, 'utf8')) } - 读取本地文件lib/helper.cjs:641
const record = JSON.parse(fs.readFileSync(lock.path, 'utf8')) - 写入本地文件lib/helper.cjs:645
fs.writeFileSync(temporary, JSON.stringify({...record,pid:process.pid}), 'utf8') - 读取本地文件lib/helper.cjs:654
const record = JSON.parse(fs.readFileSync(lock.path, 'utf8')) - 删除本地文件lib/helper.cjs:655
if (record.token === lock.token) fs.unlinkSync(lock.path) - 执行外部命令固定命令lib/helper.cjs:711
execFileSync('taskkill', ['/PID', String(expected.pid), '/F'], { stdio: 'ignore', windowsHide: true }) - 执行外部命令固定命令lib/helper.cjs:724
const out = execFileSync('netstat', ['-ano'], { - 执行外部命令来自配置/变量lib/helper.cjs:986
const child = spawn(exe, relaunchArgs ?? [], { - 读取本地文件lib/sweep.js:67
const info = await lstat(current) - 读取本地文件lib/sweep.js:77
const children = await readdir(current, { withFileTypes: true }) - 读取本地文件lib/sweep.js:127
dirents = await readdir(tempRoot, { withFileTypes: true })
扫描于 2026/11/3(0 天前) · 范围 入口闭包 + 全包扫描 · 来源 npm 发布包
静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。
给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个
功能亮点
- 一键重启桌面应用
- 会话日志安全落盘
- 多入口触发重启
最近更新
在 GitHub 查看全部 →v0.4.62026-10-30
· 修复
· 等待中主按钮点了没反应:等待/倒计时/暂停期间主按钮现在明确禁用并显示「已锁定」,
点击不再落入「第一次静默武装确认、第二次直接重启」的路径。常规出口是旁边的「取消等待」。
· 标题栏点击无反馈:宽屏下也会显示状态文字(确认重启?/仍要重启?/已锁定),
并按状态改变颜色。此前宽屏只有图标,点第一下看不出任何变化。
· 红色警告不会消失:有任务时的「仍要重启?」现在 15 秒后自动撤销,
可在设置里改成 5–60 秒。此前会一直挂着,离开后误碰一次就重启。
· 面板出现两个「刷新状态」:执行期间「状态重试」按钮与常驻「刷新」按钮文案重复,
已改为只在状态读取真的失败时才出现。
· 账号菜单重启项首次打开不显示:此前要开关两次才出现。
真实原因是菜单在点击回调执行前就已提交进页面,只订阅「后续变更」的观察器永远等不到;
现改为点击时立即探测,观察器仅作兜底。
· 账号菜单重启项位置跳动:改为锚定菜单内容容器最前,并在绘制前完成插入。
· 悬停按钮时文字闪烁:悬停会触发一次状态读取,期间「刷新状态」变成「稍等…」并变半透明。
现在悬停走后台读取(仍更新守卫信息),不再改变按钮文案与禁用外观;显式点击刷新保留加载反馈。
· 重启执行期间出现重复按钮、helper 启动失败未被捕获、插件停用后入口复活、
旧确认定时器干扰新一轮警告、设置保存无超时导致控件永久禁用:均已修复。
· op 记录读取失败会用空对象覆盖原文件(可能导致 profile 被永久锁定);
关键写入失败不记录日志(失败路径静默丢失)。现读失败不覆盖、写失败必记日志。
· 安全性
· 认证 Cookie 检查改为只读统计 + 可选备份,不再自动删除任何 Cookie。
数据库始终只读打开,备份保留最近 3 份;超限时给出手动处理指引。
· Cookie 检查移入独立线程,8 秒预算后终止并等线程退出,避免数据库卡住时拖住重启。
· 跨进程重启锁(原子创建 + 陈旧锁接管),避免两个实例同时结束同一主进程。
· 日志与交接记录中的可执行文件路径改为只记文件名,启动参数值脱敏。
· 平台鉴权服务不可用时,写路由返回 503 而不是放行。
· 变更
· 有任务在跑时,主按钮第二击直接强制重启(跳过等待与倒计时),
不再保留独立的「仍要立即重启」按钮;危险操作使用官方危险色。
· 标题栏的「刷新」按钮改为可配置,默认关闭。
· 快照扫描只保留预览,移除不可达的删除能力;界面不再显示「已删除/释放空间」。
· 移除设置页内部的「账号菜单诊断」块(开发者调试信息)。
· Cookie 检查时间显示为本地时间。
· 工程
· 引入 ESLint 与 CI lint 步骤。
· npm 包瘦身:只发布运行时文件(9 个文件,此前 22 个)。
· 测试:新增行为契约用例;保留的源码文本断言单独登记并标注「非行为验收」,
不再计入行为覆盖;npm test 共 smoke 119 / client 191 / opt02 7。
· 已知边界
· 账号菜单入口依赖宿主菜单 DOM 结构,结构变化时会静默跳过(不破坏原菜单)。
· 读屏软件、逐帧视觉、菜单键盘导航、多窗口并发、各主题与窄屏像素布局尚未验收。
· Cookie 健康检查要求 Node.js ≥ 22.16(node:sqlite),低版本自动跳过该功能。
v0.4.52026-10-28
0.4.5 变更(自 0.4.4)
移除
· 自动续跑整体移除:v4 消息来源格式不兼容,投递必然失败。宿主函数、启动块、
状态字段、设置项、标记里的会话清单、客户端界面/文案/解析、README 描述与相关
测试全部删除,代码中的 resume 引用为 0。
(0.4.4 曾计划过这项能力,本版确认它无法工作,故彻底移除而不是保留开关。)
新增
· 重启阶段与失败原因可见;完成后显示「宿主已于 HH:MM:SS 完成重启(停机 N 秒)」。
· 默认先等空闲:有任务在跑时排队等待,任务结束自动重启;
/restart-desktop --now 或界面上「仍要立即重启」立即执行。
· 失败或结果未知时写入未读诊断,右下角通知只打开脱敏 HTML,
不启动 DSH、不重放模型动作;已读只是独立标记,不删除诊断、不解除保护。
· 扫描改为只预览不删除,显示候选数、估算大小、跳过原因与错误码。
安全性
· helper 身份核验(PID / 父 PID / 创建时间 / 路径);启动参数读不出时拒绝执行;
端口归属无法证明时中止;记录缺失标为结果未知且不放行。
工程
· 测试拆为 7 个套件(冒烟 / 宿主 / 客户端 / 引擎 / opt02 / 启动 / 墙上时钟),
CI 矩阵扩到 ubuntu 20/22/24 与 windows 24 并运行完整 npm test。
· 发布前校验标签与 package.json 版本一致;Release 正文取标签注解。
· 内部交接文档不再进入 npm 包。
验收
· 离线门禁退出 0:冒烟 116 / 宿主 41 / 客户端 56 / opt02 7 / 启动 6,墙上时钟在预算内。
· Linux 腿实测 111 项通过(4 项仅 Windows 跳过),各套件均已真实执行。
· 真实 DSH 重启链路(含停机测量与完成回执)于 2026-10-22 在本机验收通过;
读屏验收尚未进行,离线测试不能替代它。
Full Changelog:
v0.4.42026-10-20
0.4.4 变更(自 0.4.3)
新增
· 「附带功能」(清理遗留快照 / 隐藏「意见反馈」)对新用户默认关闭:
装上插件不再顺手改掉系统行为。老用户手动设过的值不受影响。
修复
· 第三方皮肤(如 dsh-claude-style)把 aria-label 当行标题时,整句
「有任务在跑(…);点两次仍可重启」会被画成按钮文字 —— aria-label 改为
四字以内的状态词,详细提示走 aria-describedby 指向的隐藏文本。
· 主区重启面板按同座位(任务看板)的页面版式重排:铺满高度、14/16 内边距、
标题 16px/700 与状态词同行,说明 / 警告 / 结果各自卡片化。
调整
· 侧栏两个入口改短名「重启应用」(设置分区与页面标题保留「重启桌面应用」)。
· 中文无障碍文案统一压到 4 字以内。
工程
· CI 发版时自动创建 GitHub Release(正文取标签注解 + compare 链接)。
· 冒烟测试 108 → 112 项。
Full Changelog:
数据来自仓库的 GitHub Release,可能与插件实际版本不一致。
README
由 DeepSeek 最新模型翻译生成桌面应用重启(dsh-desktop-restart)
给 DeepSeek Harness 桌面版加一个「重启」按钮 —— 不用退出再手动打开。
这个插件解决什么问题
桌面版没有提供重启入口。想重启应用,你只能:
- 关掉窗口再重新打开(容易漏掉后台进程)
- 或者去任务管理器结束进程(有风险)
这个插件把「重启」做成一个按钮,放在你顺手的位置。更重要的是:它会先看你在不在忙 —— 有任务在跑的时候不会直接重启,而是先问你。
装好后第一次需要手动重启一次应用(那时插件还没跑起来)。之后就不用管了。
装好后你会看到什么
「重启」会出现在这些地方,默认全部开启:
| 位置 | 怎么用 |
| 左侧边栏顶部 | 点一下,主区打开重启面板,面板里再点「重启」 |
| 左侧边栏底部 | 点两次即可重启 |
| 会话标题栏 | 会话页抬头右侧的「重启」按钮 |
| 账号菜单里 | 打开左下角头像菜单,最上面一项 |
| 输入框斜杠命令 | 打 /restart-desktop 回车 |
| 设置 → 通用 | 只读状态行,不执行重启 |
不想要的入口可以在 设置 → 重启桌面应用 里逐个关掉,改完立即生效,不用重启应用。
展开完整文档(README 剩余 1 节)
桌面应用重启(dsh-desktop-restart)
给 DeepSeek Harness 桌面版加一个「重启」按钮 —— 不用退出再手动打开。
这个插件解决什么问题
桌面版没有提供重启入口。想重启应用,你只能:
- 关掉窗口再重新打开(容易漏掉后台进程)
- 或者去任务管理器结束进程(有风险)
这个插件把「重启」做成一个按钮,放在你顺手的位置。更重要的是:它会先看你在不在忙 —— 有任务在跑的时候不会直接重启,而是先问你。
装好后第一次需要手动重启一次应用(那时插件还没跑起来)。之后就不用管了。
装好后你会看到什么
「重启」会出现在这些地方,默认全部开启:
| 位置 | 怎么用 |
| 左侧边栏顶部 | 点一下,主区打开重启面板,面板里再点「重启」 |
| 左侧边栏底部 | 点两次即可重启 |
| 会话标题栏 | 会话页抬头右侧的「重启」按钮 |
| 账号菜单里 | 打开左下角头像菜单,最上面一项 |
| 输入框斜杠命令 | 打 /restart-desktop 回车 |
| 设置 → 通用 | 只读状态行,不执行重启 |
不想要的入口可以在 设置 → 重启桌面应用 里逐个关掉,改完立即生效,不用重启应用。
界面预览
主面板 —— 状态、重启、刷新状态、等空闲再重启
主面板
账号菜单里的入口 —— 打开左下角头像菜单,最上面就是它
账号菜单
侧栏底部的入口 —— 点两次直接重启
侧栏底部入口
设置页 —— 五个入口开关、倒计时、Cookie 健康检查
设置页
会话标题栏的按钮 —— 会话页抬头右侧,随手可用
标题栏按钮
它怎么保护你
有任务在跑的时候(正在生成、正在执行工具、有排队消息、有后台任务),点一下不会立刻重启:
1. 第一次点击:按钮变红,文字变成「仍要重启?」,并告诉你会打断什么
2. 再点一次:才真的重启
3. 或者选「等空闲再重启」:让它自己在任务结束后动手
红色警告 15 秒后自动消失,避免你离开后误碰(可在设置里改成 5–60 秒)。
选择「等空闲再重启」时最长等 30 分钟;超时会明确告诉你「没有执行重启」。
插件正在安装或更新时,重启会被硬拦 —— 那时结束进程会把依赖装坏,等它装完就好(通常一两分钟)。
重启之后
会告诉你这次停机了多久,以及新实例是否已经起来。
需要说明的是:这个回执证明「新应用已经加载了本插件」,不代表你的所有插件和后台任务都已恢复。被打断的工作需要你自己继续 —— 本插件不会替你恢复对话或任务,也不向会话投递消息。
附带功能(都能单独关掉)
这三个和重启没有直接关系,只是搭这个插件一起发。
登录数据体检
DSH 每启动一次就会存一份登录凭据,而这些凭据不会自动清理。攒到一定数量后,应用可能启动失败(表现为 Web authentication failed)。
设置里会显示上次检查的结果:用了多少、上限多少(16 KiB)、有多少条。默认只在通过本插件重启时检查(那时数据库没被占用)。
数据库始终只读打开,不会删除或修改任何登录数据。可以开启「重启时备份」保留最近 3 份;接近上限时会提醒你手动处理,并给出具体路径。
遗留临时文件预览
DSH 做改动对比时会在系统临时目录留下快照副本,异常退出可能残留。
这个功能只扫描和预览,不会删除任何文件 —— 因为无法可靠判断哪个副本还在被别的实例使用。它会告诉你发现了多少候选、大概占多少空间,删不删由你自己决定。
隐藏账号菜单里的「意见反馈」
账号菜单里「设置」下面紧挨着「意见反馈」,点它会跳到外部网页,容易误点。
打开这个开关可以把它藏起来(默认不藏)。认不出菜单项时什么都不做,宁可留着也不误伤别的项。
安装
从 npm(推荐)
dsh plugin add @luoshuizhiwei/dsh-desktop-restart
从源码
// ~/.dsh/profiles/desktop/package.json
{
"dependencies": {
"@luoshuizhiwei/dsh-desktop-restart": "link:../../local-plugins/dsh-desktop-restart"
},
"dsh": { "profile": { "bundles": [ "...", "@luoshuizhiwei/dsh-desktop-restart" ] } }
}
装好后重启一次应用才会加载 —— 这第一次得手动。
常见问题
为什么不能直接用「重启 dsh web」那类插件? 桌面版的进程结构和终端里的 dsh web 完全不同,那些插件在桌面版里用不了。原因见下方「为什么需要专门的桌面版重启插件」。
重启会不会丢数据? 正常关闭会保存会话。插件结束的是 Electron 主进程,并故意让宿主子进程自己优雅退出,把会话日志正常落盘。但正在生成的回复会被打断,这部分不会自动恢复。
按钮显示「已锁定」,点不动? 说明有等待或倒计时正在进行。用旁边的「取消等待」退出。
红色的「仍要重启?」一直挂着? 它会 15 秒后自动消失。如果一直不消失,说明状态读取出了问题,可以点「刷新状态」重试。
装好插件后应用启动失败? 很可能是上面说的登录数据攒太多了。完全退出应用,删除 %APPDATA%\@deepseek-ai\dsh-desktop\Network\Cookies 后重新打开(该文件会自动重建,需要重新登录)。
技术细节
以下内容面向想了解实现或参与开发的读者,普通使用不需要看。
为什么需要专门的桌面版重启插件
桌面版的进程模型和终端里的 dsh web 完全不同:
DeepSeek Harness.exe ← Electron 主进程:窗口、托盘、自动更新 └── DeepSeek Harness.exe ← host 子进程(同一个 exe + ELECTRON_RUN_AS_NODE=1) └── 本插件 跑 dsh-desktop-host,监听 127.0.0.1
于是两件事同时成立:
1. host 自己退出 ≠ 重启应用。 主进程把子进程退出当成故障,弹「启动失败」恢复框(默认按钮是「重启应用」)—— 能用,但每次都要人点一下。
2. 主进程没给插件留重启通道。 完整 IPC 只有 shutdown / quit-inspection / update-tasks(主进程 → host)与 ready / fatal / shutdown-complete / platform-session(host → 主进程);渲染进程侧的 DESKTOP_IPC 表里也没有 restart。内置的「重启应用与 Host」菜单项只在 development 下加入菜单。
所以一键重启只能由宿主之外的进程完成。
重启是怎么完成的
1. 入口向本插件自己的宿主路由发一个同源 POST。
2. 宿主先检查安装硬守卫,再持有等待、倒计时、暂停询问和执行状态;正常请求等到已核实空闲,显式立即执行仍须通过安装及身份守卫。
3. 校验通过后写下交接描述,然后用 process.execPath(就是桌面应用本身)+ ELECTRON_RUN_AS_NODE=1 拉起一个分离的 helper,并立刻回 202。helper 因此不依赖系统 Node,也不依赖 dsh 在 PATH 里。
4. helper 等 1.5 秒让响应先送达浏览器,趁主进程还活着问一次它的命令行与可执行文件路径(重启要带上原参数,兜底强杀也要按路径认人)。
5. helper 结束 Electron 主进程 —— 故意不带 /T:host 子进程必须活下来,才能在 IPC 断开后走自己的优雅关闭(application.shutdown),把会话日志正常落盘。带 /T 会把 host 一起强杀,那才会真丢数据。
6. helper 等主进程消失,再等监听端口释放(用「连接」探测,不用「绑定」—— 试探性绑定恰恰会占住替代进程要用的那个端口)。
7. 端口迟迟不放时,只处理能由进程父子关系、路径和启动身份证明属于本实例的残留。不会按映像名扩大范围,也不会仅凭同路径结束另一个实例;无法证明身份时中止并记录原因。
8. helper 用干净的环境拉起新实例 —— 必须去掉 ELECTRON_RUN_AS_NODE,否则新的「应用」会以 Node 模式启动,变成一个没有窗口的进程。新实例立刻退出就再试(默认 3 次),试完还不行才认输,并弹窗叫人手动打开。
9. 交接目录保留最近 20 组 handoff/log;已完成操作记录按 profile 保留最近 20 份可清理记录,pending、unknown 及未读诊断引用的记录受保护。
守卫判据
「能重启」不等于「该重启」。应用自己的退出流程会先问宿主「现在停掉会打断什么工作」(hasDesktopActiveTasks),有就弹窗问用户;而本插件走的是直接结束主进程,绕过了那道检查。于是补上等价的两道守卫:
| 守卫 | 判据 | 挡住谁 | 出路 |
| 有任务在跑或无法核实 | agents + jobs 服务 | 本实例各会话、工具、排队消息及后台任务 | 正常请求等待;倒计时中新工作触发暂停询问;额外确认后可立即执行 |
| 插件正在安装/更新 | <profile>/.plugin-manager/run.json 里的进程此刻还活着 | 内置插件管理、插件市场、任何 dsh plugin 操作 | 硬拦:带 ?force=1 也不放行 |
任务变化由宿主判定:等待时每 5 秒复查;倒计时中最多每秒复查。发现新工作后只创建一个询问标识,询问提供「继续等待 / 立即重启 / 取消」。继续等待会重新计算完整倒计时,但不延长原总期限。
判据是进程还活着,而不是「记录文件在不在」:run.json 在应用被硬杀后会残留,拿「文件在不在」硬拦会把重启按钮永久锁死。
回执机制
回执绑定到当前 profile、安装实例和本次重启的唯一标识;helper 只把这个标识传给这次拉起的新应用,新宿主在短期有效窗口内认领并消费一次。排程失败会让本次记录失效。
回执区分发起时间与 Host 恢复时间;它证明新宿主已加载插件,不代表整个界面、所有插件或后台任务已完成恢复。downtimeMs 展示为「交接耗时」,不称精确界面停机时间。
界面规范
界面直接用官方标准件,不是照着画的:
- 左上角那一条挂在官方的面板座位上(sidebar.panellist):插件只登记 id / order / 文案 和一个图标组件,整行由侧栏自己绘制 —— 所以它不可能不像官方。
- 主区面板按同座位(任务看板)的页面版式:铺满主区高度、官方底色、16px/700 标题与右上角状态词同一行。
- 侧栏底部入口挂在 sidebar.footer.action,照抄同座位占用者的 42px 动作行规格;侧栏收起成 rail 时变 36×36 圆形。
- 标题栏按钮照抄同座位里官方那个真实按钮(ui-jobs 的 JobListAction)。
- 颜色语义用官方 token:确认与警告用危险红,等待/倒计时用次要警示色,失败用错误色;系统开了「减少动效」就不转圈。
- 官方组件在 dsh.client.inject 里声明后由加载器先注册再执行本插件;万一 DSH 改名或挪包导致 require 失败,会退回自己那份逐字照抄官方数值的样式。
完整对照(含官方逐字 CSS 与官方源码路径)见 docs/official-ui-standard.md。
入口开关与默认值
五个界面入口开关、标题栏刷新按钮与斜杠命令,默认全开(标题栏刷新按钮默认关闭;菜单项仍须通过结构检查)。选择存在本机浏览器(localStorage);默认值来自插件的 config —— 在 ~/.dsh/profiles/<profile>/cordis.patch.yml 里本插件那一行写:
config: { panel: false, footer: false, header: false, settingsRow: false, accountMenu: false, command: false, enabled: false }
可以一次改掉默认值(command 是斜杠命令,enabled 是总开关,headerRefresh 控制标题栏那个「刷新」按钮)。
| 开关 | 关掉之后 |
| 左上角的面板入口 | 侧栏左上角那一条消失(连带主区那个重启面板) |
| 侧栏底部的入口 | 侧栏底部那一条消失 |
| 会话标题栏按钮 | 会话页抬头右侧那个按钮消失 |
| 标题栏的刷新按钮 | 会话页抬头右侧的「刷新」消失(默认关闭) |
| 设置 → 通用 那一行 | 通用设置页里的只读状态行消失 |
| 账号菜单入口 | 账号菜单里的重启项消失 |
认证 Cookie 健康检查
设置页显示上次停机检查的估算值、16 KiB 默认上限、认证 Cookie 条数和检查时间;它不是实时监控。没有样本时显示「尚无停机检查结果」,普通手工启动/退出不会更新采样。
「重启时备份 Cookie 数据库」默认关闭,「80% 提前警告」默认开启。数据库始终只读;可选一致性备份保留最近 3 份,不自动删除任何 Cookie。超限时显示退出 DSH、保留备份及手动处理指引。此功能不提高请求头上限。
检查在独立线程中执行,8 秒预算后终止并等线程退出,避免数据库卡住时拖住重启。备份先写 .partial 再 rename,只有完整文件参与保留。
临时快照候选预览
DSH 的改动对比快照位于系统 Temp 的 dsh-workspace-changes-* 目录;崩溃可能留下副本,但年龄并不能证明它是孤儿。
当前只扫描和预览,不删除快照。 上游没有可靠的活跃快照归属记录,生产路由不会伪造删除许可。扫描开关保留为「启动时自动扫描」(默认关闭),打开后启动 10 秒扫描一次,也可以手动立即扫描。
候选必须是指定前缀的真实目录、创建于本宿主启动前、至少 24 小时,且整棵树没有启动后的写入。符号链接、junction、读取失败或无法完整检查的树保守跳过。结果显示扫描数量、候选数量、估算大小及未删除的原因,不将候选大小说成已释放空间。
接口
GET /dsh-desktop-restart/api/status 返回 restartRequest,包含 state(idle / waiting / countdown / paused / executing / terminal)、countdownRemainingMs、countdownUntil、deadlineAt、pausedReason 与 ask。旧 waiting 在等待、倒计时、暂停时继续存在。
POST /dsh-desktop-restart/api/restart?choice=wait|now|cancel 仅在 paused 有效;新客户端同时携带 ask,过期标识返回 409。其他阶段取消用 cancelWait=1;立即执行兼容 force=1&now=1。
POST /dsh-desktop-restart/api/prefs?countdown=<整数> 保存下次倒计时(0–60),越界或非整数返回 400 且不改变原值。
客户端只镜像宿主状态,共用一条 2 秒网络轮询;倒计时显示用宿主 countdownUntil 和本地每秒时钟推算,不能决定执行。
安全模型
POST /api/restart 在动手之前完成全部校验:
| 检查 | 拒绝方式 |
| 方法不是 POST | 405,带 Allow: POST |
| TCP 对端不是回环地址 | 403 |
| Host 不是回环名字 | 403 |
| 带 forwarded / x-forwarded-for / x-real-ip | 403 |
平台鉴权服务不可用时,写路由返回 503 而不是放行。
诊断与设置存储
宿主设置使用 desktop-restart/settings-<profileId>.json,按 profile 目录身份隔离。新文件不存在且旧 settings.json 存在时,排他创建新文件并复制一次;保留旧文件原内容。
失败及结果未确认按「操作 ID + 结果类别」各创建一次脱敏诊断记录,并尝试 Windows toast。无权限或发送状态未确认时,插件仍显示未读提醒,直到明确点「标记已读」。诊断内容不包含进程路径、命令行或凭据。
跨进程重启使用 desktop-restart/restart.lock(原子创建 + 陈旧锁接管),避免两个实例同时结束同一主进程。
开发状态与验收边界
- 包版本 0.4.6。
- 已验收:真实 DSH 上的重启完整链路(含停机测量与回执)、账号菜单首开注入、面板按钮状态、日志脱敏。
- 未验收:读屏软件、逐帧视觉无闪动、菜单键盘导航、多窗口并发、各主题与窄屏像素布局。
- 离线冒烟测试(119 项,其中 4 项仅 Windows)保留为基础检查。npm test 还运行宿主、客户端与进程/快照行为回归,覆盖一次性消费、取消、失败回滚、重试、多语言、跨实例隔离和预览保护。组件使用 React/fetch 桩,helper 使用隔离进程/VM 桩;不连接真实 DSH,不向真实会话投递消息,也不扫描删除真实 Temp。CI 在 Ubuntu Node 20/22/24 与 Windows Node 24 运行这些检查。测试数量与范围以实际输出为准,源码正则检查不等同于真实界面验收。
- 核心重启要求 Node.js ≥ 20;Cookie 健康检查要求 Node.js ≥ 22.16(node:sqlite),低版本自动跳过该功能。预览分享卡片长什么样 ↓
分享 · DeepSeek Harness Hub
luoshuizhiwei/dsh-desktop-restart
DeepSeek Harness 桌面版重启插件:会话标题栏按钮 / 斜杠命令 / 设置页入口,经分离的 helper…
相关插件 · 桌面端
Codex 任务面板chuspeeism/dashi-taskboard
现代化可灵活嵌入的任务面板,支持 Codex、DeepSeek Harness
★ 3,409已汉化DSH
71.7
多智能体自进化开发平台Prism-Shadow/penguin-harness
统一、稳定的 Agent 自进化框架全自动优化:任意模型 · 任意算法 · 任意 Benchmark
★ 3,111已汉化DSH
71.1
桌面宠物PC2005-cloud/dsh-pet
DSH 桌面宠物:一行命令装好即用的透明动画小桌宠,支持多开、大小位置随心配置;还内置 DIY 素材链,能用 AI…
★ 5,341已汉化DSH
71
多端 Agent 工作台GCWing/OpenBitFun
OpenBitFun 是一个高度可定制的开源 Agent 工作台,让任务按你的方式持续推进。
★ 3,449已汉化DSH
70.8
DSH Desktopanywhere-labs/dsh-desktop
为 DeepSeek Harness DSH 插件生态打造的现代化桌面端解决方案。万物皆「插件」,桌面本身也是「插件」。
★ 34,532已汉化DSH
70.7
桌面宠物伴侣OpenPetsHQ/openpets
桌面伴侣平台,带宠物、插件,还能选配本地智能体集成。
★ 1,353已汉化DSH
70.2