dsh-speak 1.0.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.zh-CN.md CHANGED
@@ -1,177 +1,274 @@
1
- # dsh-speak 🔊 — 为 AI 编程 harness 提供语音播报
2
-
3
- 让 Agent 在长任务完成时**开口告诉你**——不用再盯着屏幕等。
4
-
5
- dsh-speak 通过 Windows 语音合成把 Agent 的最终回复朗读出来,优先使用自然语音
6
- (Windows 11 内置,或 Windows 10 上经 [NaturalVoiceSAPIAdapter] 注册,如晓晓),
7
- 没有时优雅回退到系统自带中文语音。本项目为
8
- [DeepSeek Harness](https://github.com/deepseek-ai/dsh) 而生,但结构上
9
- 任何 harness 都能接入。
10
-
11
- > **项目定位**:本项目只是为了给想让 harness 开口说话的用户提供一种**已经验证过的方案**;
12
- > 没有意外的话,后续不会再更新。
13
-
14
- ```
15
- harness 事件(DSH 会话事件 / Claude Code Stop hook / 任意方式)
16
-
17
- ▼ adapters/… (harness 专属触发器:过滤、节流、取消)
18
- ▼ engine/speak.ps1 (与 harness 无关:清洗文本 → Windows SAPI5)
19
- ▼ 🔊 你听到最终回复
20
- ```
21
-
22
- ## 特性
23
-
24
- - **全自动**:DSH web 插件监听会话事件流,自动播报最终回复
25
- (跳过 reasoning/工具调用旁白,合并同一回复的多步消息)。
26
- - **尽力而为**:绝不抛错、绝不阻塞 harness、绝不破坏会话。
27
- - **自然语音**:优先使用自然语音——Windows 11 内置语音包,或 Windows 10 上经
28
- NaturalVoiceSAPIAdapter 注册的语音(如晓晓),回退到任意已安装语音。
29
- - **健壮的文本清洗**:去掉会让 SAPI `Speak()` 静默失败的 markdown/URL/emoji,
30
- 并守卫适配器单次朗读的字数上限。
31
- - **引擎可移植**:任意进程一行即可朗读:
32
- `powershell -File speak.ps1 -Text "你好"`。
33
-
34
- ## 前置条件
35
-
36
- - Windows 10 或 11,任意较新的 PowerShell。
37
- - 自然语音:
38
- - **Windows 11**:系统已内置自然语音包,无需额外安装——在
39
- *设置 → 辅助功能 → 讲述人* 或 *设置 → 时间和语言 → 语音* 中启用/切换即可。
40
- - **Windows 10**:需要安装
41
- [NaturalVoiceSAPIAdapter](https://github.com/gexgd0419/NaturalVoiceSAPIAdapter),
42
- 并用它的 VoiceDownloader 手动下载你需要的中文或其他语言的自然语音包。
43
- - 没有自然语音时,引擎回退到系统自带语音(如 Huihui)。
44
-
45
- ## 快速开始 — DSH
46
-
47
- ### 方式 A — npm 插件(推荐)
48
-
49
- ```powershell
50
- # 1. 把插件装进你的 web profile(会写入 ~/.dsh/profiles/web/package.json 的 dependencies)
51
- dsh plugin --profile web add dsh-speak
52
-
53
- # 2. 在 ~/.dsh/profiles/web/cordis.patch.yml 里注册(npm 包直接用包名,无需 file:/// URL):
54
- # - insert:
55
- # - id: speech-hook
56
- # name: 'dsh-speak'
57
-
58
- # 3. 重启 DSH web 应用 — 之后回复会被自动播报
59
- ```
60
-
61
- 引擎随包分发(`node_modules/dsh-speak/engine/`),无需额外拷贝。
62
-
63
- ### 方式 B — 文件安装(不需要 npm)
64
-
65
- ```powershell
66
- # 1. 克隆
67
- git clone https://github.com/Alan2Z/dsh-speak.git
68
- cd dsh-speak
69
-
70
- # 2. 一键安装:拷贝引擎 + 插件,并注册到 cordis.patch.yml
71
- powershell.exe -NoProfile -ExecutionPolicy Bypass -File adapters\dsh\install.ps1
72
-
73
- # 3. 验证引擎能出声
74
- powershell -NoProfile -ExecutionPolicy Bypass -File "$env:USERPROFILE\.dsh\hooks\speak.ps1" -Text "你好,语音播报已就绪。"
75
-
76
- # 4. 重启 DSH web 应用 — 之后回复会被自动播报
77
- ```
78
-
79
- 文件安装脚本做了这些事:
80
-
81
- | 文件 | 目标位置 |
82
- | ---- | -------- |
83
- | `engine/*.ps1` | `%USERPROFILE%\.dsh\hooks\` |
84
- | `adapters/dsh/speech-hook.js` | `%USERPROFILE%\.dsh\profiles\web\plugins\` |
85
- | 注册条目 | 追加到 `%USERPROFILE%\.dsh\profiles\web\cordis.patch.yml`(先备份) |
86
-
87
- ## 快速开始 — Claude Code
88
-
89
- 在 `~/.claude/settings.json` 注册 Stop hook:
90
-
91
- ```json
92
- {
93
- "hooks": {
94
- "Stop": [
95
- {
96
- "hooks": [
97
- {
98
- "type": "command",
99
- "command": "powershell.exe -NoProfile -ExecutionPolicy Bypass -File C:\\path\\to\\dsh-speak\\adapters\\claude-code\\stop-hook.ps1"
100
- }
101
- ]
102
- }
103
- ]
104
- }
105
- }
106
- ```
107
-
108
- ## 快速开始 — 其他任何 harness
109
-
110
- 直接从你的 Agent / 包装脚本 / 工具里调用引擎:
111
-
112
- ```powershell
113
- # 播报一句话
114
- powershell -NoProfile -ExecutionPolicy Bypass -File engine\speak.ps1 -Text "构建完成"
115
-
116
- # 播报较长总结(阻塞,读完才返回)
117
- powershell -NoProfile -ExecutionPolicy Bypass -File engine\speech-summary.ps1 -Text "…"
118
-
119
- # 需要用户注意时(阻塞,适合提问/授权场景)
120
- powershell -NoProfile -ExecutionPolicy Bypass -File engine\speech-prompt.ps1 -Text "请做出选择"
121
- ```
122
-
123
- ## 配置
124
-
125
- 引擎参数(详见 [docs/DESIGN.zh-CN.md](docs/DESIGN.zh-CN.md#5-配置参考)):
126
-
127
- ```powershell
128
- speak.ps1 -Text "…" -Volume 50 -Rate 1 -MaxChars 300 -LongTextMessage "本次播报内容较长,请自行阅读。"
129
- ```
130
-
131
- DSH 插件环境变量:
132
-
133
- | 变量 | 默认值 | 含义 |
134
- | --- | ------- | ---- |
135
- | `DSH_SPEAK_ENGINE` | `%USERPROFILE%\.dsh\hooks\speak.ps1` | 引擎路径 |
136
- | `DSH_SPEAK_THROTTLE_MS` | `1500` | 播报前的合并延迟(毫秒) |
137
-
138
- ## 排障
139
-
140
- | 现象 | 原因 | 解决 |
141
- | ---- | ---- | ---- |
142
- | 完全没有声音、无报错 | 未启用/安装自然语音 | Win11:在 设置 → 讲述人/语音 中启用自然语音;Win10:安装 NaturalVoiceSAPIAdapter 并下载语音包。直接测 `speak.ps1` |
143
- | 长回复从不播报 | 适配器单次 `Speak` 有字数上限 | 已默认在 300 字处守卫——必要时调低 `-MaxChars` |
144
- | 含大量 emoji 的文本静默 | SAPI 遇到 emoji 会静默失败 | 引擎已自动剥离 |
145
- | 插件加载失败 | 插件名用了 Windows 原始路径 | 改用 `file:///C:/…` URL 形式(安装脚本会自动处理) |
146
-
147
- 插件诊断日志:`%TEMP%\dsh-speech-hook.log`
148
-
149
- ## 仓库结构
150
-
151
- ```
152
- engine/ 与 harness 无关的语音引擎(PowerShell + SAPI5)
153
- speak.ps1 清洗 + 朗读(适配层唯一需要打交道的接口)
154
- speech-prompt.ps1 阻塞式短提示播报
155
- speech-summary.ps1 阻塞式回复总结播报
156
- adapters/
157
- dsh/ DSH web 插件 + 一键安装脚本
158
- speech-hook.js 会话事件触发器(节流 + 工具调用取消)
159
- install.ps1 拷贝 + 注册 + 备份
160
- claude-code/
161
- stop-hook.ps1 Claude Code Stop hook 触发器
162
- docs/
163
- DESIGN.zh-CN.md 完整设计文档:设计取舍、踩坑记录、扩展指南
164
- ```
165
-
166
- ## 编写新适配器
167
-
168
- 三种参考模式:**事件流**(DSH)、**Stop hook**(Claude Code)、**Agent 自调用**
169
- (在 shell 里调 `speech-summary.ps1`)。无论哪种,适配器只需做一件事:
170
- 拿到*最终回复文本* 调用引擎。详见
171
- [docs/DESIGN.zh-CN.md §7 扩展](docs/DESIGN.zh-CN.md#7-扩展)。
172
-
173
- ## License
174
-
175
- MIT — 见 [LICENSE](LICENSE)。
176
-
177
- [NaturalVoiceSAPIAdapter]: https://github.com/gexgd0419/NaturalVoiceSAPIAdapter
1
+ # dsh-speak 🔊 — 为 AI 编程 harness 提供语音播报
2
+
3
+ ![鲸鱼娘大喇叭](鲸鱼娘大喇叭.png)
4
+
5
+ Agent 在长任务完成时**开口告诉你**——不用再盯着屏幕等。
6
+
7
+ dsh-speak 通过系统语音合成把 Agent 的最终回复朗读出来——Windows 上优先使用自然
8
+ 语音(Windows 11 内置,或 Windows 10 上经
9
+ [NaturalVoiceSAPIAdapter] 注册,如晓晓),macOS 上使用系统自带的 `say`
10
+ (可跟随 Siri 自然音色);没有时优雅回退到系统自带中文语音。本项目为
11
+ [DeepSeek Harness](https://github.com/deepseek-ai/dsh) 而生,但结构上
12
+ 任何 harness 都能接入。
13
+
14
+ > **项目定位**:本项目只是为了给想让 harness 开口说话的用户提供一种**已经验证过的方案**;
15
+ > 没有意外的话,后续不会再更新。
16
+
17
+ ## 三分钟安装 — DSH
18
+
19
+ 1. 把包装进你的 web profile(二选一):
20
+
21
+ ```powershell
22
+ dsh plugin --profile web add dsh-speak
23
+ # 或(没有 pnpm 时):
24
+ npm install --prefix "$env:USERPROFILE\.dsh\profiles\web" dsh-speak
25
+ ```
26
+
27
+ macOS 上(bash):
28
+
29
+ ```bash
30
+ npm install --prefix "$HOME/.dsh/profiles/web" dsh-speak
31
+ ```
32
+
33
+ 2. 在 `~/.dsh/profiles/web/cordis.patch.yml` 末尾追加:
34
+
35
+ ```yaml
36
+ - insert:
37
+ - id: speech-hook
38
+ name: 'dsh-speak'
39
+ ```
40
+
41
+ 3. 重启 DSH web 应用——之后回复就会被朗读出来。
42
+
43
+ > **想让 Agent 帮你装?** 把本仓库地址(`https://github.com/Alan2Z/dsh-speak`)
44
+ > 丢给你的 DSH 会话,让它照着这份 README 安装即可——它读的就是你正在看的这份文档。
45
+ > 只需要同意它对 `~/.dsh`(工作区外)的写入审批。
46
+
47
+ ```
48
+ harness 事件(DSH 会话事件 / Claude Code Stop hook / 任意方式)
49
+
50
+ ▼ adapters/… (harness 专属触发器:过滤、节流、取消)
51
+ ▼ engine/speak.ps1 / speak.sh (与 harness 无关:清洗文本 → SAPI5 / say)
52
+ ▼ 🔊 你听到最终回复
53
+ ```
54
+
55
+ ## 特性
56
+
57
+ - **全自动**:DSH web 插件监听会话事件流,自动播报最终回复
58
+ (跳过 reasoning/工具调用旁白,合并同一回复的多步消息)。
59
+ - **尽力而为**:绝不抛错、绝不阻塞 harness、绝不破坏会话。
60
+ - **自然语音**:Windows 优先使用自然语音——Windows 11 内置语音包,或 Windows 10
61
+ 上经 NaturalVoiceSAPIAdapter 注册的语音(如晓晓);macOS 使用系统朗读声音
62
+ (新版可跟随 Siri 自然音色)。均回退到任意已安装语音。
63
+ - **健壮的文本清洗**:去掉会让语音合成静默失败的 markdown/URL/emoji,
64
+ 并守卫适配器单次朗读的字数上限。
65
+ - **引擎可移植**:任意进程一行即可朗读:
66
+ Windows `powershell -File speak.ps1 -Text "你好"` / macOS `./speak.sh -t "你好"`。
67
+
68
+ ## 前置条件
69
+
70
+ Windows:
71
+
72
+ - Windows 10 或 11,任意较新的 PowerShell。
73
+ - 自然语音:
74
+ - **Windows 11**:系统已内置自然语音包,无需额外安装——在
75
+ *设置 → 辅助功能 → 讲述人* 或 *设置 → 时间和语言 → 语音* 中启用/切换即可。
76
+ - **Windows 10**:需要安装
77
+ [NaturalVoiceSAPIAdapter](https://github.com/gexgd0419/NaturalVoiceSAPIAdapter),
78
+ 并用它的 VoiceDownloader 手动下载你需要的中文或其他语言的自然语音包。
79
+ - 没有自然语音时,引擎回退到系统自带语音(如 Huihui)。
80
+
81
+ macOS:
82
+
83
+ - macOS(Apple Silicon / Intel 均可),系统自带 `say` 命令,**无需安装任何软件**。
84
+ - 中文音色见 [macOS](#macos) 一节(含 Siri 自然音色的选择入口与坑)。
85
+
86
+ ## 快速开始 — DSH
87
+
88
+ ### 方式 A — npm 插件(推荐)
89
+
90
+ ```powershell
91
+ # 1. 把插件装进你的 web profile(会写入 ~/.dsh/profiles/web/package.json 的 dependencies)
92
+ dsh plugin --profile web add dsh-speak
93
+
94
+ # 2. 在 ~/.dsh/profiles/web/cordis.patch.yml 里注册(npm 包直接用包名,无需 file:/// URL):
95
+ # - insert:
96
+ # - id: speech-hook
97
+ # name: 'dsh-speak'
98
+
99
+ # 3. 重启 DSH web 应用 — 之后回复会被自动播报
100
+ ```
101
+
102
+ > **没有 pnpm?** `dsh plugin` 内部转发给 pnpm,并非所有机器都装了。可以用 npm
103
+ > 直接完成同样的安装:
104
+ >
105
+ > ```powershell
106
+ > npm install --prefix "$env:USERPROFILE\.dsh\profiles\web" dsh-speak
107
+ > ```
108
+
109
+ 引擎随包分发(`node_modules/dsh-speak/engine/`),无需额外拷贝。
110
+
111
+ ### 方式 B — 文件安装(不需要 npm)
112
+
113
+ ```powershell
114
+ # 1. 克隆
115
+ git clone https://github.com/Alan2Z/dsh-speak.git
116
+ cd dsh-speak
117
+
118
+ # 2. 一键安装:拷贝引擎 + 插件,并注册到 cordis.patch.yml
119
+ powershell.exe -NoProfile -ExecutionPolicy Bypass -File adapters\dsh\install.ps1
120
+
121
+ # 3. 验证引擎能出声
122
+ powershell -NoProfile -ExecutionPolicy Bypass -File "$env:USERPROFILE\.dsh\hooks\speak.ps1" -Text "你好,语音播报已就绪。"
123
+
124
+ # 4. 重启 DSH web 应用 — 之后回复会被自动播报
125
+ ```
126
+
127
+ 文件安装脚本做了这些事:
128
+
129
+ | 文件 | 目标位置 |
130
+ | ---- | -------- |
131
+ | `engine/*.ps1` | `%USERPROFILE%\.dsh\hooks\` |
132
+ | `adapters/dsh/speech-hook.js` | `%USERPROFILE%\.dsh\profiles\web\plugins\` |
133
+ | 注册条目 | 追加到 `%USERPROFILE%\.dsh\profiles\web\cordis.patch.yml`(先备份) |
134
+
135
+ ## macOS
136
+
137
+ 同一套适配层原生支持 macOS——插件自动检测平台,改调 `engine/speak.sh`
138
+ (系统自带的 `say` 命令)而不是 `speak.ps1`。**自 1.2.0 起 macOS 引擎随 npm 包
139
+ 正式分发**,无需安装任何额外软件。
140
+
141
+ ### 安装(npm 方式,与 Windows 等价)
142
+
143
+ ```bash
144
+ # 1. 装进你的 web profile(没有 pnpm 也能装——dsh plugin 才依赖 pnpm)
145
+ npm install --prefix "$HOME/.dsh/profiles/web" dsh-speak
146
+
147
+ # 2. 在 ~/.dsh/profiles/web/cordis.patch.yml 末尾注册(裸包名即可,无需 file:/// URL):
148
+ # - insert:
149
+ # - id: speech-hook
150
+ # name: 'dsh-speak'
151
+
152
+ # 3. 无需重启——patch 监视器会热更新;纯文字回复约 1.5 秒后自动播报
153
+ # (带工具调用的回复按设计不播报)
154
+ ```
155
+
156
+ > 装过 pnpm 也可以 `dsh plugin --profile web add dsh-speak`,效果相同。
157
+
158
+ ### 音色(重要,有两个坑)
159
+
160
+ - 默认跟随**系统朗读声音**(系统设置 → 辅助功能 → 朗读内容 → 系统朗读声音)。
161
+ **macOS 26** 上该选择框旁有个 **ⓘ 圆圈图标**,点开才是完整音色列表——普通
162
+ 下拉框里**没有 Siri 自然音色**;可在 ⓘ 列表里选"普通话 Siri 声音1(男声)"等。
163
+ - **Siri 声音**(设置 → Siri → 声音)与系统朗读声音是**两个独立设置**;Siri
164
+ 音色不暴露给 `say -v '?'`,无法按名选择,只能作为系统默认生效。
165
+ - ⚠️ **坑 1(实测复现)**:打开"朗读内容 / Siri 声音"设置面板(**哪怕不改任何
166
+ 选项**)会把系统朗读声音漂移/重置成经典音色"婷婷(Tingting)"——音色突然变了
167
+ 就回到 ⓘ 入口重新选择。
168
+ - ⚠️ **坑 2**:日志在 `$TMPDIR/dsh-speech-hook.log`(`os.tmpdir()`,**不是**
169
+ `/tmp`)。
170
+ - 想强制指定音色用 `-v Eddy|Flo|Tingting`(`say -v '?'` 列出可用音色)。
171
+ - `say` 没有音量参数——音量跟随系统输出音量。
172
+
173
+ ### 单独测试引擎(不装 DSH 也行)
174
+
175
+ ```bash
176
+ curl -sfL -o ~/speak.sh "https://cdn.jsdelivr.net/gh/Alan2Z/dsh-speak@main/engine/speak.sh"
177
+ chmod +x ~/speak.sh
178
+ ~/speak.sh -t "你好,Mac 版语音播报测试"
179
+ ~/speak.sh -t "测试" -v Eddy -r 200 # 指定音色 + 语速
180
+ ```
181
+
182
+ ## 快速开始 — Claude Code
183
+
184
+ 在 `~/.claude/settings.json` 注册 Stop hook:
185
+
186
+ ```json
187
+ {
188
+ "hooks": {
189
+ "Stop": [
190
+ {
191
+ "hooks": [
192
+ {
193
+ "type": "command",
194
+ "command": "powershell.exe -NoProfile -ExecutionPolicy Bypass -File C:\\path\\to\\dsh-speak\\adapters\\claude-code\\stop-hook.ps1"
195
+ }
196
+ ]
197
+ }
198
+ ]
199
+ }
200
+ }
201
+ ```
202
+
203
+ ## 快速开始 — 其他任何 harness
204
+
205
+ 直接从你的 Agent / 包装脚本 / 工具里调用引擎:
206
+
207
+ ```powershell
208
+ # 播报一句话
209
+ powershell -NoProfile -ExecutionPolicy Bypass -File engine\speak.ps1 -Text "构建完成"
210
+
211
+ # 播报较长总结(阻塞,读完才返回)
212
+ powershell -NoProfile -ExecutionPolicy Bypass -File engine\speech-summary.ps1 -Text "…"
213
+
214
+ # 需要用户注意时(阻塞,适合提问/授权场景)
215
+ powershell -NoProfile -ExecutionPolicy Bypass -File engine\speech-prompt.ps1 -Text "请做出选择"
216
+ ```
217
+
218
+ ## 配置
219
+
220
+ 引擎参数(详见 [docs/DESIGN.zh-CN.md](docs/DESIGN.zh-CN.md#5-配置参考)):
221
+
222
+ ```powershell
223
+ speak.ps1 -Text "…" -Volume 50 -Rate 1 -MaxChars 300 -LongTextMessage "本次播报内容较长,请自行阅读。"
224
+ ```
225
+
226
+ DSH 插件环境变量:
227
+
228
+ | 变量 | 默认值 | 含义 |
229
+ | --- | ------- | ---- |
230
+ | `DSH_SPEAK_ENGINE` | 空(自动解析) | 引擎路径覆盖;否则按"包内 `engine/<平台脚本>` → `~/.dsh/hooks/<平台脚本>`"顺序解析(Windows `speak.ps1` / macOS `speak.sh`) |
231
+ | `DSH_SPEAK_THROTTLE_MS` | `1500` | 播报前的合并延迟(毫秒) |
232
+
233
+ ## 排障
234
+
235
+ | 现象 | 原因 | 解决 |
236
+ | ---- | ---- | ---- |
237
+ | 完全没有声音、无报错 | 未启用/安装自然语音 | Win11:在 设置 → 讲述人/语音 中启用自然语音;Win10:安装 NaturalVoiceSAPIAdapter 并下载语音包。直接测 `speak.ps1` |
238
+ | 长回复从不播报 | 适配器单次 `Speak` 有字数上限 | 已默认在 300 字处守卫——必要时调低 `-MaxChars` |
239
+ | 含大量 emoji 的文本静默 | SAPI 遇到 emoji 会静默失败 | 引擎已自动剥离 |
240
+ | 插件加载失败 | 插件名用了 Windows 原始路径 | 改用 `file:///C:/…` URL 形式(安装脚本会自动处理) |
241
+ | macOS:音色突然变成"婷婷" | 打开过"朗读内容 / Siri 声音"设置面板导致系统朗读声音漂移 | 系统设置 → 辅助功能 → 阅读与朗读 → 系统声音 → ⓘ 入口重新选择 |
242
+ | macOS:在 `/tmp` 找不到日志 | `os.tmpdir()` 是 `/var/folders/.../T`,不是 `/tmp` | 日志在 `$TMPDIR/dsh-speech-hook.log` |
243
+
244
+ 插件诊断日志:Windows `%TEMP%\dsh-speech-hook.log`;macOS `$TMPDIR/dsh-speech-hook.log`
245
+
246
+ ## 仓库结构
247
+
248
+ ```
249
+ engine/ 与 harness 无关的语音引擎(PowerShell + SAPI5 / bash + say)
250
+ speak.ps1 / speak.sh 清洗 + 朗读(适配层唯一需要打交道的接口)
251
+ speech-prompt.ps1 阻塞式短提示播报
252
+ speech-summary.ps1 阻塞式回复总结播报
253
+ adapters/
254
+ dsh/ DSH web 插件 + 一键安装脚本
255
+ speech-hook.js 会话事件触发器(节流 + 工具调用取消)
256
+ install.ps1 拷贝 + 注册 + 备份
257
+ claude-code/
258
+ stop-hook.ps1 Claude Code Stop hook 触发器
259
+ docs/
260
+ DESIGN.zh-CN.md 完整设计文档:设计取舍、踩坑记录、扩展指南
261
+ ```
262
+
263
+ ## 编写新适配器
264
+
265
+ 三种参考模式:**事件流**(DSH)、**Stop hook**(Claude Code)、**Agent 自调用**
266
+ (在 shell 里调 `speech-summary.ps1`)。无论哪种,适配器只需做一件事:
267
+ 拿到*最终回复文本* → 调用引擎。详见
268
+ [docs/DESIGN.zh-CN.md §7 扩展](docs/DESIGN.zh-CN.md#7-扩展)。
269
+
270
+ ## License
271
+
272
+ MIT — 见 [LICENSE](LICENSE)。
273
+
274
+ [NaturalVoiceSAPIAdapter]: https://github.com/gexgd0419/NaturalVoiceSAPIAdapter
@@ -1,81 +1,81 @@
1
- # install.ps1 — one-command installer for the DSH adapter
2
- # ========================================================
3
- # 1. copies engine/*.ps1 to ~/.dsh/hooks/
4
- # 2. copies speech-hook.js to ~/.dsh/profiles/web/plugins/
5
- # 3. registers the plugin in ~/.dsh/profiles/web/cordis.patch.yml (backs it up first)
6
- #
7
- # Usage:
8
- # powershell.exe -NoProfile -ExecutionPolicy Bypass -File install.ps1
9
- # powershell.exe -NoProfile -ExecutionPolicy Bypass -File install.ps1 -DshHome C:\Users\you\.dsh -PluginsDir C:\Users\you\.dsh\profiles\web\plugins
10
- #
11
- # After installing, restart the DSH web app (the profile tree is composed at boot).
12
-
13
- param(
14
- [string]$DshHome = (Join-Path $env:USERPROFILE '.dsh'),
15
- [string]$EngineDir = '',
16
- [string]$PluginsDir = ''
17
- )
18
-
19
- $ErrorActionPreference = 'Stop'
20
- $here = Split-Path -Parent $MyInvocation.MyCommand.Path
21
- $repoRoot = Resolve-Path (Join-Path $here '..\..')
22
-
23
- if (-not $EngineDir) { $EngineDir = Join-Path $DshHome 'hooks' }
24
- if (-not $PluginsDir) { $PluginsDir = Join-Path $DshHome 'profiles\web\plugins' }
25
- $cordisPatch = Join-Path $DshHome 'profiles\web\cordis.patch.yml'
26
-
27
- # ---------- 1. engine ----------
28
- Write-Host "==> Installing engine -> $EngineDir"
29
- New-Item -ItemType Directory -Force -Path $EngineDir | Out-Null
30
- Copy-Item -Force (Join-Path $repoRoot 'engine\*.ps1') $EngineDir
31
- Write-Host " copied: $((Get-ChildItem (Join-Path $repoRoot 'engine\*.ps1')).Count) script(s)"
32
-
33
- # ---------- 2. plugin ----------
34
- Write-Host "==> Installing DSH plugin -> $PluginsDir"
35
- New-Item -ItemType Directory -Force -Path $PluginsDir | Out-Null
36
- Copy-Item -Force (Join-Path $here 'speech-hook.js') $PluginsDir
37
- $pluginUrl = 'file:///' + ((Join-Path $PluginsDir 'speech-hook.js') -replace '\\', '/' -replace ' ', '%20')
38
-
39
- # ---------- 3. register in cordis.patch.yml ----------
40
- Write-Host "==> Registering plugin in cordis.patch.yml"
41
- if (Test-Path $cordisPatch) {
42
- $existing = Get-Content $cordisPatch -Raw -Encoding UTF8
43
- if ($existing -match '(?m)^\s*- id:\s*speech-hook\b') {
44
- Write-Host " speech-hook already registered — skipping (nothing to do)."
45
- Write-Host ""
46
- Write-Host "Done. Restart the DSH web app to pick up the plugin."
47
- exit 0
48
- }
49
- # backup before modifying
50
- $backup = "$cordisPatch.bak-$(Get-Date -Format 'yyyyMMdd-HHmmss')"
51
- Copy-Item $cordisPatch $backup
52
- Write-Host " backup -> $backup"
53
- $block = @"
54
-
55
- # speech-hook: auto voice-announce assistant replies (installed by dsh-speak)
56
- - insert:
57
- - id: speech-hook
58
- name: '$pluginUrl'
59
- "@
60
- Add-Content -Path $cordisPatch -Value $block -Encoding UTF8
61
- Write-Host " appended insert entry -> $cordisPatch"
62
- } else {
63
- New-Item -ItemType Directory -Force -Path (Split-Path $cordisPatch) | Out-Null
64
- $content = @"
65
- # dsh profile patch layer (created by dsh-speak installer)
66
- # speech-hook: auto voice-announce assistant replies
67
- - insert:
68
- - id: speech-hook
69
- name: '$pluginUrl'
70
- "@
71
- Set-Content -Path $cordisPatch -Value $content -Encoding UTF8
72
- Write-Host " created -> $cordisPatch"
73
- }
74
-
75
- Write-Host ""
76
- Write-Host "Installed. Next steps:"
77
- Write-Host " 1. Restart the DSH web app (profile tree is composed at boot)."
78
- Write-Host " 2. Verify voices: run"
79
- Write-Host " powershell -NoProfile -ExecutionPolicy Bypass -File `"$EngineDir\speak.ps1`" -Text `"你好,语音播报已就绪。`""
80
- Write-Host " 3. If no sound: install NaturalVoiceSAPIAdapter and register natural voices"
81
- Write-Host " (see README.md -> Prerequisites)."
1
+ # install.ps1 — one-command installer for the DSH adapter
2
+ # ========================================================
3
+ # 1. copies engine/*.ps1 to ~/.dsh/hooks/
4
+ # 2. copies speech-hook.js to ~/.dsh/profiles/web/plugins/
5
+ # 3. registers the plugin in ~/.dsh/profiles/web/cordis.patch.yml (backs it up first)
6
+ #
7
+ # Usage:
8
+ # powershell.exe -NoProfile -ExecutionPolicy Bypass -File install.ps1
9
+ # powershell.exe -NoProfile -ExecutionPolicy Bypass -File install.ps1 -DshHome C:\Users\you\.dsh -PluginsDir C:\Users\you\.dsh\profiles\web\plugins
10
+ #
11
+ # After installing, restart the DSH web app (the profile tree is composed at boot).
12
+
13
+ param(
14
+ [string]$DshHome = (Join-Path $env:USERPROFILE '.dsh'),
15
+ [string]$EngineDir = '',
16
+ [string]$PluginsDir = ''
17
+ )
18
+
19
+ $ErrorActionPreference = 'Stop'
20
+ $here = Split-Path -Parent $MyInvocation.MyCommand.Path
21
+ $repoRoot = Resolve-Path (Join-Path $here '..\..')
22
+
23
+ if (-not $EngineDir) { $EngineDir = Join-Path $DshHome 'hooks' }
24
+ if (-not $PluginsDir) { $PluginsDir = Join-Path $DshHome 'profiles\web\plugins' }
25
+ $cordisPatch = Join-Path $DshHome 'profiles\web\cordis.patch.yml'
26
+
27
+ # ---------- 1. engine ----------
28
+ Write-Host "==> Installing engine -> $EngineDir"
29
+ New-Item -ItemType Directory -Force -Path $EngineDir | Out-Null
30
+ Copy-Item -Force (Join-Path $repoRoot 'engine\*.ps1') $EngineDir
31
+ Write-Host " copied: $((Get-ChildItem (Join-Path $repoRoot 'engine\*.ps1')).Count) script(s)"
32
+
33
+ # ---------- 2. plugin ----------
34
+ Write-Host "==> Installing DSH plugin -> $PluginsDir"
35
+ New-Item -ItemType Directory -Force -Path $PluginsDir | Out-Null
36
+ Copy-Item -Force (Join-Path $here 'speech-hook.js') $PluginsDir
37
+ $pluginUrl = 'file:///' + ((Join-Path $PluginsDir 'speech-hook.js') -replace '\\', '/' -replace ' ', '%20')
38
+
39
+ # ---------- 3. register in cordis.patch.yml ----------
40
+ Write-Host "==> Registering plugin in cordis.patch.yml"
41
+ if (Test-Path $cordisPatch) {
42
+ $existing = Get-Content $cordisPatch -Raw -Encoding UTF8
43
+ if ($existing -match '(?m)^\s*- id:\s*speech-hook\b') {
44
+ Write-Host " speech-hook already registered — skipping (nothing to do)."
45
+ Write-Host ""
46
+ Write-Host "Done. Restart the DSH web app to pick up the plugin."
47
+ exit 0
48
+ }
49
+ # backup before modifying
50
+ $backup = "$cordisPatch.bak-$(Get-Date -Format 'yyyyMMdd-HHmmss')"
51
+ Copy-Item $cordisPatch $backup
52
+ Write-Host " backup -> $backup"
53
+ $block = @"
54
+
55
+ # speech-hook: auto voice-announce assistant replies (installed by dsh-speak)
56
+ - insert:
57
+ - id: speech-hook
58
+ name: '$pluginUrl'
59
+ "@
60
+ Add-Content -Path $cordisPatch -Value $block -Encoding UTF8
61
+ Write-Host " appended insert entry -> $cordisPatch"
62
+ } else {
63
+ New-Item -ItemType Directory -Force -Path (Split-Path $cordisPatch) | Out-Null
64
+ $content = @"
65
+ # dsh profile patch layer (created by dsh-speak installer)
66
+ # speech-hook: auto voice-announce assistant replies
67
+ - insert:
68
+ - id: speech-hook
69
+ name: '$pluginUrl'
70
+ "@
71
+ Set-Content -Path $cordisPatch -Value $content -Encoding UTF8
72
+ Write-Host " created -> $cordisPatch"
73
+ }
74
+
75
+ Write-Host ""
76
+ Write-Host "Installed. Next steps:"
77
+ Write-Host " 1. Restart the DSH web app (profile tree is composed at boot)."
78
+ Write-Host " 2. Verify voices: run"
79
+ Write-Host " powershell -NoProfile -ExecutionPolicy Bypass -File `"$EngineDir\speak.ps1`" -Text `"你好,语音播报已就绪。`""
80
+ Write-Host " 3. If no sound: install NaturalVoiceSAPIAdapter and register natural voices"
81
+ Write-Host " (see README.md -> Prerequisites)."