@goodandready/dsh-voice 0.9.19 → 0.9.21
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 +27 -10
- package/lib/local-daemon.js +96 -37
- package/lib/updater.js +11 -7
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -912,7 +912,12 @@ window.__ModuleLoader__.load({
|
|
|
912
912
|
if (typeof actions.insertText === 'function') {
|
|
913
913
|
try {
|
|
914
914
|
const res = actions.insertText(text, span)
|
|
915
|
-
if (res !== false)
|
|
915
|
+
if (res !== false) {
|
|
916
|
+
if (target && typeof actions.captureInsertion === 'function') {
|
|
917
|
+
try { target.span = actions.captureInsertion() } catch (_) {}
|
|
918
|
+
}
|
|
919
|
+
return true
|
|
920
|
+
}
|
|
916
921
|
return false
|
|
917
922
|
} catch (_) {
|
|
918
923
|
return false
|
|
@@ -936,10 +941,10 @@ window.__ModuleLoader__.load({
|
|
|
936
941
|
}
|
|
937
942
|
|
|
938
943
|
function appendDraft(text, target) {
|
|
939
|
-
if (voice.disposed || !text) return
|
|
944
|
+
if (voice.disposed || !text) return false
|
|
940
945
|
let clean = voice.settings.voiceCommands ? applyVoiceCommands(text) : tidyPhrase(text)
|
|
941
|
-
if (!clean) return
|
|
942
|
-
insertDraftText(clean, target)
|
|
946
|
+
if (!clean) return false
|
|
947
|
+
return insertDraftText(clean, target)
|
|
943
948
|
}
|
|
944
949
|
|
|
945
950
|
function announceVoice(phase) {
|
|
@@ -1322,8 +1327,12 @@ voice._chainsPromise = null
|
|
|
1322
1327
|
}
|
|
1323
1328
|
const delay = Number(voice.settings.sendDelayMs) || 0
|
|
1324
1329
|
if (text && delay > 0 && !voice.holding) {
|
|
1325
|
-
appendDraft(text, target)
|
|
1326
|
-
|
|
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
|
+
}
|
|
1327
1336
|
return
|
|
1328
1337
|
}
|
|
1329
1338
|
if (text) appendDraft(text, target)
|
|
@@ -1486,8 +1495,12 @@ voice._chainsPromise = null
|
|
|
1486
1495
|
voice.browserFinals = null
|
|
1487
1496
|
if (!said) { voice.set({ phase: 'idle' }); return }
|
|
1488
1497
|
if (mode === 'message') {
|
|
1489
|
-
appendDraft(said, target)
|
|
1490
|
-
|
|
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
|
+
}
|
|
1491
1504
|
} else {
|
|
1492
1505
|
voice.set({ phase: 'idle' })
|
|
1493
1506
|
}
|
|
@@ -1551,10 +1564,14 @@ voice._chainsPromise = null
|
|
|
1551
1564
|
return
|
|
1552
1565
|
}
|
|
1553
1566
|
if (action === 'cancel') { cancelCurrent(); return }
|
|
1554
|
-
appendDraft(text, target)
|
|
1567
|
+
const ok = appendDraft(text, target)
|
|
1555
1568
|
if (voice.lastNote) voice.lastNote.text = text
|
|
1556
1569
|
if (mode === 'message') {
|
|
1557
|
-
|
|
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
|
+
}
|
|
1558
1575
|
} else {
|
|
1559
1576
|
voice.set({ phase: 'idle' })
|
|
1560
1577
|
}
|
package/lib/local-daemon.js
CHANGED
|
@@ -68,17 +68,32 @@ export function createLocalDaemons({ live, ctx }) {
|
|
|
68
68
|
child.done.then(async (res) => {
|
|
69
69
|
let code = res && res.code
|
|
70
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
|
+
}
|
|
71
83
|
if (typeof child.result === 'function') {
|
|
72
84
|
try {
|
|
73
85
|
const r = await child.result()
|
|
74
86
|
if (r) {
|
|
75
87
|
if (r.exitCode !== undefined && r.exitCode !== null) code = r.exitCode
|
|
76
|
-
if (r.stderr) detail = String(r.stderr).trim()
|
|
88
|
+
if (r.stderr && !detail) detail = String(r.stderr).trim()
|
|
77
89
|
}
|
|
78
|
-
} catch (
|
|
90
|
+
} catch (err) {
|
|
91
|
+
const msg = err && err.message ? err.message : String(err)
|
|
92
|
+
if (msg && !detail) detail = msg.trim()
|
|
93
|
+
}
|
|
79
94
|
} else if (child.result && typeof child.result === 'object') {
|
|
80
95
|
code = child.result.exitCode ?? code
|
|
81
|
-
if (child.result.stderr) detail = String(child.result.stderr).trim()
|
|
96
|
+
if (child.result.stderr && !detail) detail = String(child.result.stderr).trim()
|
|
82
97
|
}
|
|
83
98
|
if (child.status === 'killed') {
|
|
84
99
|
whisperError = detail ? `whisper server process was killed: ${detail}` : 'whisper server process was killed'
|
|
@@ -106,18 +121,21 @@ export function createLocalDaemons({ live, ctx }) {
|
|
|
106
121
|
}
|
|
107
122
|
if (child) {
|
|
108
123
|
let detail = ''
|
|
109
|
-
if (typeof child.
|
|
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') {
|
|
110
131
|
try {
|
|
111
132
|
const out = child.read()
|
|
112
133
|
if (out && out.stderr) detail = String(out.stderr).trim()
|
|
113
134
|
} catch (_) {}
|
|
114
135
|
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
}
|
|
119
|
-
if (child.status === 'completed') {
|
|
120
|
-
let code = child.exitCode
|
|
136
|
+
let code = child.exitCode
|
|
137
|
+
const isTerminal = (child.status && child.status !== 'running') || (code !== null && code !== undefined)
|
|
138
|
+
if (isTerminal) {
|
|
121
139
|
if (typeof child.result === 'function') {
|
|
122
140
|
try {
|
|
123
141
|
const r = await child.result()
|
|
@@ -125,22 +143,31 @@ export function createLocalDaemons({ live, ctx }) {
|
|
|
125
143
|
if (r.exitCode !== undefined && r.exitCode !== null) code = r.exitCode
|
|
126
144
|
if (r.stderr && !detail) detail = String(r.stderr).trim()
|
|
127
145
|
}
|
|
128
|
-
} catch (
|
|
146
|
+
} catch (err) {
|
|
147
|
+
const msg = err && err.message ? err.message : String(err)
|
|
148
|
+
if (msg && !detail) detail = msg.trim()
|
|
149
|
+
}
|
|
129
150
|
} else if (child.result && typeof child.result === 'object') {
|
|
130
151
|
code = child.result.exitCode ?? code
|
|
131
152
|
if (child.result.stderr && !detail) detail = String(child.result.stderr).trim()
|
|
132
153
|
}
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
154
|
+
if (child.status === 'killed') {
|
|
155
|
+
whisperError = detail ? `whisper server process was killed: ${detail}` : 'whisper server process was killed'
|
|
156
|
+
return false
|
|
157
|
+
}
|
|
158
|
+
if (child.status === 'completed') {
|
|
159
|
+
code = code ?? 0
|
|
160
|
+
whisperError = `whisper server process exited prematurely with code ${code}${detail ? `: ${detail}` : ''}`
|
|
161
|
+
return false
|
|
162
|
+
}
|
|
163
|
+
if (child.status === 'exited' || child.status === 'failed') {
|
|
164
|
+
whisperError = `whisper server process exited prematurely with status ${child.status}${detail ? `: ${detail}` : ''}`
|
|
165
|
+
return false
|
|
166
|
+
}
|
|
167
|
+
if (child.exitCode !== null && child.exitCode !== undefined) {
|
|
168
|
+
whisperError = `whisper server process exited prematurely with code ${child.exitCode}${detail ? `: ${detail}` : ''}`
|
|
169
|
+
return false
|
|
170
|
+
}
|
|
144
171
|
}
|
|
145
172
|
}
|
|
146
173
|
}
|
|
@@ -257,22 +284,54 @@ export function createLocalDaemons({ live, ctx }) {
|
|
|
257
284
|
return true
|
|
258
285
|
}
|
|
259
286
|
if (sensevoiceChild) {
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
287
|
+
let detail = ''
|
|
288
|
+
if (typeof sensevoiceChild.readOutput === 'function') {
|
|
289
|
+
try {
|
|
290
|
+
const out = sensevoiceChild.readOutput()
|
|
291
|
+
if (out && out.stderr) detail = String(out.stderr).trim()
|
|
292
|
+
else if (out && out.stdout) detail = String(out.stdout).trim()
|
|
293
|
+
} catch (_) {}
|
|
294
|
+
} else if (typeof sensevoiceChild.read === 'function') {
|
|
295
|
+
try {
|
|
296
|
+
const out = sensevoiceChild.read()
|
|
297
|
+
if (out && out.stderr) detail = String(out.stderr).trim()
|
|
298
|
+
} catch (_) {}
|
|
272
299
|
}
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
300
|
+
let code = sensevoiceChild.exitCode
|
|
301
|
+
const isTerminal = (sensevoiceChild.status && sensevoiceChild.status !== 'running') || (code !== null && code !== undefined)
|
|
302
|
+
if (isTerminal) {
|
|
303
|
+
if (typeof sensevoiceChild.result === 'function') {
|
|
304
|
+
try {
|
|
305
|
+
const r = await sensevoiceChild.result()
|
|
306
|
+
if (r) {
|
|
307
|
+
if (r.exitCode !== undefined && r.exitCode !== null) code = r.exitCode
|
|
308
|
+
if (r.stderr && !detail) detail = String(r.stderr).trim()
|
|
309
|
+
}
|
|
310
|
+
} catch (err) {
|
|
311
|
+
const msg = err && err.message ? err.message : String(err)
|
|
312
|
+
if (msg && !detail) detail = msg.trim()
|
|
313
|
+
}
|
|
314
|
+
} else if (sensevoiceChild.result && typeof sensevoiceChild.result === 'object') {
|
|
315
|
+
code = sensevoiceChild.result.exitCode ?? code
|
|
316
|
+
if (sensevoiceChild.result.stderr && !detail) detail = String(sensevoiceChild.result.stderr).trim()
|
|
317
|
+
}
|
|
318
|
+
if (sensevoiceChild.status === 'killed') {
|
|
319
|
+
sensevoiceError = detail ? `sensevoice process was killed: ${detail}` : 'sensevoice process was killed'
|
|
320
|
+
return false
|
|
321
|
+
}
|
|
322
|
+
if (sensevoiceChild.status === 'completed') {
|
|
323
|
+
code = code ?? 0
|
|
324
|
+
sensevoiceError = `sensevoice process exited prematurely with code ${code}${detail ? `: ${detail}` : ''}`
|
|
325
|
+
return false
|
|
326
|
+
}
|
|
327
|
+
if (sensevoiceChild.status === 'exited' || sensevoiceChild.status === 'failed') {
|
|
328
|
+
sensevoiceError = `sensevoice process exited prematurely with status ${sensevoiceChild.status}${detail ? `: ${detail}` : ''}`
|
|
329
|
+
return false
|
|
330
|
+
}
|
|
331
|
+
if (sensevoiceChild.exitCode !== null && sensevoiceChild.exitCode !== undefined) {
|
|
332
|
+
sensevoiceError = `sensevoice process exited prematurely with code ${sensevoiceChild.exitCode}${detail ? `: ${detail}` : ''}`
|
|
333
|
+
return false
|
|
334
|
+
}
|
|
276
335
|
}
|
|
277
336
|
}
|
|
278
337
|
}
|
package/lib/updater.js
CHANGED
|
@@ -203,17 +203,21 @@ export function checkProfileLock(profileDir) {
|
|
|
203
203
|
return { locked: true, pid };
|
|
204
204
|
}
|
|
205
205
|
// Stale lock: PID is not running.
|
|
206
|
-
// Verify before unlinking:
|
|
207
|
-
//
|
|
206
|
+
// Verify before unlinking: re-check PID to ensure a concurrent live successor
|
|
207
|
+
// was not written during the check (#205).
|
|
208
208
|
try {
|
|
209
|
-
const
|
|
210
|
-
if (
|
|
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
|
+
const p3 = readLockPid(lockPath);
|
|
214
|
+
if (p3 !== null && isProcessAlive(p3)) return { locked: true, pid: p3 };
|
|
215
|
+
if (p1 === pid && p2 === pid && p3 === pid) {
|
|
211
216
|
unlinkSync(lockPath);
|
|
212
217
|
return { locked: false, cleanedStale: true };
|
|
213
218
|
}
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
}
|
|
219
|
+
const pFinal = readLockPid(lockPath);
|
|
220
|
+
if (pFinal !== null && isProcessAlive(pFinal)) return { locked: true, pid: pFinal };
|
|
217
221
|
return { locked: false };
|
|
218
222
|
} catch {
|
|
219
223
|
if (!existsSync(lockPath)) return { locked: false };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@goodandready/dsh-voice",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.21",
|
|
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",
|