npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

dsh-sound-notifier

v1.2.0

Published

DSH 提示音插件:需要你决策(审批/提问)时播放提示音,可在设置页里配置,自带原创音效

Readme

dsh-sound-notifier

DSH 提示音插件:AI 需要你做决定时,播放一声提示音。

当 AI 需要你决策时(请求你批准某个操作,或向你提问并等待回答),它会停下来等你。 如果你正在做别的事,很容易错过——这个插件在这些时刻播放提示音,你就不必一直盯着屏幕。

特性

  • 🔔 两种决策场景:审批确认、用户提问,可分别开关
  • 🎛 在设置页配置:DSH「设置 → 插件 → 插件配置」里的一张卡片,默认收起
  • ▶️ 一键试听:改完声音当场听效果,不用等下次提问
  • 改动立即生效:无需保存、无需重启
  • 🌏 中英双语:跟随 DSH 的界面语言
  • 🎵 自带原创音效:包内附 4 个原创提示音(Ding / Chime / Alert / Tick),装上即用
  • 🎚 也可自定义:支持 macOS 系统音效,或任意音频文件的绝对路径
  • 🔀 双半侧插件:host 半侧监听事件,浏览器半侧渲染配置卡片

安装

从 npm 安装(推荐)

dsh plugin --profile web add dsh-sound-notifier

本包声明了 dsh.bundle.patch,把包名加入 profile 的 dsh.profile.bundles 即可挂载:

{
  "dsh": {
    "profile": {
      "bundles": [
        "@deepseek-ai/dsh-base",
        "@deepseek-ai/dsh-web-app",
        "dsh-sound-notifier"
      ]
    }
  }
}

手动挂载(本地开发)

把包放到 profile 的 plugins/ 目录,然后在 cordis.patch.yml 里按相对路径挂载:

- insert:
    - id: sound-notifier
      name: "./plugins/dsh-sound-notifier/lib/index.js"
      config:
        enabled: true
        sound: /System/Library/Sounds/Funk.aiff
        onApproval: true
        onQuestion: true

配置

设置 → 插件 → 插件配置 → 展开 提示音 卡片。

| 字段 | 类型 | 默认值 | 说明 | |---|---|---|---| | enabled | boolean | true | 总开关,关闭后完全不发声 | | sound | string | ding | 内置音效名,或音频文件绝对路径 | | onApproval | boolean | true | 审批确认时是否响 | | onQuestion | boolean | true | 用户提问时是否响 |

  • 被改过的字段会显示 已自定义,可点 重置 恢复默认。
  • 改动立即生效,不需要保存按钮。
  • 声音字段旁的 试听 按钮会立即播放一遍,可当场确认路径是否有效。

声音的两种写法

1)内置音效名(推荐) —— 直接填名字,host 半侧解析到包内 assets/sounds/<name>.wav

| 名字 | 音色 | |---|---| | ding | 温和双音铃(A5 → E6),默认 | | chime | 上行三音铃(C6 → E6 → G6) | | alert | 下行双音(G5 → D5),更有紧迫感 | | tick | 单音轻点(C6),最不打扰 |

名字大小写不敏感,.wav 后缀可省略;只允许 [a-z0-9-],因此不可能被用来做路径穿越。

2)绝对路径 —— 填任意 .aiff / .mp3 / .wav 文件的绝对路径,例如 macOS 自带音效位于 /System/Library/Sounds/

Basso  Blow  Bottle  Frog  Funk  Glass  Hero  Morse
Ping   Pop   Purr    Sosumi  Submarine  Tink

版权说明:包内自带的 4 个音效是本项目原创合成的(生成脚本见提交历史),以 MIT 随包分发。 macOS 系统音效(Funk / Glass 等)只作为路径被引用,不随包分发——它们的版权属于 Apple。 请勿把这些系统文件打包进你发布的分支。

跨平台播放

内置音效是标准 16-bit PCM WAV,任何系统都能播。插件会自动探测本机可用的播放器

| 平台 | 使用的播放器 | |---|---| | macOS | afplay(系统自带) | | Linux | 按序探测 paplayaplaypw-playplayffplaympvcvlc,取第一个可用的 | | Windows | PowerShell 的 Media.SoundPlayer(仅支持 WAV) |

探测只做一次并缓存。若系统上一个播放器都没有,「试听」会返回明确提示 (提示安装 paplay / aplay / ffplay / mpv),而不是静默失败。

Windows 注意SoundPlayer 只支持 WAV,所以自定义的 mp3/m4a 在 Windows 上 无法播放;内置的 4 个音效都是 WAV,不受影响。

工作原理

host 半侧 — lib/index.js

  1. inject = ["shell"],通过 shell 服务执行 afplay
  2. 监听两个 Cordis waterfall 事件:
    • approval/request —— 审批请求
    • user-questions/request —— 用户提问
  3. 监听器必须调用并返回 next() 透传,否则会 veto 掉真正的 UI answerer。本插件严格透传,绝不干扰决策流程
  4. 通过 settings.installSection(...) 把部署配置注册成用户可覆盖的 settings 命名空间, 并用 setSource 拿到的取值函数在每次触发时读取当前配置。

试听路由

host 半侧另外注册了一个仅回环可达的接口,供卡片上的「试听」按钮调用:

POST /api/dsh-sound-notifier/preview
body: { "sound": "/absolute/path.aiff" }   # 可省略,省略则播放当前配置
→ 200 { "ok": true, "sound": "..." }
→ 400 { "error": "invalid sound: expected an absolute file path" }
→ 403 非回环 / 跨站请求

与事件播放的 fire-and-forget 不同,试听会 await 命令结果,把失败如实回报给界面。

浏览器半侧 — lib/client.js

手写的 module-loader 模块(不需要构建工具链):

window.__ModuleLoader__.load({
  id: 'dsh-sound-notifier',
  factory: function (require) {
    var React = require('react');   // 外部依赖,用 createElement 而非 JSX
    // ...
  }
});

settings.plugin.item 这个 keyed slot 上注册卡片,key 即 settings 命名空间

ctx.slots.inject('settings.plugin.item', function () {
  return ctx.slots.register(
    { name: 'settings.plugin.item', key: 'sound-notifier' },
    SoundCard
  );
});

读写配置用 ctx.settingsScope.bind({ namespace })getSnapshot() / subscribe() / set(field, value) / unset(field)

开发要点(踩过的坑)

  1. 设置页渲染的是「交集」:宿主服务的 settings 命名空间 ∩ 注册在这些 key 上的卡片。 只有 host 半侧时命名空间无人认领 → 什么都不渲染。两个半侧的命名空间名必须完全一致。
  2. 跨插件 value import 被禁止:官方卡片的 PluginCard 外壳不能直接复用, 第三方插件必须自绘卡片 chrome(这也是本插件自带样式的原因)。
  3. waterfall 事件必须透传:忘了 return next() 会直接破坏决策弹窗。
  4. schemastery 没有 .optional():可选字段用 .default() 表达,0~1 的数值用 z.percent()
  5. 插件代码改动需要重启 dsh webcordis.patch.yml 是 live 热重载的, 但 Node 的 ESM 模块按 URL 缓存,已加载的模块不会因文件变化自动重新求值。
  6. 路径插进 shell 命令必须转义:声音路径是可配置字符串,直接拼进 afplay "..." 会形成命令注入面。本插件把路径整体包进单引号并转义内部单引号(shQuote), 已用真实 bash 验证恶意载荷只会被当成 1 个普通参数。
  7. 路由只对回环 + 同源开放:试听路由复用了 DSH 其他插件的 isLoopbackRequest 约定, 校验远端地址、Host 头、sec-fetch-siteOrigin
  8. ctx.shell.run 必须收 resolve() 后的 spec:直接 run({ command }) 会跳过 执行器对 workdir / stdoutMaxBytes / sandboxPolicy 的补全,沙箱执行器随即在 const { mode } = policy 处抛 Cannot destructure property 'mode' of 'policy' as it is undefined。 正确写法:
    const result = await ctx.shell.run(ctx.shell.resolve({ command }));
    resolve() 是同步的,会补上 request.sandboxPolicy ?? ctx.sandboxPolicy.resolve()。)
  9. 别用 .catch(() => {}) 吞掉播放错误:本项目就因此让「声音其实一直没响」被掩盖了很久。 失败要经 ctx.logger.warn 留痕。
  10. CSS 变量名要对:用 --dsw-alias-border-l2 这类真实存在的主题变量 (--dsw-alias-border-secondary 不存在),才能跟随明暗主题。

License

MIT