Sidebar actions · 侧边栏快捷操作
给 DeepSeek Harness 的左侧边栏底部加三个按钮:刷新 / 重启 / 退出。 刷新只是重新加载页面;重启和退出会让整个桌面端退出(重启后自动拉起)。
A DSH plugin that puts reload / restart / quit in the sidebar footer.
仓库 / repo: https://github.com/jiboda394-glitch/dsh-sidebar-actions ·
从 Releases 下载 dsh-sidebar-actions-<版本>.tgz 即可离线分发。
功能
- 刷新:重新加载 Web 界面(桌面端没有 Ctrl+R 菜单项,这个按钮补上)。
- 重启:退出桌面端后用同一条命令行重新启动,插件改动、profile 改动随之生效。
- 退出:关闭桌面端。
- 侧边栏展开时显示「图标 + 文字」,折叠成 56px 窄栏时收成一个按钮,点开是三行菜单。
- 重启 / 退出需要连点两次确认(4 秒内),避免误触把应用关掉。
- 按钮会读宿主状态:非桌面环境(纯
dsh web)下重启/退出自动禁用;失败时按钮旁出现小红点,悬停可看原因。
安装
DSH Desktop
- 打开 设置 → 插件(或插件市场)。
- 选「从本地目录安装」并选中本包的目录,或把
dist/dsh-sidebar-actions-1.0.0.tgz的路径填进安装框。 - 安装完成后刷新一次页面(页面里的「刷新」按钮就行),底部就会出现三个按钮。
普通 dsh web
dsh plugin --profile <profile> add ./dsh-sidebar-actions-<版本>.tgz
# 或直接从 GitHub 安装(源码):
dsh plugin --profile <profile> add github:jiboda394-glitch/dsh-sidebar-actions
# 或用 release 里的 tarball 地址(版本号按需替换):
dsh plugin --profile <profile> add https://github.com/jiboda394-glitch/dsh-sidebar-actions/releases/download/v1.0.1/dsh-sidebar-actions-1.0.1.tgz
装完重启一次 dsh web。
重启 / 退出是怎么实现的
桌面端本身没有把「退出」暴露给插件:页面拿不到 app.quit(),宿主进程也只是 Electron 的一个子进程。所以插件按机器能力选两条路径,并在 GET /dsh-sidebar-actions/api/state 里如实报告用的是哪一条。
1. 优雅桥接(graceful,推荐)
桌面端主进程里加一小段轮询(见文末「可选:让重启走优雅路径」),插件写请求文件,主进程自己调用 app.relaunch() / app.quit()——和托盘菜单同一条路径,运行中的任务会走原生确认框。
插件不靠猜:桥接方启动时在 %LOCALAPPDATA%\dsh-restart.bridge 留下标记文件,插件看到标记才走桥接。标记过期(应用升级把补丁覆盖掉)也不会卡死——请求文件 2.5 秒没人消费就自动退回下面的独立助手。
2. 独立助手(forced,无人值守)
没有桥接时,插件用一个每次用户级计划任务(任务名 DSH-SidebarActions)启动 scripts/dsh-stop.ps1。助手跑在应用进程树之外,所以:
- 只结束请求里的那个 shell 进程(并校验它确实是主进程,不是渲染进程);
- 等同一可执行文件的其余进程(渲染进程、Host)自己退出;
- 重启用原始命令行(WMI
Win32_Process.Create)把应用拉起来。
这条路径能在未打补丁的原版应用上工作,代价是:它是强制结束,不会弹「当前任务会被中断」的原生确认——所以按钮要求二次确认,悬停提示也会写明。Windows 专属;其他平台只能用桥接。
状态、日志与排错
| 东西 | 位置 |
|---|---|
| 状态接口 | GET /dsh-sidebar-actions/api/state(同源) |
| 只读自检 | GET /dsh-sidebar-actions/api/diagnose:shell 解析、助手任务、桥接、路由、页面是否挂载 |
| 插件日志 | %LOCALAPPDATA%\dsh-sidebar-actions\host.log |
| 助手日志 | %LOCALAPPDATA%\dsh-sidebar-actions\helper.log |
| 助手任务 | schtasks /query /tn DSH-SidebarActions /v /fo list |
/diagnose 的 checks 就是「功能是否可用」的清单:desktop、shellReachable、helperReady、routeReady、clientMounted。应用升级后先打开这个地址跑一次,比点按钮试错快。
clientMounted 由页面自己请求 /state 时置位。桌面端页面来自 dsh-app://,Chromium 不会带 Sec-Fetch-Site,所以这里也认「浏览器类 User-Agent」;命令行 curl/PowerShell 探测不算。刚重载宿主时它是 false,刷新一次页面再看。
常见现象:
- 按钮点了没反应,9 秒后出现「没有响应」:看
helper.log。若里面连helper start都没有,多半是任务没建起来(schtasks /query查不到),看host.log里的task ensure failed。 - 重启后窗口没回来:
helper.log里的replacement started与replacement processes after 4s会写明是否起来了;start failed一般是杀毒软件拦截,手动启动一次即可。 - 想确认当前是哪种模式:
/state里的via是graceful/forced/unsupported。
卸载
- 在「设置 → 插件」里移除本插件(或
dsh plugin --profile <profile> remove dsh-sidebar-actions)。 - 可选清理:
schtasks /delete /tn DSH-SidebarActions /f
Remove-Item -Recurse -Force "$env:LOCALAPPDATA\dsh-sidebar-actions"
可选:让重启走优雅路径
这一步改的是应用自己的
app.asar,属于「你自己机器上的取舍」:应用每次升级都会覆盖回去,需要重新打。 不打也能用,只是走上面的「独立助手」强制路径。
在主进程(resources/app.asar 里的 lib/main.js)中,托盘初始化之后加:
// dsh-sidebar-actions: graceful relaunch / quit requested by the sidebar.
{
const { join } = require('node:path');
const { existsSync, rmSync, writeFileSync } = require('node:fs');
const base = process.env.LOCALAPPDATA || app.getPath('temp');
const restart = join(base, 'dsh-restart.request');
const quit = join(base, 'dsh-quit.request');
// The marker tells the plugin this shell really polls the two files.
try { writeFileSync(join(base, 'dsh-restart.bridge'), new Date().toISOString()); } catch {}
setInterval(() => {
try {
if (existsSync(restart)) { rmSync(restart, { force: true }); app.relaunch(); app.quit(); }
if (existsSync(quit)) { rmSync(quit, { force: true }); app.quit(); }
} catch (error) { console.warn('dsh-sidebar-actions: request poll failed', error); }
}, 1000);
}
改完重启一次应用,/state 的 via 会变成 graceful。
若之后不再想用桥接,删掉 %LOCALAPPDATA%\dsh-restart.bridge 即可,插件会退回独立助手。
兼容性与安全
- 目标环境:DeepSeek Harness Desktop 0.2.x(Windows)。
dsh web下只有「刷新」可用,这是有意为之。 - 升级适配:不绑定 DSH 版本,也不读私有 API —— 桌面与否看
process.versions.electron/ELECTRON_RUN_AS_NODE/resources/app.asar,桥接看标记文件,重启只看操作系统进程。/state.dshVersion会报出当前宿主版本。 - 每次重启前会用 PowerShell 校验
process.ppid确实是桌面 shell(可执行文件一致、命令行不含--type=)。将来若宿主不再是 shell 的直接子进程,插件会返回shell-not-found并拒绝执行,而不是杀错进程。 webServer.register或槽位接口若在某次升级里改名,按钮/接口会失效但不会拖垮应用:宿主日志会写webServer.register unavailable,页面半则只是不注册。- 不联网、不收集任何数据;所有逻辑都在本机。
- API 只接受同源
POST(Sec-Fetch-Site/Origin校验),别的网页无法触发重启。 - 助手任务以当前用户运行,仅当需要重启/退出时被
schtasks /run拉起;不会常驻。
English
Adds reload / restart / quit to the sidebar footer.
- Reload is plain
location.reload(). - Restart and quit need the desktop shell. The plugin uses a graceful bridge when
%LOCALAPPDATA%\dsh-restart.bridgeexists and the shell pollsdsh-restart.request/dsh-quit.request; an ignored request falls back to a detached helper after 2.5 s: a per-user scheduled task (DSH-SidebarActions) runningscripts/dsh-stop.ps1outside the app's process tree, which stops the shell process and starts the executable again with its original command line. - The forced path works on an unpatched app but interrupts running tasks without the native prompt, so every stop needs a second click.
- Windows only for the forced path; other platforms use the bridge exclusively.
State and logs: GET /dsh-sidebar-actions/api/state, %LOCALAPPDATA%\dsh-sidebar-actions\{host,helper}.log.
License
MIT. See LICENSE.
No comments yet. Be the first to write one.