前言¶
给 DSH / 智能体客户端做语音输入时,常见做法是外挂一个录音工具,录完导出文件再手动传上去,中途要切窗口、找路径、等接口。dsh-record 把这条链路收进输入框:在 composer 里加一个麦克风按钮,点击开始录、再点一次停止,录音会作为对话附件直接挂在当前 prompt 上,跟着正常上传队列一起发出去。输出格式在插件设置页选,支持 MP3(默认)、WAV、WebM。
下面介绍这个插件。它由 most-silence 维护,开源协议 MIT,仓库 https://github.com/most-silence/dsh-record ,社区目录页是 https://www.skillhub.cn/plugins/most-silence/dsh-record 。
能做什么¶
一句话定位:一个客户端插件,给 DSH 的 composer 加录音按钮,录完把音频文件作为 conversation 附件附到当前 prompt 上。它只负责录制、转码、挂载这三步,不写磁盘文件、不做语音识别——需要转文字的话要另配一个 speech-to-text 插件(README 建议 dsh-stt)。
已经核实的能力:
- 按钮注册在
conversation.input.right槽位、提交动作之前,没有悬浮窗,可以和其他 composer 控件共存。 - 输出格式
mp3/wav/webm,默认mp3,在 Settings → Plugins → dsh-record 里选。 - 悬停提示跟随界面语言:中文显示「录音」,英文显示「Record」。按钮向宿主 locale 服务注册了
zh/en字典,切换语言后提示、aria-label、状态通知会跟着更新。 - 录音走浏览器
MediaRecorder,优先 webm/opus,按能力回退;录音中有计时器和文件名预览(rec-YYYYMMDD-HHMMSS-<n>s.mp3)。 - 三个格式都在浏览器内完成:MP3 由 lamejs 编码(128 kbps CBR),WAV 是 16-bit PCM 无损重封装,WebM 就是浏览器原生 Opus 容器不转码。
- 挂载走宿主 draft-attachment 通道(
conversation.createDrafts+inputActions.addAttachments),输入框里显示为文件卡片并带上传进度。 - 转码失败时(比如
OfflineAudioContext不可用)直接挂原始 WebM,保证录音不丢。
安装与启用¶
README 给的安装命令是本地构建后按文件路径添加,不是从 registry 拉包:
git clone https://github.com/most-silence/dsh-record
cd dsh-record && npm install && npm run build
dsh plugin --profile <your-profile> add file:$PWD
安装完重启 dsh 宿主,再刷新网页。可以用 npm run build 重新构建(tsdown 产出 lib/index.js 和 lib/client.js),npm test 跑 14 个 node:test 用例(客户端工厂契约 9 个、编码器 5 个),测试在 Node 24 上跑,不需要浏览器。
配置¶
配置只有一个字段,在 Settings → Plugins → dsh-record 里改:
| 字段 | 取值 | 含义 |
|---|---|---|
format |
mp3(默认)· wav · webm |
输出容器。MP3 / WAV 在停止录音后于浏览器内转码,WebM 原样挂载。改动在下次页面加载时生效,因为浏览器那半边只在启动时读一次配置。 |
浏览器端启动时通过 GET /dsh-record/config 读取这个配置,路由不可达就回退到 mp3。
架构与两个坑¶
插件是典型的前端 + 宿主两段式:
browser half (lib/client.js) host half (lib/index.js)
├─ mic button @ conversation.input.right ├─ Config schema { format: mp3 | wav | webm }
├─ zh/en dictionaries → locale service └─ GET /dsh-record/config → serves resolved config
├─ MediaRecorder → webm/opus (same-origin guarded)
├─ decode → lamejs (mp3) / PCM wrap (wav)
└─ attach via createDrafts + addAttachments
README 里特别记了两条从真实崩溃里学到的加固规则,做同类插件值得抄:
@breezystack/lamejs要内联打包。DSH 的模块表是冻结的,里面没有 lamejs,如果把它当构建外置依赖,require("@breezystack/lamejs")会查不到模块表并导致整个 web 启动中断。构建工具 tsdown 用noExternal强制内联。- 宿主端的
Config用z.union([...]),不能用z.enum([...]),并且 schemastery 保持external。宿主自带的 schemastery 版本没有enum方法,用打包进来的私有副本构造的 schema 又和宿主校验器不兼容,任一种都会在 import 阶段抛错,把这行插件判成required从而拖垮整个 profile。
注意点¶
- 这个插件以当前 dsh 进程的权限运行,安装第三方插件前建议先读一遍源码,也注意它的开源协议(本项目是 MIT)。
- 附件通道依赖宿主客户端契约,README 验证版本是 dsh 0.2.1-rc.2(
conversation.input.right槽位、createDrafts/addAttachments)。上游接口变动后可能退化为行内错误提示。 - 转码发生在停止录音之后,不是边录边转,所以很长的录音会有一小段编码等待。
- 录音需要浏览器麦克风授权,页面必须走 HTTPS 或 localhost。
- 输出格式放在宿主 profile 配置里而不是浏览器里,所以每次改完要刷新页面才生效。
小结¶
dsh-record 解决的是一个很具体的链路问题:输入框里录音 → 浏览器内转码 → 作为 prompt 附件上传,不碰磁盘、不做识别。如果你已经在用 DSH 做客户端语音输入,可以先在测试 profile 里装上试一轮,确认附件通道和格式符合预期,再考虑接到正式流程里。
- 目录页:https://www.skillhub.cn/plugins/most-silence/dsh-record
- 代码仓库:https://github.com/most-silence/dsh-record