dsh-record:给 DSH 输入框加一个录音按钮,录完直接挂到 prompt 上

前言

给 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 里特别记了两条从真实崩溃里学到的加固规则,做同类插件值得抄:

  1. @breezystack/lamejs 要内联打包。DSH 的模块表是冻结的,里面没有 lamejs,如果把它当构建外置依赖,require("@breezystack/lamejs") 会查不到模块表并导致整个 web 启动中断。构建工具 tsdown 用 noExternal 强制内联。
  2. 宿主端的 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
羽毛球分组比赛记分
小程序二维码

欢迎使用《羽毛球分组比赛记分》微信小程序

小夜