ugly-app 0.1.992 → 0.1.994
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/dist/cli/version.d.ts +1 -1
- package/dist/cli/version.js +1 -1
- package/dist/client/audio/useTTS.d.ts.map +1 -1
- package/dist/client/audio/useTTS.js +44 -0
- package/dist/client/audio/useTTS.js.map +1 -1
- package/dist/native/bundledTools.d.ts +4 -0
- package/dist/native/bundledTools.d.ts.map +1 -1
- package/dist/native/bundledTools.js +9 -0
- package/dist/native/bundledTools.js.map +1 -1
- package/dist/native/facades/net.d.ts.map +1 -1
- package/dist/native/facades/net.js +65 -4
- package/dist/native/facades/net.js.map +1 -1
- package/dist/native/facades/process.d.ts.map +1 -1
- package/dist/native/facades/process.js +61 -3
- package/dist/native/facades/process.js.map +1 -1
- package/package.json +1 -1
- package/src/cli/version.ts +1 -1
- package/src/client/audio/useTTS.ts +71 -0
- package/src/native/bundledTools.test.ts +11 -0
- package/src/native/bundledTools.ts +9 -0
- package/src/native/facades/net.test.ts +191 -0
- package/src/native/facades/net.ts +92 -29
- package/src/native/facades/process.test.ts +149 -0
- package/src/native/facades/process.ts +86 -27
package/dist/cli/version.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const CLI_VERSION = "0.1.
|
|
1
|
+
export declare const CLI_VERSION = "0.1.994";
|
|
2
2
|
//# sourceMappingURL=version.d.ts.map
|
package/dist/cli/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useTTS.d.ts","sourceRoot":"","sources":["../../../src/client/audio/useTTS.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAa,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAEtE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAGzD,sFAAsF;AACtF,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,aAAa,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,UAAU,UAAU;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;OAGG;IACH,QAAQ,CAAC,EAAE,CAAC,CAAC,EAAE,cAAc,KAAK,IAAI,CAAC;IACvC;;;;OAIG;IACH,eAAe,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;CAC/C;
|
|
1
|
+
{"version":3,"file":"useTTS.d.ts","sourceRoot":"","sources":["../../../src/client/audio/useTTS.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAa,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAEtE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAGzD,sFAAsF;AACtF,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,aAAa,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,UAAU,UAAU;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;OAGG;IACH,QAAQ,CAAC,EAAE,CAAC,CAAC,EAAE,cAAc,KAAK,IAAI,CAAC;IACvC;;;;OAIG;IACH,eAAe,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;CAC/C;AAmCD,wBAAgB,MAAM,CAAC,MAAM,EAAE,aAAa;iBAoE3B,MAAM,YAAY,UAAU;;;;;;;;;;;;;;;;;EAiQ5C"}
|
|
@@ -8,6 +8,26 @@ let streamCounter = 0;
|
|
|
8
8
|
// silently or the server hung. Fail loudly instead of leaving `playing` true
|
|
9
9
|
// forever with the rAF loop spinning and the socket held.
|
|
10
10
|
const WATCHDOG_MS = 30_000;
|
|
11
|
+
/**
|
|
12
|
+
* Report a failed or silent read-aloud on the framework's UX audit channel.
|
|
13
|
+
*
|
|
14
|
+
* ⚠️ THIS EXISTS BECAUSE "TTS does not play" WAS UNDIAGNOSABLE. The hook set its
|
|
15
|
+
* local `error` state and stopped there, so a failure was visible only to
|
|
16
|
+
* whatever the app chose to render — and apps mostly render nothing, or abort
|
|
17
|
+
* quietly. Production reports therefore said "TTS does not play" with no page,
|
|
18
|
+
* no route and no reason, and answering them meant asking the user which screen
|
|
19
|
+
* they were on. `[ugly.ux] …` is the same channel the image/keyboard/safe-area
|
|
20
|
+
* audits use: the Logger ships it to the project's error log with the page
|
|
21
|
+
* attached, so every app gets this for free and none has to build its own.
|
|
22
|
+
*/
|
|
23
|
+
function reportAudioFailure(reason, detail, extra) {
|
|
24
|
+
const page = typeof window === 'undefined' ? '' : window.location.pathname;
|
|
25
|
+
console.error(`[ugly.ux] audio: read-aloud ${detail}`, {
|
|
26
|
+
reason,
|
|
27
|
+
page,
|
|
28
|
+
...extra,
|
|
29
|
+
});
|
|
30
|
+
}
|
|
11
31
|
export function useTTS(socket) {
|
|
12
32
|
const [playing, setPlaying] = useState(false);
|
|
13
33
|
const [currentWord, setCurrentWord] = useState('');
|
|
@@ -116,6 +136,17 @@ export function useTTS(socket) {
|
|
|
116
136
|
// Flush any remaining caption indices so the text fully reveals.
|
|
117
137
|
while (wi < words.length)
|
|
118
138
|
options?.onSubtitleIndex?.(words[wi++].charEnd);
|
|
139
|
+
// Audio was delivered and the stream is over, but the worklet's own
|
|
140
|
+
// sample clock never moved: whatever the cause (a context that says
|
|
141
|
+
// "running" and isn't, a worklet that failed to load, output routed
|
|
142
|
+
// nowhere), the user heard nothing. Belt to the first-chunk check's
|
|
143
|
+
// braces — that one names the common cause, this one catches the rest.
|
|
144
|
+
if (statsRef.current.chunks > 0 && player.getPlaybackTimeMs() === 0)
|
|
145
|
+
reportAudioFailure('no-playback', 'delivered audio but the playback clock never advanced — the user heard nothing', {
|
|
146
|
+
voice: options?.voice,
|
|
147
|
+
ctx: player.contextState(),
|
|
148
|
+
...statsRef.current,
|
|
149
|
+
});
|
|
119
150
|
setPlaying(false);
|
|
120
151
|
setCurrentWord('');
|
|
121
152
|
cleanupListeners();
|
|
@@ -130,6 +161,7 @@ export function useTTS(socket) {
|
|
|
130
161
|
if (streamIdRef.current !== streamId)
|
|
131
162
|
return;
|
|
132
163
|
setError('TTS stream timed out — no response from server');
|
|
164
|
+
reportAudioFailure('stream-timeout', `received no audio for ${WATCHDOG_MS / 1000}s and gave up`, { voice: options?.voice, ...statsRef.current });
|
|
133
165
|
finish();
|
|
134
166
|
}, WATCHDOG_MS);
|
|
135
167
|
};
|
|
@@ -179,6 +211,7 @@ export function useTTS(socket) {
|
|
|
179
211
|
if (streamIdRef.current !== streamId || serverDone)
|
|
180
212
|
return;
|
|
181
213
|
setError('connection lost');
|
|
214
|
+
reportAudioFailure('socket-closed', 'lost its connection to ugly.bot mid-stream', { voice: options?.voice, ...statsRef.current });
|
|
182
215
|
finish();
|
|
183
216
|
});
|
|
184
217
|
unsubsRef.current.push(socket.on('tts:chunk', (data) => {
|
|
@@ -186,6 +219,16 @@ export function useTTS(socket) {
|
|
|
186
219
|
return;
|
|
187
220
|
armWatchdog();
|
|
188
221
|
statsRef.current.chunks++;
|
|
222
|
+
// THE SILENT FAILURE, caught at the one moment it is unambiguous.
|
|
223
|
+
// Audio has arrived, so the server and the socket are fine — but a
|
|
224
|
+
// context the browser never unlocked will not play a sample of it,
|
|
225
|
+
// and nothing downstream will ever raise an error. This is what
|
|
226
|
+
// "TTS does not play" has meant every time so far, and it was
|
|
227
|
+
// invisible: `playing` stays true, no error fires, the app shows a
|
|
228
|
+
// spinner or nothing. Report it on the first chunk, once per stream.
|
|
229
|
+
if (statsRef.current.chunks === 1 &&
|
|
230
|
+
player.contextState() !== 'running')
|
|
231
|
+
reportAudioFailure('context-not-running', `arrived but the AudioContext is "${player.contextState()}" — the browser never unlocked it, so nothing will be heard. Call warmup() inside the tap that starts playback.`, { voice: options?.voice });
|
|
189
232
|
void player.feedChunk(data.audio);
|
|
190
233
|
if (data.visemes) {
|
|
191
234
|
for (const v of data.visemes) {
|
|
@@ -225,6 +268,7 @@ export function useTTS(socket) {
|
|
|
225
268
|
if (data.streamId !== streamId)
|
|
226
269
|
return;
|
|
227
270
|
setError(data.message);
|
|
271
|
+
reportAudioFailure('server-error', `was refused by ugly.bot — ${data.message}`, { voice: options?.voice, ...statsRef.current });
|
|
228
272
|
finish();
|
|
229
273
|
}), ...(unsubClose ? [unsubClose] : []));
|
|
230
274
|
rafRef.current = requestAnimationFrame(tick);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useTTS.js","sourceRoot":"","sources":["../../../src/client/audio/useTTS.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"useTTS.js","sourceRoot":"","sources":["../../../src/client/audio/useTTS.ts"],"names":[],"mappings":"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"}
|
|
@@ -94,6 +94,10 @@ export declare const BUNDLED_TOOLS: readonly [{
|
|
|
94
94
|
readonly name: "yt-dlp";
|
|
95
95
|
readonly category: "media";
|
|
96
96
|
readonly description: "yt-dlp — download media from the web.";
|
|
97
|
+
}, {
|
|
98
|
+
readonly name: "whisper-cli";
|
|
99
|
+
readonly category: "media";
|
|
100
|
+
readonly description: "whisper.cpp — offline speech-to-text transcription.";
|
|
97
101
|
}, {
|
|
98
102
|
readonly name: "pandoc";
|
|
99
103
|
readonly category: "doc";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bundledTools.d.ts","sourceRoot":"","sources":["../../src/native/bundledTools.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,WAAW;IAC1B,6DAA6D;IAC7D,IAAI,EAAE,MAAM,CAAC;IACb,mFAAmF;IACnF,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,WAAW,GAAG,OAAO,GAAG,MAAM,GAAG,KAAK,GAAG,KAAK,CAAC;IACzD,4EAA4E;IAC5E,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,4DAA4D;IAC5D,WAAW,EAAE,MAAM,CAAC;CACrB;AAKD,eAAO,MAAM,aAAa
|
|
1
|
+
{"version":3,"file":"bundledTools.d.ts","sourceRoot":"","sources":["../../src/native/bundledTools.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,WAAW;IAC1B,6DAA6D;IAC7D,IAAI,EAAE,MAAM,CAAC;IACb,mFAAmF;IACnF,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,WAAW,GAAG,OAAO,GAAG,MAAM,GAAG,KAAK,GAAG,KAAK,CAAC;IACzD,4EAA4E;IAC5E,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,4DAA4D;IAC5D,WAAW,EAAE,MAAM,CAAC;CACrB;AAKD,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA2IiB,CAAC;AAE5C;;;;;;GAMG;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC;AAMrE,2DAA2D;AAC3D,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS,CAEjE"}
|
|
@@ -114,6 +114,15 @@ export const BUNDLED_TOOLS = [
|
|
|
114
114
|
category: 'media',
|
|
115
115
|
description: 'yt-dlp — download media from the web.',
|
|
116
116
|
},
|
|
117
|
+
// whisper.cpp's Homebrew formula is `whisper-cpp`, but its executable —
|
|
118
|
+
// and what every consumer actually spawns — is `whisper-cli`. `name` is
|
|
119
|
+
// the request/grant key AND the default spawn name (what `checkProcess`
|
|
120
|
+
// matches against), so it must be `whisper-cli`, not the formula name.
|
|
121
|
+
{
|
|
122
|
+
name: 'whisper-cli',
|
|
123
|
+
category: 'media',
|
|
124
|
+
description: 'whisper.cpp — offline speech-to-text transcription.',
|
|
125
|
+
},
|
|
117
126
|
// doc
|
|
118
127
|
{
|
|
119
128
|
name: 'pandoc',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bundledTools.js","sourceRoot":"","sources":["../../src/native/bundledTools.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"bundledTools.js","sourceRoot":"","sources":["../../src/native/bundledTools.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"net.d.ts","sourceRoot":"","sources":["../../../src/native/facades/net.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,UAAU;IACzB,SAAS,CAAC,EAAE,EAAE,MAAM,IAAI,GAAG,IAAI,CAAC;IAChC,MAAM,CAAC,EAAE,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,GAAG,IAAI,CAAC;IAC9C,OAAO,CAAC,EAAE,EAAE,MAAM,IAAI,GAAG,IAAI,CAAC;IAC9B,OAAO,CAAC,EAAE,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI,CAAC;IACzC,KAAK,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,GAAG,IAAI,CAAC;IACvC,GAAG,IAAI,IAAI,CAAC;IACZ,KAAK,IAAI,IAAI,CAAC;CACf;AACD,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,UAAU,KAAK,IAAI,GAAG,IAAI,CAAC;IAChD,KAAK,IAAI,IAAI,CAAC;CACf;AAED,MAAM,WAAW,SAAS;IACxB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,UAAU,CAAC;IAChD,MAAM,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;CAC5C;AAED,KAAK,MAAM,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;AACtE,KAAK,SAAS,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,KAAK,MAAM,IAAI,CAAC;AAiE5E;;;;GAIG;AACH,wBAAgB,eAAe,CAC7B,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,SAAS,EACpB,MAAM,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAC1B,SAAS,
|
|
1
|
+
{"version":3,"file":"net.d.ts","sourceRoot":"","sources":["../../../src/native/facades/net.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,UAAU;IACzB,SAAS,CAAC,EAAE,EAAE,MAAM,IAAI,GAAG,IAAI,CAAC;IAChC,MAAM,CAAC,EAAE,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,GAAG,IAAI,CAAC;IAC9C,OAAO,CAAC,EAAE,EAAE,MAAM,IAAI,GAAG,IAAI,CAAC;IAC9B,OAAO,CAAC,EAAE,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI,CAAC;IACzC,KAAK,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,GAAG,IAAI,CAAC;IACvC,GAAG,IAAI,IAAI,CAAC;IACZ,KAAK,IAAI,IAAI,CAAC;CACf;AACD,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,UAAU,KAAK,IAAI,GAAG,IAAI,CAAC;IAChD,KAAK,IAAI,IAAI,CAAC;CACf;AAED,MAAM,WAAW,SAAS;IACxB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,UAAU,CAAC;IAChD,MAAM,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;CAC5C;AAED,KAAK,MAAM,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;AACtE,KAAK,SAAS,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,KAAK,MAAM,IAAI,CAAC;AAiE5E;;;;GAIG;AACH,wBAAgB,eAAe,CAC7B,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,SAAS,EACpB,MAAM,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAC1B,SAAS,CAuIX"}
|
|
@@ -54,7 +54,13 @@ function buildSocket(invoke, subscribe, id, alreadyConnected) {
|
|
|
54
54
|
export function createNetFacade(invoke, subscribe, ensure) {
|
|
55
55
|
return {
|
|
56
56
|
connect(host, port) {
|
|
57
|
-
|
|
57
|
+
// Fire the permission check; the host daemon also enforces it inside
|
|
58
|
+
// `invoke('net.connect', …)` below, so a rejection here is redundant
|
|
59
|
+
// with (and always followed by) that call's own rejection — which IS
|
|
60
|
+
// routed to `onError` below. Swallow it here purely to avoid an
|
|
61
|
+
// unhandled-rejection warning; don't double-report. (Mirrors the
|
|
62
|
+
// `process` facade's `spawn()` — see the comment there for why.)
|
|
63
|
+
void ensure().catch(() => { });
|
|
58
64
|
const cbs = {
|
|
59
65
|
connect: [],
|
|
60
66
|
data: [],
|
|
@@ -62,7 +68,14 @@ export function createNetFacade(invoke, subscribe, ensure) {
|
|
|
62
68
|
error: [],
|
|
63
69
|
};
|
|
64
70
|
const unsubs = [];
|
|
65
|
-
|
|
71
|
+
// Set once the connect invoke rejects (e.g. the permission gate denies
|
|
72
|
+
// `net`). Lets `onError` replay the failure to a callback registered
|
|
73
|
+
// AFTER the rejection already happened, without double-delivering to
|
|
74
|
+
// one registered before — see the `onError` implementation below for
|
|
75
|
+
// exactly how, and why the set-before-forEach ordering here matters.
|
|
76
|
+
let connectFailure;
|
|
77
|
+
const ready = invoke('net.connect', { host, port })
|
|
78
|
+
.then((r) => {
|
|
66
79
|
const id = r.id;
|
|
67
80
|
unsubs.push(subscribe(`net.connect:${id}`, () => cbs.connect.forEach((cb) => cb())));
|
|
68
81
|
unsubs.push(subscribe(`net.data:${id}`, (d) => cbs.data.forEach((cb) => cb(binStrToBytes(d.data)))));
|
|
@@ -72,20 +85,68 @@ export function createNetFacade(invoke, subscribe, ensure) {
|
|
|
72
85
|
unsubs.forEach((u) => u());
|
|
73
86
|
}));
|
|
74
87
|
return id;
|
|
88
|
+
})
|
|
89
|
+
.catch((e) => {
|
|
90
|
+
// The socket itself never connected — e.g. the permission gate
|
|
91
|
+
// denied `net`. That's an `error`, never a `close`: no socket ever
|
|
92
|
+
// opened, so there is nothing to report as closed, and a caller
|
|
93
|
+
// that treated a denial as "closed" would mask the real failure.
|
|
94
|
+
// Callbacks already registered get notified now; `onError` below
|
|
95
|
+
// replays this to any callback registered later, exactly once each.
|
|
96
|
+
connectFailure = e instanceof Error ? e.message : String(e);
|
|
97
|
+
cbs.error.forEach((cb) => cb(connectFailure));
|
|
98
|
+
throw e; // keep `ready` rejected so `withId` below stays a no-op
|
|
75
99
|
});
|
|
100
|
+
// `ready` must stay rejected (so every `withId` call below is a no-op),
|
|
101
|
+
// but that leaves it a dangling rejected promise whenever a caller
|
|
102
|
+
// never invokes write/end/close — which Node/browsers flag as an
|
|
103
|
+
// unhandled rejection even though the failure was already delivered
|
|
104
|
+
// via `onError` above. Attach a standing no-op handler to silence
|
|
105
|
+
// that; it doesn't consume `ready`'s rejection for the `withId` chain
|
|
106
|
+
// below, since each `.then()/.catch()` call derives its own
|
|
107
|
+
// independent promise.
|
|
108
|
+
ready.catch(() => { });
|
|
76
109
|
const withId = (fn) => void ready.then(fn).catch(() => { });
|
|
77
110
|
return {
|
|
78
111
|
onConnect: (cb) => cbs.connect.push(cb),
|
|
79
112
|
onData: (cb) => cbs.data.push(cb),
|
|
80
113
|
onClose: (cb) => cbs.close.push(cb),
|
|
81
|
-
onError: (cb) =>
|
|
114
|
+
onError: (cb) => {
|
|
115
|
+
cbs.error.push(cb);
|
|
116
|
+
// Late subscriber: the connect already failed before this
|
|
117
|
+
// callback registered, so it missed the `.catch` above's
|
|
118
|
+
// `forEach` (that `forEach` only visits entries `cbs.error` held
|
|
119
|
+
// when it started iterating — a push during/after it is never
|
|
120
|
+
// visited). Replay the failure here instead. This does NOT
|
|
121
|
+
// double-deliver to a callback that was already notified:
|
|
122
|
+
// `connectFailure` is set BEFORE that `forEach` runs — so by the
|
|
123
|
+
// time any `onError` call happens, either (a) the failure hasn't
|
|
124
|
+
// happened yet and this `if` is skipped, or (b) it has, and every
|
|
125
|
+
// callback in `cbs.error` before this `push()` was already
|
|
126
|
+
// invoked by that `forEach`, so only the newly-pushed one still
|
|
127
|
+
// needs it. That ordering (set-before-forEach) is load-bearing —
|
|
128
|
+
// swap it and a callback registered while inside another
|
|
129
|
+
// `onError` callback is silently dropped instead of replayed.
|
|
130
|
+
if (connectFailure !== undefined)
|
|
131
|
+
cb(connectFailure);
|
|
132
|
+
},
|
|
133
|
+
// write/end/close after a failed connect: `withId` never calls `fn`
|
|
134
|
+
// because `ready` stays rejected, so these are silent no-ops.
|
|
135
|
+
// That's intentional — the socket never existed, there's nothing to
|
|
136
|
+
// write/end/close, and the caller already got the failure via
|
|
137
|
+
// `onError`; a second error here would just duplicate that signal.
|
|
82
138
|
write: (data) => withId((id) => void invoke('net.write', { id, data: bytesToBinStr(data) })),
|
|
83
139
|
end: () => withId((id) => void invoke('net.end', { id })),
|
|
84
140
|
close: () => withId((id) => void invoke('net.close', { id })),
|
|
85
141
|
};
|
|
86
142
|
},
|
|
87
143
|
async listen(port) {
|
|
88
|
-
|
|
144
|
+
// Advisory only, like `connect()` above: `invoke('net.listen', …)`
|
|
145
|
+
// just below is awaited directly, so its own rejection already
|
|
146
|
+
// propagates to the caller as a rejected `listen()` promise — no
|
|
147
|
+
// buffering needed here. Swallow this one purely to avoid an
|
|
148
|
+
// unhandled-rejection warning from the redundant advisory check.
|
|
149
|
+
void ensure().catch(() => { });
|
|
89
150
|
const r = (await invoke('net.listen', { port }));
|
|
90
151
|
const connCbs = [];
|
|
91
152
|
subscribe(`net.connection:${r.id}`, (d) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"net.js","sourceRoot":"","sources":["../../../src/native/facades/net.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"net.js","sourceRoot":"","sources":["../../../src/native/facades/net.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"process.d.ts","sourceRoot":"","sources":["../../../src/native/facades/process.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAEhD,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,EAAE,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI,CAAC;IAC5C,QAAQ,CAAC,EAAE,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI,CAAC;IAC5C,MAAM,CAAC,EAAE,EAAE,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,GAAG,IAAI,CAAC;IAChD,OAAO,CAAC,EAAE,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI,CAAC;IACzC,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,UAAU,IAAI,IAAI,CAAC;IACnB,IAAI,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,IAAI,CAAC,EAAE,SAAS,GAAG,WAAW,CAAC;CACnE;AAED;;;mFAGmF;AACnF,KAAK,MAAM,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;AACtE,KAAK,SAAS,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,KAAK,MAAM,IAAI,CAAC;AAE5E;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,SAAS,EACpB,MAAM,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAC1B,aAAa,
|
|
1
|
+
{"version":3,"file":"process.d.ts","sourceRoot":"","sources":["../../../src/native/facades/process.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAEhD,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,EAAE,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI,CAAC;IAC5C,QAAQ,CAAC,EAAE,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI,CAAC;IAC5C,MAAM,CAAC,EAAE,EAAE,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,GAAG,IAAI,CAAC;IAChD,OAAO,CAAC,EAAE,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI,CAAC;IACzC,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,UAAU,IAAI,IAAI,CAAC;IACnB,IAAI,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,IAAI,CAAC,EAAE,SAAS,GAAG,WAAW,CAAC;CACnE;AAED;;;mFAGmF;AACnF,KAAK,MAAM,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;AACtE,KAAK,SAAS,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,KAAK,MAAM,IAAI,CAAC;AAE5E;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,SAAS,EACpB,MAAM,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAC1B,aAAa,CAmHf"}
|
|
@@ -9,7 +9,12 @@
|
|
|
9
9
|
export function createProcessFacade(invoke, subscribe, ensure) {
|
|
10
10
|
return {
|
|
11
11
|
spawn(cmd, args, opts) {
|
|
12
|
-
|
|
12
|
+
// Fire the permission check; the host daemon also enforces it inside
|
|
13
|
+
// `invoke('process.spawn', …)` below, so a rejection here is redundant
|
|
14
|
+
// with (and always followed by) that call's own rejection — which IS
|
|
15
|
+
// routed to `onError` below. Swallow it here purely to avoid an
|
|
16
|
+
// unhandled-rejection warning; don't double-report.
|
|
17
|
+
void ensure().catch(() => { });
|
|
13
18
|
const cbs = {
|
|
14
19
|
stdout: [],
|
|
15
20
|
stderr: [],
|
|
@@ -17,7 +22,15 @@ export function createProcessFacade(invoke, subscribe, ensure) {
|
|
|
17
22
|
error: [],
|
|
18
23
|
};
|
|
19
24
|
const unsubs = [];
|
|
20
|
-
|
|
25
|
+
// Set once the spawn invoke rejects (e.g. the permission gate denies
|
|
26
|
+
// the binary). Lets `onError` replay the failure to a callback that
|
|
27
|
+
// registers AFTER the rejection already happened — callers may call
|
|
28
|
+
// `spawn()` and register `onError` in the same synchronous tick, but
|
|
29
|
+
// nothing guarantees that ordering, so a purely "fire once at rejection
|
|
30
|
+
// time" callback would silently drop late subscribers.
|
|
31
|
+
let spawnFailure;
|
|
32
|
+
const ready = invoke('process.spawn', { cmd, args, opts })
|
|
33
|
+
.then((r) => {
|
|
21
34
|
const id = r.id;
|
|
22
35
|
unsubs.push(subscribe(`process.stdout:${id}`, (d) => cbs.stdout.forEach((cb) => cb(d.chunk))));
|
|
23
36
|
unsubs.push(subscribe(`process.stderr:${id}`, (d) => cbs.stderr.forEach((cb) => cb(d.chunk))));
|
|
@@ -27,13 +40,58 @@ export function createProcessFacade(invoke, subscribe, ensure) {
|
|
|
27
40
|
unsubs.forEach((u) => u());
|
|
28
41
|
}));
|
|
29
42
|
return id;
|
|
43
|
+
})
|
|
44
|
+
.catch((e) => {
|
|
45
|
+
// The spawn itself never happened — e.g. the permission gate
|
|
46
|
+
// denied the binary (`not a bundled tool` / `tool not granted`).
|
|
47
|
+
// That's an `error`, never an `exit`: no process ever started, so
|
|
48
|
+
// there is no exit code to report, and a caller that treated a
|
|
49
|
+
// denial as "exited" could log a bogus code. Callbacks already
|
|
50
|
+
// registered get notified now; `onError` below replays this to
|
|
51
|
+
// any callback registered later, exactly once each.
|
|
52
|
+
spawnFailure = e instanceof Error ? e.message : String(e);
|
|
53
|
+
cbs.error.forEach((cb) => cb(spawnFailure));
|
|
54
|
+
throw e; // keep `ready` rejected so `withId` below stays a no-op
|
|
30
55
|
});
|
|
56
|
+
// `ready` must stay rejected (so every `withId` call below is a no-op),
|
|
57
|
+
// but that leaves it a dangling rejected promise whenever a caller
|
|
58
|
+
// never invokes write/kill/closeStdin — which Node/browsers flag as an
|
|
59
|
+
// unhandled rejection even though the failure was already delivered via
|
|
60
|
+
// `onError` above. Attach a standing no-op handler to silence that;
|
|
61
|
+
// it doesn't consume `ready`'s rejection for the `withId` chain below,
|
|
62
|
+
// since each `.then()/.catch()` call derives its own independent promise.
|
|
63
|
+
ready.catch(() => { });
|
|
31
64
|
const withId = (fn) => void ready.then(fn).catch(() => { });
|
|
32
65
|
return {
|
|
33
66
|
onStdout: (cb) => cbs.stdout.push(cb),
|
|
34
67
|
onStderr: (cb) => cbs.stderr.push(cb),
|
|
35
68
|
onExit: (cb) => cbs.exit.push(cb),
|
|
36
|
-
onError: (cb) =>
|
|
69
|
+
onError: (cb) => {
|
|
70
|
+
cbs.error.push(cb);
|
|
71
|
+
// Late subscriber: the spawn already failed before this callback
|
|
72
|
+
// registered, so it missed the `.catch` above's `forEach` (that
|
|
73
|
+
// `forEach` only visits entries `cbs.error` held at the time it
|
|
74
|
+
// started iterating — a push during/after it, e.g. from another
|
|
75
|
+
// `onError` callback, is never visited). Replay the failure here
|
|
76
|
+
// instead. This does NOT double-deliver to a callback that was
|
|
77
|
+
// already notified: `spawnFailure` is set at line 98, BEFORE the
|
|
78
|
+
// `forEach` at line 99 runs — so by the time any `onError` call
|
|
79
|
+
// happens, either (a) the failure hasn't happened yet and this
|
|
80
|
+
// `if` is skipped, or (b) it has, and every callback in
|
|
81
|
+
// `cbs.error` before this `push()` was already invoked by that
|
|
82
|
+
// `forEach`, so only the newly-pushed one still needs it. That
|
|
83
|
+
// ordering (set-before-forEach) is load-bearing — swap it and a
|
|
84
|
+
// callback registered while inside another `onError` callback is
|
|
85
|
+
// silently dropped instead of replayed.
|
|
86
|
+
if (spawnFailure !== undefined)
|
|
87
|
+
cb(spawnFailure);
|
|
88
|
+
},
|
|
89
|
+
// write/kill/closeStdin after a failed spawn: `withId` never calls
|
|
90
|
+
// `fn` because `ready` stays rejected, so these are silent no-ops.
|
|
91
|
+
// That's intentional, not an oversight — the process never existed,
|
|
92
|
+
// there's nothing to kill/write/close, and the caller already got
|
|
93
|
+
// the failure via `onError`; surfacing a second error here would
|
|
94
|
+
// just be a duplicate signal for the same event.
|
|
37
95
|
write: (data) => withId((id) => void invoke('process.write', { id, data })),
|
|
38
96
|
closeStdin: () => withId((id) => void invoke('process.closeStdin', { id })),
|
|
39
97
|
kill: (signal) => withId((id) => void invoke('process.kill', { id, signal })),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"process.js","sourceRoot":"","sources":["../../../src/native/facades/process.ts"],"names":[],"mappings":"AAuBA;;;;;;;GAOG;AACH,MAAM,UAAU,mBAAmB,CACjC,MAAc,EACd,SAAoB,EACpB,MAA2B;IAE3B,OAAO;QACL,KAAK,CAAC,GAAG,EAAE,IAAI,EAAE,IAAI;YACnB,KAAK,MAAM,EAAE,CAAC,CAAC,
|
|
1
|
+
{"version":3,"file":"process.js","sourceRoot":"","sources":["../../../src/native/facades/process.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ugly-app",
|
|
3
3
|
"packageManager": "pnpm@10.34.5",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.994",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"comment:files": "Allowlist what ships to npm. dist = runtime; src = sourcemap targets (dist/*.js.map reference ../../src/); templates = CLI scaffold. Everything else at repo root (.pgdata local Postgres, coverage, assets/icons sources, test/, test-results/) is excluded by omission. The !negations strip the scaffold's installed deps + cruft (templates/node_modules is 200MB+ and must never ship). package.json/README/LICENSE ship automatically.",
|
|
7
7
|
"files": [
|
package/src/cli/version.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// Auto-generated by prebuild — do not edit manually
|
|
2
|
-
export const CLI_VERSION = "0.1.
|
|
2
|
+
export const CLI_VERSION = "0.1.994";
|
|
@@ -35,6 +35,31 @@ let streamCounter = 0;
|
|
|
35
35
|
// forever with the rAF loop spinning and the socket held.
|
|
36
36
|
const WATCHDOG_MS = 30_000;
|
|
37
37
|
|
|
38
|
+
/**
|
|
39
|
+
* Report a failed or silent read-aloud on the framework's UX audit channel.
|
|
40
|
+
*
|
|
41
|
+
* ⚠️ THIS EXISTS BECAUSE "TTS does not play" WAS UNDIAGNOSABLE. The hook set its
|
|
42
|
+
* local `error` state and stopped there, so a failure was visible only to
|
|
43
|
+
* whatever the app chose to render — and apps mostly render nothing, or abort
|
|
44
|
+
* quietly. Production reports therefore said "TTS does not play" with no page,
|
|
45
|
+
* no route and no reason, and answering them meant asking the user which screen
|
|
46
|
+
* they were on. `[ugly.ux] …` is the same channel the image/keyboard/safe-area
|
|
47
|
+
* audits use: the Logger ships it to the project's error log with the page
|
|
48
|
+
* attached, so every app gets this for free and none has to build its own.
|
|
49
|
+
*/
|
|
50
|
+
function reportAudioFailure(
|
|
51
|
+
reason: string,
|
|
52
|
+
detail: string,
|
|
53
|
+
extra: Record<string, unknown>,
|
|
54
|
+
): void {
|
|
55
|
+
const page = typeof window === 'undefined' ? '' : window.location.pathname;
|
|
56
|
+
console.error(`[ugly.ux] audio: read-aloud ${detail}`, {
|
|
57
|
+
reason,
|
|
58
|
+
page,
|
|
59
|
+
...extra,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
38
63
|
export function useTTS(socket: UglyBotSocket) {
|
|
39
64
|
const [playing, setPlaying] = useState(false);
|
|
40
65
|
const [currentWord, setCurrentWord] = useState('');
|
|
@@ -152,6 +177,21 @@ export function useTTS(socket: UglyBotSocket) {
|
|
|
152
177
|
// Flush any remaining caption indices so the text fully reveals.
|
|
153
178
|
while (wi < words.length)
|
|
154
179
|
options?.onSubtitleIndex?.(words[wi++]!.charEnd);
|
|
180
|
+
// Audio was delivered and the stream is over, but the worklet's own
|
|
181
|
+
// sample clock never moved: whatever the cause (a context that says
|
|
182
|
+
// "running" and isn't, a worklet that failed to load, output routed
|
|
183
|
+
// nowhere), the user heard nothing. Belt to the first-chunk check's
|
|
184
|
+
// braces — that one names the common cause, this one catches the rest.
|
|
185
|
+
if (statsRef.current.chunks > 0 && player.getPlaybackTimeMs() === 0)
|
|
186
|
+
reportAudioFailure(
|
|
187
|
+
'no-playback',
|
|
188
|
+
'delivered audio but the playback clock never advanced — the user heard nothing',
|
|
189
|
+
{
|
|
190
|
+
voice: options?.voice,
|
|
191
|
+
ctx: player.contextState(),
|
|
192
|
+
...statsRef.current,
|
|
193
|
+
},
|
|
194
|
+
);
|
|
155
195
|
setPlaying(false);
|
|
156
196
|
setCurrentWord('');
|
|
157
197
|
cleanupListeners();
|
|
@@ -166,6 +206,11 @@ export function useTTS(socket: UglyBotSocket) {
|
|
|
166
206
|
watchdogRef.current = null;
|
|
167
207
|
if (streamIdRef.current !== streamId) return;
|
|
168
208
|
setError('TTS stream timed out — no response from server');
|
|
209
|
+
reportAudioFailure(
|
|
210
|
+
'stream-timeout',
|
|
211
|
+
`received no audio for ${WATCHDOG_MS / 1000}s and gave up`,
|
|
212
|
+
{ voice: options?.voice, ...statsRef.current },
|
|
213
|
+
);
|
|
169
214
|
finish();
|
|
170
215
|
}, WATCHDOG_MS);
|
|
171
216
|
};
|
|
@@ -215,6 +260,11 @@ export function useTTS(socket: UglyBotSocket) {
|
|
|
215
260
|
// let the rAF loop finish it normally.
|
|
216
261
|
if (streamIdRef.current !== streamId || serverDone) return;
|
|
217
262
|
setError('connection lost');
|
|
263
|
+
reportAudioFailure(
|
|
264
|
+
'socket-closed',
|
|
265
|
+
'lost its connection to ugly.bot mid-stream',
|
|
266
|
+
{ voice: options?.voice, ...statsRef.current },
|
|
267
|
+
);
|
|
218
268
|
finish();
|
|
219
269
|
});
|
|
220
270
|
unsubsRef.current.push(
|
|
@@ -222,6 +272,22 @@ export function useTTS(socket: UglyBotSocket) {
|
|
|
222
272
|
if (data.streamId !== streamId) return;
|
|
223
273
|
armWatchdog();
|
|
224
274
|
statsRef.current.chunks++;
|
|
275
|
+
// THE SILENT FAILURE, caught at the one moment it is unambiguous.
|
|
276
|
+
// Audio has arrived, so the server and the socket are fine — but a
|
|
277
|
+
// context the browser never unlocked will not play a sample of it,
|
|
278
|
+
// and nothing downstream will ever raise an error. This is what
|
|
279
|
+
// "TTS does not play" has meant every time so far, and it was
|
|
280
|
+
// invisible: `playing` stays true, no error fires, the app shows a
|
|
281
|
+
// spinner or nothing. Report it on the first chunk, once per stream.
|
|
282
|
+
if (
|
|
283
|
+
statsRef.current.chunks === 1 &&
|
|
284
|
+
player.contextState() !== 'running'
|
|
285
|
+
)
|
|
286
|
+
reportAudioFailure(
|
|
287
|
+
'context-not-running',
|
|
288
|
+
`arrived but the AudioContext is "${player.contextState()}" — the browser never unlocked it, so nothing will be heard. Call warmup() inside the tap that starts playback.`,
|
|
289
|
+
{ voice: options?.voice },
|
|
290
|
+
);
|
|
225
291
|
void player.feedChunk(data.audio);
|
|
226
292
|
if (data.visemes) {
|
|
227
293
|
for (const v of data.visemes) {
|
|
@@ -261,6 +327,11 @@ export function useTTS(socket: UglyBotSocket) {
|
|
|
261
327
|
socket.on('tts:error', (data) => {
|
|
262
328
|
if (data.streamId !== streamId) return;
|
|
263
329
|
setError(data.message);
|
|
330
|
+
reportAudioFailure(
|
|
331
|
+
'server-error',
|
|
332
|
+
`was refused by ugly.bot — ${data.message}`,
|
|
333
|
+
{ voice: options?.voice, ...statsRef.current },
|
|
334
|
+
);
|
|
264
335
|
finish();
|
|
265
336
|
}),
|
|
266
337
|
...(unsubClose ? [unsubClose] : []),
|
|
@@ -23,4 +23,15 @@ describe('bundled tool catalog', () => {
|
|
|
23
23
|
it('returns undefined for unknown tools', () => {
|
|
24
24
|
expect(bundledTool('definitely-not-a-tool')).toBeUndefined();
|
|
25
25
|
});
|
|
26
|
+
|
|
27
|
+
it('whisper.cpp is requestable under the name consumers actually spawn', () => {
|
|
28
|
+
// The Homebrew formula is `whisper-cpp`, but the executable — and what
|
|
29
|
+
// callers pass to `host.spawn(...)` — is `whisper-cli`. `checkProcess`
|
|
30
|
+
// matches on the spawn name, so the catalog `name` must be
|
|
31
|
+
// `whisper-cli` for a real spawn request to be granted.
|
|
32
|
+
const t = bundledTool('whisper-cli');
|
|
33
|
+
expect(t).toBeDefined();
|
|
34
|
+
expect(t!.category).toBe('media');
|
|
35
|
+
expect(bundledTool('whisper-cpp')).toBeUndefined();
|
|
36
|
+
});
|
|
26
37
|
});
|
|
@@ -130,6 +130,15 @@ export const BUNDLED_TOOLS = [
|
|
|
130
130
|
category: 'media',
|
|
131
131
|
description: 'yt-dlp — download media from the web.',
|
|
132
132
|
},
|
|
133
|
+
// whisper.cpp's Homebrew formula is `whisper-cpp`, but its executable —
|
|
134
|
+
// and what every consumer actually spawns — is `whisper-cli`. `name` is
|
|
135
|
+
// the request/grant key AND the default spawn name (what `checkProcess`
|
|
136
|
+
// matches against), so it must be `whisper-cli`, not the formula name.
|
|
137
|
+
{
|
|
138
|
+
name: 'whisper-cli',
|
|
139
|
+
category: 'media',
|
|
140
|
+
description: 'whisper.cpp — offline speech-to-text transcription.',
|
|
141
|
+
},
|
|
133
142
|
// doc
|
|
134
143
|
{
|
|
135
144
|
name: 'pandoc',
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { createNetFacade } from './net.js';
|
|
3
|
+
|
|
4
|
+
describe('net facade — uniform invoke/subscribe protocol (no UglyDesktop)', () => {
|
|
5
|
+
it('connects, streams data, closes, and routes write/close by id', async () => {
|
|
6
|
+
const listeners = new Map<string, (d: unknown) => void>();
|
|
7
|
+
const invoked: { ch: string; p: unknown }[] = [];
|
|
8
|
+
const invoke = async (ch: string, p: unknown): Promise<unknown> => {
|
|
9
|
+
invoked.push({ ch, p });
|
|
10
|
+
if (ch === 'net.connect') return { id: 'S1' };
|
|
11
|
+
return undefined;
|
|
12
|
+
};
|
|
13
|
+
const subscribe = (ev: string, cb: (d: unknown) => void) => {
|
|
14
|
+
listeners.set(ev, cb);
|
|
15
|
+
return () => listeners.delete(ev);
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const facade = createNetFacade(invoke, subscribe, async () => {});
|
|
19
|
+
const chunks: Uint8Array[] = [];
|
|
20
|
+
let closed = false;
|
|
21
|
+
|
|
22
|
+
const sock = facade.connect('example.com', 443);
|
|
23
|
+
sock.onData((c) => chunks.push(c));
|
|
24
|
+
sock.onClose(() => (closed = true));
|
|
25
|
+
|
|
26
|
+
await Promise.resolve();
|
|
27
|
+
await Promise.resolve();
|
|
28
|
+
|
|
29
|
+
expect(invoked[0]).toEqual({
|
|
30
|
+
ch: 'net.connect',
|
|
31
|
+
p: { host: 'example.com', port: 443 },
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
listeners.get('net.data:S1')!({ data: 'hi' });
|
|
35
|
+
listeners.get('net.close:S1')!({});
|
|
36
|
+
expect(chunks).toHaveLength(1);
|
|
37
|
+
expect(closed).toBe(true);
|
|
38
|
+
|
|
39
|
+
sock.write('x');
|
|
40
|
+
sock.close();
|
|
41
|
+
await Promise.resolve();
|
|
42
|
+
await Promise.resolve();
|
|
43
|
+
expect(invoked).toContainEqual({
|
|
44
|
+
ch: 'net.write',
|
|
45
|
+
p: { id: 'S1', data: 'x' },
|
|
46
|
+
});
|
|
47
|
+
expect(invoked).toContainEqual({ ch: 'net.close', p: { id: 'S1' } });
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('routes a denied/rejected connect to onError (callback registered BEFORE the rejection)', async () => {
|
|
51
|
+
const invoke = async (ch: string): Promise<unknown> => {
|
|
52
|
+
if (ch === 'net.connect') {
|
|
53
|
+
throw new Error(
|
|
54
|
+
'Permission denied for … (net example.com:443): net not granted',
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
return undefined;
|
|
58
|
+
};
|
|
59
|
+
const subscribe = () => () => {};
|
|
60
|
+
|
|
61
|
+
const facade = createNetFacade(invoke, subscribe, async () => {});
|
|
62
|
+
const errors: string[] = [];
|
|
63
|
+
const closes: number[] = [];
|
|
64
|
+
|
|
65
|
+
const sock = facade.connect('example.com', 443);
|
|
66
|
+
sock.onError((e) => errors.push(e));
|
|
67
|
+
sock.onClose(() => closes.push(1));
|
|
68
|
+
|
|
69
|
+
// Let the invoke() rejection propagate through the connect promise chain.
|
|
70
|
+
await Promise.resolve();
|
|
71
|
+
await Promise.resolve();
|
|
72
|
+
await Promise.resolve();
|
|
73
|
+
|
|
74
|
+
expect(errors).toHaveLength(1);
|
|
75
|
+
expect(errors[0]).toContain('net not granted');
|
|
76
|
+
// A denied connect never opened a socket — it must never look like close.
|
|
77
|
+
expect(closes).toHaveLength(0);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('routes a denied/rejected connect to onError (callback registered AFTER the rejection), exactly once', async () => {
|
|
81
|
+
const invoke = async (ch: string): Promise<unknown> => {
|
|
82
|
+
if (ch === 'net.connect') throw new Error('net not granted');
|
|
83
|
+
return undefined;
|
|
84
|
+
};
|
|
85
|
+
const subscribe = () => () => {};
|
|
86
|
+
|
|
87
|
+
const facade = createNetFacade(invoke, subscribe, async () => {});
|
|
88
|
+
const early: string[] = [];
|
|
89
|
+
const late: string[] = [];
|
|
90
|
+
|
|
91
|
+
const sock = facade.connect('example.com', 443);
|
|
92
|
+
sock.onError((e) => early.push(e));
|
|
93
|
+
|
|
94
|
+
// Let the rejection actually happen before the late subscriber registers.
|
|
95
|
+
await Promise.resolve();
|
|
96
|
+
await Promise.resolve();
|
|
97
|
+
await Promise.resolve();
|
|
98
|
+
|
|
99
|
+
sock.onError((e) => late.push(e));
|
|
100
|
+
await Promise.resolve();
|
|
101
|
+
|
|
102
|
+
// Both callbacks see the failure exactly once each — the early one is
|
|
103
|
+
// not re-invoked by the late registration, and the late one isn't
|
|
104
|
+
// dropped for having missed the original rejection.
|
|
105
|
+
expect(early).toEqual(['net not granted']);
|
|
106
|
+
expect(late).toEqual(['net not granted']);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it('write/end/close after a failed connect are silent no-ops (no invoke calls)', async () => {
|
|
110
|
+
const invoked: { ch: string; p: unknown }[] = [];
|
|
111
|
+
const invoke = async (ch: string, p: unknown): Promise<unknown> => {
|
|
112
|
+
invoked.push({ ch, p });
|
|
113
|
+
if (ch === 'net.connect') throw new Error('net not granted');
|
|
114
|
+
return undefined;
|
|
115
|
+
};
|
|
116
|
+
const subscribe = () => () => {};
|
|
117
|
+
|
|
118
|
+
const facade = createNetFacade(invoke, subscribe, async () => {});
|
|
119
|
+
const sock = facade.connect('example.com', 443);
|
|
120
|
+
sock.onError(() => {}); // observe, but don't assert on it here
|
|
121
|
+
|
|
122
|
+
await Promise.resolve();
|
|
123
|
+
await Promise.resolve();
|
|
124
|
+
await Promise.resolve();
|
|
125
|
+
|
|
126
|
+
sock.write('x');
|
|
127
|
+
sock.end();
|
|
128
|
+
sock.close();
|
|
129
|
+
await Promise.resolve();
|
|
130
|
+
await Promise.resolve();
|
|
131
|
+
|
|
132
|
+
expect(invoked).toEqual([
|
|
133
|
+
{ ch: 'net.connect', p: { host: 'example.com', port: 443 } },
|
|
134
|
+
]);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it('a rejected ensure() permission pre-check does not throw unhandled and does not itself fire onError', async () => {
|
|
138
|
+
const invoke = async (ch: string): Promise<unknown> => {
|
|
139
|
+
if (ch === 'net.connect') return { id: 'S2' };
|
|
140
|
+
return undefined;
|
|
141
|
+
};
|
|
142
|
+
const listeners = new Map<string, (d: unknown) => void>();
|
|
143
|
+
const subscribe = (ev: string, cb: (d: unknown) => void) => {
|
|
144
|
+
listeners.set(ev, cb);
|
|
145
|
+
return () => listeners.delete(ev);
|
|
146
|
+
};
|
|
147
|
+
const ensure = async () => {
|
|
148
|
+
throw new Error('permission not yet granted');
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
const facade = createNetFacade(invoke, subscribe, ensure);
|
|
152
|
+
const errors: string[] = [];
|
|
153
|
+
const sock = facade.connect('example.com', 443);
|
|
154
|
+
sock.onError((e) => errors.push(e));
|
|
155
|
+
|
|
156
|
+
await Promise.resolve();
|
|
157
|
+
await Promise.resolve();
|
|
158
|
+
await Promise.resolve();
|
|
159
|
+
|
|
160
|
+
// The real connect succeeded (host daemon is the actual enforcer), so no
|
|
161
|
+
// error should fire even though the advisory ensure() rejected.
|
|
162
|
+
expect(errors).toHaveLength(0);
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it('a callback registered from inside another onError callback is not dropped', async () => {
|
|
166
|
+
const invoke = async (ch: string): Promise<unknown> => {
|
|
167
|
+
if (ch === 'net.connect') throw new Error('net not granted');
|
|
168
|
+
return undefined;
|
|
169
|
+
};
|
|
170
|
+
const subscribe = () => () => {};
|
|
171
|
+
|
|
172
|
+
const facade = createNetFacade(invoke, subscribe, async () => {});
|
|
173
|
+
let outer = 0;
|
|
174
|
+
let nested = 0;
|
|
175
|
+
|
|
176
|
+
const sock = facade.connect('example.com', 443);
|
|
177
|
+
sock.onError(() => {
|
|
178
|
+
outer++;
|
|
179
|
+
sock.onError(() => {
|
|
180
|
+
nested++;
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
await Promise.resolve();
|
|
185
|
+
await Promise.resolve();
|
|
186
|
+
await Promise.resolve();
|
|
187
|
+
|
|
188
|
+
expect(outer).toBe(1);
|
|
189
|
+
expect(nested).toBe(1);
|
|
190
|
+
});
|
|
191
|
+
});
|
|
@@ -96,7 +96,14 @@ export function createNetFacade(
|
|
|
96
96
|
): NetFacade {
|
|
97
97
|
return {
|
|
98
98
|
connect(host, port) {
|
|
99
|
-
|
|
99
|
+
// Fire the permission check; the host daemon also enforces it inside
|
|
100
|
+
// `invoke('net.connect', …)` below, so a rejection here is redundant
|
|
101
|
+
// with (and always followed by) that call's own rejection — which IS
|
|
102
|
+
// routed to `onError` below. Swallow it here purely to avoid an
|
|
103
|
+
// unhandled-rejection warning; don't double-report. (Mirrors the
|
|
104
|
+
// `process` facade's `spawn()` — see the comment there for why.)
|
|
105
|
+
void ensure().catch(() => {});
|
|
106
|
+
|
|
100
107
|
const cbs = {
|
|
101
108
|
connect: [] as (() => void)[],
|
|
102
109
|
data: [] as ((b: Uint8Array) => void)[],
|
|
@@ -104,40 +111,91 @@ export function createNetFacade(
|
|
|
104
111
|
error: [] as ((e: string) => void)[],
|
|
105
112
|
};
|
|
106
113
|
const unsubs: (() => void)[] = [];
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
114
|
+
|
|
115
|
+
// Set once the connect invoke rejects (e.g. the permission gate denies
|
|
116
|
+
// `net`). Lets `onError` replay the failure to a callback registered
|
|
117
|
+
// AFTER the rejection already happened, without double-delivering to
|
|
118
|
+
// one registered before — see the `onError` implementation below for
|
|
119
|
+
// exactly how, and why the set-before-forEach ordering here matters.
|
|
120
|
+
let connectFailure: string | undefined;
|
|
121
|
+
|
|
122
|
+
const ready = invoke('net.connect', { host, port })
|
|
123
|
+
.then((r) => {
|
|
124
|
+
const id = (r as { id: string }).id;
|
|
125
|
+
unsubs.push(
|
|
126
|
+
subscribe(`net.connect:${id}`, () =>
|
|
127
|
+
cbs.connect.forEach((cb) => cb()),
|
|
118
128
|
),
|
|
119
|
-
)
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
129
|
+
);
|
|
130
|
+
unsubs.push(
|
|
131
|
+
subscribe(`net.data:${id}`, (d) =>
|
|
132
|
+
cbs.data.forEach((cb) =>
|
|
133
|
+
cb(binStrToBytes((d as { data: string }).data)),
|
|
134
|
+
),
|
|
135
|
+
),
|
|
136
|
+
);
|
|
137
|
+
unsubs.push(
|
|
138
|
+
subscribe(`net.error:${id}`, (d) =>
|
|
139
|
+
cbs.error.forEach((cb) => cb((d as { err: string }).err)),
|
|
140
|
+
),
|
|
141
|
+
);
|
|
142
|
+
unsubs.push(
|
|
143
|
+
subscribe(`net.close:${id}`, () => {
|
|
144
|
+
cbs.close.forEach((cb) => cb());
|
|
145
|
+
unsubs.forEach((u) => u());
|
|
146
|
+
}),
|
|
147
|
+
);
|
|
148
|
+
return id;
|
|
149
|
+
})
|
|
150
|
+
.catch((e: unknown) => {
|
|
151
|
+
// The socket itself never connected — e.g. the permission gate
|
|
152
|
+
// denied `net`. That's an `error`, never a `close`: no socket ever
|
|
153
|
+
// opened, so there is nothing to report as closed, and a caller
|
|
154
|
+
// that treated a denial as "closed" would mask the real failure.
|
|
155
|
+
// Callbacks already registered get notified now; `onError` below
|
|
156
|
+
// replays this to any callback registered later, exactly once each.
|
|
157
|
+
connectFailure = e instanceof Error ? e.message : String(e);
|
|
158
|
+
cbs.error.forEach((cb) => cb(connectFailure!));
|
|
159
|
+
throw e; // keep `ready` rejected so `withId` below stays a no-op
|
|
160
|
+
});
|
|
161
|
+
// `ready` must stay rejected (so every `withId` call below is a no-op),
|
|
162
|
+
// but that leaves it a dangling rejected promise whenever a caller
|
|
163
|
+
// never invokes write/end/close — which Node/browsers flag as an
|
|
164
|
+
// unhandled rejection even though the failure was already delivered
|
|
165
|
+
// via `onError` above. Attach a standing no-op handler to silence
|
|
166
|
+
// that; it doesn't consume `ready`'s rejection for the `withId` chain
|
|
167
|
+
// below, since each `.then()/.catch()` call derives its own
|
|
168
|
+
// independent promise.
|
|
169
|
+
ready.catch(() => {});
|
|
134
170
|
const withId = (fn: (id: string) => void) =>
|
|
135
171
|
void ready.then(fn).catch(() => {});
|
|
136
172
|
return {
|
|
137
173
|
onConnect: (cb) => cbs.connect.push(cb),
|
|
138
174
|
onData: (cb) => cbs.data.push(cb),
|
|
139
175
|
onClose: (cb) => cbs.close.push(cb),
|
|
140
|
-
onError: (cb) =>
|
|
176
|
+
onError: (cb) => {
|
|
177
|
+
cbs.error.push(cb);
|
|
178
|
+
// Late subscriber: the connect already failed before this
|
|
179
|
+
// callback registered, so it missed the `.catch` above's
|
|
180
|
+
// `forEach` (that `forEach` only visits entries `cbs.error` held
|
|
181
|
+
// when it started iterating — a push during/after it is never
|
|
182
|
+
// visited). Replay the failure here instead. This does NOT
|
|
183
|
+
// double-deliver to a callback that was already notified:
|
|
184
|
+
// `connectFailure` is set BEFORE that `forEach` runs — so by the
|
|
185
|
+
// time any `onError` call happens, either (a) the failure hasn't
|
|
186
|
+
// happened yet and this `if` is skipped, or (b) it has, and every
|
|
187
|
+
// callback in `cbs.error` before this `push()` was already
|
|
188
|
+
// invoked by that `forEach`, so only the newly-pushed one still
|
|
189
|
+
// needs it. That ordering (set-before-forEach) is load-bearing —
|
|
190
|
+
// swap it and a callback registered while inside another
|
|
191
|
+
// `onError` callback is silently dropped instead of replayed.
|
|
192
|
+
if (connectFailure !== undefined) cb(connectFailure);
|
|
193
|
+
},
|
|
194
|
+
// write/end/close after a failed connect: `withId` never calls `fn`
|
|
195
|
+
// because `ready` stays rejected, so these are silent no-ops.
|
|
196
|
+
// That's intentional — the socket never existed, there's nothing to
|
|
197
|
+
// write/end/close, and the caller already got the failure via
|
|
198
|
+
// `onError`; a second error here would just duplicate that signal.
|
|
141
199
|
write: (data) =>
|
|
142
200
|
withId(
|
|
143
201
|
(id) => void invoke('net.write', { id, data: bytesToBinStr(data) }),
|
|
@@ -147,7 +205,12 @@ export function createNetFacade(
|
|
|
147
205
|
};
|
|
148
206
|
},
|
|
149
207
|
async listen(port) {
|
|
150
|
-
|
|
208
|
+
// Advisory only, like `connect()` above: `invoke('net.listen', …)`
|
|
209
|
+
// just below is awaited directly, so its own rejection already
|
|
210
|
+
// propagates to the caller as a rejected `listen()` promise — no
|
|
211
|
+
// buffering needed here. Swallow this one purely to avoid an
|
|
212
|
+
// unhandled-rejection warning from the redundant advisory check.
|
|
213
|
+
void ensure().catch(() => {});
|
|
151
214
|
const r = (await invoke('net.listen', { port })) as {
|
|
152
215
|
id: string;
|
|
153
216
|
port: number;
|
|
@@ -51,4 +51,153 @@ describe('process facade — uniform invoke/subscribe protocol (no UglyDesktop)'
|
|
|
51
51
|
p: { id: 'P1', signal: 'SIGTERM' },
|
|
52
52
|
});
|
|
53
53
|
});
|
|
54
|
+
|
|
55
|
+
it('routes a denied/rejected spawn to onError (callback registered BEFORE the rejection)', async () => {
|
|
56
|
+
const invoke = async (ch: string): Promise<unknown> => {
|
|
57
|
+
if (ch === 'process.spawn') {
|
|
58
|
+
throw new Error(
|
|
59
|
+
'Permission denied for … (process whisper-cli): not a bundled tool',
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
return undefined;
|
|
63
|
+
};
|
|
64
|
+
const subscribe = () => () => {};
|
|
65
|
+
|
|
66
|
+
const facade = createProcessFacade(invoke, subscribe, async () => {});
|
|
67
|
+
const errors: string[] = [];
|
|
68
|
+
const exits: (number | null)[] = [];
|
|
69
|
+
|
|
70
|
+
const proc = facade.spawn('whisper-cli', []);
|
|
71
|
+
proc.onError((e) => errors.push(e));
|
|
72
|
+
proc.onExit((c) => exits.push(c));
|
|
73
|
+
|
|
74
|
+
// Let the invoke() rejection propagate through the spawn promise chain.
|
|
75
|
+
await Promise.resolve();
|
|
76
|
+
await Promise.resolve();
|
|
77
|
+
await Promise.resolve();
|
|
78
|
+
|
|
79
|
+
expect(errors).toHaveLength(1);
|
|
80
|
+
expect(errors[0]).toContain('not a bundled tool');
|
|
81
|
+
// A denied spawn never started a process — it must never look like exit.
|
|
82
|
+
expect(exits).toHaveLength(0);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it('routes a denied/rejected spawn to onError (callback registered AFTER the rejection), exactly once', async () => {
|
|
86
|
+
const invoke = async (ch: string): Promise<unknown> => {
|
|
87
|
+
if (ch === 'process.spawn') throw new Error('tool not granted');
|
|
88
|
+
return undefined;
|
|
89
|
+
};
|
|
90
|
+
const subscribe = () => () => {};
|
|
91
|
+
|
|
92
|
+
const facade = createProcessFacade(invoke, subscribe, async () => {});
|
|
93
|
+
const early: string[] = [];
|
|
94
|
+
const late: string[] = [];
|
|
95
|
+
|
|
96
|
+
const proc = facade.spawn('whisper-cli', []);
|
|
97
|
+
proc.onError((e) => early.push(e));
|
|
98
|
+
|
|
99
|
+
// Let the rejection actually happen before the late subscriber registers.
|
|
100
|
+
await Promise.resolve();
|
|
101
|
+
await Promise.resolve();
|
|
102
|
+
await Promise.resolve();
|
|
103
|
+
|
|
104
|
+
proc.onError((e) => late.push(e));
|
|
105
|
+
await Promise.resolve();
|
|
106
|
+
|
|
107
|
+
// Both callbacks see the failure exactly once each — the early one is
|
|
108
|
+
// not re-invoked by the late registration, and the late one isn't
|
|
109
|
+
// dropped for having missed the original rejection.
|
|
110
|
+
expect(early).toEqual(['tool not granted']);
|
|
111
|
+
expect(late).toEqual(['tool not granted']);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it('write/kill/closeStdin after a failed spawn are silent no-ops (no invoke calls)', async () => {
|
|
115
|
+
const invoked: { ch: string; p: unknown }[] = [];
|
|
116
|
+
const invoke = async (ch: string, p: unknown): Promise<unknown> => {
|
|
117
|
+
invoked.push({ ch, p });
|
|
118
|
+
if (ch === 'process.spawn') throw new Error('not a bundled tool');
|
|
119
|
+
return undefined;
|
|
120
|
+
};
|
|
121
|
+
const subscribe = () => () => {};
|
|
122
|
+
|
|
123
|
+
const facade = createProcessFacade(invoke, subscribe, async () => {});
|
|
124
|
+
const proc = facade.spawn('whisper-cli', []);
|
|
125
|
+
proc.onError(() => {}); // observe, but don't assert on it here
|
|
126
|
+
|
|
127
|
+
await Promise.resolve();
|
|
128
|
+
await Promise.resolve();
|
|
129
|
+
await Promise.resolve();
|
|
130
|
+
|
|
131
|
+
proc.write('x');
|
|
132
|
+
proc.kill('SIGTERM');
|
|
133
|
+
proc.closeStdin();
|
|
134
|
+
await Promise.resolve();
|
|
135
|
+
await Promise.resolve();
|
|
136
|
+
|
|
137
|
+
expect(invoked).toEqual([
|
|
138
|
+
{
|
|
139
|
+
ch: 'process.spawn',
|
|
140
|
+
p: { cmd: 'whisper-cli', args: [], opts: undefined },
|
|
141
|
+
},
|
|
142
|
+
]);
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it('a rejected ensure() permission pre-check does not throw unhandled and does not itself fire onError', async () => {
|
|
146
|
+
const invoke = async (ch: string): Promise<unknown> => {
|
|
147
|
+
if (ch === 'process.spawn') return { id: 'P2', pid: 7 };
|
|
148
|
+
return undefined;
|
|
149
|
+
};
|
|
150
|
+
const listeners = new Map<string, (d: unknown) => void>();
|
|
151
|
+
const subscribe = (ev: string, cb: (d: unknown) => void) => {
|
|
152
|
+
listeners.set(ev, cb);
|
|
153
|
+
return () => listeners.delete(ev);
|
|
154
|
+
};
|
|
155
|
+
const ensure = async () => {
|
|
156
|
+
throw new Error('permission not yet granted');
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
const facade = createProcessFacade(invoke, subscribe, ensure);
|
|
160
|
+
const errors: string[] = [];
|
|
161
|
+
const proc = facade.spawn('whisper-cli', []);
|
|
162
|
+
proc.onError((e) => errors.push(e));
|
|
163
|
+
|
|
164
|
+
await Promise.resolve();
|
|
165
|
+
await Promise.resolve();
|
|
166
|
+
await Promise.resolve();
|
|
167
|
+
|
|
168
|
+
// The real spawn succeeded (host daemon is the actual enforcer), so no
|
|
169
|
+
// error should fire even though the advisory ensure() rejected.
|
|
170
|
+
expect(errors).toHaveLength(0);
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
it('a callback registered from inside another onError callback is not dropped', async () => {
|
|
174
|
+
const invoke = async (ch: string): Promise<unknown> => {
|
|
175
|
+
if (ch === 'process.spawn') throw new Error('tool not granted');
|
|
176
|
+
return undefined;
|
|
177
|
+
};
|
|
178
|
+
const subscribe = () => () => {};
|
|
179
|
+
|
|
180
|
+
const facade = createProcessFacade(invoke, subscribe, async () => {});
|
|
181
|
+
let outer = 0;
|
|
182
|
+
let nested = 0;
|
|
183
|
+
|
|
184
|
+
const proc = facade.spawn('whisper-cli', []);
|
|
185
|
+
proc.onError(() => {
|
|
186
|
+
outer++;
|
|
187
|
+
// Registering from inside another onError callback is exactly the
|
|
188
|
+
// ordering where a naive "set spawnFailure after the forEach" would
|
|
189
|
+
// drop this nested callback (forEach caches length before this push
|
|
190
|
+
// ever runs, and the flag wouldn't be set yet either).
|
|
191
|
+
proc.onError(() => {
|
|
192
|
+
nested++;
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
await Promise.resolve();
|
|
197
|
+
await Promise.resolve();
|
|
198
|
+
await Promise.resolve();
|
|
199
|
+
|
|
200
|
+
expect(outer).toBe(1);
|
|
201
|
+
expect(nested).toBe(1);
|
|
202
|
+
});
|
|
54
203
|
});
|
|
@@ -36,7 +36,13 @@ export function createProcessFacade(
|
|
|
36
36
|
): ProcessFacade {
|
|
37
37
|
return {
|
|
38
38
|
spawn(cmd, args, opts) {
|
|
39
|
-
|
|
39
|
+
// Fire the permission check; the host daemon also enforces it inside
|
|
40
|
+
// `invoke('process.spawn', …)` below, so a rejection here is redundant
|
|
41
|
+
// with (and always followed by) that call's own rejection — which IS
|
|
42
|
+
// routed to `onError` below. Swallow it here purely to avoid an
|
|
43
|
+
// unhandled-rejection warning; don't double-report.
|
|
44
|
+
void ensure().catch(() => {});
|
|
45
|
+
|
|
40
46
|
const cbs = {
|
|
41
47
|
stdout: [] as ((c: string) => void)[],
|
|
42
48
|
stderr: [] as ((c: string) => void)[],
|
|
@@ -45,31 +51,60 @@ export function createProcessFacade(
|
|
|
45
51
|
};
|
|
46
52
|
const unsubs: (() => void)[] = [];
|
|
47
53
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
)
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
)
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
54
|
+
// Set once the spawn invoke rejects (e.g. the permission gate denies
|
|
55
|
+
// the binary). Lets `onError` replay the failure to a callback that
|
|
56
|
+
// registers AFTER the rejection already happened — callers may call
|
|
57
|
+
// `spawn()` and register `onError` in the same synchronous tick, but
|
|
58
|
+
// nothing guarantees that ordering, so a purely "fire once at rejection
|
|
59
|
+
// time" callback would silently drop late subscribers.
|
|
60
|
+
let spawnFailure: string | undefined;
|
|
61
|
+
|
|
62
|
+
const ready = invoke('process.spawn', { cmd, args, opts })
|
|
63
|
+
.then((r) => {
|
|
64
|
+
const id = (r as { id: string }).id;
|
|
65
|
+
unsubs.push(
|
|
66
|
+
subscribe(`process.stdout:${id}`, (d) =>
|
|
67
|
+
cbs.stdout.forEach((cb) => cb((d as { chunk: string }).chunk)),
|
|
68
|
+
),
|
|
69
|
+
);
|
|
70
|
+
unsubs.push(
|
|
71
|
+
subscribe(`process.stderr:${id}`, (d) =>
|
|
72
|
+
cbs.stderr.forEach((cb) => cb((d as { chunk: string }).chunk)),
|
|
73
|
+
),
|
|
74
|
+
);
|
|
75
|
+
unsubs.push(
|
|
76
|
+
subscribe(`process.error:${id}`, (d) =>
|
|
77
|
+
cbs.error.forEach((cb) => cb((d as { err: string }).err)),
|
|
78
|
+
),
|
|
79
|
+
);
|
|
80
|
+
unsubs.push(
|
|
81
|
+
subscribe(`process.exit:${id}`, (d) => {
|
|
82
|
+
cbs.exit.forEach((cb) => cb((d as { code: number | null }).code));
|
|
83
|
+
unsubs.forEach((u) => u());
|
|
84
|
+
}),
|
|
85
|
+
);
|
|
86
|
+
return id;
|
|
87
|
+
})
|
|
88
|
+
.catch((e: unknown) => {
|
|
89
|
+
// The spawn itself never happened — e.g. the permission gate
|
|
90
|
+
// denied the binary (`not a bundled tool` / `tool not granted`).
|
|
91
|
+
// That's an `error`, never an `exit`: no process ever started, so
|
|
92
|
+
// there is no exit code to report, and a caller that treated a
|
|
93
|
+
// denial as "exited" could log a bogus code. Callbacks already
|
|
94
|
+
// registered get notified now; `onError` below replays this to
|
|
95
|
+
// any callback registered later, exactly once each.
|
|
96
|
+
spawnFailure = e instanceof Error ? e.message : String(e);
|
|
97
|
+
cbs.error.forEach((cb) => cb(spawnFailure!));
|
|
98
|
+
throw e; // keep `ready` rejected so `withId` below stays a no-op
|
|
99
|
+
});
|
|
100
|
+
// `ready` must stay rejected (so every `withId` call below is a no-op),
|
|
101
|
+
// but that leaves it a dangling rejected promise whenever a caller
|
|
102
|
+
// never invokes write/kill/closeStdin — which Node/browsers flag as an
|
|
103
|
+
// unhandled rejection even though the failure was already delivered via
|
|
104
|
+
// `onError` above. Attach a standing no-op handler to silence that;
|
|
105
|
+
// it doesn't consume `ready`'s rejection for the `withId` chain below,
|
|
106
|
+
// since each `.then()/.catch()` call derives its own independent promise.
|
|
107
|
+
ready.catch(() => {});
|
|
73
108
|
const withId = (fn: (id: string) => void) =>
|
|
74
109
|
void ready.then(fn).catch(() => {});
|
|
75
110
|
|
|
@@ -77,7 +112,31 @@ export function createProcessFacade(
|
|
|
77
112
|
onStdout: (cb) => cbs.stdout.push(cb),
|
|
78
113
|
onStderr: (cb) => cbs.stderr.push(cb),
|
|
79
114
|
onExit: (cb) => cbs.exit.push(cb),
|
|
80
|
-
onError: (cb) =>
|
|
115
|
+
onError: (cb) => {
|
|
116
|
+
cbs.error.push(cb);
|
|
117
|
+
// Late subscriber: the spawn already failed before this callback
|
|
118
|
+
// registered, so it missed the `.catch` above's `forEach` (that
|
|
119
|
+
// `forEach` only visits entries `cbs.error` held at the time it
|
|
120
|
+
// started iterating — a push during/after it, e.g. from another
|
|
121
|
+
// `onError` callback, is never visited). Replay the failure here
|
|
122
|
+
// instead. This does NOT double-deliver to a callback that was
|
|
123
|
+
// already notified: `spawnFailure` is set at line 98, BEFORE the
|
|
124
|
+
// `forEach` at line 99 runs — so by the time any `onError` call
|
|
125
|
+
// happens, either (a) the failure hasn't happened yet and this
|
|
126
|
+
// `if` is skipped, or (b) it has, and every callback in
|
|
127
|
+
// `cbs.error` before this `push()` was already invoked by that
|
|
128
|
+
// `forEach`, so only the newly-pushed one still needs it. That
|
|
129
|
+
// ordering (set-before-forEach) is load-bearing — swap it and a
|
|
130
|
+
// callback registered while inside another `onError` callback is
|
|
131
|
+
// silently dropped instead of replayed.
|
|
132
|
+
if (spawnFailure !== undefined) cb(spawnFailure);
|
|
133
|
+
},
|
|
134
|
+
// write/kill/closeStdin after a failed spawn: `withId` never calls
|
|
135
|
+
// `fn` because `ready` stays rejected, so these are silent no-ops.
|
|
136
|
+
// That's intentional, not an oversight — the process never existed,
|
|
137
|
+
// there's nothing to kill/write/close, and the caller already got
|
|
138
|
+
// the failure via `onError`; surfacing a second error here would
|
|
139
|
+
// just be a duplicate signal for the same event.
|
|
81
140
|
write: (data) =>
|
|
82
141
|
withId((id) => void invoke('process.write', { id, data })),
|
|
83
142
|
closeStdin: () =>
|