dlinkclaw-openclaw 1.0.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.
Files changed (85) hide show
  1. package/LICENSE +201 -0
  2. package/README.en.md +46 -0
  3. package/README.md +15 -0
  4. package/dist/index.d.ts +9 -0
  5. package/dist/index.js +1 -0
  6. package/dist/package.json +95 -0
  7. package/dist/src/account.d.ts +54 -0
  8. package/dist/src/account.js +394 -0
  9. package/dist/src/channel.d.ts +17 -0
  10. package/dist/src/channel.js +153 -0
  11. package/dist/src/client/client-manager.d.ts +4 -0
  12. package/dist/src/client/client-manager.js +10 -0
  13. package/dist/src/client/const.d.ts +37 -0
  14. package/dist/src/client/const.js +155 -0
  15. package/dist/src/client/crypto.d.ts +47 -0
  16. package/dist/src/client/crypto.js +260 -0
  17. package/dist/src/client/dl-client.d.ts +117 -0
  18. package/dist/src/client/dl-client.js +588 -0
  19. package/dist/src/client/dl-http.d.ts +27 -0
  20. package/dist/src/client/dl-http.js +223 -0
  21. package/dist/src/client/media-handler.d.ts +21 -0
  22. package/dist/src/client/media-handler.js +196 -0
  23. package/dist/src/client/message.d.ts +96 -0
  24. package/dist/src/client/message.js +1211 -0
  25. package/dist/src/client/timeout.d.ts +20 -0
  26. package/dist/src/client/timeout.js +36 -0
  27. package/dist/src/client/types.d.ts +226 -0
  28. package/dist/src/client/types.js +1 -0
  29. package/dist/src/monitor.d.ts +18 -0
  30. package/dist/src/monitor.js +322 -0
  31. package/dist/src/outbound.d.ts +6 -0
  32. package/dist/src/outbound.js +78 -0
  33. package/dist/src/policy.d.ts +42 -0
  34. package/dist/src/policy.js +216 -0
  35. package/dist/src/runtime.d.ts +9 -0
  36. package/dist/src/runtime.js +65 -0
  37. package/dist/src/services/gateway-rpc.d.ts +37 -0
  38. package/dist/src/services/gateway-rpc.js +37 -0
  39. package/dist/src/services/index.d.ts +41 -0
  40. package/dist/src/services/index.js +245 -0
  41. package/dist/src/services/services.d.ts +169 -0
  42. package/dist/src/services/services.js +1160 -0
  43. package/dist/src/services/types.d.ts +391 -0
  44. package/dist/src/services/types.js +5 -0
  45. package/dist/src/services/ws-client.d.ts +186 -0
  46. package/dist/src/services/ws-client.js +896 -0
  47. package/dist/src/utils.d.ts +20 -0
  48. package/dist/src/utils.js +128 -0
  49. package/ext_capabilities/workrelay-speak/CLONE_GUIDE.md +129 -0
  50. package/ext_capabilities/workrelay-speak/HARDWARE.md +84 -0
  51. package/ext_capabilities/workrelay-speak/QWEN_GUIDE.md +151 -0
  52. package/ext_capabilities/workrelay-speak/README.md +79 -0
  53. package/ext_capabilities/workrelay-speak/__pycache__/patch-kokoro-en.cpython-312.pyc +0 -0
  54. package/ext_capabilities/workrelay-speak/install.sh +329 -0
  55. package/ext_capabilities/workrelay-speak/install.sh.bak-20260814 +376 -0
  56. package/ext_capabilities/workrelay-speak/manifest.json +63 -0
  57. package/ext_capabilities/workrelay-speak/patch-kokoro-en.py +85 -0
  58. package/ext_capabilities/workrelay-speak/status.sh +111 -0
  59. package/ext_capabilities/workrelay-speak/workrelay-tts.sh +118 -0
  60. package/ext_capabilities/workrelay-speak-luxtts/README.md +44 -0
  61. package/ext_capabilities/workrelay-speak-luxtts/install.sh +330 -0
  62. package/ext_capabilities/workrelay-speak-luxtts/manifest.json +63 -0
  63. package/ext_capabilities/workrelay-speak-luxtts/samples/45ea2e2c-0863-4966-a739-8bff8817f4c0.wav +0 -0
  64. package/ext_capabilities/workrelay-speak-luxtts/samples/800e5af4-cba5-441e-8003-6d36f35409e6.wav +0 -0
  65. package/ext_capabilities/workrelay-speak-luxtts/samples/85fafe01-d4d6-4a48-890e-9ae113f21905.wav +0 -0
  66. package/ext_capabilities/workrelay-speak-luxtts/samples/reference_text.txt +4 -0
  67. package/ext_capabilities/workrelay-speak-luxtts/samples/stone-sample-6.4s.wav +0 -0
  68. package/ext_capabilities/workrelay-speak-luxtts/status.sh +115 -0
  69. package/ext_capabilities/workrelay-speak-luxtts/workrelay-tts.sh +98 -0
  70. package/ext_capabilities/workrelay-speak-qwen/README.md +62 -0
  71. package/ext_capabilities/workrelay-speak-qwen/install.sh +349 -0
  72. package/ext_capabilities/workrelay-speak-qwen/manifest.json +63 -0
  73. package/ext_capabilities/workrelay-speak-qwen/status.sh +106 -0
  74. package/ext_capabilities/workrelay-speak-qwen/workrelay-tts.sh +98 -0
  75. package/ext_capabilities/workrelay-transcribe/README.md +76 -0
  76. package/ext_capabilities/workrelay-transcribe/__pycache__/transcribe.cpython-312.pyc +0 -0
  77. package/ext_capabilities/workrelay-transcribe/__pycache__/transcribe_server.cpython-312.pyc +0 -0
  78. package/ext_capabilities/workrelay-transcribe/install.sh +227 -0
  79. package/ext_capabilities/workrelay-transcribe/manifest.json +42 -0
  80. package/ext_capabilities/workrelay-transcribe/status.sh +63 -0
  81. package/ext_capabilities/workrelay-transcribe/transcribe.py +56 -0
  82. package/ext_capabilities/workrelay-transcribe/transcribe_server.py +116 -0
  83. package/ext_capabilities/workrelay-transcribe/voicebox-transcribe.sh +88 -0
  84. package/openclaw.plugin.json +142 -0
  85. package/package.json +95 -0
@@ -0,0 +1,329 @@
1
+ #!/bin/bash
2
+ # WorkRelay 语音合成(TTS)一键安装脚本
3
+ # 通过 Docker 部署 Voicebox(jamiepine/voicebox),自动配置 OpenClaw
4
+ set -eo pipefail
5
+
6
+ RED='\033[0;31m'
7
+ GREEN='\033[0;32m'
8
+ YELLOW='\033[1;33m'
9
+ NC='\033[0m'
10
+
11
+ VB_DIR="$HOME/.workrelay/voicebox"
12
+ VB_HOST="${WR_VOICEBOX_HOST:-127.0.0.1}"
13
+ VB_PORT="${WR_VOICEBOX_PORT:-17493}"
14
+ VB_DOCKER_PORT="${WR_VOICEBOX_DOCKER_PORT:-17600}"
15
+ # 2026-08-14:改为直接拉取阿里云公开镜像(已含 Kokoro 模型 + 英文修复补丁),
16
+ # 不再本地 clone + patch + build。镜像由石头哥推送到阿里云个人版仓库,公开仓库拉取免登录。
17
+ # 2026-09-14 双源:国内=阿里云公共仓库(待发布);国际=GitHub Container Registry (ghcr.io)
18
+ # 2026-09-14 改名为 dlinkclaw-tts(统一仓库名,tag 区分模型);命名空间=GitHub 用户名
19
+ IMG_TAG="${WR_IMAGE_TAG:-kokoro-20260914}"
20
+ # 国内源:暂未发布(留空自动跳过);发布后填入 registry.cn-hangzhou.aliyuncs.com/NAMESPACE/dlinkclaw-tts:${IMG_TAG}
21
+ IMG_CN="${WR_IMAGE_CN:-}"
22
+ IMG_INTL="${WR_IMAGE_INTL:-ghcr.io/rockychen2016/dlinkclaw-tts:${IMG_TAG}}"
23
+ VB_IMAGE=""
24
+ INSTALL_DIR="$(dirname "$(readlink -f "$0")")"
25
+
26
+ # ── 2026-09-14 镜像双源(国内=阿里云公共仓库 / 国际=ghcr.io)──
27
+ # 按 WR_IMAGE_REGION(auto|cn|intl)优先尝试,失败自动回退另一源;WR_VOICEBOX_IMAGE 可显式覆盖。
28
+ IMAGE_CANDIDATES=()
29
+ if [ -n "${WR_VOICEBOX_IMAGE:-}" ]; then
30
+ IMAGE_CANDIDATES=("$WR_VOICEBOX_IMAGE")
31
+ else
32
+ # 2026-09-14:国内源暂未发布(IMG_CN 为空则自动跳过),默认国际源优先;国内源上架后自动生效
33
+ case "${WR_IMAGE_REGION:-auto}" in
34
+ cn) CAND_PRIMARY="$IMG_CN"; CAND_SECONDARY="$IMG_INTL" ;;
35
+ intl) CAND_PRIMARY="$IMG_INTL"; CAND_SECONDARY="$IMG_CN" ;;
36
+ *) CAND_PRIMARY="$IMG_INTL"; CAND_SECONDARY="$IMG_CN" ;;
37
+ esac
38
+ [ -n "$CAND_PRIMARY" ] && IMAGE_CANDIDATES+=("$CAND_PRIMARY")
39
+ [ -n "$CAND_SECONDARY" ] && IMAGE_CANDIDATES+=("$CAND_SECONDARY")
40
+ fi
41
+
42
+ pull_image_with_fallback() {
43
+ local log="$1"
44
+ for img in "${IMAGE_CANDIDATES[@]}"; do
45
+ echo " → 尝试镜像源: $img"
46
+ if docker pull "$img" > "$log" 2>&1; then
47
+ VB_IMAGE="$img"
48
+ return 0
49
+ fi
50
+ echo " ⚠ 该源拉取失败,尝试下一个..."
51
+ done
52
+ return 1
53
+ }
54
+
55
+
56
+ echo "============================================"
57
+ echo " WorkRelay 语音合成(TTS)- 一键安装"
58
+ echo "============================================"
59
+ echo ""
60
+
61
+ # ── 系统检查 ──
62
+ echo -n "[1/6] 检查系统环境... "
63
+
64
+ OS="$(uname -s)"
65
+ case "$OS" in
66
+ Linux) ;;
67
+ *)
68
+ echo -e "${RED}失败${NC}"
69
+ echo "错误:当前仅支持 Linux(TTS 需要 Docker)"
70
+ exit 1
71
+ ;;
72
+ esac
73
+
74
+ # 2026-08-11 修复:snap 安装的 docker 位于 /snap/bin,spawn 环境的 PATH 不含该目录,
75
+ # 导致 command -v docker 误判"未找到"。必须在 docker 检测之前补全 PATH。
76
+ export PATH="$PATH:/snap/bin:/usr/local/bin"
77
+
78
+ if ! command -v docker &>/dev/null; then
79
+ echo -e "${RED}失败${NC}"
80
+ echo "错误:未找到 Docker,请先安装 Docker"
81
+ echo " Ubuntu: sudo apt install docker.io docker-compose-v2"
82
+ exit 1
83
+ fi
84
+
85
+ if ! docker info &>/dev/null 2>&1; then
86
+ # 2026-08-18 增强:docker 权限多级自适应(尽量零 sudo,任何机器可用):
87
+ # ① 用户已在 docker 组(含会话未刷新)→ sg docker 切组执行
88
+ # ② 免密 sudo(sudo -n)可用 → sudo 包装
89
+ # ③ 都不行 → 给出一键修复指引,而不是干失败
90
+ if sg docker -c "docker info" &>/dev/null 2>&1; then
91
+ echo " ℹ 用户已加入 docker 组,通过 sg docker 执行(无需 sudo)"
92
+ docker() { sg docker -c "docker $(printf '%q ' "$@")"; }
93
+ elif sudo -n docker info &>/dev/null 2>&1; then
94
+ echo " ⚠ 当前用户无 docker 组权限,检测到免密 sudo,自动使用 sudo 执行 docker"
95
+ docker() { sudo docker "$@"; }
96
+ else
97
+ echo -e "${RED}失败${NC}"
98
+ echo "错误:Docker 未运行或无权限。请先执行一次以下命令:"
99
+ echo ""
100
+ echo " ① 启动 Docker:sudo systemctl start docker"
101
+ echo " ② 把当前用户加入 docker 组(避免以后每次要密码):"
102
+ echo " sudo usermod -aG docker \$USER"
103
+ echo " ③ 重启 openclaw 服务(或重新登录会话),然后重新安装"
104
+ echo ""
105
+ echo " 备选:为当前用户配置免密 sudo(sudo visudo 添加:\$USER ALL=(ALL) NOPASSWD: ALL)"
106
+ exit 1
107
+ fi
108
+ fi
109
+
110
+ echo -e "${GREEN}OK${NC} ($(docker --version | head -1), $(uname -o))"
111
+
112
+ # 2026-08-13 方案A:snap 版 docker 的 compose 插件与 docker CLI 客户端受 AppArmor 隔离,
113
+ # 读不了 $HOME 下的文件(compose 报 permission denied、build 报 open Dockerfile: no such file),
114
+ # 只有 dockerd daemon 放行 $HOME(所以 docker run 正常)。检测到 snap docker 时,
115
+ # 项目目录改用 /var/snap/docker/common/(AppArmor 明确放行 rw),compose/build/run 全通。
116
+ if [ -d /var/snap/docker/common ]; then
117
+ VB_DIR="/var/snap/docker/common/workrelay-voicebox"
118
+ echo " ℹ 检测到 snap docker,项目目录使用 $VB_DIR(AppArmor 放行读写)"
119
+ # /var/snap/docker/common 属 root,当前用户无写权限:需 root 预创建项目目录并移交给当前用户,
120
+ # 后续 docker pull / docker run 挂载的 output 目录写入不再需要提权。
121
+ # 2026-08-18:无免密 sudo 时给出手动命令指引(不依赖 sudo 密码)。
122
+ if [ -d "$VB_DIR" ]; then
123
+ sudo -n chown -R "$(id -u):$(id -g)" "$VB_DIR" 2>/dev/null || true
124
+ elif ! sudo -n mkdir -p "$VB_DIR" 2>/dev/null; then
125
+ echo -e "${RED}失败${NC}"
126
+ echo "错误:无法创建项目目录 $VB_DIR(需要 root 权限)。"
127
+ echo "请手动执行一次以下命令后重新安装:"
128
+ echo " sudo mkdir -p $VB_DIR && sudo chown \$USER:\$USER $VB_DIR"
129
+ exit 1
130
+ fi
131
+ sudo -n chown "$(id -u):$(id -g)" "$VB_DIR" 2>/dev/null || true
132
+ fi
133
+
134
+ # ── 磁盘空间 ──
135
+ echo -n "[2/6] 检查磁盘空间... "
136
+ # 2026-08-13:snap 模式下 VB_DIR 在 /var/snap/docker/common(可能与 $HOME 不同分区),
137
+ # 磁盘检查以实际项目目录所在分区为准
138
+ DF_TARGET="$VB_DIR"; [ -d "$DF_TARGET" ] || DF_TARGET="$HOME"
139
+ AVAIL=$(df -BM "$DF_TARGET" | tail -1 | awk '{print $4}' | sed 's/M//')
140
+ if [ "$AVAIL" -lt 10000 ]; then
141
+ echo -e "${RED}不足${NC}"
142
+ echo "错误:可用空间 ${AVAIL}MB,至少需要 10GB(Docker 镜像 + 模型 ~6GB)"
143
+ exit 1
144
+ fi
145
+ echo -e "${GREEN}OK${NC} (可用 ${AVAIL}MB)"
146
+
147
+ # ── 内存检查 ──
148
+ echo -n "[3/6] 检查内存... "
149
+ # 2026-09-15 修复:中文 locale 下 free -m 输出“内存:”,awk 匹配 Mem: 失败会误报内存不足 → 强制 LANG=C
150
+ RAM=$(LANG=C free -m | awk '/^Mem:/{print $2}')
151
+ if [ "$RAM" -lt 6000 ]; then
152
+ echo -e "${RED}不足 (${RAM}MB)${NC}"
153
+ echo "错误:至少需要 6GB 内存,当前仅 ${RAM}MB"
154
+ exit 1
155
+ fi
156
+ echo -e "${GREEN}OK${NC} (${RAM}MB)"
157
+
158
+ # ── 端口检查 ──
159
+ echo -n "[4/6] 检查端口 $VB_PORT... "
160
+ if ss -tlnp 2>/dev/null | grep -q ":$VB_PORT "; then
161
+ echo -e "${YELLOW}已被占用${NC}"
162
+ echo "提示:端口 $VB_PORT 已被占用,将使用 $VB_DOCKER_PORT"
163
+ VB_ACTUAL_PORT=$VB_DOCKER_PORT
164
+ else
165
+ echo -e "${GREEN}空闲${NC}"
166
+ VB_ACTUAL_PORT=$VB_PORT
167
+ fi
168
+
169
+ # ── 拉取 Voicebox 镜像 ──
170
+ echo -n "[5/6] 拉取 Voicebox 镜像... "
171
+ # 2026-08-14:镜像已由石头哥构建并推送到阿里云(含 Kokoro 模型 + 英文修复补丁),
172
+ # 直接 docker pull 即可,无需 clone 仓库 / patch / build。
173
+ mkdir -p "$VB_DIR"
174
+ if pull_image_with_fallback /tmp/wr-pull.log; then
175
+ echo -e "${GREEN}OK${NC} ($VB_IMAGE)"
176
+ else
177
+ echo -e "${RED}失败${NC}"
178
+ tail -5 /tmp/wr-pull.log
179
+ echo "错误:所有镜像源均拉取失败,检查网络连接(国内=阿里云公共仓库 / 国际=ghcr.io)"
180
+ exit 1
181
+ fi
182
+
183
+ # ── 启动容器 ──
184
+ echo "[6/6] 启动 Voicebox 容器..."
185
+
186
+ # 创建输出目录(容器内 voicebox 用户需要可写)
187
+ # 2026-09-15 修复(生产环境反馈):持久化整个 /app/data
188
+ # 含 voicebox.db(音色库)+ profiles + generations,避免容器重建后音色/数据丢失
189
+ mkdir -p "$VB_DIR/data/generations" "$VB_DIR/data/profiles" "$VB_DIR/data/cache"
190
+ chmod -R 777 "$VB_DIR/data" 2>/dev/null || true
191
+
192
+ # 2026-08-14:镜像已内置 Kokoro 模型(/home/voicebox/.cache/huggingface)且 ENV 已含
193
+ # HF_HUB_OFFLINE=1 / HF_HUB_CACHE,直接 docker run 即可。
194
+ # ⚠ 注意:不能挂【空卷】到 /home/voicebox/.cache/huggingface(会遮蔽内置模型);
195
+ # /app/data 现已挂载宿主机预建目录(含 generations/profiles/cache),用于持久化 voicebox.db 音色库。
196
+ # 否则会遮蔽镜像内已内置的模型/数据(旧版 clone+build 方案才需要挂卷)。
197
+ docker rm -f voicebox >/dev/null 2>&1 || true
198
+ if ! docker run -d --name voicebox --restart unless-stopped \
199
+ -p "127.0.0.1:${VB_ACTUAL_PORT}:17493" \
200
+ -v "$VB_DIR/data:/app/data" \
201
+ -e LOG_LEVEL=info \
202
+ -e NUMBA_CACHE_DIR=/tmp/numba_cache \
203
+ --cpus 4 --memory 8g \
204
+ "$VB_IMAGE" >/dev/null 2>&1; then
205
+ echo -e "${RED}失败${NC}"
206
+ echo "错误:docker run 失败,执行 docker logs voicebox 查看日志"
207
+ exit 1
208
+ fi
209
+ echo " ✅ docker run 启动成功"
210
+
211
+ # 等待 API 就绪
212
+ echo -n " 等待 Voicebox API 就绪..."
213
+ for i in $(seq 1 90); do
214
+ if curl -s --max-time 2 "http://${VB_HOST}:${VB_ACTUAL_PORT}/mcp" >/dev/null 2>&1; then
215
+ echo -e " ${GREEN}OK${NC} (耗时 ${i} 次检查)"
216
+ break
217
+ fi
218
+ sleep 5
219
+ echo -n "."
220
+ done
221
+
222
+ if ! curl -s --max-time 2 "http://${VB_HOST}:${VB_ACTUAL_PORT}/mcp" >/dev/null 2>&1; then
223
+ echo ""
224
+ echo -e "${RED}Voicebox 启动失败${NC}"
225
+ echo " 检查日志: docker logs voicebox"
226
+ exit 1
227
+ fi
228
+
229
+ # ── 预设声音(安装后开箱即用)──
230
+ # 2026-08-14:模型已内置镜像,无需宿主机预下载/离线配置;只需创建预设声音 profile。
231
+ echo " ⚙ 配置预设声音..."
232
+
233
+ # 创建预设声音 profile(kokoro 中文女声 zf_xiaoxiao),id 写入 profile_id 供 workrelay-tts.sh 自动读取
234
+ mkdir -p "$HOME/.workrelay/speak"
235
+ PROFILE_FILE="$HOME/.workrelay/speak/profile_id"
236
+ # 先查已存在的同款 preset profile,避免重名 400
237
+ EXIST_ID=$(curl --noproxy '*' -s --max-time 5 "http://${VB_HOST}:${VB_ACTUAL_PORT}/profiles" 2>/dev/null \
238
+ | python3 -c '
239
+ import json,sys
240
+ try:
241
+ d=json.load(sys.stdin)
242
+ for p in d:
243
+ if p.get("preset_engine")=="kokoro" and p.get("preset_voice_id")=="zf_xiaoxiao":
244
+ print(p.get("id","")); break
245
+ except Exception:
246
+ print("")
247
+ ' 2>/dev/null)
248
+ if [ -z "$EXIST_ID" ]; then
249
+ EXIST_ID=$(curl --noproxy '*' -s --max-time 10 -X POST "http://${VB_HOST}:${VB_ACTUAL_PORT}/profiles" \
250
+ -H "Content-Type: application/json" \
251
+ -d '{"name":"WorkRelay 默认声音","description":"WorkRelay TTS 预设中文女声","language":"zh","voice_type":"preset","preset_engine":"kokoro","preset_voice_id":"zf_xiaoxiao"}' \
252
+ | python3 -c 'import json,sys; print(json.load(sys.stdin).get("id",""))' 2>/dev/null)
253
+ fi
254
+ if [ -n "$EXIST_ID" ]; then
255
+ echo "$EXIST_ID" > "$PROFILE_FILE"
256
+ echo " ✅ 预设声音已就绪"
257
+ else
258
+ echo " ⚠ 预设声音创建失败,可手动设 WR_VOICEBOX_PROFILE_ID 指定声音"
259
+ fi
260
+
261
+ # ── TTS 互斥:停止其它 TTS 容器(2026-09-14:TTS 只能启用一个)──
262
+ echo " 🔒 互斥处理:停止其它 TTS 容器..."
263
+ for other in voicebox-luxtts voicebox-qwen; do
264
+ if docker ps --format '{{.Names}}' 2>/dev/null | grep -qx "$other"; then
265
+ docker stop "$other" >/dev/null 2>&1 && echo " ⏹ 已停止 $other" || true
266
+ fi
267
+ done
268
+
269
+ # ── 部署脚本 & 配置 OpenClaw ──
270
+ echo -n "[收尾] 部署 TTS 桥接脚本 & 配置 OpenClaw... "
271
+
272
+ mkdir -p "$HOME/.workrelay/speak"
273
+ cp "$INSTALL_DIR/workrelay-tts.sh" "$HOME/.workrelay/speak/"
274
+ chmod +x "$HOME/.workrelay/speak/workrelay-tts.sh"
275
+
276
+ # 2026-08-12:随安装部署说明文档(README + 声音克隆说明书 + 千问引擎说明书),用户安装后即可查看
277
+ cp "$INSTALL_DIR/README.md" "$HOME/.workrelay/speak/" 2>/dev/null || true
278
+ cp "$INSTALL_DIR/CLONE_GUIDE.md" "$HOME/.workrelay/speak/" 2>/dev/null || true
279
+ cp "$INSTALL_DIR/QWEN_GUIDE.md" "$HOME/.workrelay/speak/" 2>/dev/null || true
280
+
281
+ # 写入 OpenClaw TTS 配置
282
+ if command -v openclaw &>/dev/null; then
283
+ TTS_SCRIPT="$HOME/.workrelay/speak/workrelay-tts.sh"
284
+ # 2026-09-14:openclaw 升级后 TTS 配置在顶层 tts.*(旧 messages.tts.* 已失效,写入会被忽略)
285
+ # 注意:不再写 tts.auto(避免覆盖用户已有的 inbound 自动语音回复设置)
286
+ openclaw config set tts.provider tts-local-cli 2>/dev/null || true
287
+ openclaw config set tts.providers.tts-local-cli.command "$TTS_SCRIPT" 2>/dev/null || true
288
+ openclaw config set tts.providers.tts-local-cli.args '["{{Text}}","{{OutputPath}}"]' 2>/dev/null || true
289
+ openclaw config set tts.providers.tts-local-cli.outputFormat wav 2>/dev/null || true
290
+ openclaw config set tts.providers.tts-local-cli.timeoutMs 180000 2>/dev/null || true
291
+
292
+ # MCP 配置
293
+ openclaw config set mcp.servers.voicebox.url "http://${VB_HOST}:${VB_ACTUAL_PORT}/mcp" 2>/dev/null || true
294
+ openclaw config set mcp.servers.voicebox.transport streamable-http 2>/dev/null || true
295
+ openclaw config set mcp.servers.voicebox.timeout 60 2>/dev/null || true
296
+
297
+ openclaw gateway restart 2>/dev/null || true
298
+ else
299
+ echo -e "${YELLOW}⚠ 未检测到 openclaw CLI,跳过自动配置${NC}"
300
+ fi
301
+
302
+ echo -e "${GREEN}OK${NC}"
303
+ echo ""
304
+
305
+ # ── 验证 ──
306
+ echo "============================================"
307
+ echo " 安装验证"
308
+ echo "============================================"
309
+
310
+ echo -n " Docker 容器: "
311
+ docker ps --format '{{.Names}}' 2>/dev/null | grep -q voicebox && echo -e "${GREEN}运行中${NC}" || echo -e "${RED}未运行${NC}"
312
+
313
+ echo -n " API 连通性: "
314
+ curl -s --max-time 2 "http://${VB_HOST}:${VB_ACTUAL_PORT}/mcp" >/dev/null 2>&1 \
315
+ && echo -e "${GREEN}OK${NC} (http://${VB_HOST}:${VB_ACTUAL_PORT})" \
316
+ || echo -e "${RED}无法连接${NC}"
317
+
318
+ echo -n " TTS 桥接脚本: "
319
+ [ -f "$HOME/.workrelay/speak/workrelay-tts.sh" ] && echo -e "${GREEN}已部署${NC}" || echo -e "${RED}缺失${NC}"
320
+
321
+ echo ""
322
+ echo -e "${GREEN}✅ Voicebox TTS 安装完成!${NC}"
323
+ echo " API 地址: http://${VB_HOST}:${VB_ACTUAL_PORT}"
324
+ echo " 管理命令: docker ps | grep voicebox"
325
+ echo " 查看日志: docker logs -f voicebox"
326
+ echo ""
327
+ echo " 🎤 预设声音:开箱即用(Voicebox 内置 50+ 音色)"
328
+ echo " 🎙️ 声音克隆:可选,设 WR_VOICEBOX_PROFILE_ID=你的profile_id"
329
+ echo ""