notifier
by JohnXu22786
dsh-chime:DeepSeek Harness桌面信号插件 — 任务完成、等待审批或出错时桌面通知和音效
dsh-chime: desktop signal plugin for DeepSeek Harness — desktop notifications and tones when a task finishes, waits for approval, or errors out
安装
dsh plugin --profile web add github:JohnXu22786/notifierGitHub 源码安装:首次需按提示配置 allowBuilds 构建授权后重试
安装与环境配置指引、插件开发教程见 DSH 中文社区文档 ↗
安装即在你的机器上以你的权限运行第三方代码——它可读写文件、使用凭据、访问网络,DSH 的工具审批不会为插件代码加沙箱。「检测到 manifest」仅代表发现 dsh.bundle / dsh.plugin 清单,不构成兼容性或安全审查;安装前请审阅源码,不熟悉的插件先在不含密钥的环境试用。
README
目录
dsh-chime
在 dsh(DeepSeek Harness)上运行的桌面提醒信号插件:任务完成、等待批准、运行出错时,在 dsh 所在机器的桌面弹出系统通知,并按需播放提示音——不用一直盯着终端或网页界面。
零运行时依赖:不需要 npm 通知库,直接调用操作系统自带能力(macOS 的 osascript/afplay、Linux 的 notify-send/音频后端、Windows 的 PowerShell 原生组件)。
目录
设计理念
「只在需要人回来的时候提醒,不为每个微事件打扰」是插件的核心原则。dsh 每次生成响应、每次工具调用都会产生大量事件,如果全部弹窗,提醒就变成了噪音。因此 dsh-chime 只提炼三类信号:
| 信号 | 含义 | 默认提示音 |
|---|---|---|
done |
一个回合完整跑完(生成结束) | 完成音 |
blocked |
正在等待真人批准 / 需要人介入 | 警示音 |
failed |
回合或请求出错 | 错误音 |
信号在内部沿一条四段流水线流动,每一段都可以独立配置或关闭:
捕获(bridge 监听 dsh 事件)
→ 裁定(policy:总开关 → 种类开关 → 免打扰时段 → 频率限制)
→ 渲染(templates:标题/正文模板)
→ 分发(channels:桌面通知 / 提示音 / 终端响铃)
特性
- 三类信号独立控制:完成 / 批准 / 报错可分别开关、分别定制文案与音效(事件类型过滤)。
- 跨平台桌面通知:macOS、Windows、Linux 原生实现,无需任何第三方包。
- 可定制提示音:系统音效或自定义音频文件,支持音量;也可整体静音。
- 自定义文案模板:标题与正文均支持占位符(会话、项目、耗时、错误摘要等)。
- 自定义图标:桌面通知图标可指定图片文件(Windows 需 .ico,Linux 任意格式)。
- 免打扰时段(hush):支持跨午夜窗口(如 22:00–08:00)与按星期生效。
- 频率限制(cadence):同类信号最小间隔 + 全局突发上限,防刷屏。
- 审批宽限期:审批请求若被自动裁决(策略拒绝/无应答者),不会打扰;只有确实在等真人时才提醒。
- 会话耗时统计:完成通知里带上本回合耗时。
- 命令行自检:不启动 dsh 也能试响、探测通道可用性、查看生效配置。
快速开始
前置条件:Node.js ≥ 22.19(dsh 的运行要求),已装好 dsh CLI 并初始化过 profile。
# 在包含本目录的位置执行;link: 指向本地目录
dsh plugin --profile web add link:/绝对/路径/notifier
或者发布到 npm 后:
dsh plugin --profile web add dsh-chime
验证接入:
# 配置树中应出现 # == dsh-chime 层
dsh --profile web --dump-config
# 启动 dsh,观察日志中的 [chime] 行
dsh --profile web
试响(不需要 dsh 运行):
chime probe # 检查各通道可用性
chime ping done # 弹一条「任务完成」测试通知 + 提示音
注意:通知发生在 dsh 服务进程所在的机器上。若 dsh 运行在远程服务器,提醒会出现在服务器桌面,而不是你的本地桌面。
在 DSH 中安装
直接用 dsh 插件命令从 GitHub 仓库安装:
dsh plugin --profile demo add github:JohnXu22786/notifier
安装后插件会以配置层(dsh.bundle.patch → cordis.patch.yml)注册自身,下次启动 dsh 时即开始弹出桌面通知。卸载:
dsh plugin --profile demo remove notifier
接入说明(给 dsh 的加载机制)
dsh 的插件体系是 bundle(包)+ patch(配置层) 双层模型。本插件包包含三个要素:
1. bundle 声明(package.json)
{
"name": "dsh-chime",
"type": "module",
"main": "dist/index.js",
"dsh": { "bundle": { "patch": "./cordis.patch.yml" } }
}
dsh.bundle.patch 告诉 dsh:这个包贡献一个配置层。没有这个字段的包只会被当作普通依赖,不会激活成插件。
2. 配置层(cordis.patch.yml)
- insert:
- id: chime
name: dsh-chime
insert 把一行插件配置插入 profile 的配置树,id 是行标识(全局唯一,可被上层 patch 覆盖),name 指向包名,由 Node 模块解析加载实际代码。
3. 入口文件(dist/index.js)
按 dsh 插件入口约定导出两个成员:
export const name = 'dsh-chime'; // 插件名
export function apply(ctx, config) { /* ... */ } // 插件主体
apply在配置就绪后被调用,ctx是 Cordis 上下文,config是本包插件行的config字段。- 插件只使用
ctx.on()订阅事件、ctx.effect()注册卸载清理,不注入任何服务,因此不依赖 dsh 内部服务的版本细节。 - 所有注册都是可逆副作用:dsh 卸载/热重载插件时会自动撤销监听,挂起的审批定时器也会经
ctx.effect注册的清理函数被一并清除。 - 配置被修改时会热重载插件实例(dsh 的 HMR),无需重启。
接入注意事项
- dsh 目前处于开发者预览期,官方声明事件名与接口可能发生不兼容变更。事件映射集中在
src/bridge.ts,事件契约声明在src/bridge.ts的头部注释中;若未来事件改名,只需同步更新该文件。 - 插件不依赖任何运行时第三方包,
dist/是编译产物,已被 .gitignore 忽略且未提交到仓库;从源码目录直接安装(link:)前请先执行npm run build。npm 在日常安装(dsh plugin add github:JohnXu22786/notifier)时会通过prepare脚本自动构建。
事件与信号映射
| dsh 事件 | 信号 | 说明 |
|---|---|---|
session/event(turn/start) |
— | 记录回合开始时刻,用于耗时统计 |
session/event(turn/end,reason 正常结束) |
done |
回合完整跑完 |
session/event(turn/end,reason.kind = 'error') |
failed |
回合以错误收尾(detail 取 reason.error) |
session/event(turn/end,reason.kind = 'aborted') |
— | 用户主动中止,不提醒 |
session/event(approval/asked) |
blocked(延迟判定) |
审批请求挂起,宽限期(bridge.decisionGraceMs)内若收到 approval/decided(自动裁决/快速应答)则不打扰;超时未决才发信号 |
agent/error |
failed |
回合/步骤出错(emit 事件,载荷可能不含 agent 字段;会话不可知时按全局窗口去重,避免与随后 turn/end 的 failed 重复提醒) |
bridge.attentionFrom 中列出的事件 |
blocked |
逃生舱:dsh 事件名演进或自定义事件时,把额外事件视为「等待人」 |
session/event 是持久会话事件流,turn/*、approval/* 都是其中的事件类型;agent/error 是实时事件。信号内容全部防御式提取(字段缺失时使用占位文案),任何事件畸形都不会让插件抛错。
配置
配置来源按优先级从低到高:
内置默认值(见
src/config.ts的DEFAULT_CONFIG)——插件总是先与内置默认值深层合并,因此任意来源只需写想改的键。插件行
config字段——用户在自己 profile 层的cordis.patch.yml里按同一行 id(chime)覆盖。注意 dsh 的 patch 层语义:后一层会整体替换前一层的config(不做深层合并),但保留行的name。对本插件而言,行的名字由 bundle 自带,用户覆盖行只需给出 id 与要设置的键:$DSH_HOME/profiles/<profile名>/cordis.patch.yml:- id: chime config: hush: armed: true from: '22:00' to: '08:00'配置被修改后会触发插件热重载(HMR),无需重启 dsh。
配置文件(机器级覆盖,可跨 profile 共享):
$DSH_HOME/chime.config.json,或通过环境变量DSH_CHIME_CONFIG指定其他路径。支持 JSONC(注释、行尾逗号)。文件覆盖在行配置之上,同样按键深层合并:// $DSH_HOME/chime.config.json { "hush": { "armed": true, "from": "22:00", "to": "08:00" }, // 只改这几个键即可 "kinds": { "blocked": { "tone": { "mode": "file", "file": "~/sounds/urgent.wav" } } } }配置有误时插件拒绝加载并打印具体原因(失败要响亮,不静默降级)。
完整配置项
{
"armed": true, // 总开关
"channels": {
"desktop": true, // 桌面通知通道
"tone": true, // 提示音通道
"bell": false, // 终端响铃(BEL,仅交互式终端有效)
"toneProgram": "auto" // Linux 音效后端:auto | canberra | paplay | aplay | ffplay
},
"lingerMs": 8000, // 通知展示时长(毫秒)
"kinds": {
"done": {
"armed": true, // 完成信号开关
"title": "任务完成", // 标题模板
"message": "会话 {session} 已完成,耗时 {elapsed}", // 正文模板
"icon": "", // 自定义图标路径(Windows 需 .ico)
"urgency": "normal", // low | normal | critical(Linux 生效)
"tone": {
"mode": "system", // none | system | file
"name": "Glass", // macOS 系统音效名 / Linux canberra 主题 id
"file": "", // 自定义音效文件(mode=file 时生效;Windows 仅 .wav)
"volume": 60 // 音量 0-100(macOS/Linux 部分后端生效)
}
},
"blocked": { /* 等待批准:默认 title「等待批准」,urgency critical */ },
"failed": { /* 运行出错:默认 title「运行出错」,urgency critical */ }
},
"cadence": {
"minIntervalMs": 5000, // 同类信号最小触发间隔
"burstLimit": 8, // 突发上限:窗口内最多条数
"burstWindowMs": 60000 // 突发统计窗口
},
"hush": {
"armed": false, // 免打扰时段开关
"from": "22:00", // 开始 HH:MM(本地时区)
"to": "08:00", // 结束 HH:MM,支持跨午夜
"weekdays": [] // 生效星期 [0=周日…6=周六],空=每天
},
"bridge": {
"decisionGraceMs": 600, // 审批宽限期(毫秒)
"attentionFrom": [] // 额外视为「等待人」的 dsh 事件名
}
}
文案占位符
标题与正文模板支持以下占位符(未提供的占位符会原样保留,便于发现拼写错误):
| 占位符 | 含义 |
|---|---|
{kind} |
信号类别中文名(完成 / 等待批准 / 出错) |
{session} |
会话 id |
{project} |
项目名(dsh 提供时才有值) |
{subject} |
主题(如发起审批的工具名、错误摘要) |
{detail} |
细节(审批原因、错误消息) |
{elapsed} |
本回合耗时(done 信号,以及 turn/end 来源的 failed 信号) |
{source} |
触发信号的事件名 |
{time} |
触发时刻 HH:MM |
命令行工具
包自带 CLI(npm 全局安装后为 chime,也可 node dist/cli.js):
chime ping [done|blocked|failed] 发送一条测试信号(试响)
(退出码:0=至少一通道成功,1=无启用通道或全部失败,2=被策略拦截/用法错误)
chime probe 自检通道可用性与配置来源
chime config 打印生效配置(JSON)
chime help 帮助
CLI 与插件共享同一套配置加载逻辑(默认值 + 配置文件;注意 CLI 不读取 dsh 插件行的 config,因为它在 dsh 之外运行),因此 chime probe 验证的是插件实际会使用的系统通道。
平台与系统依赖
| 平台 | 桌面通知 | 提示音(system) | 提示音(file) | 图标 | 音量 |
|---|---|---|---|---|---|
| macOS | osascript(系统自带) | afplay + /System/Library/Sounds/*.aiff | afplay + 任意格式 | 不支持(系统通知无图标位) | afplay -v |
| Windows | PowerShell + NotifyIcon 气泡(系统自带) | winmm PlaySound 系统音效事件 | winmm PlaySound,仅 .wav | .ico 文件 | 不支持 |
| Linux | notify-send(需安装 libnotify-bin / libnotify) | canberra-gtk-play 主题音 | paplay → aplay → ffplay → canberra(-f) 探测链 | 任意图片格式 | paplay/ffplay |
- 各平台通道都有可用性自检(
chime probe),不可用时会在日志中给出原因,不影响其他通道。 - Linux 的 system 音效需要
canberra-gtk-play(GNOME 桌面通常自带),kinds.<类别>.tone.name可指定 canberra 主题 id(如dialog-warning、complete);file 音效按paplay(PulseAudio)→aplay(ALSA)→ffplay(ffmpeg)→canberra-gtk-play -f兜底的顺序探测,可用channels.toneProgram强制指定。 - macOS 系统音效名可选:Basso、Blow、Bottle、Frog、Funk、Glass、Hero、Morse、Ping、Pop、Purr、Sosumi、Submarine、Tink。
- Windows 桌面通知为托盘气泡样式(Windows 10/11 会以通知中心样式展示)。无桌面会话的环境(服务账户、SSH 无桌面)中气泡可能不显示,属系统行为。
故障排查
| 症状 | 检查 |
|---|---|
| 完全没有提醒 | chime probe 看通道是否可用;确认 armed 与 kinds.*.armed 未关;确认不在免打扰时段;看 dsh 日志中 [chime] 行是 [已发] 还是 [拦截] |
| 只有桌面通知没有声音 | kinds.<类别>.tone.mode 是否被置为 none;Linux 用 channels.toneProgram 换后端;自定义文件是否被播放器支持(Windows 仅 wav) |
| 审批请求没提醒 | 策略为 never 或没有应答者时会自动裁决,宽限期内 approval/decided 到达即不打扰(这是预期行为);人工应答极慢时可调大 bridge.decisionGraceMs |
| 报错没提醒 | failed 信号来自 agent/error 与 turn/end(reason.kind='error')两条路径,均会经会话级去重合并为一条;若 dsh 版本改动了事件名或载荷结构,更新 src/bridge.ts 中的提取逻辑(或把新事件名加入 bridge.attentionFrom,会作为 blocked 处理) |
| 启动时报 duplicate loader entry id | 通常是同一插件在 profile 层与 bundle 层被重复挂载(例如旧式手动 insert 行与 dsh plugin add 的 bundle 行同时存在),删除旧的手动行即可 |
| 通知刷屏 | 调大 cadence.minIntervalMs 或调小 burstLimit;也可直接关闭 done 类信号 |
| 配置改完没生效 | 配置文件改完需触发插件热重载(dsh HMR);确认没有语法错误(有错会拒绝加载并打印原因) |
开发
npm install # 安装开发依赖(typescript、tsx、类型包)
npm test # 运行单元测试(node:test + tsx,无测试框架依赖)
npm run build # 编译到 dist/
代码结构:
src/
index.ts 入口(name / apply,装配流水线)
bridge.ts 事件桥:dsh 事件 → 信号(事件契约与映射集中在此)
pipeline.ts 流水线:裁定 → 渲染 → 分发
policy.ts 裁定:cadence(频率)与 hush(免打扰)
config.ts 配置:默认值、JSONC 解析、合并、严格校验
templates.ts 文案模板渲染与耗时格式化
memory.ts 会话记忆(回合开始时刻,带上限)
desktop.ts 桌面通知通道(三平台)
tone.ts 提示音通道(三平台)
runner.ts 子进程执行器(命令探测、超时强杀、PowerShell 编码调用)
types.ts 类型定义
cli.ts 命令行入口
test/ 单元测试(node:test)
许可
基于 MIT License 发布。
原始 README: https://github.com/JohnXu22786/notifier/blob/main/README.zh.md ↗
同类插件
查看全部 →
dsh
将 DeepSeek Harness 的生命周期状态、错误与审批请求,桥接到本地运行的 OpenPets 桌面伙伴。

dsh-qqbot
让 QQ Bot 接入 DeepSeek Harness(dsh)的官方插件

dsh-open-in-vscode
从 Web GUI 一键在 VS Code 中打开工作区目录。

dsh-notification
回合完成桌面通知,按结果分控 + 关键词过滤。

dsh-notifier
DSH 统一通知推送与远程控制:一个 `notify()` API 打通 25+ 渠道(Telegram / 钉钉 / 飞书 / 企业微信 / QQ 机器人 / WxPusher / PushPlus / Server 酱 / Bark / Discord / Slack / ntfy / webhook 等),timeSensitive / active / passive 分级路由并重试;五通道反向审批(Telegram 按钮 / 飞书卡片 / QQ / WxPusher / 微信 iLink);QQ/钉钉/飞书官方扫码登录;本地 Web 管理台;多 agent 路由;系统桌面通知——以及**手机指挥中心**:在手机上发 `!status` / `!stop` / `!retry` 遥控 agent,通知带可操作按钮(查看结果 / 重试 / 日志,点击回调 agent)。密钥脱敏、工具限流、零运行时依赖。

chatccc
飞书(Lark)或微信(WeChat)聊天控制 DeepSeek Harness / Claude Code / Cursor / Codex / CCC Agent