桌面应用重启(dsh-desktop-restart)
给 DeepSeek Harness 桌面版加一个「重启」按钮 —— 不用退出再手动打开。
English | 简体中文
这个插件解决什么问题
桌面版没有提供重启入口。想重启应用,你只能:
- 关掉窗口再重新打开(容易漏掉后台进程)
- 或者去任务管理器结束进程(有风险)
这个插件把「重启」做成一个按钮,放在你顺手的位置。更重要的是:它会先看你在不在忙 —— 有任务在跑的时候不会直接重启,而是先问你。
装好后第一次需要手动重启一次应用(那时插件还没跑起来)。之后就不用管了。
装好后你会看到什么
「重启」会出现在这些地方,默认全部开启:
| 位置 | 怎么用 |
|---|---|
| 左侧边栏顶部 | 点一下,主区打开重启面板,面板里再点「重启」 |
| 左侧边栏底部 | 点两次即可重启 |
| 会话标题栏 | 会话页抬头右侧的「重启」按钮 |
| 账号菜单里 | 打开左下角头像菜单,最上面一项 |
| 输入框斜杠命令 | 打 /restart-desktop 回车 |
| 设置 → 通用 | 只读状态行,不执行重启 |
不想要的入口可以在 设置 → 重启桌面应用 里逐个关掉,改完立即生效,不用重启应用。
界面预览
主面板 —— 状态、重启、刷新状态、等空闲再重启

账号菜单里的入口 —— 打开左下角头像菜单,最上面就是它

侧栏底部的入口 —— 点两次直接重启

设置页 —— 五个入口开关、倒计时、Cookie 健康检查

会话标题栏的按钮 —— 会话页抬头右侧,随手可用

它怎么保护你
有任务在跑的时候(正在生成、正在执行工具、有排队消息、有后台任务),点一下不会立刻重启:
- 第一次点击:按钮变红,文字变成「仍要重启?」,并告诉你会打断什么
- 再点一次:才真的重启
- 或者选「等空闲再重启」:让它自己在任务结束后动手
红色警告 15 秒后自动消失,避免你离开后误碰(可在设置里改成 5–60 秒)。
选择「等空闲再重启」时最长等 30 分钟;超时会明确告诉你「没有执行重启」。
插件正在安装或更新时,重启会被硬拦 —— 那时结束进程会把依赖装坏,等它装完就好(通常一两分钟)。
重启之后
会告诉你这次停机了多久,以及新实例是否已经起来。
需要说明的是:这个回执证明「新应用已经加载了本插件」,不代表你的所有插件和后台任务都已恢复。被打断的工作需要你自己继续 —— 本插件不会替你恢复对话或任务,也不向会话投递消息。
出问题时
重启偶尔会没能顺利收尾(旧实例没能正常退出、新实例没来得及确认、或中间记录被磁盘清理工具/杀软动过)。这时面板会给出一句人话说明原因,并保留一份脱敏诊断供排查:
- 重启成功后,之前留下的「结果未知」提示会自动结案,收进折叠的「已读诊断」里 —— 成功本身就是对之前疑问的回答,不再用告警色挂着。
- 没被回答的提示需要你看过之后点「标记已读」,它才会收进历史。
- 面板提示操作状态记录缺失时:确认应用窗口一切正常,然后点面板上的**「我已核对,清除重启状态」**即可恢复重启入口。若提示清除被拒绝,说明还有重启动作在进行,等一会儿再试。
- 提示重启记录读不出开始时间时:状态可能已经过期,请核对窗口是否已恢复;必要时手动重开应用。
这些提示只描述现象,不会自动删除任何记录;删掉对应的「已读」标记即可恢复原状(存储位置见下文「诊断与设置存储」)。
附带功能(都能单独关掉)
这三个和重启没有直接关系,只是搭这个插件一起发。
登录数据体检
DSH 每启动一次就会存一份登录凭据,而这些凭据不会自动清理。攒到一定数量后,应用可能启动失败(表现为 Web authentication failed)。
设置里会显示上次检查的结果:用了多少、上限多少(16 KiB)、有多少条。默认只在通过本插件重启时检查(那时数据库没被占用)。
检查与备份只读打开数据库;只有你开启「清理旧登录令牌」时才会写库——那一步会先备份、再把多余的
dsh-auth-*收到最新 3 条。可以开启「重启时备份」保留最近 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
于是两件事同时成立:
- host 自己退出 ≠ 重启应用。 主进程把子进程退出当成故障,弹「启动失败」恢复框(默认按钮是「重启应用」)—— 能用,但每次都要人点一下。
- 主进程没给插件留重启通道。 完整 IPC 只有
shutdown/quit-inspection/update-tasks(主进程 → host)与ready/fatal/shutdown-complete/platform-session(host → 主进程);渲染进程侧的DESKTOP_IPC表里也没有 restart。内置的「重启应用与 Host」菜单项只在 development 下加入菜单。
所以一键重启只能由宿主之外的进程完成。
重启是怎么完成的
- 入口向本插件自己的宿主路由发一个同源 POST。
- 宿主先检查安装硬守卫,再持有等待、倒计时、暂停询问和执行状态;正常请求等到已核实空闲,显式立即执行仍须通过安装及身份守卫。
- 校验通过后写下交接描述,然后用
process.execPath(就是桌面应用本身)+ELECTRON_RUN_AS_NODE=1拉起一个分离的 helper,并立刻回 202。helper 因此不依赖系统 Node,也不依赖dsh在 PATH 里。 - helper 等 1.5 秒让响应先送达浏览器,趁主进程还活着问一次它的命令行与可执行文件路径(重启要带上原参数,兜底强杀也要按路径认人)。
- helper 结束 Electron 主进程 —— 故意不带
/T:host 子进程必须活下来,才能在 IPC 断开后走自己的优雅关闭(application.shutdown),把会话日志正常落盘。带/T会把 host 一起强杀,那才会真丢数据。 - helper 等主进程消失,再等监听端口释放(用「连接」探测,不用「绑定」—— 试探性绑定恰恰会占住替代进程要用的那个端口)。
- 端口迟迟不放时,只处理能由进程父子关系、路径和启动身份证明属于本实例的残留。不会按映像名扩大范围,也不会仅凭同路径结束另一个实例;无法证明身份时中止并记录原因。
- helper 用干净的环境拉起新实例 —— 必须去掉
ELECTRON_RUN_AS_NODE,否则新的「应用」会以 Node 模式启动,变成一个没有窗口的进程。新实例立刻退出就再试(默认 3 次),试完还不行才认输,并弹窗叫人手动打开。 - 交接目录保留最近 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 份。
「重启时清理旧登录令牌」默认关闭。开启后,每次通过本插件重启(旧实例已完全退出)时,会把 127.0.0.1 上多余的 dsh-auth-* 删到只剩最新 3 条——这是唯一会写数据库的动作:开启后会自动先备份一次,备份成功才删;备份失败这一轮就不删。删了几条会显示在设置页。留 3 条是安全余量:宿主是否只认一条令牌无法由插件证明。
超限时显示退出 DSH、保留备份及手动处理指引。此功能不提高请求头上限。
检查在独立线程中执行,8 秒预算后终止并等线程退出,避免数据库卡住时拖住重启。备份先写 .partial 再 rename,只有完整文件参与保留。新实例的原始 stdout/stderr 默认不写入诊断日志(其中含 token 等私密串);仅在排查时用环境变量 DSH_DESKTOP_RESTART_CAPTURE=1 显式开启。
临时快照候选预览
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 且不改变原值。
POST /dsh-desktop-restart/api/prefs?clearOp=1 清除一条已经结束的重启状态(界面上的「我已核对,清除重启状态」)。仅接受终态或记录缺失;restart.lock 仍被活进程持有时返回 409。
客户端只镜像宿主状态:等待/倒计时/重启进行中每 2 秒轮询一次,面板上还有未读诊断时降为每 5 秒;倒计时显示用宿主 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。无权限或发送状态未确认时,插件仍显示未读提醒,直到明确点「标记已读」。诊断内容不包含进程路径、命令行或凭据。
一次重启被新实例确认恢复后,宿主会把此前创建、仍未读的诊断自动写成已读(标记里带 auto 与结案它的重启 ID),归入「已读诊断」折叠区;记录本身保留,删掉已读标记即可恢复。诊断长期未清理时只回收「已读」且超出保留数量的最旧记录,未读记录永不自动清理。
跨进程重启使用 desktop-restart/restart.lock(原子创建 + 陈旧锁接管),避免两个实例同时结束同一主进程。
开发状态与验收边界
- 包版本
0.4.8。 - 已验收:真实 DSH 上的重启完整链路(含停机测量与回执)、账号菜单首开注入、面板按钮状态、日志脱敏。
- 未验收:读屏软件、逐帧视觉无闪动、菜单键盘导航、多窗口并发、各主题与窄屏像素布局。
- 离线冒烟测试(124 项,其中 5 项仅 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),低版本自动跳过该功能。
No comments yet. Be the first to write one.