dsh-web
zhu1090093659/dsh-webDeepSeek Harness(DSH)Web 插件聚合生态包 · 一切皆插件,创意工坊分发(dsh-market.com)
DeepSeek Harness (DSH) task-completion notifications: fast native desktop card, exact session jump, in-app toast, settings preview.
DeepSeek Harness 任务完成通知插件(适配 DSH 0.2.x,已在 0.2.1-rc.2 桌面版验证):会话的一轮任务结束时提醒你。
DSH 是 Web UI,任务跑完没有提醒——切到别的窗口等结果时容易错过。本插件提供飞书式桌面通知:DSH 宿主进程(Node 常驻进程)监听任务状态,任务完成时弹出自定义置顶卡片(无边框窗口,不经过 Windows 通知管道,系统通知总开关关闭时依然显示),与浏览器页面是否打开无关;页面内另有应用内 toast。
~/.dsh/plugins/task-notify.json。配色对齐 DSH 设计令牌:深色卡片取 --dsw-alias-bg-layer-1(#232324)表面、浅色卡片白底,强调色取 DSH 的 success / deepseek / warn / red 状态色;设置页色板与宿主弹窗共用同一份色板定义。ask_user_question)或提交计划等审批(exit_plan_mode)时,也会弹卡片+提示音+toast。DSH 0.2.x 起客户端等待标记位于 uiSession.sessionStatus.pendingInteraction(不再挂在会话列表行上),本插件已按新位置订阅;Host 半仍监听 session/event 的 tool/call 与 approval/request。shell.overlay 槽位),6 秒自动消失,可手动关闭,点击通过 uiWorkspace.openSession 跳转对应会话。agent/status(running→idle = 一轮任务完成),2 秒防抖。sessions.list 快照做页面内提示。agent/error,任务失败时弹红色错误卡片(独立 2 秒防抖),可在设置中关闭;桌面关闭、提示音开启时只播放提示音。Show/Hide,DPI 和字体只初始化一次);之后的通知只写 command.json,由渲染进程 20ms 轮询消费,通过 stdout 回传 ready/recv/visible/closed/error 事件。冷启动脚本保留为兜底。scripts/measure-latency.ps1 -Mode persistent|cold -Count N(需插件已挂载);Host 端 /task-notify/measure 返回分段耗时;冷启动脚本内置 boot/types/form/shown/paint 埋点。DpiX/96 缩放,200% 缩放下不再整窗位图放大。/task-notify/action → SSE /task-notify/events → 页面内 uiWorkspace.openSession(sessionId),精确打开对应会话;卡片进程用 AppActivate/SetForegroundWindow 聚焦 DSH;卡片位置跟随 DSH 主窗口所在屏幕(兜底主屏)。WS_EX_NOACTIVATE + ShowWithoutActivation 确保卡片出现/点击不抢焦点;同一时刻只保留一张卡片(新通知替换旧卡),插件停止时关闭仍在显示的卡片;自绘卡片支持悬停暂停、两行省略与淡入淡出。POST /task-notify/test 支持可选 { title, body, variant, sessionId },方便验收长文本、三种变体与点击跳转。BrowserWindow/WebContentsView,无法由插件直接创建 Electron HTML 卡片;因此 0.9.x 采用 DPI-aware 原生卡片 + DWM 方案,HTML 卡片保留为 DSH 后续提供窗口 API 时的升级项。插件 0.8.0 针对 DSH 0.2.x 适配,主要差异:
| 0.1.x 的写法 | 0.2.x 的写法 |
|---|---|
ctx.sessions.open(id) 打开会话 | ctx.uiWorkspace.openSession(id) |
会话行的 pendingInteraction 标记等待 | ctx.uiSession.sessionStatus(ReadonlyMap<SessionId, SessionStatus>) |
dsh.client.inject: ["@deepseek-ai/dsh-client-runtime"] | inject: [](react 由平台 seed 提供,已无 dsh-client-runtime 包) |
其余使用到的接口在 0.2.1-rc.2 中保持兼容:agent/status、agent/error、agent/disposed、session/event(tool/call)、approval/request、sessionTitle.get()、webServer.register({ kind: 'exact', path, handler })、slots.inject/register、locale.bind/register、settings.general.item 与 settings.section 槽位。
src/
index.ts # Host 半:事件检测 → 常驻渲染器/冷启动兜底 + HTTP/SSE 桥
renderer.ps1 # 常驻 PowerShell/WinForms 渲染进程(预创建 + 预热 + 指令循环)
renderer-manager.ts # 常驻进程生命周期、command.json 写入、stdout 事件解析
client.ts # Client 半入口:inject + apply(订阅、toast、SSE 跳转、注册槽位)
theme.ts # 卡片色板/令牌(Host 弹窗与设置页预览共用)
client/
types.ts # sessions / uiSession / uiWorkspace / slots 服务的最小结构视图
config.ts # 客户端配置(localStorage:toast 开关)
watcher.ts # running→idle 完成检测 + pendingInteraction 等待检测
toasts.ts # toast store + shell.overlay 堆栈组件
settings.ts # settings.general.item 设置行 + 设置页(含实时预览)
styles.ts # 样式注入(含预览卡样式,主题变量)
Host 半(src/index.ts + src/renderer-manager.ts):
src/renderer.ps1):DPI/字体/窗口只初始化一次,离屏 Show/Hide 预热;后续通知写入 command.json,渲染进程每 20ms 轮询消费并通过 stdout 回传 ready/recv/visible/closed/error。POPUP_SCRIPT)保留为兜底:常驻进程未就绪/异常退出时使用,保证通知不丢。GET /task-notify/config — 读完整通知配置POST /task-notify/config — 写配置项({ "desktop": boolean, ... })POST /task-notify/test — 立即弹测试卡片(支持 {title, body, variant, sessionId})GET /task-notify/events — SSE:向页面广播"打开指定会话"POST /task-notify/action — 卡片点击回调({ action: 'open', sessionId })POST /task-notify/measure — 延迟测量({mode:'persistent'|'cold'}),返回分段耗时卡片点击由渲染进程回调 Host(HTTP action)→ SSE 通知页面执行
uiWorkspace.openSession(sessionId),从而精确打开对应会话;页面未打开时回退为打开 DSH 根地址。卡片默认跟随 DSH 主窗口所在屏幕,可用环境变量DSH_WEB_URL覆盖打开地址。
Client 半:订阅 sessions.list 完成翻转与 uiSession.sessionStatus 等待标记 → 应用内 toast;订阅 /task-notify/events 处理桌面卡片点击的会话跳转;设置页通过 fetch 调 HTTP 桥控制宿主开关,并渲染同源卡片预览。客户端不使用浏览器 Notification API(系统级通知统一由宿主负责)。设置 UI 已接入 ctx.locale(中/英文案)。
pnpm install
pnpm typecheck # tsc --noEmit
pnpm build # tsdown → lib/index.mjs + lib/client.js;copy-assets 复制 renderer.ps1 并写 BOM
pnpm smoke # node scripts/smoke-client.mjs:模拟加载器验证 client bundle
产物 lib/ 需提交(link: 挂载直接指向源码目录)。renderer.ps1 由 scripts/copy-assets.mjs 复制到 lib/,必须带 UTF-8 BOM(Windows PowerShell 5.1 依赖 BOM 识别中文)。
Client 半的构建特殊性:DSH 客户端模块加载器要求 bundle 是
window.__ModuleLoader__.load({ id, factory })包装格式(classic script, CJS 风格 factory,react 经注入的 require 解析)。tsdown.config.ts用 format cjs + banner/footer 包外壳实现;不要改成普通 ESM 裸 import 输出。
# 需插件已挂载、DSH 正在运行;端口以实际 webServer 为准
powershell -NoProfile -ExecutionPolicy Bypass -File scripts/measure-latency.ps1 -Port 55803 -Mode persistent -Count 5
powershell -NoProfile -ExecutionPolicy Bypass -File scripts/measure-latency.ps1 -Port 55803 -Mode cold -Count 3
输出分段:triggerToCmd / cmdToRecv / recvToShow / showToPaint / paintToHost / total(cold 模式为 triggerToSpawn / spawnToBoot / bootToTypes / typesToForm / formToShown / shownToPaint)。
DSH 桌面版默认使用 desktop profile(~/.dsh/profiles/desktop);CLI Web 版常用 web profile。推荐用官方插件管理器安装(自动装依赖并并入 bundles 层):
dsh plugin --profile desktop add "link:<本插件文件夹绝对路径>"
# CLI Web 版对应:dsh plugin --profile web add "link:<本插件文件夹绝对路径>"
也可以在本会话用 plugin_manager 工具的 install_bundle 安装本地目录。手动方式(等价):
package.json:
dependencies 添加:"dsh-plugin-task-notify": "link:<本插件文件夹绝对路径>"dsh.profile.bundles 数组追加:"dsh-plugin-task-notify"pnpm install。注意:
cordis.yml是合成产物,不要直接编辑;配置覆盖写进cordis.patch.yml。
osascript / Linux notify-send)