Bundle
dsh-desktop-notifier
DeepSeek Harness Web: raise an OS notification when a task finishes (or needs your input) while the harness page is not focused
- Source
- lurejewel
- License
- MIT
- Updated
- Updated 6 days ago
Readme
# dsh-desktop-notifier
[](https://www.npmjs.com/package/dsh-desktop-notifier) [](LICENSE)
[DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) Web 的桌面提醒插件:**当你不在 harness 页面上时,任务结束或需要你操作,就通过系统通知栏(Windows 操作中心)提醒你。**
整个功能跑在浏览器里,用 `Notification` API 投递;在 Windows 上通知会进入操作中心,点击通知会切回该会话。插件**不新增宿主路由、不写会话日志、不改 agent 循环**——状态全部来自客户端 runtime 已经维护的会话列表。
## 触发条件
| 时机 | 是否提醒 | 说明 |
|---|---|---|
| 任意会话任务完成 | ✅ | 包括你正在看的那个会话,只要你切走了窗口 |
| 会话运行时长 < 阈值(默认 1 分钟) | ❌ | 避免几秒钟的短任务频繁弹窗 |
| 会话开始等待你操作 | ✅ | 审批 / 计划确认 / 提问,**不受时长阈值限制**(阻塞类事件更值得提醒) |
| 子代理(subagent)会话完成 | ❌ | 默认跳过,父会话完成时会提醒;可用 `includeSubagents` 打开 |
| 页面在前台(可见且持有焦点) | ❌ | 人就在看,不需要打扰 |
| 页面可见但焦点在别的程序(含双屏) | ✅ | `document.hasFocus()` 判定 |
| 另一个 DSH 标签页在前台 | ❌ | 用 `BroadcastChannel` 跨标签页抑制重复提醒 |
通知内容:标题是会话标题,正文是「✅ 任务完成 · 用时 3 分 12 秒」或「⏳ 等待你审批」。
## 环境要求
| 要求 | 版本 |
|---|---|
| DeepSeek Harness | `>= 0.1.0`(已在 `0.1.5-rc` 的 web 表面验证) |
| Node.js | `>= 20` |
| 浏览器 | Chrome / Edge(通知授权依赖用户手势,Safari 未验证) |
## 安装
```sh
dsh plugin --profile web add dsh-desktop-notifier
```
包声明了 `dsh.bundle` patch,`dsh plugin` 会自动把它挂进 profile 的配置层栈,无需手写挂载行。之后:
1. **重启 `dsh web`**——`dsh.profile.bundles` 只在 boot 时读取一次,装到正在运行的实例里不会被感知;
2. 浏览器**硬刷新**(`Ctrl+Shift+R`);
3. **点击页面任意位置一次**:Chrome/Edge 只允许在用户手势里申请通知权限,这是唯一的授权入口。
### 快速自检
打开 DevTools 控制台执行:
```js
__dshNotifyTest__() // 直接弹一条测试通知,绕过「失焦才提醒」规则
```
返回 `false` 且控制台提示权限未授权,说明第 3 步还没做。
### 安装状态自检
```sh
node scripts/verify-install.mjs <port> <token>
# 例:dsh web: http://127.0.0.1:3080/?token=XXXX -> node scripts/verify-install.mjs 3080 XXXX
```
它检查两件事:web boot graph 里有本插件的行、该行指向的客户端 bundle 返回 200。裸 `curl` 拿不到结果——Web 表面(含 `/plugins`)在进程令牌栅栏之后,令牌只在 `dsh web` 启动那一行里。npm 安装的用户可以在 `~/.dsh/profiles/web/node_modules/dsh-desktop-notifier` 里执行它。
### 从源码(link:)安装
在本仓库目录里:
```powershell
powershell -ExecutionPolicy Bypass -File scripts\install-local.ps1
```
它用 `pnpm add "link:<本目录>"` 直连 profile(**不走** `dsh plugin add`——那条路径经 cmd 转发参数时不给路径加引号,含空格的路径会被拆成多个 spec),并把加载行写进 `~\.dsh\profiles\web\cordis.patch.yml`。该 profile 的用户 patch 层是热应用的,所以这种方式**不需要重启**,硬刷新即可。
### ⚠️ 从 link: 安装切换到 npm 正式版
两条安装路径用两个不同的挂载点,同时存在就是**两行同 id**,加载直接报错。切换前先把用户 patch 层里的那一段删掉:
```sh
# 1) 删掉 ~/.dsh/profiles/web/cordis.patch.yml 里
# "# >>> dsh-desktop-notifier" 到 "# <<< dsh-desktop-notifier" 之间的内容
# 2) 卸掉本地 link(在 ~/.dsh/profiles/web 下执行)
pnpm remove dsh-desktop-notifier
# 3) 再装正式版
dsh plugin --profile web add dsh-desktop-notifier
```
## 配置
默认值写在 `lib/client.js` 的 `DEFAULTS` 里;不改代码的覆盖方式是在浏览器 localStorage 放一个 JSON 对象,键 `dsh-desktop-notifier:config`:
```js
localStorage.setItem('dsh-desktop-notifier:config', JSON.stringify({
thresholdMs: 60000, // 只有运行超过这个毫秒数的任务才提醒
includeSubagents: false, // true = 子代理会话完成也提醒
pendingInteraction: true, // 等待审批/提问时提醒
silent: false, // true = 通知不出声
debug: true // true = 控制台打印 [dsh-desktop-notifier] 判定日志
}))
```
字段类型不匹配或 JSON 坏了会整段回退到内置默认值。改完刷新页面。
## 实现
- `lib/index.js` — node 半边,空的:功能全在浏览器里,这一行存在的意义是让客户端半边进入 web boot graph。
- `lib/client.js` — `__ModuleLoader__` 格式的浏览器 bundle:`inject: ['sessions']`,订阅 `sessions.list`(客户端 runtime 的快照 store,行上带 `running` / `pendingInteraction` / `displayTitle`),自己做 running→idle 边沿与 `pendingInteraction` 首次出现边沿的检测,按焦点规则投递通知。
- `scripts/verify-install.mjs` — 安装自检(随包发布)。
- `scripts/install-local.ps1`、`scripts/restart-web.ps1` — 本地开发/排障脚本,只在本仓库里。
- `test/client.test.mjs` — 在 Node 里用桩全局变量执行**真实的** `lib/client.js`,覆盖阈值、焦点、跨标签页抑制、子代理过滤、权限、点击回调等分支:
```sh
node test/client.test.mjs
```
## 已知限制
- **需要浏览器进程在运行。** 标签页被关掉就不会有提醒(宿主进程仍在跑任务)。要覆盖这种情况得走宿主侧原生 toast,本插件目前只做浏览器通知。
- **插件加载时已在运行的任务按加载时刻计时。** 会话日志里没有「本轮开始的时刻」可用,所以刚刷新页面时正在跑的任务,其耗时从页面加载开始算——只会少算,不会误报长任务。
- **通知身份是浏览器。** Windows 操作中心里显示的是 Chrome/Edge 的图标与名字,不是独立的 DSH 应用。
- 同一会话的同类通知带同一个 `tag`,会互相替换而不是无限堆叠。
## License
[MIT](LICENSE)
Install
dsh plugin --profile web add github:lurejewel/dsh-desktop-notifier
Profile: web
With the hub plugin installed, ask your agent to install it by name — it resolves the same plan shown here.
dsh plugin --profile web add github:stvlynn/dsh.fish#path:packages/dsh-plugin-hub
install dsh-desktop-notifier from the hub
- This source has no pinned commit, so a later push upstream changes what installs. Prefer pinning a commit.