@goodandready/dsh-voice 0.9.18 → 0.9.20
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/lib/client.js +69 -25
- package/lib/local-daemon.js +123 -22
- package/lib/sensevoice-installer.js +24 -4
- package/lib/updater.js +24 -11
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -884,11 +884,23 @@ window.__ModuleLoader__.load({
|
|
|
884
884
|
const span = target && target.span
|
|
885
885
|
if (span) {
|
|
886
886
|
if (typeof span.insert === 'function') {
|
|
887
|
-
try {
|
|
887
|
+
try {
|
|
888
|
+
const r = span.insert(text)
|
|
889
|
+
if (r !== false) return true
|
|
890
|
+
return false
|
|
891
|
+
} catch (_) {}
|
|
888
892
|
} else if (typeof span.insertText === 'function') {
|
|
889
|
-
try {
|
|
893
|
+
try {
|
|
894
|
+
const r = span.insertText(text)
|
|
895
|
+
if (r !== false) return true
|
|
896
|
+
return false
|
|
897
|
+
} catch (_) {}
|
|
890
898
|
} else if (typeof span === 'function') {
|
|
891
|
-
try {
|
|
899
|
+
try {
|
|
900
|
+
const r = span(text)
|
|
901
|
+
if (r !== false) return true
|
|
902
|
+
return false
|
|
903
|
+
} catch (_) {}
|
|
892
904
|
}
|
|
893
905
|
}
|
|
894
906
|
const actions = (target && target.actions) || voice.inputActions
|
|
@@ -898,13 +910,28 @@ window.__ModuleLoader__.load({
|
|
|
898
910
|
return false
|
|
899
911
|
}
|
|
900
912
|
if (typeof actions.insertText === 'function') {
|
|
901
|
-
try {
|
|
913
|
+
try {
|
|
914
|
+
const res = actions.insertText(text, span)
|
|
915
|
+
if (res !== false) {
|
|
916
|
+
if (target && typeof actions.captureInsertion === 'function') {
|
|
917
|
+
try { target.span = actions.captureInsertion() } catch (_) {}
|
|
918
|
+
}
|
|
919
|
+
return true
|
|
920
|
+
}
|
|
921
|
+
return false
|
|
922
|
+
} catch (_) {
|
|
923
|
+
return false
|
|
924
|
+
}
|
|
902
925
|
}
|
|
903
926
|
const draft = input && typeof input.draft === 'string' ? input.draft : ''
|
|
904
927
|
const before = draft
|
|
905
928
|
const sep = (!draft || draft.endsWith('\n') || text.startsWith('\n')) ? '' : ' '
|
|
906
929
|
const after = draft + sep + text
|
|
907
|
-
|
|
930
|
+
try {
|
|
931
|
+
actions.setDraft(after)
|
|
932
|
+
} catch (_) {
|
|
933
|
+
return false
|
|
934
|
+
}
|
|
908
935
|
const limit = Number(voice.settings.historyLimit)
|
|
909
936
|
if (limit > 0) {
|
|
910
937
|
insertHistory.push({ before, added: sep + text, after })
|
|
@@ -914,10 +941,10 @@ window.__ModuleLoader__.load({
|
|
|
914
941
|
}
|
|
915
942
|
|
|
916
943
|
function appendDraft(text, target) {
|
|
917
|
-
if (voice.disposed || !text) return
|
|
944
|
+
if (voice.disposed || !text) return false
|
|
918
945
|
let clean = voice.settings.voiceCommands ? applyVoiceCommands(text) : tidyPhrase(text)
|
|
919
|
-
if (!clean) return
|
|
920
|
-
insertDraftText(clean, target)
|
|
946
|
+
if (!clean) return false
|
|
947
|
+
return insertDraftText(clean, target)
|
|
921
948
|
}
|
|
922
949
|
|
|
923
950
|
function announceVoice(phase) {
|
|
@@ -1291,7 +1318,7 @@ voice._chainsPromise = null
|
|
|
1291
1318
|
}
|
|
1292
1319
|
}
|
|
1293
1320
|
if (action === 'send' || action === 'clear' || action === 'newline') {
|
|
1294
|
-
executeVoiceAction(action, text)
|
|
1321
|
+
executeVoiceAction(action, text, target)
|
|
1295
1322
|
return
|
|
1296
1323
|
}
|
|
1297
1324
|
if (action === 'cancel') {
|
|
@@ -1300,8 +1327,12 @@ voice._chainsPromise = null
|
|
|
1300
1327
|
}
|
|
1301
1328
|
const delay = Number(voice.settings.sendDelayMs) || 0
|
|
1302
1329
|
if (text && delay > 0 && !voice.holding) {
|
|
1303
|
-
appendDraft(text, target)
|
|
1304
|
-
|
|
1330
|
+
const ok = appendDraft(text, target)
|
|
1331
|
+
if (ok !== false) {
|
|
1332
|
+
voice.set({ phase: 'pending', pending: { text, target, undoOnly: true, leftMs: delay } })
|
|
1333
|
+
} else {
|
|
1334
|
+
voice.set({ phase: 'idle' })
|
|
1335
|
+
}
|
|
1305
1336
|
return
|
|
1306
1337
|
}
|
|
1307
1338
|
if (text) appendDraft(text, target)
|
|
@@ -1464,8 +1495,12 @@ voice._chainsPromise = null
|
|
|
1464
1495
|
voice.browserFinals = null
|
|
1465
1496
|
if (!said) { voice.set({ phase: 'idle' }); return }
|
|
1466
1497
|
if (mode === 'message') {
|
|
1467
|
-
appendDraft(said, target)
|
|
1468
|
-
|
|
1498
|
+
const ok = appendDraft(said, target)
|
|
1499
|
+
if (ok !== false) {
|
|
1500
|
+
voice.set({ phase: 'pending', pending: { text: said, target, totalMs: voice.settings.autoSendMs, leftMs: voice.settings.autoSendMs } })
|
|
1501
|
+
} else {
|
|
1502
|
+
voice.set({ phase: 'idle' })
|
|
1503
|
+
}
|
|
1469
1504
|
} else {
|
|
1470
1505
|
voice.set({ phase: 'idle' })
|
|
1471
1506
|
}
|
|
@@ -1525,14 +1560,18 @@ voice._chainsPromise = null
|
|
|
1525
1560
|
}
|
|
1526
1561
|
}
|
|
1527
1562
|
if (action === 'send' || action === 'clear' || action === 'newline') {
|
|
1528
|
-
executeVoiceAction(action, text)
|
|
1563
|
+
executeVoiceAction(action, text, target)
|
|
1529
1564
|
return
|
|
1530
1565
|
}
|
|
1531
1566
|
if (action === 'cancel') { cancelCurrent(); return }
|
|
1532
|
-
appendDraft(text, target)
|
|
1567
|
+
const ok = appendDraft(text, target)
|
|
1533
1568
|
if (voice.lastNote) voice.lastNote.text = text
|
|
1534
1569
|
if (mode === 'message') {
|
|
1535
|
-
|
|
1570
|
+
if (ok !== false) {
|
|
1571
|
+
voice.set({ phase: 'pending', pending: { text, target, totalMs: voice.settings.autoSendMs, leftMs: voice.settings.autoSendMs, audioUrl: noteUrl } })
|
|
1572
|
+
} else {
|
|
1573
|
+
voice.set({ phase: 'idle' })
|
|
1574
|
+
}
|
|
1536
1575
|
} else {
|
|
1537
1576
|
voice.set({ phase: 'idle' })
|
|
1538
1577
|
}
|
|
@@ -1549,26 +1588,29 @@ voice._chainsPromise = null
|
|
|
1549
1588
|
}
|
|
1550
1589
|
})
|
|
1551
1590
|
}
|
|
1552
|
-
function clearDraft() {
|
|
1553
|
-
const
|
|
1591
|
+
function clearDraft(target) {
|
|
1592
|
+
const activeTarget = target || voice._activeTarget
|
|
1593
|
+
const actions = (activeTarget && activeTarget.actions) || voice.inputActions
|
|
1554
1594
|
if (actions && typeof actions.setDraft === 'function') actions.setDraft('')
|
|
1555
1595
|
}
|
|
1556
1596
|
|
|
1557
1597
|
// Voice actions (#29-7): spoken triggers perform composer actions.
|
|
1558
|
-
function executeVoiceAction(action, rawText) {
|
|
1598
|
+
function executeVoiceAction(action, rawText, target) {
|
|
1599
|
+
const activeTarget = target || voice._activeTarget
|
|
1600
|
+
const actions = (activeTarget && activeTarget.actions) || voice.inputActions
|
|
1559
1601
|
if (action === 'send') {
|
|
1560
1602
|
const text = rawText.replace(/(^|[\s,.!?])(отправь|пошли|send)([\s,.!?]|$)/gi, '').trim()
|
|
1561
|
-
if (text) appendDraft(text)
|
|
1562
|
-
if (typeof
|
|
1563
|
-
|
|
1603
|
+
if (text) appendDraft(text, activeTarget)
|
|
1604
|
+
if (typeof actions?.submit === 'function') {
|
|
1605
|
+
actions.submit()
|
|
1564
1606
|
}
|
|
1565
1607
|
voice.set({ phase: 'idle' })
|
|
1566
1608
|
} else if (action === 'clear') {
|
|
1567
|
-
clearDraft()
|
|
1609
|
+
clearDraft(activeTarget)
|
|
1568
1610
|
voice.set({ phase: 'idle' })
|
|
1569
1611
|
} else if (action === 'newline') {
|
|
1570
1612
|
const payload = (rawText.replace(/(^|[\s,.!?])(с новой строки|новая строка|абзац|new line|paragraph)([\s,.!?]|$)/gi, '').trim())
|
|
1571
|
-
appendDraft(payload)
|
|
1613
|
+
appendDraft(payload, activeTarget)
|
|
1572
1614
|
voice.set({ phase: 'idle' })
|
|
1573
1615
|
}
|
|
1574
1616
|
}
|
|
@@ -1577,7 +1619,8 @@ voice._chainsPromise = null
|
|
|
1577
1619
|
const pending = voice.pending
|
|
1578
1620
|
if (!pending) return
|
|
1579
1621
|
const text = pending.text
|
|
1580
|
-
const
|
|
1622
|
+
const target = pending.target || voice._activeTarget
|
|
1623
|
+
const actions = (target && target.actions) || voice.inputActions
|
|
1581
1624
|
voice.pending = null
|
|
1582
1625
|
voice.set({ phase: 'idle' })
|
|
1583
1626
|
|
|
@@ -3935,6 +3978,7 @@ voice._chainsPromise = null
|
|
|
3935
3978
|
startRecording,
|
|
3936
3979
|
cancelCurrent,
|
|
3937
3980
|
stopCurrent,
|
|
3981
|
+
submitPending,
|
|
3938
3982
|
loadWebGpuWhisper,
|
|
3939
3983
|
}
|
|
3940
3984
|
return module.exports
|
package/lib/local-daemon.js
CHANGED
|
@@ -65,16 +65,45 @@ export function createLocalDaemons({ live, ctx }) {
|
|
|
65
65
|
})
|
|
66
66
|
}
|
|
67
67
|
if (child && child.done && typeof child.done.then === 'function') {
|
|
68
|
-
child.done.then((res) => {
|
|
68
|
+
child.done.then(async (res) => {
|
|
69
|
+
let code = res && res.code
|
|
70
|
+
let detail = ''
|
|
71
|
+
if (typeof child.readOutput === 'function') {
|
|
72
|
+
try {
|
|
73
|
+
const out = child.readOutput()
|
|
74
|
+
if (out && out.stderr) detail = String(out.stderr).trim()
|
|
75
|
+
else if (out && out.stdout) detail = String(out.stdout).trim()
|
|
76
|
+
} catch (_) {}
|
|
77
|
+
} else if (typeof child.read === 'function') {
|
|
78
|
+
try {
|
|
79
|
+
const out = child.read()
|
|
80
|
+
if (out && out.stderr) detail = String(out.stderr).trim()
|
|
81
|
+
} catch (_) {}
|
|
82
|
+
}
|
|
83
|
+
if (typeof child.result === 'function') {
|
|
84
|
+
try {
|
|
85
|
+
const r = await child.result()
|
|
86
|
+
if (r) {
|
|
87
|
+
if (r.exitCode !== undefined && r.exitCode !== null) code = r.exitCode
|
|
88
|
+
if (r.stderr && !detail) detail = String(r.stderr).trim()
|
|
89
|
+
}
|
|
90
|
+
} catch (err) {
|
|
91
|
+
const msg = err && err.message ? err.message : String(err)
|
|
92
|
+
if (msg && !detail) detail = msg.trim()
|
|
93
|
+
}
|
|
94
|
+
} else if (child.result && typeof child.result === 'object') {
|
|
95
|
+
code = child.result.exitCode ?? code
|
|
96
|
+
if (child.result.stderr && !detail) detail = String(child.result.stderr).trim()
|
|
97
|
+
}
|
|
69
98
|
if (child.status === 'killed') {
|
|
70
|
-
whisperError = 'whisper server process was killed'
|
|
99
|
+
whisperError = detail ? `whisper server process was killed: ${detail}` : 'whisper server process was killed'
|
|
71
100
|
} else if (child.status === 'completed') {
|
|
72
|
-
|
|
101
|
+
code = code ?? child.exitCode ?? 0
|
|
73
102
|
if (code !== 0 && code !== null && code !== undefined) {
|
|
74
|
-
whisperError = `whisper server process exited with code ${code}`
|
|
103
|
+
whisperError = `whisper server process exited with code ${code}${detail ? `: ${detail}` : ''}`
|
|
75
104
|
}
|
|
76
|
-
} else if (
|
|
77
|
-
whisperError = `whisper server process exited with code ${
|
|
105
|
+
} else if (code !== 0 && code !== null && code !== undefined) {
|
|
106
|
+
whisperError = `whisper server process exited with code ${code}${detail ? `: ${detail}` : ''}`
|
|
78
107
|
}
|
|
79
108
|
}).catch((err) => {
|
|
80
109
|
whisperError = err?.message || String(err)
|
|
@@ -91,21 +120,50 @@ export function createLocalDaemons({ live, ctx }) {
|
|
|
91
120
|
return true
|
|
92
121
|
}
|
|
93
122
|
if (child) {
|
|
123
|
+
let detail = ''
|
|
124
|
+
if (typeof child.readOutput === 'function') {
|
|
125
|
+
try {
|
|
126
|
+
const out = child.readOutput()
|
|
127
|
+
if (out && out.stderr) detail = String(out.stderr).trim()
|
|
128
|
+
else if (out && out.stdout) detail = String(out.stdout).trim()
|
|
129
|
+
} catch (_) {}
|
|
130
|
+
} else if (typeof child.read === 'function') {
|
|
131
|
+
try {
|
|
132
|
+
const out = child.read()
|
|
133
|
+
if (out && out.stderr) detail = String(out.stderr).trim()
|
|
134
|
+
} catch (_) {}
|
|
135
|
+
}
|
|
136
|
+
let code = child.exitCode
|
|
137
|
+
if (typeof child.result === 'function') {
|
|
138
|
+
try {
|
|
139
|
+
const r = await child.result()
|
|
140
|
+
if (r) {
|
|
141
|
+
if (r.exitCode !== undefined && r.exitCode !== null) code = r.exitCode
|
|
142
|
+
if (r.stderr && !detail) detail = String(r.stderr).trim()
|
|
143
|
+
}
|
|
144
|
+
} catch (err) {
|
|
145
|
+
const msg = err && err.message ? err.message : String(err)
|
|
146
|
+
if (msg && !detail) detail = msg.trim()
|
|
147
|
+
}
|
|
148
|
+
} else if (child.result && typeof child.result === 'object') {
|
|
149
|
+
code = child.result.exitCode ?? code
|
|
150
|
+
if (child.result.stderr && !detail) detail = String(child.result.stderr).trim()
|
|
151
|
+
}
|
|
94
152
|
if (child.status === 'killed') {
|
|
95
|
-
whisperError = 'whisper server process was killed'
|
|
153
|
+
whisperError = detail ? `whisper server process was killed: ${detail}` : 'whisper server process was killed'
|
|
96
154
|
return false
|
|
97
155
|
}
|
|
98
156
|
if (child.status === 'completed') {
|
|
99
|
-
|
|
100
|
-
whisperError = `whisper server process exited prematurely with code ${code}`
|
|
157
|
+
code = code ?? 0
|
|
158
|
+
whisperError = `whisper server process exited prematurely with code ${code}${detail ? `: ${detail}` : ''}`
|
|
101
159
|
return false
|
|
102
160
|
}
|
|
103
161
|
if (child.status === 'exited' || child.status === 'failed') {
|
|
104
|
-
whisperError = `whisper server process exited prematurely with status ${child.status}`
|
|
162
|
+
whisperError = `whisper server process exited prematurely with status ${child.status}${detail ? `: ${detail}` : ''}`
|
|
105
163
|
return false
|
|
106
164
|
}
|
|
107
165
|
if (child.exitCode !== null && child.exitCode !== undefined) {
|
|
108
|
-
whisperError = `whisper server process exited prematurely with code ${child.exitCode}`
|
|
166
|
+
whisperError = `whisper server process exited prematurely with code ${child.exitCode}${detail ? `: ${detail}` : ''}`
|
|
109
167
|
return false
|
|
110
168
|
}
|
|
111
169
|
}
|
|
@@ -183,16 +241,30 @@ export function createLocalDaemons({ live, ctx }) {
|
|
|
183
241
|
})
|
|
184
242
|
}
|
|
185
243
|
if (sensevoiceChild && sensevoiceChild.done && typeof sensevoiceChild.done.then === 'function') {
|
|
186
|
-
sensevoiceChild.done.then((res) => {
|
|
244
|
+
sensevoiceChild.done.then(async (res) => {
|
|
245
|
+
let code = res && res.code
|
|
246
|
+
let detail = ''
|
|
247
|
+
if (typeof sensevoiceChild.result === 'function') {
|
|
248
|
+
try {
|
|
249
|
+
const r = await sensevoiceChild.result()
|
|
250
|
+
if (r) {
|
|
251
|
+
if (r.exitCode !== undefined && r.exitCode !== null) code = r.exitCode
|
|
252
|
+
if (r.stderr) detail = String(r.stderr).trim()
|
|
253
|
+
}
|
|
254
|
+
} catch (_) {}
|
|
255
|
+
} else if (sensevoiceChild.result && typeof sensevoiceChild.result === 'object') {
|
|
256
|
+
code = sensevoiceChild.result.exitCode ?? code
|
|
257
|
+
if (sensevoiceChild.result.stderr) detail = String(sensevoiceChild.result.stderr).trim()
|
|
258
|
+
}
|
|
187
259
|
if (sensevoiceChild.status === 'killed') {
|
|
188
|
-
sensevoiceError = 'sensevoice process was killed'
|
|
260
|
+
sensevoiceError = detail ? `sensevoice process was killed: ${detail}` : 'sensevoice process was killed'
|
|
189
261
|
} else if (sensevoiceChild.status === 'completed') {
|
|
190
|
-
|
|
262
|
+
code = code ?? sensevoiceChild.exitCode ?? 0
|
|
191
263
|
if (code !== 0 && code !== null && code !== undefined) {
|
|
192
|
-
sensevoiceError = `sensevoice process exited with code ${code}`
|
|
264
|
+
sensevoiceError = `sensevoice process exited with code ${code}${detail ? `: ${detail}` : ''}`
|
|
193
265
|
}
|
|
194
|
-
} else if (
|
|
195
|
-
sensevoiceError = `sensevoice process exited with code ${
|
|
266
|
+
} else if (code !== 0 && code !== null && code !== undefined) {
|
|
267
|
+
sensevoiceError = `sensevoice process exited with code ${code}${detail ? `: ${detail}` : ''}`
|
|
196
268
|
}
|
|
197
269
|
}).catch((err) => {
|
|
198
270
|
sensevoiceError = err?.message || String(err)
|
|
@@ -209,21 +281,50 @@ export function createLocalDaemons({ live, ctx }) {
|
|
|
209
281
|
return true
|
|
210
282
|
}
|
|
211
283
|
if (sensevoiceChild) {
|
|
284
|
+
let detail = ''
|
|
285
|
+
if (typeof sensevoiceChild.readOutput === 'function') {
|
|
286
|
+
try {
|
|
287
|
+
const out = sensevoiceChild.readOutput()
|
|
288
|
+
if (out && out.stderr) detail = String(out.stderr).trim()
|
|
289
|
+
else if (out && out.stdout) detail = String(out.stdout).trim()
|
|
290
|
+
} catch (_) {}
|
|
291
|
+
} else if (typeof sensevoiceChild.read === 'function') {
|
|
292
|
+
try {
|
|
293
|
+
const out = sensevoiceChild.read()
|
|
294
|
+
if (out && out.stderr) detail = String(out.stderr).trim()
|
|
295
|
+
} catch (_) {}
|
|
296
|
+
}
|
|
297
|
+
let code = sensevoiceChild.exitCode
|
|
298
|
+
if (typeof sensevoiceChild.result === 'function') {
|
|
299
|
+
try {
|
|
300
|
+
const r = await sensevoiceChild.result()
|
|
301
|
+
if (r) {
|
|
302
|
+
if (r.exitCode !== undefined && r.exitCode !== null) code = r.exitCode
|
|
303
|
+
if (r.stderr && !detail) detail = String(r.stderr).trim()
|
|
304
|
+
}
|
|
305
|
+
} catch (err) {
|
|
306
|
+
const msg = err && err.message ? err.message : String(err)
|
|
307
|
+
if (msg && !detail) detail = msg.trim()
|
|
308
|
+
}
|
|
309
|
+
} else if (sensevoiceChild.result && typeof sensevoiceChild.result === 'object') {
|
|
310
|
+
code = sensevoiceChild.result.exitCode ?? code
|
|
311
|
+
if (sensevoiceChild.result.stderr && !detail) detail = String(sensevoiceChild.result.stderr).trim()
|
|
312
|
+
}
|
|
212
313
|
if (sensevoiceChild.status === 'killed') {
|
|
213
|
-
sensevoiceError = 'sensevoice process was killed'
|
|
314
|
+
sensevoiceError = detail ? `sensevoice process was killed: ${detail}` : 'sensevoice process was killed'
|
|
214
315
|
return false
|
|
215
316
|
}
|
|
216
317
|
if (sensevoiceChild.status === 'completed') {
|
|
217
|
-
|
|
218
|
-
sensevoiceError = `sensevoice process exited prematurely with code ${code}`
|
|
318
|
+
code = code ?? 0
|
|
319
|
+
sensevoiceError = `sensevoice process exited prematurely with code ${code}${detail ? `: ${detail}` : ''}`
|
|
219
320
|
return false
|
|
220
321
|
}
|
|
221
322
|
if (sensevoiceChild.status === 'exited' || sensevoiceChild.status === 'failed') {
|
|
222
|
-
sensevoiceError = `sensevoice process exited prematurely with status ${sensevoiceChild.status}`
|
|
323
|
+
sensevoiceError = `sensevoice process exited prematurely with status ${sensevoiceChild.status}${detail ? `: ${detail}` : ''}`
|
|
223
324
|
return false
|
|
224
325
|
}
|
|
225
326
|
if (sensevoiceChild.exitCode !== null && sensevoiceChild.exitCode !== undefined) {
|
|
226
|
-
sensevoiceError = `sensevoice process exited prematurely with code ${sensevoiceChild.exitCode}`
|
|
327
|
+
sensevoiceError = `sensevoice process exited prematurely with code ${sensevoiceChild.exitCode}${detail ? `: ${detail}` : ''}`
|
|
227
328
|
return false
|
|
228
329
|
}
|
|
229
330
|
}
|
|
@@ -232,16 +232,36 @@ export function registerSensevoiceInstaller(ctx, { liveConfig, isAlive, updateCo
|
|
|
232
232
|
}
|
|
233
233
|
|
|
234
234
|
export function buildSensevoiceArgs(cfg, port) {
|
|
235
|
-
|
|
236
|
-
const
|
|
235
|
+
let modelPath = String(cfg?.sensevoiceModel || '')
|
|
236
|
+
const modelDir = modelPath ? path.dirname(modelPath) : getDefaultModelDir()
|
|
237
|
+
const tokensPath = (cfg?.sensevoiceTokens && existsSync(cfg.sensevoiceTokens))
|
|
238
|
+
? cfg.sensevoiceTokens
|
|
239
|
+
: findTokensFile(modelDir)
|
|
237
240
|
const tokensArg = tokensPath ? ` --tokens=${JSON.stringify(tokensPath)}` : ''
|
|
238
241
|
const rawProv = String(cfg?.sensevoiceProvider || '').trim().toLowerCase()
|
|
239
|
-
|
|
242
|
+
let normProv = rawProv === 'rknpu' ? 'rknn' : rawProv
|
|
243
|
+
|
|
244
|
+
if (normProv === 'rknn') {
|
|
245
|
+
const ext = path.extname(modelPath).toLowerCase()
|
|
246
|
+
if (ext === '.onnx') {
|
|
247
|
+
const rknnCandidate = path.join(modelDir, path.basename(modelPath, ext) + '.rknn')
|
|
248
|
+
const defaultRknn = path.join(modelDir, 'model.rknn')
|
|
249
|
+
if (existsSync(rknnCandidate)) {
|
|
250
|
+
modelPath = rknnCandidate
|
|
251
|
+
} else if (existsSync(defaultRknn)) {
|
|
252
|
+
modelPath = defaultRknn
|
|
253
|
+
} else {
|
|
254
|
+
// Guard: ordinary ONNX model cannot be loaded by sherpa-onnx RKNN runtime (#221)
|
|
255
|
+
normProv = 'cpu'
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
240
260
|
const provider = (normProv && normProv !== 'cpu')
|
|
241
261
|
? ` --provider=${normProv}`
|
|
242
262
|
: ''
|
|
243
263
|
const threads = Number(cfg?.sensevoiceThreads) || 4
|
|
244
|
-
return ` --sense-voice-model=${JSON.stringify(
|
|
264
|
+
return ` --sense-voice-model=${JSON.stringify(modelPath)}`
|
|
245
265
|
+ tokensArg
|
|
246
266
|
+ ` --port=${port} --num-threads=${threads}`
|
|
247
267
|
+ provider
|
package/lib/updater.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// Implements canonical DSH plugin-updater specification.
|
|
3
3
|
|
|
4
4
|
import { spawn } from 'node:child_process';
|
|
5
|
-
import { existsSync, readFileSync, unlinkSync,
|
|
5
|
+
import { existsSync, readFileSync, unlinkSync, statSync } from 'node:fs';
|
|
6
6
|
import { readFile } from 'node:fs/promises';
|
|
7
7
|
import { homedir } from 'node:os';
|
|
8
8
|
import { basename, dirname, isAbsolute, resolve } from 'node:path';
|
|
@@ -202,18 +202,21 @@ export function checkProfileLock(profileDir) {
|
|
|
202
202
|
if (isProcessAlive(pid)) {
|
|
203
203
|
return { locked: true, pid };
|
|
204
204
|
}
|
|
205
|
-
//
|
|
206
|
-
//
|
|
205
|
+
// Stale lock: PID is not running.
|
|
206
|
+
// Verify before unlinking: re-check PID to ensure a concurrent live successor
|
|
207
|
+
// was not written during the check (#205).
|
|
207
208
|
try {
|
|
208
|
-
const
|
|
209
|
-
|
|
210
|
-
const
|
|
211
|
-
if (
|
|
212
|
-
|
|
209
|
+
const p1 = readLockPid(lockPath);
|
|
210
|
+
if (p1 !== null && isProcessAlive(p1)) return { locked: true, pid: p1 };
|
|
211
|
+
const p2 = readLockPid(lockPath);
|
|
212
|
+
if (p2 !== null && isProcessAlive(p2)) return { locked: true, pid: p2 };
|
|
213
|
+
if (p1 === pid && p2 === pid) {
|
|
214
|
+
unlinkSync(lockPath);
|
|
213
215
|
return { locked: false, cleanedStale: true };
|
|
214
216
|
}
|
|
215
|
-
|
|
216
|
-
return { locked: true, pid:
|
|
217
|
+
if (p2 !== null && isProcessAlive(p2)) return { locked: true, pid: p2 };
|
|
218
|
+
if (p1 !== null && isProcessAlive(p1)) return { locked: true, pid: p1 };
|
|
219
|
+
return { locked: false };
|
|
217
220
|
} catch {
|
|
218
221
|
if (!existsSync(lockPath)) return { locked: false };
|
|
219
222
|
const currentPid = readLockPid(lockPath);
|
|
@@ -223,7 +226,17 @@ export function checkProfileLock(profileDir) {
|
|
|
223
226
|
return { locked: false };
|
|
224
227
|
}
|
|
225
228
|
}
|
|
226
|
-
|
|
229
|
+
// Lockfile without valid PID: check age (> 10 min)
|
|
230
|
+
try {
|
|
231
|
+
const stat = statSync(lockPath);
|
|
232
|
+
if (Date.now() - stat.mtimeMs > 10 * 60_000) {
|
|
233
|
+
try { unlinkSync(lockPath); } catch {}
|
|
234
|
+
return { locked: false, cleanedStale: true };
|
|
235
|
+
}
|
|
236
|
+
return { locked: true, pid: null };
|
|
237
|
+
} catch {
|
|
238
|
+
return { locked: false };
|
|
239
|
+
}
|
|
227
240
|
}
|
|
228
241
|
|
|
229
242
|
async function installExact(target, packageSpec, options) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@goodandready/dsh-voice",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.20",
|
|
4
4
|
"description": "Voice input for DeepSeek Harness: dictation chunked by pauses and voice messages, each with its own provider fallback chain (Deepgram, Groq, HuggingFace, local whisper.cpp, plus any OpenAI-compatible API of your own).",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|