dsh-tts
English | 中文
A text-to-speech plugin for dsh web: it reads the final answer of each assistant reply aloud with Edge TTS (Microsoft neural voices), enabling hands-free voice conversation.
- Real Edge TTS: the host (Node) half synthesizes MP3 through Microsoft Edge's free "Read Aloud" neural-voice endpoint (a hand-rolled RFC 6455 WebSocket — Node's native WebSocket filters the handshake headers Edge requires); the browser plays it. No OpenAI/third-party keys.
- Final result only: intermediate steps and tool-call narration are skipped; each turn reads only its closing answer, and a burst of completed turns plays just the latest. Long replies are auto-split into sentence segments and read back-to-back. Falls back to the browser's
speechSynthesisoffline. - Voice & rate: 20+ Edge neural voices built in (default
zh-CN-XiaoxiaoNeural), with a 0.5×–2× rate slider. - Speaker toggle: a speaker button in the composer tool row (with a hover tooltip) flips the single "read new replies automatically" toggle; it also stops the current utterance. Linked to the setting of the same name.
- Approval alerts: announces “need approval” when an approval is requested, independently toggleable; a custom alert sound can be picked (preset “Ding / Beep-beep / Dong”, or upload mp3/wav/ogg ≤ 1 MB — playing instantly when selected).
- Fully local, zero runtime dependencies: the Edge protocol uses Node built-ins only, so the package installs offline.
dsh-plugin topic: (https://github.com/topics/dsh-plugin)
> Default voice zh-CN-XiaoxiaoNeural. To add official voices, append an Edge ShortName to src/voices.ts.
Settings

Install
> Prerequisite: the dsh app closure must include the plugin's @deepseek-ai/dsh-* peer packages — any deployment carrying the @deepseek-ai/dsh-web-app bundle (every official dsh web profile qualifies).
Method 1: build a local tarball, then install (most common)
> This repo is published as source — there is no prebuilt release tgz. Build dsh-tts-0.1.0.tgz in the repo first, then install from it.
1. Clone/enter the repo and build:
``sh git clone https://github.com/Vim0x3c/dsh-tts.git dsh-tts cd dsh-tts pnpm install pnpm build # tsdown: produces lib/index.js, lib/client.js, etc. npm pack # produces dsh-tts-0.1.0.tgz ``
> If you only need it on this machine, you can just run pnpm build in the repo to get lib/; but installing to dsh is best done with the tarball (generated by npm pack).
2. Install into the web profile:
``sh dsh plugin --profile web add -w ./dsh-tts-0.1.0.tgz ``
> The -w flag is required: every profile ships a pnpm-workspace.yaml, so pnpm treats the profile dir as a workspace root and a bare add fails with ERR_PNPM_ADDING_TO_ROOT.
3. Restart dsh web (the host half loads on boot):
``sh dsh web # or: dsh --profile web ``
4. Verify the install:
``sh # ① the plugin is in the composed config (expect - id: dsh-tts`)
dsh --profile web --dump-config | grep dsh-tts
# ② the host bridge is up (expect 200 {"ok":true}) curl http://127.0.0.1:8765/health ```
Method 2: git URL install (not recommended)
dsh plugin --profile web add github:Vim0x3c/dsh-tts> ⚠️ Not recommended: a git install resolves source, not the built lib/, and the peer packages are not published to npm, so a git install usually cannot build or resolve them. Build the tarball locally and use Method 1 instead.
Remove / reinstall
dsh plugin --profile web remove dsh-tts # remove
dsh plugin --profile web add -w ./dsh-tts-0.1.0.tgz # reinstall (overwrites)Usage
1. Open dsh web Settings → Voice (see the screenshot above). 2. Turn on Read new replies automatically, then click Test voice to confirm the Edge bridge is reachable. 3. From then on, each assistant final answer is read aloud in the selected voice.
- Speaker toggle: the speaker button in the composer tool row (hover for a tip). It flips “read new replies automatically” on/off; turning it off also stops the current utterance.
- Read new replies automatically: the single master toggle. When off, replies are not auto-read, but Test voice still works manually.
- Speak approval requests: announces “need approval” when an approval is requested; independently toggleable, with an optional custom approval alert sound (preset or uploaded) that plays instantly when selected.
- Stop: interrupts the current playback at any time.
> Autoplay policy: browsers usually require a prior user interaction before playing audio. Because you only receive replies after sending a message, this is generally satisfied; if playback is ever blocked, click Test voice once in Settings to unlock audio.
> Voice input (mic/STT) is not included: browser SpeechRecognition works only in Chrome/Edge (not Safari), and dsh has no built-in STT. For voice input, see dsh-plugin-voice-input or dsh-voice.
Development
pnpm install
pnpm build # tsdown: lib/index.js (host), lib/invariant.js, lib/client.js (browser)
pnpm test # vitest: unit/regression testsThe build is self-contained (tsdown + lightningcss); no monorepo checkout is required. prepare runs the same build, so a permitted git install also produces artifacts.
> This repo's local node_modules reuses the dsh-skin-appearance pnpm store for offline type-check/build; release tarballs need not include the peer packages (the dsh app closure resolves them at runtime).
How it works
- Host half (
lib/index.js): a loopback-only127.0.0.1HTTP server (probes ports 8765–8780):
- GET /health — bridge liveness; - POST /synthesize — takes { text, voice, rate }, synthesizes MP3 via the native Edge protocol, returns it. - CORS is hardened: it reflects only allow-listed dsh web origins and 403s unknown browser Origins; no-Origin callers (curl) still work. The Edge protocol (src/edge/) mirrors Edge's "Read Aloud": Sec-MS-GEC (SHA-256 FILETIME) + Sec-MS-GEC-Version, browser-fingerprint handshake headers (Origin/User-Agent/Cookie: muid), speech.config → SSML messages, binary frames parsed as [2-byte header length][Path: audio header][MP3]. WebSocket is a hand-rolled RFC 6455 client. Zero third-party dependencies.
- Browser half (
lib/client.js): subscribes viactx.sessions, picks each completed turn's final answer (only the newest of a burst), splits long text into segments, and plays with a generation-token + AbortController so stop/interrupt races are correct (stopping also silences approval sounds). Falls back to the browser Web Speech API on Edge failure. Settings persist to browser localStorage (dsh-tts.settings) because the dsh web host only exposes a fixed settings-namespace allowlist to the browser, which a standalone plugin cannot extend (seeWEB_SETTINGS_NAMESPACESinapi-proxy.ts).
Known limitations
- Edge cloud synthesis needs network: the host must reach
speech.platform.bing.com. Test with “Test voice” first; oncould not connector 403 it automatically falls back to the browser's local Web Speech voices. Sec-MS-GECtoken is aligned with the currentedge-ttsalgorithm (SHA-256 FILETIME +Sec-MS-GEC-Version). If Microsoft rotates it again, fixsrc/edge/token.ts(the "Test voice" button surfaces a 403 immediately).- Active session only: auto-reading follows the session currently in view; background sessions are not spoken.
- In-process bridge: the bridge lives with the
dsh webhost process and shuts down when dsh web stops. - No voice input in this plugin (see Usage above).
License
MIT