READMESource: main@a1a55312
dsh-desktop-notify
DSH 桌面提醒插件:需要你拍板或跑完/出错时,在屏幕右下角弹一条 DeepSeek 通知, 并在任务栏 DSH 图标上点亮一枚徽标(黑底数字 = 有几件事等你处理:等你拍板的、刚跑完的、出错的,全都算进去)。
状态:✅ 已在 DSH 桌面版完成真实审批 / 运行结束 / 出错三类提醒的端到端验证(2026-11-15)。
目录
| 文件 | 作用 |
|---|---|
index.js |
宿主半。旁听 approval/request、user-questions/request、session/event(turn/end,跳过 subagent 会话)与 agent/error,记下提醒并提供 HTTP/SSE 接口。 |
client.js |
客户端半(页面里的 lazy factory)。收提醒 → new Notification(...) 弹 Windows toast;点通知让宿主把窗口提到前台。 |
lib/raise-window.js |
常驻 powershell.exe worker:ITaskbarList3.SetOverlayIcon 画任务栏点、SetForegroundWindow 提窗口。 |
cordis.patch.yml |
profile 里的插入行(id: desktop-notify)。 |
安装
本插件以 DSH bundle 的形式分发(package.json 的 dsh.bundle.patch 指向仓库里的 cordis.patch.yml):
- 插件市场(收录后):DSH 侧栏 →「插件市场」→ 搜索
dsh-desktop-notify→ 安装。 - 手动:把本仓库放进 DSH profile 的依赖(本地开发用
"dsh-desktop-notify": "link:E:/dsh-plugins/dsh-desktop-notify"),并在 profile 的 bundle 列表里启用include:desktop-notify,重启 DSH 桌面版。
什么时候提醒
| 触发 | 类别 | 任务栏点 |
|---|---|---|
| 工具要你确认(审批) | approval | 黑底 + 数字 |
| DSH 有选择题等你答 | question | 黑底 + 数字 |
顶层会话一轮跑完(session/event 的 turn/end,subagent 会话跳过) |
finish | 黑底数字 +1 |
| agent 报错 | error | 黑底数字 +1 |
- 同类提醒按
key幂等(同一次审批不会重复弹)。 - 子 agent(subagent)会话跑完不报,避免一次任务刷屏;只报顶层会话。
- 你正看着 DSH 窗口时:提醒自动 ack,不弹 toast,点也立刻熄掉。
HTTP 接口
前缀 http://127.0.0.1:<port>/dsh-desktop-notify/,所有请求必须带请求头
x-dsh-desktop-notify: 1(缺头 403;自定义头让跨站请求必须预检,服务端不回 CORS 头,所以外部网页打不进来)。
| 动作 | 说明 |
|---|---|
GET /events |
SSE 推提醒(event: alert),客户端首选通道 |
POST /state |
{since: n} → 游标 + 新提醒(轮询兜底,客户端 5s 一次) |
POST /ack |
用户正在看窗口,熄灯 |
POST /raise |
把 DSH 窗口提到前台 |
POST /report |
客户端诊断上报(toast 到底弹没弹、为什么没弹) |
POST /test |
自测:{kind,title,body} 造提醒;`{overlay:"alert" |
POST /ping |
体检:badge / pending / events / subscribers / worker / lastReport |
自测
$h = @{ 'x-dsh-desktop-notify' = '1'; 'content-type' = 'application/json; charset=utf-8' }
$body = '{"kind":"approval","title":"需要你的确认","body":"这条会停留几秒。"}'
Invoke-WebRequest -Uri 'http://127.0.0.1:60506/dsh-desktop-notify/test' -Method Post -Headers $h `
-Body ([System.Text.Encoding]::UTF8.GetBytes($body)) -TimeoutSec 10 -UseBasicParsing | Select-Object -Expand Content
# 只画任务栏徽标(会先呼吸放大一次,不弹 toast)
Invoke-WebRequest -Uri 'http://127.0.0.1:60506/dsh-desktop-notify/test' -Method Post -Headers $h `
-Body ([System.Text.Encoding]::UTF8.GetBytes('{"overlay":"alert","label":"1","pulse":true}')) -TimeoutSec 10 -UseBasicParsing | Select-Object -Expand Content
中文 body 必须传字节(UTF8.GetBytes),直接传字符串 PowerShell 会把它变成 ?。
排障
/ping的subscribers应为 1;是 0 说明页面里的客户端半没挂上。raiserRunning/raiserReady应为true;lastReply是 worker 最近一句回话(READY表示 C# 类型编译成功)。lastReport是客户端最近一次自述:stage: shown就是 toast 真弹出来了,no-api/denied/construct-failed则是没弹的原因。- 注意:DSH 文件沙箱内跑
Add-Type会报A required privilege is not held by the client;worker 由 Host 进程拉起,不受该限制,别拿沙箱里的 PowerShell 结果判断 worker 坏了。
重载
- 宿主半:
plugin_manager set_plugin target=include:desktop-notify enabled=false再true。 - 客户端半:改一下
client.js的 mtime((Get-Item client.js).LastWriteTime = Get-Date)会触发客户端 HMR, 不用刷新页面;宿主半重载会把客户端半卸载,重载后再 touch 一次即可。
开发注意(改完代码怎么让它生效)
DSH 重载插件时不会重新求值已经加载过的模块 URL(disable/enable、remove+install 都不会),所以:
- 改
index.js:新建一个出口文件(entry-3.js、entry-4.js…,内容只有export * from './index.js?rev=N'),把package.json的exports["."]指过去,再remove_bundle+install_bundle。 - 改
lib/raise-window.js:把index.js顶部那条 import 的?rev=加一。 - 改
client.js:(Get-Item client.js).LastWriteTime = Get-Date触发页面侧 HMR,/ping的subscribers会回到 1。 - 每次重载后先轮询
/ping,等raiserReady: true再发/test指令。
观感
- 徽标:黑底白字数字(N 件要处理的事——等拍板的、刚跑完的、出错的全都合并计数,不区分颜色);状态切换时先呼吸放大一次(约 0.4s)。
- 通知:没人点时 6 秒自动消失(
client.js的AUTO_CLOSE_MS);点一下会关掉并把 DSH 窗口提到前台。
自测记录
- 2026-11-15:真实审批弹窗链路(这次写入就是触发点)、统一黑底数字徽标、卡片 6 秒自动消失、点窗口/点通知熄灯、DSH 重启后自动恢复。
- 2026-11-15 18:39:跨会话弹窗验证——用户切到别的对话窗口时,这个审批请求应当把右下角卡片弹出来。
什么时候弹、什么时候不弹
- 审批 / 提问(kind 为 pproval / question):永远弹。你人可能在 DSH 里看着别的会话,客户端没法知道你在看哪个,所以这类提醒不套用「前台静默」。
- 运行结束 / 出错:窗口在前台且有焦点时静默(只点任务栏徽标),切到别的窗口才弹。
- 自测事件(/test 推的 est:true)一律弹,方便验证链路。
No comments yet. Be the first to write one.