dsh-line-bot 0.10.3 → 0.10.4

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/CHANGELOG.md CHANGED
@@ -1,3 +1,8 @@
1
+ ## 0.10.4
2
+
3
+ - Windows 語音引擎:優先選擇含 `openblas.dll` 的完整 CrispASR bundle,忽略舊安裝留下的裸 `crispasr.exe`(避免 `STATUS_DLL_NOT_FOUND`)。
4
+ - `--version` 驗證改在執行檔所在資料夾執行,確保 DLL 可被找到;失敗訊息保留實際 stderr。
5
+
1
6
  ## 0.10.3
2
7
 
3
8
  - 面板「重新偵測」不再同步匯入 MLX 套件(原本可能卡住 8–30 秒),偵測一律走快取與檔案檢查。
package/lib/host.mjs CHANGED
@@ -51,7 +51,7 @@ const { createVision } = visionMod
51
51
  const { createTts } = ttsMod
52
52
  const { chunkText, clip, inQuietWindow, jsonFail, jsonOk, maskSecret, readJsonBody, safeArray, sleep, readJsonSafe } = utilMod
53
53
 
54
- export const VERSION = '0.10.3'
54
+ export const VERSION = '0.10.4'
55
55
 
56
56
  // 環境變數提供的 Cloudflare API Token(方便用 shell 注入、不用把 token 寫進設定檔)。
57
57
  // 設定檔裡的 token 優先;兩者都沒有時才用環境變數。
package/lib/tts.mjs CHANGED
@@ -212,6 +212,18 @@ export function createTts({ dir, logger, getConfig }) {
212
212
  }
213
213
  }
214
214
 
215
+ function hasWindowsRuntime(exe) {
216
+ if (!isWin()) return true
217
+ try {
218
+ const names = fs.readdirSync(path.dirname(exe)).map((item) => String(item).toLowerCase())
219
+ // Windows CPU 版 CrispASR 需要同資料夾的 openblas.dll。舊安裝可能留下
220
+ // 只有 exe 的裸副本,不能只憑「檔案存在」就選它。
221
+ return names.includes('openblas.dll')
222
+ } catch {
223
+ return false
224
+ }
225
+ }
226
+
215
227
  function findCrispExe(dir, depth = 3) {
216
228
  if (depth < 0) return ''
217
229
  let entries = []
@@ -220,19 +232,38 @@ export function createTts({ dir, logger, getConfig }) {
220
232
  } catch {
221
233
  return ''
222
234
  }
235
+ const candidates = []
223
236
  for (const entry of entries) {
224
- if (entry.isFile() && /^crispasr\.exe$/i.test(entry.name)) return path.join(dir, entry.name)
237
+ if (entry.isFile() && /^crispasr\.exe$/i.test(entry.name)) candidates.push(path.join(dir, entry.name))
225
238
  }
226
239
  for (const entry of entries) {
227
240
  if (!entry.isDirectory()) continue
228
241
  const hit = findCrispExe(path.join(dir, entry.name), depth - 1)
229
- if (hit) return hit
242
+ if (hit) candidates.push(hit)
243
+ }
244
+ // 優先完整 bundle;舊的裸 exe 即使存在也不選。
245
+ return candidates.find((item) => hasWindowsRuntime(item)) || candidates[0] || ''
246
+ }
247
+
248
+ function crispVersion(exe) {
249
+ try {
250
+ const output = execFileSync(exe, ['--version'], {
251
+ encoding: 'utf8',
252
+ timeout: 15000,
253
+ cwd: path.dirname(exe),
254
+ })
255
+ return { ok: true, output: String(output || '') }
256
+ } catch (error) {
257
+ return {
258
+ ok: false,
259
+ error: String(error?.stderr || error?.message || error).slice(0, 500),
260
+ }
230
261
  }
231
- return ''
232
262
  }
233
263
 
234
264
  function ggufBinary() {
235
- if (state.ggufBinary && fileExists(state.ggufBinary)) return state.ggufBinary
265
+ // 忽略舊 Windows 安裝留下的裸 exe:它存在但載不到同 bundle 的 DLL。
266
+ if (state.ggufBinary && fileExists(state.ggufBinary) && hasWindowsRuntime(state.ggufBinary)) return state.ggufBinary
236
267
  if (isWin()) {
237
268
  const found = findCrispExe(paths.winDir)
238
269
  if (found) return found
@@ -245,12 +276,7 @@ export function createTts({ dir, logger, getConfig }) {
245
276
  const bin = ggufBinary()
246
277
  if (!bin) return false
247
278
  if (!validate) return true
248
- try {
249
- execFileSync(bin, ['--version'], { stdio: 'ignore', timeout: 8000 })
250
- return true
251
- } catch {
252
- return false
253
- }
279
+ return crispVersion(bin).ok
254
280
  }
255
281
 
256
282
  function detect({ force = false, validate = false } = {}) {
@@ -418,11 +444,9 @@ export function createTts({ dir, logger, getConfig }) {
418
444
  await run('powershell', ['-NoProfile', '-Command', `Expand-Archive -LiteralPath ${JSON.stringify(zip)} -DestinationPath ${JSON.stringify(paths.winDir)} -Force`], { timeout: 300000 })
419
445
  const exe = findCrispExe(paths.winDir)
420
446
  if (!exe) throw new Error('解壓縮後找不到 crispasr.exe')
421
- try {
422
- execFileSync(exe, ['--version'], { encoding: 'utf8', timeout: 15000 })
423
- } catch (error) {
424
- const detail = String(error?.stderr || error?.message || error).slice(0, 300)
425
- throw new Error(`crispasr.exe 無法執行(可能是缺少 DLL):${detail}`)
447
+ const version = crispVersion(exe)
448
+ if (!version.ok) {
449
+ throw new Error(`crispasr.exe 無法執行(可能是缺少 DLL):${version.error}`)
426
450
  }
427
451
  state.ggufBinary = exe
428
452
  return 'gguf'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-line-bot",
3
- "version": "0.10.3",
3
+ "version": "0.10.4",
4
4
  "description": "把 LINE 官方帳號(Messaging API)接進 DeepSeek Harness 的插件:內建 Cloudflare Tunnel、人設選擇(大肥魚或插件自訂)、主動傳訊與完整設定面板。",
5
5
  "type": "module",
6
6
  "main": "index.js",