对话完成或需授权时响铃,把你叫回窗口
31.5/100
综合分
2
GitHub 星标
—
npm 周下载
—/5
用户评分
数据截至 2026/10/17 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。
安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add Fur1naaaaaaaa/dsh-notify-sound该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)
状态速览
判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态信任档位:已验证
图例:✓ 通过! 需注意— 未判定 / 无数据· 「未判定」= 本站尚无数据,不代表插件有问题
档位由哪些信号合成
本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。
给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个
给 DSH 加提示音:对话完成 / 需要授权 / 弹出提问时响铃。零依赖,Web Audio 现场合成,内置设置界面(音量滑杆 + 四段试听)。
README
由 DeepSeek 最新模型翻译生成dsh-notify-sound
给 DSH(DeepSeek Harness) 加提示音:这三种时刻会响一声,把你从别的窗口叫回来。
| 触发时刻 | 默认提示音 | 检测来源 |
| 一轮对话完成 | 两声上行清亮音(A5 → E6) | session/event 的 turn/end,reason=completed |
| 需要你授权 | 三声急促方波(C6 C6 F6) | approval/request(审批弹框出现的那一刻) |
| 弹出提问 / 计划确认 | 两声询问音(E5 → B5) | user-questions/request(ask_user_question 与 plan-review 都走它) |
| 出错 / 被拦截 / 超长结束 | 两声低沉警示音 | 同一个 turn/end,只是 reason 不同 |
- 四段提示音由 Web Audio 现场合成:不需要音频文件,零第三方依赖。
- 自带一页设置界面(设置 → 插件 → 提示音),音量滑杆 + 四个试听按钮,改完即时生效。
安装
要求:DSH 0.2.x(开发与验证环境为 0.2.0-rc.2 桌面版)、Node ≥ 20。插件本身不依赖任何 npm 包。
展开完整文档(README 剩余 1 节)
dsh-notify-sound
给 DSH(DeepSeek Harness) 加提示音:这三种时刻会响一声,把你从别的窗口叫回来。
| 触发时刻 | 默认提示音 | 检测来源 |
| 一轮对话完成 | 两声上行清亮音(A5 → E6) | session/event 的 turn/end,reason=completed |
| 需要你授权 | 三声急促方波(C6 C6 F6) | approval/request(审批弹框出现的那一刻) |
| 弹出提问 / 计划确认 | 两声询问音(E5 → B5) | user-questions/request(ask_user_question 与 plan-review 都走它) |
| 出错 / 被拦截 / 超长结束 | 两声低沉警示音 | 同一个 turn/end,只是 reason 不同 |
- 四段提示音由 Web Audio 现场合成:不需要音频文件,零第三方依赖。
- 自带一页设置界面(设置 → 插件 → 提示音),音量滑杆 + 四个试听按钮,改完即时生效。
安装
要求:DSH 0.2.x(开发与验证环境为 0.2.0-rc.2 桌面版)、Node ≥ 20。插件本身不依赖任何 npm 包。
从 npm(已发布时最省事)
dsh plugin --profile <你的 profile> add dsh-notify-sound
直接从 GitHub
dsh plugin --profile <你的 profile> add github:Fur1naaaaaaaa/dsh-notify-sound
本地目录(开发用)
dsh plugin --profile <你的 profile> add link:/绝对路径/dsh-notify-sound
装完 刷新一次页面:注入脚本是页面加载时写进 index.html 的,已经打开的页面不会自己长出来。
卸载:插件管理器里禁用或删除该条目即可;插件会在卸载时注销自己的路由与 index 注入。
在设置里直接调(推荐)
打开 设置 → 插件 → 提示音(在「插件市场」旁边):
- 音量滑杆:拖动即时生效,并写进 ~/.dsh/dsh-notify-sound.json(重启后保持);
- 四个试听按钮:完成 / 提问 / 授权 / 异常,逐个听;
- 什么时候响:对话完成、需要授权、弹出提问/计划确认三个开关,外加忽略子代理、只在后台响、总开关;
- 提示气泡策略(auto / always / never)与「后台时闪烁标题」;
- 如果本机浏览器有覆盖设置(比如点过提示气泡上的「静音」),这里会显示出来并给一个「清除本机覆盖」。
保存后立刻生效:页面里已经跑着的提示音会马上重拉配置 —— 不用刷新页面,也不用重启 DSH。
想确认设置页有没有挂上,看 /dsh-notify/ping 的 ui.module.tabRegistered。
调节音量
三种方式,越靠前越推荐:
1. 设置界面:设置 → 插件 → 提示音 → 拖滑杆。
2. 配置文件 ~/.dsh/dsh-notify-sound.json 写 { "volume": 0.35 }(0~1)。改完刷新页面即生效,或者控制台执行 dshNotifySound.refreshConfig(),连刷新都省了 —— 都不用重启 DSH。
3. 控制台(即时且记住):dshNotifySound.setVolume(0.8)。
音量是相对值:合成音峰值 ≈ volume × 0.35 × 每个音符的相对幅度,所以 1.0 也不会刺耳;想要更大的整体响度,调高 lib/browser.js 里 CUES 的基准系数即可。
覆盖是按单键记的:控制台/静音按钮只把「你真正改过的那几个键」写进浏览器 localStorage,其余字段照常跟随主机配置 —— 所以「改过音量之后,配置文件里再改别的字段」依然有效。
想彻底回到主机配置:dshNotifySound.reset(),或 localStorage.removeItem('dsh-notify-sound.prefs')。
排查「配置文件为什么不生效」时,先看 dshNotifySound.overrides() 列出了哪些键。
配置
优先级(后面的覆盖前面的,按字段逐个生效):默认值 → profile 里该条目的 config → ~/.dsh/dsh-notify-sound.json → 浏览器 localStorage。
~/.dsh/dsh-notify-sound.json 会压住条目 config(设置页写的就是这个文件),完整字段:
{
"enabled": true,
"volume": 0.5,
"pollMs": 700,
"turnEnd": true,
"approval": true,
"question": true,
"turnEndReasons": ["completed", "error", "blocked", "max-tokens"],
"skipSubagents": true,
"onlyWhenHidden": false,
"toast": "auto",
"flashTitle": true
}
| 字段 | 默认 | 含义 |
| enabled | true | 总开关 |
| volume | 0.5 | 音量 0~1 |
| pollMs | 700 | 轮询间隔(仅在 SSE 不可用时才用到) |
| turnEnd / approval / question | true | 三类触发各自开关 |
| turnEndReasons | completed, error, blocked, max-tokens | 哪些结束原因算“该提醒”;aborted / interrupted 默认不响,免得自己按了停止还吵 |
| skipSubagents | true | 忽略子代理(subagent / workflow 子会话)的轮次结束,多智能体协作不会连环响 |
| onlyWhenHidden | false | true = 只有页面在后台时才响(最常见的用法) |
| toast | auto | auto:页面在后台 / 被拦截 / 被静音时显示;always 总是;never 从不 |
| flashTitle | true | 页面在后台时闪烁标题 🔔 … |
也可以写进 profile 的 patch(id 要跟挂载 id 一致):
- id: notify-sound
name: dsh-notify-sound
config:
volume: 0.4
onlyWhenHidden: true
浏览器端控制(F12 控制台)
dshNotifySound.status() // 配置、传输方式(sse/polling)、AudioContext 状态、统计
dshNotifySound.test() // 试听“对话完成”
dshNotifySound.test('approval') // 还有 question / error
dshNotifySound.toggle() // 开 / 关(会弹提示气泡反馈)
dshNotifySound.setVolume(0.8) // 音量 0~1,立即生效并记住
dshNotifySound.set('onlyWhenHidden', true) // 改任意偏好,只记这一个键
dshNotifySound.overrides() // 看哪些键被浏览器覆盖了(排查“配置文件不生效”)
dshNotifySound.reset() // 清掉全部浏览器覆盖,完全回到主机配置
dshNotifySound.refreshConfig() // 重新拉主机配置(改完配置文件不用刷新页面)
提示气泡上的按钮也能一键静音 / 恢复;静音状态下收到通知仍会弹气泡(带“恢复声音”),不会让你找不到开关。
主机端自检(HTTP)
<端口> 用 dsh web 启动时打印的地址,或环境变量 DSH_WEB_URL
curl http://127.0.0.1:<端口>/dsh-notify/ping # 状态:bootId / seq / counts / metrics / lastAck / ui
curl "http://127.0.0.1:<端口>/dsh-notify/test?type=approval" # 手动推一条通知试听
curl "http://127.0.0.1:<端口>/dsh-notify/test?type=question&plan=1"
curl -X POST -H 'Content-Type: application/json' \
-d '{"volume":0.35,"onlyWhenHidden":true}' \
http://127.0.0.1:<端口>/dsh-notify/config # 改配置(设置页走的就是这条)
/ping 里几个关键字段:
| 字段 | 含义 |
| ui.module | 客户端模块上报:tabRegistered = 设置页那一栏挂上了;error 有值就是失败原因 |
| ui.script | 页面脚本上报:它跑起来了、走的 sse 还是 polling |
| metrics | 各路由命中次数(config / configWrites / events / streamOpened / clientJs / test / ack) |
| lastAck | 浏览器对最近一条通知的回执:outcome: played 表示确实出声了 |
| lastEventsQuery | 页面轮询消费到的序号 |
lastAck 是浏览器自己的回执:played = 确实出声了;blocked = 被自动播放策略拦了(点一下页面即可);muted = 当前被静音;没有 ack = 页面根本没收到。
一键试听四段音效
python scripts/preview-cues.py # 默认 http://127.0.0.1:19387
python scripts/preview-cues.py http://127.0.0.1:<端口>
DSH_WEB_URL=http://127.0.0.1:<端口> python scripts/preview-cues.py
依次推「对话完成 → 需要回答 → 需要授权 → 异常结束」,并且按浏览器的回执控节奏:每推一条都等到页面回报 played 再放下一条,所以即使页面退回了轮询(后台节流)也不会挤成一团。
工作原理
主机侧 lib/index.js 浏览器侧 lib/browser.js ────────────────────────── ─────────────────────────
ctx.on('session/event') ─┐
ctx.on('approval/request') ─┼─ 通知(seq++) ─→ SSE /dsh-notify/stream ─┐
ctx.on('user-questions/…') ─┘ 轮询 /dsh-notify/events ─┴→ Web Audio 合成音
└→ 提示气泡 + 标题闪烁(后台 / 被拦截时兜底)
- 为什么用主机事件:turn/end 是“这一轮真的结束了”的精确信号,比观察“停止按钮变回发送按钮”稳; approval/request / user-questions/request 正是“弹框出现”的那一刻。两个 waterfall 监听器都 return next(),只搭便车观察、不截胡请求。
- SSE 优先:浏览器会把后台标签页的 setTimeout 节流到 1 次/分钟,纯轮询会让提示音迟到甚至成串; SSE 是网络推送,不受节流影响。连不上(连续两次失败)自动退回 pollMs 轮询,另有 15s 慢速兜底轮询,并且每 30s、以及页面重新可见时都会试着重连 SSE,不会一直赖在被节流的轮询上。
重复送达靠 seq 去重,同一件事只响一次。
- 代次 bootId:seq 是内存自增数,插件重载 / 应用重启会归零;页面靠 bootId 变化重置游标,既不会「等一个不会再出现的序号」而失聪,也不会补播上一代的旧提示。
- 子代理过滤:subagent/start 登记子会话 id,子会话的 turn/end 不响。
- 自动播放策略:脚本在第一次用户手势里解锁 AudioContext;真播不出来时仍然弹气泡 + 闪标题,并提示「点一下页面即可恢复」。
- 自检通知(test: true)只受总开关和音量约束,不受分类开关与 onlyWhenHidden 影响,试听一定出声,并带上「试听 · xxx」的气泡标签。
权限与隐私
- 不联网:插件不请求任何外部地址,只用 Node 内置模块 + 本机 http://127.0.0.1:<端口>/dsh-notify/* 路由。
- 零依赖:dependencies 为空,安装不会引入任何第三方包。
- 写什么文件:只有 ~/.dsh/dsh-notify-sound.json(保存音量和开关)。浏览器侧还会写 localStorage['dsh-notify-sound.prefs'](只记你显式改过的键)
- 鉴权说明:插件路由通过 DSH 的 webServer 注册,不经过 DSH Web 的登录鉴权。同一个页面源(即 DSH 自己)自然可以读写;跨源页面即使能发出 POST /dsh-notify/config,也会被浏览器 CORS 挡住读响应,且它能影响的只是提示音设置。若你的机器上有不受信任的本地进程,请据此评估。
文件结构
| 文件 | 作用 |
| lib/index.js | 主机半边:事件监听、通知缓冲、HTTP/SSE 路由(含设置页的读写)、index 注入 |
| lib/browser.js | 浏览器半边:传输(SSE / 轮询)、合成提示音、提示气泡、标题闪烁、控制台 API |
| lib/client.js | 客户端模块:注入 browser.js + 注册「设置 → 插件 → 提示音」那一页 |
| cordis.patch.yml | bundle 挂载声明(安装时把插件行插进加载器树) |
| scripts/preview-cues.py | 一键按顺序试听四段音效(按浏览器回执控节奏) |
| test/host.test.mjs | 主机侧单测:假 ctx / req / res |
| test/browser.test.mjs | 浏览器侧单测:最小 DOM / AudioContext / fetch / EventSource 桩 |
| test/client.test.mjs | 客户端模块单测:假模块加载器 + React 桩,验依赖声明、插槽注册、页面能渲染 |
排查
| 现象 | 看这里 |
| 完全不响 | /dsh-notify/ping 是否有响应;控制台 dshNotifySound.status() 的 transport、audio.state、stats.blocked |
| 第一次不响 | 页面还没被点过,浏览器拦了自动播放:点一下页面 |
| 只有后台响 / 从来不响 | 检查 onlyWhenHidden、enabled;浏览器 localStorage 会覆盖主机配置 |
| 子代理完成太吵 | 保持 skipSubagents: true;仍嫌吵就把 turnEnd 关掉,只留授权 / 提问 |
| 改了配置不生效 | 主机配置刷新页面即生效(或 dshNotifySound.refreshConfig());若某个键在浏览器里被显式改过,它会压过主机配置 —— dshNotifySound.overrides() 看是哪些,reset() 一键清掉 |
| 设置页没出现 | /ping 的 ui.module:tabRegistered: false 时 error 里有原因;确认打开的是「设置 → 插件」 |
| 页面刷新后没脚本 | index.html 是加载时注入的,必须刷新页面 |
| 声音迟到 / 几声挤在一起 | 页面退到了轮询且窗口在后台(定时器被节流)。刷新页面会重新走 SSE;metrics.streamOpened 能确认有没有走推送 |
| 自检推了通知但没声音 | 看 /ping 的 lastAck:blocked / muted / 没有 ack 三种情况分别对应上面的说明 |
开发
改代码后怎么让自己那台跑起来:
- 浏览器侧(lib/browser.js):每次请求现读磁盘,改完刷新页面即可。
- 客户端模块(lib/client.js):重新加载页面即可(模块在页面加载时装载)。
- 主机侧(lib/index.js):Node 会缓存 ESM 模块,只「禁用→启用」拿不到新代码。两种办法:
1. 重启 DSH(最省事)。
2. 不重启也行:先让模块缓存失效,再禁用→启用该条目。给 profile 里该条目临时加一句只做副作用的 !!js(disabled 仍求值为 false),把 <插件目录> 换成你自己的绝对路径:
- id: notify-sound
name: dsh-notify-sound
disabled: !!js "(() => { try { const url = new URL('lib/index.js', 'file:///<插件目录>/').href; const cache = ctx.loader.internal.loadCache; if (cache && cache.has(url)) Map.prototype.delete.call(cache, url) } catch (error) {} return false })()"
注意 Node 22/24 里 loadCache 的 delete() 只清类型槽位,必须用 Map.prototype.delete.call;用完把那句换回 disabled: false。
3. 也可以给 dsh-hmr 配 watch 根(- id: hmr + config.root: [<插件目录>]),DSH 启动后保存即自动重载。
测试
bash
node --test test/host.test.mjs test/browser.test.mjs test/client.test.mjs
- 主机侧:事件识别(正常 / 出错 / 中断)、子代理过滤、waterfall 必须 next()、增量接口与「不回放历史」、 SSE 首帧与推送、/test 自检、/ack 回执、/config 写配置与字段白名单、/ui-report 上报、 bootId 换代、index 注入幂等、配置优先级、卸载回收。
- 浏览器侧:四段提示音各自音符、去重、静音、分类开关、onlyWhenHidden、音频挂起后 resume 补播、 SSE → 轮询回退与重连、换代重置游标、标题闪烁与还原、重复注入只装一个实例、坏 localStorage 不崩、自检通知不受分类开关限制、浏览器覆盖只记显式改过的键。
- 客户端模块:模块声明、运行时脚本注入、依赖声明的两条路(清单 inject / 运行中 ctx.inject)、插槽注册字段(id / order / label)、设置页能渲染出滑杆与四个试听按钮、渲染失败时的兜底。
许可
MIT预览分享卡片长什么样 ↓
分享 · DeepSeek Harness Hub
Fur1naaaaaaaa/dsh-notify-sound
对话完成或需授权时响铃,把你叫回窗口
相关插件 · UI / 主题
破甲增强版JeffKeeping/dsh-purge-plus
dsh-purge 的非官方分支fork:适配 DeepSeek Harness 0.2.1-rc.2、破甲内核…
★ 387已汉化DeepSeek 客户端
71.9
无代码业务系统平台nocobase/nocobase
NocoBase 是什么
★ 24,804已汉化DSH
71.8
1Panel 服务器面板1Panel-dev/1Panel
全球 250 万+ 自托管用户共同喜爱。
★ 37,350已汉化DSH
71.5
工作状态行动态ccch1mneyyy/working-activity
作者:chimney@ccch1mneyyy。社区项目,非 DeepSeek 官方项目。
★ 664已汉化DSH
71.2
多模型配置管家farion1231/cc-switch
跨平台桌面一体化助手,支持 Claude Code、Codex、OpenCode、DeepSeek Harness 等工具,唯一官网 ccswitch.io
★ 151,506已汉化DSH
71.1
AI 矢量设计工具ZSeven-W/openpencil
全球首个 AI 原生开源矢量设计工具。
★ 7,027已汉化DSH
71.1
这个插件是你维护的吗?
作者本人可认领并核对本条目;认领通过后,详情页会显示「作者已认领」标记。