@goodandready/dsh-voice 0.9.20 → 0.9.22

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.
@@ -134,37 +134,40 @@ export function createLocalDaemons({ live, ctx }) {
134
134
  } catch (_) {}
135
135
  }
136
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()
137
+ const isTerminal = (child.status && child.status !== 'running') || (code !== null && code !== undefined)
138
+ if (isTerminal) {
139
+ if (typeof child.result === 'function') {
140
+ try {
141
+ const r = await child.result()
142
+ if (r) {
143
+ if (r.exitCode !== undefined && r.exitCode !== null) code = r.exitCode
144
+ if (r.stderr && !detail) detail = String(r.stderr).trim()
145
+ }
146
+ } catch (err) {
147
+ const msg = err && err.message ? err.message : String(err)
148
+ if (msg && !detail) detail = msg.trim()
143
149
  }
144
- } catch (err) {
145
- const msg = err && err.message ? err.message : String(err)
146
- if (msg && !detail) detail = msg.trim()
150
+ } else if (child.result && typeof child.result === 'object') {
151
+ code = child.result.exitCode ?? code
152
+ if (child.result.stderr && !detail) detail = String(child.result.stderr).trim()
153
+ }
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
147
170
  }
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
- }
152
- if (child.status === 'killed') {
153
- whisperError = detail ? `whisper server process was killed: ${detail}` : 'whisper server process was killed'
154
- return false
155
- }
156
- if (child.status === 'completed') {
157
- code = code ?? 0
158
- whisperError = `whisper server process exited prematurely with code ${code}${detail ? `: ${detail}` : ''}`
159
- return false
160
- }
161
- if (child.status === 'exited' || child.status === 'failed') {
162
- whisperError = `whisper server process exited prematurely with status ${child.status}${detail ? `: ${detail}` : ''}`
163
- return false
164
- }
165
- if (child.exitCode !== null && child.exitCode !== undefined) {
166
- whisperError = `whisper server process exited prematurely with code ${child.exitCode}${detail ? `: ${detail}` : ''}`
167
- return false
168
171
  }
169
172
  }
170
173
  }
@@ -295,37 +298,40 @@ export function createLocalDaemons({ live, ctx }) {
295
298
  } catch (_) {}
296
299
  }
297
300
  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()
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()
304
313
  }
305
- } catch (err) {
306
- const msg = err && err.message ? err.message : String(err)
307
- if (msg && !detail) detail = msg.trim()
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
308
334
  }
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
- }
313
- if (sensevoiceChild.status === 'killed') {
314
- sensevoiceError = detail ? `sensevoice process was killed: ${detail}` : 'sensevoice process was killed'
315
- return false
316
- }
317
- if (sensevoiceChild.status === 'completed') {
318
- code = code ?? 0
319
- sensevoiceError = `sensevoice process exited prematurely with code ${code}${detail ? `: ${detail}` : ''}`
320
- return false
321
- }
322
- if (sensevoiceChild.status === 'exited' || sensevoiceChild.status === 'failed') {
323
- sensevoiceError = `sensevoice process exited prematurely with status ${sensevoiceChild.status}${detail ? `: ${detail}` : ''}`
324
- return false
325
- }
326
- if (sensevoiceChild.exitCode !== null && sensevoiceChild.exitCode !== undefined) {
327
- sensevoiceError = `sensevoice process exited prematurely with code ${sensevoiceChild.exitCode}${detail ? `: ${detail}` : ''}`
328
- return false
329
335
  }
330
336
  }
331
337
  }
package/lib/updater.js CHANGED
@@ -197,25 +197,34 @@ export function isProcessAlive(pid) {
197
197
  export function checkProfileLock(profileDir) {
198
198
  const lockPath = resolve(profileDir, 'package.json.lock');
199
199
  if (!existsSync(lockPath)) return { locked: false };
200
+ let initialStat;
201
+ try { initialStat = statSync(lockPath); } catch { return { locked: false }; }
200
202
  const pid = readLockPid(lockPath);
201
203
  if (pid !== null) {
202
204
  if (isProcessAlive(pid)) {
203
205
  return { locked: true, pid };
204
206
  }
205
207
  // Stale lock: PID is not running.
206
- // Verify before unlinking: re-check PID to ensure a concurrent live successor
208
+ // Verify before unlinking: re-check PID and stat to ensure a concurrent live successor
207
209
  // was not written during the check (#205).
208
210
  try {
209
211
  const p1 = readLockPid(lockPath);
210
212
  if (p1 !== null && isProcessAlive(p1)) return { locked: true, pid: p1 };
211
213
  const p2 = readLockPid(lockPath);
212
214
  if (p2 !== null && isProcessAlive(p2)) return { locked: true, pid: p2 };
213
- if (p1 === pid && p2 === pid) {
215
+ const p3 = readLockPid(lockPath);
216
+ if (p3 !== null && isProcessAlive(p3)) return { locked: true, pid: p3 };
217
+ const currentStat = statSync(lockPath);
218
+ if (currentStat.mtimeMs !== initialStat.mtimeMs || currentStat.size !== initialStat.size) {
219
+ const live = readLockPid(lockPath);
220
+ if (live !== null && isProcessAlive(live)) return { locked: true, pid: live };
221
+ }
222
+ if (p1 === pid && p2 === pid && p3 === pid) {
214
223
  unlinkSync(lockPath);
215
224
  return { locked: false, cleanedStale: true };
216
225
  }
217
- if (p2 !== null && isProcessAlive(p2)) return { locked: true, pid: p2 };
218
- if (p1 !== null && isProcessAlive(p1)) return { locked: true, pid: p1 };
226
+ const pFinal = readLockPid(lockPath);
227
+ if (pFinal !== null && isProcessAlive(pFinal)) return { locked: true, pid: pFinal };
219
228
  return { locked: false };
220
229
  } catch {
221
230
  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.20",
3
+ "version": "0.9.22",
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",