DSH HUB
HomePlugin StorePlugin PacksCommunityRankingsResourcesPublish Guide
Plugin source
Back to catalog

chendaleiQ /

chendaleiQ/dsh-plugin-desktop-notify

Verified

Codex-style macOS notifications + Dock unread badge for the DeepSeek Harness desktop app (click the banner to jump back to the session)

★ 0 Stars0 Forks0 IssuesN/A Community rating0 Confirmed installs
View on GitHub
READMESource: main@bd389ff7

dsh-plugin-desktop-notify

给 DeepSeek Harness 桌面版补上 Codex 风格的系统通知 + 程序坞小红点:

  • 任务跑完(包括窗口在后台/最小化时)弹一条 DeepSeek Harness 自己发的 macOS 横幅;
  • 有工具等你审批、有提问等你回答、有计划等你确认时弹「需要你确认」;
  • 任务出错时弹「任务出错」,正文带错误摘要;
  • 点横幅直接回到那个会话并抬起窗口;
  • 需要你处理的会话在 Dock 图标上留一个数字,直到你真的看过它才清零。

English · MIT

和 Codex 桌面版对齐

Codex 桌面版 这里
Settings → Notifications → Task completion 标题「任务已完成」,正文「对话名」已完成,用时 3 分 12 秒
Settings → Notifications → Approval required 标题「等待你的批准」,正文「对话名」请求使用 Bash,等待你的批准
等你回复(提问 / 计划确认) 标题「等待你的回答」/「等待你的确认」,正文带会话名
任务失败 标题「任务执行失败」,正文带会话名与错误摘要
Dock 徽标 数字 = 当前需要你的会话数;打开该会话即清

规则:

  • 有通知就有红点:两者读同一份 attention 事实,弹了横幅就一定进徽标。
  • 「在看」才算看过:主视图是它 且 窗口有焦点 且 窗口可见,三者同时成立才不打扰。 宿主的 completionUnread 只覆盖第一种(切到别的会话);窗口被切到后台时它看不见, 这部分由插件用 document.hasFocus() / visibilityState 自己补——否则最常见的那种 「切到别的 App 等它跑完」永远不会提醒。
  • 红点粘住:弹过横幅的会话,红点保留到你真的切回来并看着它;宿主把 completionUnread 清掉并不会让红点消失。
  • 失败不重复播:一轮以错误结束时只发「任务出错」,不再补一条「任务完成」。
  • 子会话(subagent / Agent Teams 成员)不弹不计;同一会话同类横幅用 tag 覆盖,不堆一屏。

兼容性

  • 已在 macOS 实测:DSH Desktop 0.2.1-rc.3(Electron 44.7.0)。
  • 需要桌面端把 dsh-app:// 注册为 secure context(现版本就是这么做的),否则渲染层拿不到 navigator.setAppBadge 与 Notification;capabilities() 自检里会显示 badge: false。
  • 系统设置 → 通知 → DeepSeek Harness 必须是允许状态,否则只有红点没有横幅。
  • Windows / Linux:未实测,但理论上可用。渲染层用的是标准 Web API: Electron 从 29 起支持 Windows 上的 Badging API(任务栏 overlay,修过 Windows 下徽标文字不显示), Notification 走 Windows toast。真正的风险在 Windows 的 toast 需要 AppUserModelID + 开始菜单快捷方式,而 DSH 主进程没有显式 setAppUserModelId(我查过 lib/main.js), 所以能不能出横幅取决于安装包写的快捷方式。
  • 唯一确定只在 macOS 的是宿主兜底那半边(osascript / afplay / lsappinfo), 它默认关闭,只在你没有 Notification 时才需要。
  • 想验证 Windows:跑一次 capabilities()(会带上 platform),再 simulateComplete(), 看 toast 与任务栏数字是否出现,把结果开个 issue 就行。

点击通知没反应?

先看 __DSH_DESKTOP_NOTIFY__.state().lastClick:

  • null → 点击事件根本没送到渲染层。macOS 上基本不会;Windows 上很常见:Electron 的 WinRT toast 点击激活需要 COM activator 注册(electron/electron#24524),没注册时点横幅可能只是把系统通知收起。这一层插件修不了。
  • opened.ok: false → 点击到了,但切会话失败,opened.attempts 里写着原因(uiWorkspace 服务不可达 / retain 失败等)。 v0.5.2 起插件把 uiWorkspace 声明成了依赖(和官方 ui-chat 插件一致),此前它在这个插件里是取不到的——这就是 macOS 上「点击只激活应用、不切会话」的原因。
  • opened.ok: true 但界面没变 → 窗口被藏起来了(点横幅只激活应用,不一定会显示窗口),手动点 Dock/任务栏图标把窗口调出来即可。

也可以直接验证切会话这条链路:__DSH_DESKTOP_NOTIFY__.openSession("<会话 id>")。

Windows:点通知切会话成功,但窗口不一定到最前

这不是插件能修的:点击事件已经送到渲染层(v0.5.2 起 lastClick.opened.ok === true,会话也确实切了), 但把主窗口提到前台需要主进程的 BrowserWindow.show() / focus(),或者系统层面的 AppUserModelID 注册;渲染层只有 webContents.focus(),而 Windows 的前台锁定会忽略来自 非前台进程的请求。

两种上游修法(任一即可):

  1. 桌面端主进程调用 app.setAppUserModelId("com.deepseek.dsh"),并让安装包写出带同一 AUMID 的开始菜单快捷方式——Windows 就会在点击 toast 时激活应用,窗口自然到前台。
  2. 主进程暴露一个「focus window」的 IPC 通道,让插件请求抬窗。

在此之前:点通知后若窗口没上来,点任务栏/托盘图标即可,会话已经切好了。

Windows 上「有红点、没横幅」

v0.4.4 已修:旧版本给每条通知带了一个很长的 tag(dsh-plugin-desktop-notify:complete:<sessionId>), 而 Windows 11 会静默丢弃 tag 长度超过约 34 字符的 Web Notification(electron/electron#40433)。 现在不再设置 tag / silent,去重改由状态边沿判断完成——所以裸 new Notification("t",{body:"b"}) 能弹、 插件的不弹,正是这个原因。

如果升级后仍不弹,再按下面排查:

红点走的是任务栏 overlay,不需要 AppUserModelID;Windows toast 需要 AppUserModelID + 开始菜单快捷方式才会显示。DSH 主进程没有调用 app.setAppUserModelId(lib/main.js 里没有 这行),所以 toast 很可能被 Windows 静默丢弃——这不是插件逻辑问题。先确认:

new Notification("test", { body: "windows toast check" })  // 连这条都不弹 = 应用层问题
__DSH_DESKTOP_NOTIFY__.state().lastNotify                   // 插件最后一次投递的结果

两条路:等 DSH 主进程补上 app.setAppUserModelId(那时现有代码就能正常出 toast 并支持点击); 或改用宿主侧 PowerShell toast 兜底(能提示,但归属会显示成 Windows PowerShell,点击无法跳回会话)。

安装

方式一:从 GitHub 安装(推荐)

DSH 桌面版左侧栏 插件 → 安装,填仓库地址:

https://github.com/chendaleiQ/dsh-plugin-desktop-notify

装完按 ⌘R 刷新窗口(渲染层要重新加载)。提示 restart-required 就重启一次应用。

方式二:本地目录

git clone https://github.com/chendaleiQ/dsh-plugin-desktop-notify.git

插件页 → 安装 → 填这个目录的绝对路径(目录,不是文件):

/path/to/dsh-plugin-desktop-notify

方式三:开发调试(直接 link 进 profile)

git clone https://github.com/chendaleiQ/dsh-plugin-desktop-notify.git
cd dsh-plugin-desktop-notify
ln -s "$PWD" ~/.dsh/profiles/desktop/node_modules/dsh-plugin-desktop-notify
# 再把 dsh-plugin-desktop-notify 加进 ~/.dsh/profiles/desktop/package.json 的 dsh.profile.bundles

改完代码按 ⌘R 重载渲染层即可(Host 那半边改动需要重启应用)。

自检

窗口里按 ⌘⌥I 打开 DevTools,Console:

__DSH_DESKTOP_NOTIFY__.capabilities()       // {notification:"granted", badge:true, ...}
__DSH_DESKTOP_NOTIFY__.state()              // 当前 attention 与徽标数字
__DSH_DESKTOP_NOTIFY__.simulateComplete()   // 立刻弹一条横幅并把 Dock 置 1
__DSH_DESKTOP_NOTIFY__.reset()              // 清掉自检状态
__DSH_DESKTOP_NOTIFY__.state().lastClick    // 上一次点横幅发生了什么
__DSH_DESKTOP_NOTIFY__.openSession("<会话 id>")  // 直接按点击逻辑切会话,验证服务可达

手工验收:让一个会话跑十几秒,切到别的 App(失焦即可,不必切会话),跑完应看到横幅 + Dock 1; 点横幅回来,数字清零。

偏好

没有设置界面,偏好放在渲染进程本地(desktop / web profile 各自独立),控制台执行后 ⌘R:

localStorage.setItem("dsh-plugin-desktop-notify:preferences", JSON.stringify({
  notifications: true,    // 总开关:横幅
  notifyComplete: true,   // 任务完成
  notifyPending: true,    // 等审批 / 提问 / 计划确认
  notifyError: true,      // 任务失败
  notifyViewed: false,    // true = 盯着看它也弹(但不进红点)
  badge: true,            // 总开关:Dock 红点
  excludeSubagents: true, // 子会话不弹不计(建议保持)
  // 留空 = 用内置文案(跟随界面语言 zh/en)
  titleComplete: "",
  titlePending: "",
  titleError: "",
  maxBodyChars: 140
}))
// 恢复默认
localStorage.removeItem("dsh-plugin-desktop-notify:preferences")

为什么是渲染层插件

这两件事只能在桌面渲染进程做,Host 子进程碰不到:

能力 接口 去处
原生横幅 + 点击回调 new Notification(...) / onclick Electron → macOS 通知中心,归属 DeepSeek Harness
Dock 小红点 navigator.setAppBadge(n) / clearAppBadge() Chromium → Electron BadgeManager → NSDockTile.badgeLabel

宿主半边(index.js)保留成默认关闭的 osascript 兜底:万一某个渲染环境没有 Notification, 把 cordis.patch.yml 里的 enabled 改成 true 再重启——两者同时开会重复提醒。

数据源用的是 Harness 自己的 uiSession.sessionStatus(completionUnread / pendingInteraction, 和侧边栏状态点同源)+ api-session/error(失败),插件不自己记账,所以「读过」的语义和官方 UI 一致。

目录

dsh-plugin-desktop-notify/
├── package.json          # dsh.bundle.patch + dsh.client(渲染层入口与 inject 顺序)
├── cordis.patch.yml      # desktop-notify 一行(宿主兜底默认 enabled: false)
├── lib/client.js         # 渲染层:横幅 + 点击跳转 + Dock 徽标
├── index.js              # Host 半边:osascript 兜底,默认关闭
├── test/client.test.mjs  # 渲染层测试(node:vm + 假浏览器全局)
├── test/host.test.mjs    # 宿主兜底测试
├── icon.svg / locale/{en,zh}.json / README(.en).md / LICENSE

开发与测试

零依赖,手写 ES 模块,无构建步骤。

node test/client.test.mjs                     # 渲染层:node:vm + 假 window/navigator/Notification
node test/host.test.mjs /tmp/host-test.log    # 宿主兜底:假 ctx + 合成 session/event
npm test                                      # 两个都跑

渲染层测试覆盖 40+ 项断言:边沿只弹一次、后台窗口算「没在看」、红点粘住到真看过、 完成/审批/失败各自增减、失败不重复播、子会话排除、各偏好开关。

已知限制

  • macOS 已实测;Windows / Linux 未实测(见上面「兼容性」)。
  • 「卡住」目前覆盖等待审批 / 提问 / 计划确认 / 失败;没有做「还在 running 但长时间无输出」 的超时判定(需要宿主侧定时器)。
  • completionUnread 与失败事实在宿主内存里,重启应用从零开始(官方语义)。
  • 点击横幅若对应会话已归档,只会抬窗。
  • 与 @caohaoyangcn/dsh-dock-badge 功能重叠(那个只做徽标、带设置页),两个都装会抢写同一个 Dock 徽标,只留一个。

License

MIT,随便用、随便改、随便发。

—/ 5

No ratings yet

Verified DSH bundle

Commit bd389ff7950d

Community comments

No comments yet. Be the first to write one.

DSH HUB

A community index for DSH plugins. Not an official GitHub or DeepSeek AI product.

CommunityResourcesAPIAbout