@oadank/dsh-input-tools 0.3.14 → 0.3.16
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.md +3 -2
- package/lib/client.js +29 -5
- package/lib/index.js +24 -2
- package/package.json +1 -1
- package/scripts/install-asr.ps1 +18 -11
- package/scripts/install-local-tts.ps1 +27 -12
package/README.md
CHANGED
|
@@ -69,6 +69,7 @@ powershell -ExecutionPolicy Bypass -File scripts\setup-profile.ps1
|
|
|
69
69
|
# Linux/macOS:
|
|
70
70
|
bash scripts/setup-profile.sh
|
|
71
71
|
pnpm install
|
|
72
|
+
pnpm run build:lib # ⚠️ 必须!全新 clone 无编译产物,跳过会报 Failed to resolve @deepseek-ai/dsh-client-web
|
|
72
73
|
pnpm run build:web
|
|
73
74
|
dsh --profile web
|
|
74
75
|
```
|
|
@@ -103,7 +104,7 @@ cd deepseek-harness
|
|
|
103
104
|
git checkout 141eb6fef8 # 官方 dsh-0.1.0-rc.8 基线
|
|
104
105
|
# 打补丁(脚本自动探测/输入源码位置):
|
|
105
106
|
powershell -ExecutionPolicy Bypass -File <插件目录>\patches\apply-voice-patch.ps1
|
|
106
|
-
pnpm install && pnpm run build:web && dsh --profile web
|
|
107
|
+
pnpm install && pnpm run build:lib && pnpm run build:web && dsh --profile web
|
|
107
108
|
```
|
|
108
109
|
|
|
109
110
|
### 方案 B:直接用整合版 fork(推荐)
|
|
@@ -112,7 +113,7 @@ pnpm install && pnpm run build:web && dsh --profile web
|
|
|
112
113
|
git clone https://github.com/oadank/deepseek-harness.git
|
|
113
114
|
cd deepseek-harness
|
|
114
115
|
powershell -ExecutionPolicy Bypass -File scripts\setup-profile.ps1
|
|
115
|
-
pnpm install && pnpm run build:web && dsh --profile web
|
|
116
|
+
pnpm install && pnpm run build:lib && pnpm run build:web && dsh --profile web
|
|
116
117
|
```
|
|
117
118
|
|
|
118
119
|
> 补丁基线官方 rc.8(141eb6fef8),官方后续更新可能不兼容,请先 checkout 该基线再打。回滚:`git apply -R`。
|
package/lib/client.js
CHANGED
|
@@ -586,6 +586,12 @@ window.__ModuleLoader__.load({
|
|
|
586
586
|
const [vdSamples, setVdSamples] = useState([]); // VoiceDesign 官方示例音频(预生成)
|
|
587
587
|
// [2026-08-21] 试听失败的错误提示(之前失败静默无反馈)
|
|
588
588
|
const [previewErr, setPreviewErr] = useState(null);
|
|
589
|
+
// [2026-08-22] 试听失败浮动 Toast(fixed 顶部居中,醒目弹窗式,6 秒自动消失)
|
|
590
|
+
useEffect(() => {
|
|
591
|
+
if (previewErr === null) return;
|
|
592
|
+
const t = window.setTimeout(() => setPreviewErr(null), 6000);
|
|
593
|
+
return () => window.clearTimeout(t);
|
|
594
|
+
}, [previewErr]);
|
|
589
595
|
// [2026-08-21] API Key 明文/密文切换(眼睛图标)
|
|
590
596
|
const [showKeys, setShowKeys] = useState({});
|
|
591
597
|
// [2026-08-21] 本地 TTS 一键安装命令
|
|
@@ -727,8 +733,18 @@ window.__ModuleLoader__.load({
|
|
|
727
733
|
const audio = new Audio("data:" + d.mediaType + ";base64," + d.data);
|
|
728
734
|
previewRef.current = audio;
|
|
729
735
|
audio.onended = () => { if (previewTagRef.current === curTag) setPreviewing(null); };
|
|
730
|
-
audio.onerror = () => {
|
|
731
|
-
|
|
736
|
+
audio.onerror = () => {
|
|
737
|
+
if (previewTagRef.current === curTag) {
|
|
738
|
+
setPreviewing(null);
|
|
739
|
+
setPreviewErr("音频加载/播放失败(服务可能返回了无效音频)");
|
|
740
|
+
}
|
|
741
|
+
};
|
|
742
|
+
audio.play().catch(() => {
|
|
743
|
+
if (previewTagRef.current === curTag) {
|
|
744
|
+
setPreviewing(null);
|
|
745
|
+
setPreviewErr("音频加载/播放失败(服务可能返回了无效音频)");
|
|
746
|
+
}
|
|
747
|
+
});
|
|
732
748
|
})
|
|
733
749
|
.catch((e) => { if (previewTagRef.current === curTag) setPreviewing(null); setPreviewErr(String(e?.message ?? e)); });
|
|
734
750
|
};
|
|
@@ -893,9 +909,17 @@ window.__ModuleLoader__.load({
|
|
|
893
909
|
// 分区标题(语音图标已移到各服务商卡片前)
|
|
894
910
|
h("div", { style: { display: "flex", alignItems: "center", gap: "8px", fontSize: "15px", fontWeight: 700, color: "var(--dsw-alias-label-primary,#e6e9ef)" } },
|
|
895
911
|
"语音服务"),
|
|
896
|
-
// [2026-08-21]
|
|
897
|
-
previewErr !== null ? h("div", {
|
|
898
|
-
|
|
912
|
+
// [2026-08-21] 试听失败错误提示;[2026-08-22] fixed 顶部弹窗 Toast + 限高滚动(错误堆栈超长不撑爆)
|
|
913
|
+
previewErr !== null ? h("div", {
|
|
914
|
+
style: {
|
|
915
|
+
position: "fixed", top: "24px", left: "50%", transform: "translateX(-50%)", zIndex: 9999,
|
|
916
|
+
background: "rgba(229,72,77,.95)", color: "#fff", borderRadius: "10px",
|
|
917
|
+
padding: "10px 18px", fontSize: "13px", lineHeight: "1.5",
|
|
918
|
+
boxShadow: "0 6px 24px rgba(0,0,0,.45)",
|
|
919
|
+
maxWidth: "520px", maxHeight: "45vh", overflowY: "auto",
|
|
920
|
+
whiteSpace: "pre-wrap", wordBreak: "break-word", pointerEvents: "none",
|
|
921
|
+
},
|
|
922
|
+
}, "试听失败:" + previewErr) : null,
|
|
899
923
|
// [2026-08-21] 语音能力状态面板(安装即用 vs dsh 原生契约支持)
|
|
900
924
|
caps !== null ? h("div", { style: { border: "1px solid var(--dsw-alias-border-l1,#333a45)", borderRadius: "10px", padding: "10px 12px", display: "flex", flexDirection: "column", gap: "6px", background: "rgba(128,128,128,.05)", fontSize: "12.5px", lineHeight: "1.5" } },
|
|
901
925
|
h("div", { style: { fontSize: "12px", fontWeight: 600, opacity: .8 } }, "语音能力"),
|
package/lib/index.js
CHANGED
|
@@ -426,6 +426,28 @@ async function synthesizeXiaomiVoiceClone(text, cfg, globalCfg, voiceDesc) {
|
|
|
426
426
|
return toMp3(new Uint8Array(Buffer.from(data, 'base64')), 'audio/wav')
|
|
427
427
|
}
|
|
428
428
|
|
|
429
|
+
// [2026-08-22] 解析 Windows 命令行参数(正确处理双引号:引号内空格不拆、剥掉引号)。
|
|
430
|
+
// 之前用 command.split(/\s+/) 拆参数,用户填的带引号路径会被整段含引号传给
|
|
431
|
+
// execFileSync → node 把 "C:\...\local-tts.mjs" 当成相对路径拼上 cwd →
|
|
432
|
+
// Cannot find module 'D:\opt\...\"C:\Users\...'。本函数根治该问题。
|
|
433
|
+
function splitCommandLine(cmd) {
|
|
434
|
+
const args = []
|
|
435
|
+
let cur = ''
|
|
436
|
+
let inQuote = false
|
|
437
|
+
for (let i = 0; i < cmd.length; i++) {
|
|
438
|
+
const ch = cmd[i]
|
|
439
|
+
if (ch === '"') {
|
|
440
|
+
inQuote = !inQuote
|
|
441
|
+
} else if (ch === ' ' || ch === '\t') {
|
|
442
|
+
if (inQuote) { cur += ch } else if (cur !== '') { args.push(cur); cur = '' }
|
|
443
|
+
} else {
|
|
444
|
+
cur += ch
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
if (cur !== '') args.push(cur)
|
|
448
|
+
return args
|
|
449
|
+
}
|
|
450
|
+
|
|
429
451
|
// ── local 本地 MeloTTS:HTTP 常驻服务优先,CMD 兜底 ──
|
|
430
452
|
async function synthesizeLocalVoice(text, cfg) {
|
|
431
453
|
const url = cfg?.url?.trim() ?? ''
|
|
@@ -442,7 +464,7 @@ async function synthesizeLocalVoice(text, cfg) {
|
|
|
442
464
|
}
|
|
443
465
|
const command = cfg?.cmd?.trim() ?? ''
|
|
444
466
|
if (command === '') return null
|
|
445
|
-
const parts = command
|
|
467
|
+
const parts = splitCommandLine(command)
|
|
446
468
|
const bin = parts[0]
|
|
447
469
|
if (bin === undefined) return null
|
|
448
470
|
const rest = parts.slice(1)
|
|
@@ -523,7 +545,7 @@ async function transcribeAudio(base64Audio, cfg) {
|
|
|
523
545
|
}
|
|
524
546
|
// 2) 本地命令(sherpa-onnx-offline.exe,结果输出到 stderr,需合并双流解析)
|
|
525
547
|
if (asr.mode === 'cmd' && (asr.cmd ?? '').trim() !== '') {
|
|
526
|
-
const parts = asr.cmd.trim()
|
|
548
|
+
const parts = splitCommandLine(asr.cmd.trim())
|
|
527
549
|
const bin = parts[0]
|
|
528
550
|
if (bin === undefined) return { ok: false, error: '命令格式错误' }
|
|
529
551
|
const { spawnSync } = await import('node:child_process')
|
package/package.json
CHANGED
package/scripts/install-asr.ps1
CHANGED
|
@@ -6,28 +6,35 @@
|
|
|
6
6
|
# 3. ffmpeg(无则用 winget 安装)
|
|
7
7
|
# 4. 注册 nssm 服务 asr(端口 18790,开机自启)
|
|
8
8
|
#
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
#
|
|
9
|
+
# 安装位置(2026-08-22 改):默认装到**独立目录**,不装插件包目录——
|
|
10
|
+
# 插件包(profile 副本或源码 internal-plugins)会随升级/重装被覆盖,
|
|
11
|
+
# 模型放里面会丢。默认顺序:
|
|
12
|
+
# 1) -InstallDir 参数显式指定(最高优先)
|
|
13
|
+
# 2) 检测已有 sherpa-onnx(之前装过则复用):~\.dsh\sherpa-onnx / C:\D\opt\sherpa-onnx / D:\opt\deepseek-harness\asr
|
|
14
|
+
# 3) 以上都没有 → ~\.dsh\sherpa-onnx(dsh 数据目录,跨升级保留)
|
|
12
15
|
# 用法:以管理员身份打开 PowerShell,执行:
|
|
13
|
-
# powershell -ExecutionPolicy Bypass -File "
|
|
14
|
-
# 可选参数:-Port 18790
|
|
16
|
+
# powershell -ExecutionPolicy Bypass -File "<脚本路径>\install-asr.ps1"
|
|
17
|
+
# 可选参数:-Port 18790(自定义端口);-InstallDir "D:\opt\my-sherpa"(自定义安装目录)
|
|
15
18
|
# ============================================================
|
|
16
19
|
param(
|
|
17
|
-
[int]$Port = 18790
|
|
20
|
+
[int]$Port = 18790,
|
|
21
|
+
[string]$InstallDir = ""
|
|
18
22
|
)
|
|
19
23
|
|
|
20
24
|
$ErrorActionPreference = "Stop"
|
|
21
25
|
$Version = "v1.13.6"
|
|
22
26
|
|
|
23
|
-
# ---- 0.
|
|
24
|
-
$
|
|
25
|
-
$
|
|
26
|
-
|
|
27
|
+
# ---- 0. 确定安装目录(独立目录,不装插件包内)----
|
|
28
|
+
if ($InstallDir -eq "") {
|
|
29
|
+
foreach ($c in @("$env:USERPROFILE\.dsh\sherpa-onnx", "C:\D\opt\sherpa-onnx", "D:\opt\deepseek-harness\asr")) {
|
|
30
|
+
if (Test-Path (Join-Path $c "bin")) { $InstallDir = $c; break }
|
|
31
|
+
}
|
|
32
|
+
if ($InstallDir -eq "") { $InstallDir = Join-Path $env:USERPROFILE ".dsh\sherpa-onnx" }
|
|
33
|
+
}
|
|
27
34
|
|
|
28
35
|
Write-Host "==== dsh ASR 一键安装 ====" -ForegroundColor Cyan
|
|
29
|
-
Write-Host "插件包目录: $PluginRoot"
|
|
30
36
|
Write-Host "安装目录: $InstallDir"
|
|
37
|
+
Write-Host " (独立目录,不随插件升级/重装被覆盖)" -ForegroundColor DarkGray
|
|
31
38
|
Write-Host "服务端口: $Port"
|
|
32
39
|
|
|
33
40
|
# ---- 0b. 幂等保护:已完整安装则直接退出(不下载、不注册、不碰任何现有配置)----
|
|
@@ -7,13 +7,22 @@
|
|
|
7
7
|
# 4. 生成 local-tts.mjs 启动脚本(与插件"本地命令"契约一致:
|
|
8
8
|
# node local-tts.mjs <文本> → stdout 输出 mp3 音频字节)
|
|
9
9
|
#
|
|
10
|
-
#
|
|
11
|
-
#
|
|
12
|
-
#
|
|
10
|
+
# 安装位置(2026-08-22 改):默认装到**独立目录**,不装插件包目录——
|
|
11
|
+
# 插件包(profile 副本或源码 internal-plugins)会随升级/重装被覆盖,
|
|
12
|
+
# 模型放里面会丢。默认顺序:
|
|
13
|
+
# 1) -InstallDir 参数显式指定(最高优先,可指定源码 internal-plugins 等任意位置)
|
|
14
|
+
# 2) 检测已有 sherpa-onnx(ASR 装过则复用同一份,省一次下载):
|
|
15
|
+
# ~\.dsh\sherpa-onnx / C:\D\opt\sherpa-onnx / D:\opt\deepseek-harness\asr
|
|
16
|
+
# 3) 以上都没有 → ~\.dsh\sherpa-onnx(dsh 数据目录,跨升级保留)
|
|
13
17
|
# 用法:以管理员身份打开 PowerShell,执行:
|
|
14
|
-
# powershell -ExecutionPolicy Bypass -File "
|
|
18
|
+
# powershell -ExecutionPolicy Bypass -File "<脚本路径>\install-local-tts.ps1"
|
|
19
|
+
# 或指定安装目录:... install-local-tts.ps1 -InstallDir "D:\opt\my-sherpa"
|
|
15
20
|
# 装完后到 dsh 设置 → 语音服务 → 本地 TTS,把提示的命令填进「本地命令」。
|
|
16
21
|
# ============================================================
|
|
22
|
+
param(
|
|
23
|
+
[string]$InstallDir = ""
|
|
24
|
+
)
|
|
25
|
+
|
|
17
26
|
$ErrorActionPreference = "Stop"
|
|
18
27
|
$Version = "v1.13.6"
|
|
19
28
|
|
|
@@ -57,14 +66,18 @@ function Expand-TarBz2 {
|
|
|
57
66
|
return $true
|
|
58
67
|
}
|
|
59
68
|
|
|
60
|
-
# ---- 0.
|
|
61
|
-
$
|
|
62
|
-
|
|
63
|
-
$
|
|
69
|
+
# ---- 0. 确定安装目录(独立目录,不装插件包内)----
|
|
70
|
+
if ($InstallDir -eq "") {
|
|
71
|
+
# 复用已有 sherpa-onnx(ASR 装过则共用,省一次下载)
|
|
72
|
+
foreach ($c in @("$env:USERPROFILE\.dsh\sherpa-onnx", "C:\D\opt\sherpa-onnx", "D:\opt\deepseek-harness\asr")) {
|
|
73
|
+
if (Test-Path (Join-Path $c "bin")) { $InstallDir = $c; break }
|
|
74
|
+
}
|
|
75
|
+
if ($InstallDir -eq "") { $InstallDir = Join-Path $env:USERPROFILE ".dsh\sherpa-onnx" }
|
|
76
|
+
}
|
|
64
77
|
|
|
65
78
|
Write-Host "==== dsh 本地 TTS 一键安装 ====" -ForegroundColor Cyan
|
|
66
|
-
Write-Host "插件包目录: $PluginRoot"
|
|
67
79
|
Write-Host "安装目录: $InstallDir"
|
|
80
|
+
Write-Host " (独立目录,不随插件升级/重装被覆盖;与 ASR 共用同一份 sherpa-onnx)" -ForegroundColor DarkGray
|
|
68
81
|
|
|
69
82
|
# ---- 0b. 幂等保护:已完整安装则直接退出 ----
|
|
70
83
|
$exeExists = Test-Path "$InstallDir\bin\sherpa-onnx-offline-tts.exe"
|
|
@@ -198,14 +211,16 @@ try {
|
|
|
198
211
|
}
|
|
199
212
|
'@
|
|
200
213
|
$launcher = $launcherTemplate.Replace('__SHERPA__', $exePath).Replace('__BASE__', $basePath)
|
|
201
|
-
$launcherFile = "
|
|
214
|
+
$launcherFile = Join-Path $InstallDir "local-tts.mjs"
|
|
202
215
|
Set-Content -Path $launcherFile -Value $launcher -Encoding UTF8
|
|
203
216
|
Write-Host " local-tts.mjs: $launcherFile" -ForegroundColor Green
|
|
204
217
|
|
|
205
|
-
# ---- 6.
|
|
218
|
+
# ---- 6. 完成提示(命令只在路径含空格时才加引号;插件已能正确处理引号)----
|
|
206
219
|
Write-Host "`n==== 安装完成 ====" -ForegroundColor Cyan
|
|
207
220
|
$nodeExe = (Get-Command node).Source
|
|
208
|
-
$
|
|
221
|
+
$nodePart = if ($nodeExe -match ' ') { "`"$nodeExe`"" } else { $nodeExe }
|
|
222
|
+
$launcherPart = if ($launcherFile -match ' ') { "`"$launcherFile`"" } else { $launcherFile }
|
|
223
|
+
$cmdLine = "$nodePart $launcherPart"
|
|
209
224
|
Write-Host "请到 dsh 设置 → 语音服务 → 本地 TTS,把下面这行填进「本地命令」:"
|
|
210
225
|
Write-Host ""
|
|
211
226
|
Write-Host " $cmdLine" -ForegroundColor Green
|