ugly-app 0.1.983 → 0.1.985
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/devReap.d.ts.map +1 -1
- package/dist/cli/devReap.js +1 -1
- package/dist/cli/devReap.js.map +1 -1
- 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 +46 -1
- package/dist/client/audio/useTTS.js.map +1 -1
- package/dist/client/uglyBotSocket.d.ts +8 -0
- package/dist/client/uglyBotSocket.d.ts.map +1 -1
- package/dist/client/uglyBotSocket.js +109 -11
- package/dist/client/uglyBotSocket.js.map +1 -1
- package/dist/inspect/ui/panels/ConsolePanel.d.ts.map +1 -1
- package/dist/inspect/ui/panels/ConsolePanel.js +15 -3
- package/dist/inspect/ui/panels/ConsolePanel.js.map +1 -1
- package/dist/inspect/ui/panels/ConsolePanel.test.d.ts +2 -0
- package/dist/inspect/ui/panels/ConsolePanel.test.d.ts.map +1 -0
- package/dist/inspect/ui/panels/ConsolePanel.test.js +57 -0
- package/dist/inspect/ui/panels/ConsolePanel.test.js.map +1 -0
- package/package.json +1 -1
- package/src/cli/version.ts +1 -1
- package/src/client/audio/useTTS.ts +47 -0
- package/src/client/uglyBotSocket.ts +117 -10
- package/src/inspect/ui/panels/ConsolePanel.test.tsx +76 -0
- package/src/inspect/ui/panels/ConsolePanel.tsx +23 -5
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"devReap.d.ts","sourceRoot":"","sources":["../../src/cli/devReap.ts"],"names":[],"mappings":"AA2BA,MAAM,WAAW,OAAO;IACtB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;CACd;AAED,iFAAiF;AACjF,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,EAAE,CAQnD;AAED,wBAAgB,aAAa,IAAI,OAAO,EAAE,CAazC;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,MAAM,EAAE,CAmBrE;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,MAAM,EAAE,CAWlE;AAED,gEAAgE;AAChE,wBAAgB,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAQ5C;AAQD,MAAM,WAAW,kBAAkB;IACjC,sDAAsD;IACtD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,yBAAyB;IACzB,SAAS,CAAC,EAAE,MAAM,OAAO,EAAE,CAAC;IAC5B,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;CAC9B;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CACzB,IAAI,EAAE,MAAM,EACZ,IAAI,GAAE,kBAAuB,GAC5B,MAAM,EAAE,CAyBV;AAED,MAAM,WAAW,kBAAkB;IACjC,mBAAmB;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,OAAO,EAAE,CAAC;IAC5B,KAAK,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;CAClC;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,IAAI,EACxC,IAAI,GAAE,kBAAuB,GAC5B,MAAM,IAAI,
|
|
1
|
+
{"version":3,"file":"devReap.d.ts","sourceRoot":"","sources":["../../src/cli/devReap.ts"],"names":[],"mappings":"AA2BA,MAAM,WAAW,OAAO;IACtB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;CACd;AAED,iFAAiF;AACjF,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,EAAE,CAQnD;AAED,wBAAgB,aAAa,IAAI,OAAO,EAAE,CAazC;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,MAAM,EAAE,CAmBrE;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,MAAM,EAAE,CAWlE;AAED,gEAAgE;AAChE,wBAAgB,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAQ5C;AAQD,MAAM,WAAW,kBAAkB;IACjC,sDAAsD;IACtD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,yBAAyB;IACzB,SAAS,CAAC,EAAE,MAAM,OAAO,EAAE,CAAC;IAC5B,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;CAC9B;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CACzB,IAAI,EAAE,MAAM,EACZ,IAAI,GAAE,kBAAuB,GAC5B,MAAM,EAAE,CAyBV;AAED,MAAM,WAAW,kBAAkB;IACjC,mBAAmB;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,OAAO,EAAE,CAAC;IAC5B,KAAK,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;CAClC;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,IAAI,EACxC,IAAI,GAAE,kBAAuB,GAC5B,MAAM,IAAI,CAmBZ"}
|
package/dist/cli/devReap.js
CHANGED
|
@@ -155,7 +155,7 @@ export function killSubtree(root, opts = {}) {
|
|
|
155
155
|
* its own watchdog on the first tick.
|
|
156
156
|
*/
|
|
157
157
|
export function startOrphanWatch(onOrphan, opts = {}) {
|
|
158
|
-
const { intervalMs = 3000, procTable = readProcTable, alive = isAlive } = opts;
|
|
158
|
+
const { intervalMs = 3000, procTable = readProcTable, alive = isAlive, } = opts;
|
|
159
159
|
const chain = ancestorsOf(process.pid, procTable());
|
|
160
160
|
if (chain.length === 0)
|
|
161
161
|
return () => undefined;
|
package/dist/cli/devReap.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"devReap.js","sourceRoot":"","sources":["../../src/cli/devReap.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"devReap.js","sourceRoot":"","sources":["../../src/cli/devReap.ts"],"names":[],"mappings":"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"}
|
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.985";
|
|
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;AAUD,wBAAgB,MAAM,CAAC,MAAM,EAAE,aAAa;iBAoE3B,MAAM,YAAY,UAAU;;;;;;;;;;;;;;;;;EAyM5C"}
|
|
@@ -3,6 +3,11 @@ import { useCallback, useEffect, useRef, useState } from 'react';
|
|
|
3
3
|
import { ttsRevealIndex } from '../../shared/Audio.js';
|
|
4
4
|
import { AudioPlayer } from './AudioPlayer.js';
|
|
5
5
|
let streamCounter = 0;
|
|
6
|
+
// If no tts frame (chunk/viseme/word/done) arrives within this window after
|
|
7
|
+
// tts:start (or since the last frame), the stream is dead — the socket dropped
|
|
8
|
+
// silently or the server hung. Fail loudly instead of leaving `playing` true
|
|
9
|
+
// forever with the rAF loop spinning and the socket held.
|
|
10
|
+
const WATCHDOG_MS = 30_000;
|
|
6
11
|
export function useTTS(socket) {
|
|
7
12
|
const [playing, setPlaying] = useState(false);
|
|
8
13
|
const [currentWord, setCurrentWord] = useState('');
|
|
@@ -14,6 +19,7 @@ export function useTTS(socket) {
|
|
|
14
19
|
const streamIdRef = useRef(null);
|
|
15
20
|
const unsubsRef = useRef([]);
|
|
16
21
|
const rafRef = useRef(null);
|
|
22
|
+
const watchdogRef = useRef(null);
|
|
17
23
|
// Lightweight diagnostics (exposed via getStatus) — frames received + events
|
|
18
24
|
// dispatched, so apps/e2e can see exactly where TTS breaks.
|
|
19
25
|
const statsRef = useRef({
|
|
@@ -44,6 +50,12 @@ export function useTTS(socket) {
|
|
|
44
50
|
rafRef.current = null;
|
|
45
51
|
}
|
|
46
52
|
}
|
|
53
|
+
function clearWatchdog() {
|
|
54
|
+
if (watchdogRef.current) {
|
|
55
|
+
clearTimeout(watchdogRef.current);
|
|
56
|
+
watchdogRef.current = null;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
47
59
|
// Create + resume the AudioContext from inside a user gesture (e.g. the
|
|
48
60
|
// user's send-click). Browsers (esp. Safari) only unlock audio when resume()
|
|
49
61
|
// runs within a gesture; the TTS reply itself plays later, async, so without
|
|
@@ -91,6 +103,7 @@ export function useTTS(socket) {
|
|
|
91
103
|
return;
|
|
92
104
|
streamIdRef.current = null;
|
|
93
105
|
stopRaf();
|
|
106
|
+
clearWatchdog();
|
|
94
107
|
// Flush any remaining caption indices so the text fully reveals.
|
|
95
108
|
while (wi < words.length)
|
|
96
109
|
options?.onSubtitleIndex?.(words[wi++].charEnd);
|
|
@@ -99,6 +112,18 @@ export function useTTS(socket) {
|
|
|
99
112
|
cleanupListeners();
|
|
100
113
|
socket.release();
|
|
101
114
|
};
|
|
115
|
+
// Re-armed by every incoming tts frame; if it ever fires, the stream
|
|
116
|
+
// went silent mid-playback and we fail it like a tts:error.
|
|
117
|
+
const armWatchdog = () => {
|
|
118
|
+
clearWatchdog();
|
|
119
|
+
watchdogRef.current = setTimeout(() => {
|
|
120
|
+
watchdogRef.current = null;
|
|
121
|
+
if (streamIdRef.current !== streamId)
|
|
122
|
+
return;
|
|
123
|
+
setError('TTS stream timed out — no response from server');
|
|
124
|
+
finish();
|
|
125
|
+
}, WATCHDOG_MS);
|
|
126
|
+
};
|
|
102
127
|
const tick = () => {
|
|
103
128
|
if (streamIdRef.current !== streamId)
|
|
104
129
|
return;
|
|
@@ -136,9 +161,21 @@ export function useTTS(socket) {
|
|
|
136
161
|
await socket.acquire();
|
|
137
162
|
cleanupListeners();
|
|
138
163
|
stopRaf();
|
|
164
|
+
const unsubClose = socket.onClose?.(() => {
|
|
165
|
+
// The transport dropped mid-stream: no more frames will arrive (the
|
|
166
|
+
// stream is dead server-side and is not replayed after a reconnect).
|
|
167
|
+
// Fail like a tts:error instead of hanging in `playing` forever. A
|
|
168
|
+
// stream that already got tts:done is just draining its audio queue —
|
|
169
|
+
// let the rAF loop finish it normally.
|
|
170
|
+
if (streamIdRef.current !== streamId || serverDone)
|
|
171
|
+
return;
|
|
172
|
+
setError('connection lost');
|
|
173
|
+
finish();
|
|
174
|
+
});
|
|
139
175
|
unsubsRef.current.push(socket.on('tts:chunk', (data) => {
|
|
140
176
|
if (data.streamId !== streamId)
|
|
141
177
|
return;
|
|
178
|
+
armWatchdog();
|
|
142
179
|
statsRef.current.chunks++;
|
|
143
180
|
void player.feedChunk(data.audio);
|
|
144
181
|
if (data.visemes) {
|
|
@@ -151,6 +188,7 @@ export function useTTS(socket) {
|
|
|
151
188
|
}), socket.on('tts:viseme', (data) => {
|
|
152
189
|
if (data.streamId !== streamId)
|
|
153
190
|
return;
|
|
191
|
+
armWatchdog();
|
|
154
192
|
for (const v of data.visemes) {
|
|
155
193
|
visemes.push(v);
|
|
156
194
|
statsRef.current.visemes++;
|
|
@@ -159,6 +197,7 @@ export function useTTS(socket) {
|
|
|
159
197
|
}), socket.on('tts:word', (data) => {
|
|
160
198
|
if (data.streamId !== streamId)
|
|
161
199
|
return;
|
|
200
|
+
armWatchdog();
|
|
162
201
|
statsRef.current.words++;
|
|
163
202
|
setCurrentWord(data.word);
|
|
164
203
|
const ce = data.charEndIndex;
|
|
@@ -170,13 +209,17 @@ export function useTTS(socket) {
|
|
|
170
209
|
if (data.streamId !== streamId)
|
|
171
210
|
return;
|
|
172
211
|
serverDone = true;
|
|
212
|
+
// The stream ended cleanly; the audio tail is driven by the rAF
|
|
213
|
+
// loop, so silence from here on is expected, not a failure.
|
|
214
|
+
clearWatchdog();
|
|
173
215
|
}), socket.on('tts:error', (data) => {
|
|
174
216
|
if (data.streamId !== streamId)
|
|
175
217
|
return;
|
|
176
218
|
setError(data.message);
|
|
177
219
|
finish();
|
|
178
|
-
}));
|
|
220
|
+
}), ...(unsubClose ? [unsubClose] : []));
|
|
179
221
|
rafRef.current = requestAnimationFrame(tick);
|
|
222
|
+
armWatchdog();
|
|
180
223
|
socket.send('tts:start', {
|
|
181
224
|
streamId,
|
|
182
225
|
text,
|
|
@@ -190,6 +233,7 @@ export function useTTS(socket) {
|
|
|
190
233
|
}
|
|
191
234
|
streamIdRef.current = null;
|
|
192
235
|
stopRaf();
|
|
236
|
+
clearWatchdog();
|
|
193
237
|
playerRef.current?.stop();
|
|
194
238
|
setPlaying(false);
|
|
195
239
|
cleanupListeners();
|
|
@@ -201,6 +245,7 @@ export function useTTS(socket) {
|
|
|
201
245
|
socket.send('tts:cancel', { streamId: streamIdRef.current });
|
|
202
246
|
}
|
|
203
247
|
stopRaf();
|
|
248
|
+
clearWatchdog();
|
|
204
249
|
cleanupListeners();
|
|
205
250
|
socket.release();
|
|
206
251
|
playerRef.current?.close().catch(() => { });
|
|
@@ -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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"}
|
|
@@ -5,6 +5,14 @@ type UglyBotMsgKey = keyof UglyBotMessages;
|
|
|
5
5
|
export interface UglyBotSocket {
|
|
6
6
|
send<K extends UglyBotMsgKey>(type: K, data: MessageData<UglyBotMessages[K]>): void;
|
|
7
7
|
on<K extends UglyBotMsgKey>(event: K, handler: (data: MessageData<UglyBotMessages[K]>) => void): Unsubscribe;
|
|
8
|
+
/**
|
|
9
|
+
* Fired when the direct WebSocket transport drops unexpectedly (not after a
|
|
10
|
+
* deliberate release-driven close). Any in-flight streams on it are dead —
|
|
11
|
+
* listeners should fail them rather than wait for frames that never come.
|
|
12
|
+
* The native host transport manages its own socket and never fires this.
|
|
13
|
+
* Optional so hand-rolled socket stubs keep type-checking.
|
|
14
|
+
*/
|
|
15
|
+
onClose?(handler: () => void): Unsubscribe;
|
|
8
16
|
acquire(): Promise<void>;
|
|
9
17
|
release(): void;
|
|
10
18
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"uglyBotSocket.d.ts","sourceRoot":"","sources":["../../src/client/uglyBotSocket.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAIpE,KAAK,WAAW,GAAG,MAAM,IAAI,CAAC;AAC9B,KAAK,aAAa,GAAG,MAAM,eAAe,CAAC;AAE3C,MAAM,WAAW,aAAa;IAC5B,IAAI,CAAC,CAAC,SAAS,aAAa,EAC1B,IAAI,EAAE,CAAC,EACP,IAAI,EAAE,WAAW,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,GACpC,IAAI,CAAC;IACR,EAAE,CAAC,CAAC,SAAS,aAAa,EACxB,KAAK,EAAE,CAAC,EACR,OAAO,EAAE,CAAC,IAAI,EAAE,WAAW,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,KAAK,IAAI,GACvD,WAAW,CAAC;IACf,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACzB,OAAO,IAAI,IAAI,CAAC;CACjB;
|
|
1
|
+
{"version":3,"file":"uglyBotSocket.d.ts","sourceRoot":"","sources":["../../src/client/uglyBotSocket.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAIpE,KAAK,WAAW,GAAG,MAAM,IAAI,CAAC;AAC9B,KAAK,aAAa,GAAG,MAAM,eAAe,CAAC;AAE3C,MAAM,WAAW,aAAa;IAC5B,IAAI,CAAC,CAAC,SAAS,aAAa,EAC1B,IAAI,EAAE,CAAC,EACP,IAAI,EAAE,WAAW,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,GACpC,IAAI,CAAC;IACR,EAAE,CAAC,CAAC,SAAS,aAAa,EACxB,KAAK,EAAE,CAAC,EACR,OAAO,EAAE,CAAC,IAAI,EAAE,WAAW,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,KAAK,IAAI,GACvD,WAAW,CAAC;IACf;;;;;;OAMG;IACH,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,IAAI,GAAG,WAAW,CAAC;IAC3C,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACzB,OAAO,IAAI,IAAI,CAAC;CACjB;AA2BD,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE,MAAM,GACf,aAAa,CAgQf"}
|
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
import { native, isNativeAvailable } from '../native/index.js';
|
|
2
2
|
const IDLE_TIMEOUT_MS = 10_000;
|
|
3
|
+
// Keep-alive cadence: well under the ~60s the edge lets a frame-less socket
|
|
4
|
+
// live (createSocket.ts uses the same idiom at 30s), so an acquired-but-idle
|
|
5
|
+
// TTS/STT socket stops being recycled out from under an active stream.
|
|
6
|
+
const PING_INTERVAL_MS = 25_000;
|
|
7
|
+
// Must byte-match the edge auto-response request registered in
|
|
8
|
+
// src/server/adapter/workers/createWorkersApp.ts (PING_FRAME /
|
|
9
|
+
// setWebSocketAutoResponse): exactly `{"type":"ping","data":{}}`. The edge
|
|
10
|
+
// then answers with a pong without waking the Durable Object; a non-matching
|
|
11
|
+
// frame still gets answered by the `msg.type === 'ping'` fallback there.
|
|
12
|
+
const PING_FRAME = JSON.stringify({ type: 'ping', data: {} });
|
|
13
|
+
// Reconnect backoff after an unexpected drop — createSocket.ts's idiom
|
|
14
|
+
// (1s doubling, capped), at a smaller scale.
|
|
15
|
+
const RECONNECT_BASE_MS = 1_000;
|
|
16
|
+
const RECONNECT_MAX_MS = 30_000;
|
|
3
17
|
/** True when a native host rejected `uglybot.openStream` because it doesn't
|
|
4
18
|
* implement the channel (older shell) — fall back to a direct ugly.bot socket. */
|
|
5
19
|
function isHostMissingUglyBot(e) {
|
|
@@ -12,42 +26,124 @@ export function createUglyBotSocket(appToken, baseUrl) {
|
|
|
12
26
|
// Listeners are transport-agnostic: both the native and the direct transport
|
|
13
27
|
// dispatch incoming `{type,data}` frames here.
|
|
14
28
|
const listeners = new Map();
|
|
29
|
+
function subscribe(event, handler) {
|
|
30
|
+
let set = listeners.get(event);
|
|
31
|
+
if (!set) {
|
|
32
|
+
set = new Set();
|
|
33
|
+
listeners.set(event, set);
|
|
34
|
+
}
|
|
35
|
+
set.add(handler);
|
|
36
|
+
return () => {
|
|
37
|
+
set.delete(handler);
|
|
38
|
+
if (set.size === 0)
|
|
39
|
+
listeners.delete(event);
|
|
40
|
+
};
|
|
41
|
+
}
|
|
15
42
|
function dispatch(type, data) {
|
|
16
43
|
const set = listeners.get(type);
|
|
17
44
|
if (set)
|
|
18
45
|
for (const handler of set)
|
|
19
46
|
handler(data);
|
|
20
47
|
}
|
|
48
|
+
// Reserved pseudo-event for transport drops — not a wire message, so it is
|
|
49
|
+
// not part of UglyBotMessages and is only reachable via `onClose`.
|
|
50
|
+
const CLOSE_EVENT = 'close';
|
|
21
51
|
// ─── Direct ugly.bot WebSocket transport (plain browser / no shell) ──────────
|
|
22
52
|
let ws = null;
|
|
53
|
+
// Socket whose handshake is still in flight; lets the error/close handlers
|
|
54
|
+
// tell "the pending connect failed" from "a stale event on an old socket".
|
|
55
|
+
let pendingSocket = null;
|
|
23
56
|
let connectPromise = null;
|
|
57
|
+
let pingTimer = null;
|
|
58
|
+
let reconnectTimer = null;
|
|
59
|
+
let reconnectAttempts = 0;
|
|
60
|
+
// Set by directClose() so the ensuing close event isn't mistaken for a drop
|
|
61
|
+
// (which would fire onClose + schedule a reconnect). Re-armed per connect.
|
|
62
|
+
let intentionalClose = false;
|
|
63
|
+
function stopPing() {
|
|
64
|
+
if (pingTimer) {
|
|
65
|
+
clearInterval(pingTimer);
|
|
66
|
+
pingTimer = null;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
function startPing() {
|
|
70
|
+
stopPing();
|
|
71
|
+
pingTimer = setInterval(() => {
|
|
72
|
+
if (refCount === 0 || ws?.readyState !== WebSocket.OPEN)
|
|
73
|
+
return;
|
|
74
|
+
ws.send(PING_FRAME);
|
|
75
|
+
}, PING_INTERVAL_MS);
|
|
76
|
+
}
|
|
77
|
+
function cancelReconnect() {
|
|
78
|
+
if (reconnectTimer) {
|
|
79
|
+
clearTimeout(reconnectTimer);
|
|
80
|
+
reconnectTimer = null;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
// Unexpected drop while still acquired: retry with capped exponential
|
|
84
|
+
// backoff. In-flight streams are NOT replayed — consumers were already
|
|
85
|
+
// notified via onClose and should have failed them.
|
|
86
|
+
function scheduleReconnect() {
|
|
87
|
+
cancelReconnect();
|
|
88
|
+
const delay = Math.min(RECONNECT_MAX_MS, RECONNECT_BASE_MS * Math.pow(2, reconnectAttempts));
|
|
89
|
+
reconnectTimer = setTimeout(() => {
|
|
90
|
+
reconnectTimer = null;
|
|
91
|
+
reconnectAttempts++;
|
|
92
|
+
void directEnsure().catch(() => { });
|
|
93
|
+
}, delay);
|
|
94
|
+
}
|
|
24
95
|
function directEnsure() {
|
|
25
96
|
if (ws?.readyState === WebSocket.OPEN)
|
|
26
97
|
return Promise.resolve();
|
|
27
98
|
if (connectPromise)
|
|
28
99
|
return connectPromise;
|
|
100
|
+
intentionalClose = false;
|
|
29
101
|
connectPromise = new Promise((resolve, reject) => {
|
|
30
102
|
const httpBase = baseUrl ?? 'https://ugly.bot';
|
|
31
103
|
const wsBase = httpBase.replace(/^http/, 'ws');
|
|
32
104
|
const url = `${wsBase}/ws?token=${encodeURIComponent(appToken)}`;
|
|
33
105
|
console.log(`[UglyBotSocket] connecting to ${url.replace(/token=[^&]+/, 'token=***')}`);
|
|
34
106
|
const socket = new WebSocket(url);
|
|
107
|
+
pendingSocket = socket;
|
|
35
108
|
socket.addEventListener('open', () => {
|
|
36
109
|
console.log('[UglyBotSocket] connected');
|
|
37
110
|
ws = socket;
|
|
111
|
+
pendingSocket = null;
|
|
38
112
|
connectPromise = null;
|
|
113
|
+
reconnectAttempts = 0;
|
|
114
|
+
startPing();
|
|
39
115
|
resolve();
|
|
40
116
|
});
|
|
41
117
|
socket.addEventListener('error', (e) => {
|
|
118
|
+
// An error on an ESTABLISHED or STALE socket is always followed by
|
|
119
|
+
// 'close', which owns drop handling (onClose + reconnect) — don't
|
|
120
|
+
// double-report, and never reject a NEWER connect's promise.
|
|
121
|
+
if (pendingSocket !== socket)
|
|
122
|
+
return;
|
|
42
123
|
console.error('[UglyBotSocket] connection error', e);
|
|
43
124
|
connectPromise = null;
|
|
44
125
|
reject(new Error('ugly.bot WebSocket connection failed'));
|
|
45
126
|
});
|
|
46
127
|
socket.addEventListener('close', (e) => {
|
|
47
128
|
console.log(`[UglyBotSocket] closed code=${e.code} reason=${e.reason}`);
|
|
129
|
+
// Stale close (a newer direct connection already replaced this one):
|
|
130
|
+
// it must not fail streams or clobber the new connect's bookkeeping.
|
|
131
|
+
if (ws !== socket && pendingSocket !== socket)
|
|
132
|
+
return;
|
|
48
133
|
if (ws === socket)
|
|
49
134
|
ws = null;
|
|
135
|
+
if (pendingSocket === socket)
|
|
136
|
+
pendingSocket = null;
|
|
50
137
|
connectPromise = null;
|
|
138
|
+
stopPing();
|
|
139
|
+
if (intentionalClose)
|
|
140
|
+
return;
|
|
141
|
+
// Notify BEFORE checking refCount: a listener that fails its stream
|
|
142
|
+
// will release(), and if that drops the count to zero there is no one
|
|
143
|
+
// left to reconnect for.
|
|
144
|
+
dispatch(CLOSE_EVENT, undefined);
|
|
145
|
+
if (refCount > 0)
|
|
146
|
+
scheduleReconnect();
|
|
51
147
|
});
|
|
52
148
|
socket.addEventListener('message', (event) => {
|
|
53
149
|
const msg = JSON.parse(event.data);
|
|
@@ -73,6 +169,15 @@ export function createUglyBotSocket(appToken, baseUrl) {
|
|
|
73
169
|
}
|
|
74
170
|
}
|
|
75
171
|
function directClose() {
|
|
172
|
+
intentionalClose = true;
|
|
173
|
+
cancelReconnect();
|
|
174
|
+
stopPing();
|
|
175
|
+
// A release can land while a (re)connect handshake is still in flight —
|
|
176
|
+
// abort it too, or the orphan socket opens with no one holding it.
|
|
177
|
+
if (pendingSocket) {
|
|
178
|
+
pendingSocket.close();
|
|
179
|
+
pendingSocket = null;
|
|
180
|
+
}
|
|
76
181
|
if (ws) {
|
|
77
182
|
ws.close();
|
|
78
183
|
ws = null;
|
|
@@ -154,17 +259,10 @@ export function createUglyBotSocket(appToken, baseUrl) {
|
|
|
154
259
|
send(type, data);
|
|
155
260
|
},
|
|
156
261
|
on(event, handler) {
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
}
|
|
162
|
-
set.add(handler);
|
|
163
|
-
return () => {
|
|
164
|
-
set.delete(handler);
|
|
165
|
-
if (set.size === 0)
|
|
166
|
-
listeners.delete(event);
|
|
167
|
-
};
|
|
262
|
+
return subscribe(event, handler);
|
|
263
|
+
},
|
|
264
|
+
onClose(handler) {
|
|
265
|
+
return subscribe(CLOSE_EVENT, handler);
|
|
168
266
|
},
|
|
169
267
|
async acquire() {
|
|
170
268
|
clearIdle();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"uglyBotSocket.js","sourceRoot":"","sources":["../../src/client/uglyBotSocket.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,MAAM,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"uglyBotSocket.js","sourceRoot":"","sources":["../../src/client/uglyBotSocket.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,MAAM,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AA0B/D,MAAM,eAAe,GAAG,MAAM,CAAC;AAC/B,4EAA4E;AAC5E,6EAA6E;AAC7E,uEAAuE;AACvE,MAAM,gBAAgB,GAAG,MAAM,CAAC;AAChC,+DAA+D;AAC/D,+DAA+D;AAC/D,2EAA2E;AAC3E,6EAA6E;AAC7E,yEAAyE;AACzE,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;AAC9D,uEAAuE;AACvE,6CAA6C;AAC7C,MAAM,iBAAiB,GAAG,KAAK,CAAC;AAChC,MAAM,gBAAgB,GAAG,MAAM,CAAC;AAEhC;mFACmF;AACnF,SAAS,oBAAoB,CAAC,CAAU;IACtC,MAAM,GAAG,GAAG,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;IACvD,OAAO,mEAAmE,CAAC,IAAI,CAC7E,GAAG,CACJ,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,mBAAmB,CACjC,QAAgB,EAChB,OAAgB;IAEhB,IAAI,QAAQ,GAAG,CAAC,CAAC;IACjB,IAAI,SAAS,GAAyC,IAAI,CAAC;IAE3D,6EAA6E;IAC7E,+CAA+C;IAC/C,MAAM,SAAS,GAAG,IAAI,GAAG,EAAwC,CAAC;IAClE,SAAS,SAAS,CAChB,KAAa,EACb,OAAgC;QAEhC,IAAI,GAAG,GAAG,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAC/B,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,GAAG,GAAG,IAAI,GAAG,EAAE,CAAC;YAChB,SAAS,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;QAC5B,CAAC;QACD,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QACjB,OAAO,GAAG,EAAE;YACV,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;YACpB,IAAI,GAAG,CAAC,IAAI,KAAK,CAAC;gBAAE,SAAS,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAC9C,CAAC,CAAC;IACJ,CAAC;IACD,SAAS,QAAQ,CAAC,IAAY,EAAE,IAAa;QAC3C,MAAM,GAAG,GAAG,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAChC,IAAI,GAAG;YAAE,KAAK,MAAM,OAAO,IAAI,GAAG;gBAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IACpD,CAAC;IACD,2EAA2E;IAC3E,mEAAmE;IACnE,MAAM,WAAW,GAAG,OAAO,CAAC;IAE5B,gFAAgF;IAChF,IAAI,EAAE,GAAqB,IAAI,CAAC;IAChC,2EAA2E;IAC3E,2EAA2E;IAC3E,IAAI,aAAa,GAAqB,IAAI,CAAC;IAC3C,IAAI,cAAc,GAAyB,IAAI,CAAC;IAChD,IAAI,SAAS,GAA0C,IAAI,CAAC;IAC5D,IAAI,cAAc,GAAyC,IAAI,CAAC;IAChE,IAAI,iBAAiB,GAAG,CAAC,CAAC;IAC1B,4EAA4E;IAC5E,2EAA2E;IAC3E,IAAI,gBAAgB,GAAG,KAAK,CAAC;IAE7B,SAAS,QAAQ;QACf,IAAI,SAAS,EAAE,CAAC;YACd,aAAa,CAAC,SAAS,CAAC,CAAC;YACzB,SAAS,GAAG,IAAI,CAAC;QACnB,CAAC;IACH,CAAC;IACD,SAAS,SAAS;QAChB,QAAQ,EAAE,CAAC;QACX,SAAS,GAAG,WAAW,CAAC,GAAG,EAAE;YAC3B,IAAI,QAAQ,KAAK,CAAC,IAAI,EAAE,EAAE,UAAU,KAAK,SAAS,CAAC,IAAI;gBAAE,OAAO;YAChE,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QACtB,CAAC,EAAE,gBAAgB,CAAC,CAAC;IACvB,CAAC;IACD,SAAS,eAAe;QACtB,IAAI,cAAc,EAAE,CAAC;YACnB,YAAY,CAAC,cAAc,CAAC,CAAC;YAC7B,cAAc,GAAG,IAAI,CAAC;QACxB,CAAC;IACH,CAAC;IACD,sEAAsE;IACtE,uEAAuE;IACvE,oDAAoD;IACpD,SAAS,iBAAiB;QACxB,eAAe,EAAE,CAAC;QAClB,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CACpB,gBAAgB,EAChB,iBAAiB,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,iBAAiB,CAAC,CACnD,CAAC;QACF,cAAc,GAAG,UAAU,CAAC,GAAG,EAAE;YAC/B,cAAc,GAAG,IAAI,CAAC;YACtB,iBAAiB,EAAE,CAAC;YACpB,KAAK,YAAY,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QACtC,CAAC,EAAE,KAAK,CAAC,CAAC;IACZ,CAAC;IAED,SAAS,YAAY;QACnB,IAAI,EAAE,EAAE,UAAU,KAAK,SAAS,CAAC,IAAI;YAAE,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;QAChE,IAAI,cAAc;YAAE,OAAO,cAAc,CAAC;QAC1C,gBAAgB,GAAG,KAAK,CAAC;QACzB,cAAc,GAAG,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACrD,MAAM,QAAQ,GAAG,OAAO,IAAI,kBAAkB,CAAC;YAC/C,MAAM,MAAM,GAAG,QAAQ,CAAC,OAAO,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;YAC/C,MAAM,GAAG,GAAG,GAAG,MAAM,aAAa,kBAAkB,CAAC,QAAQ,CAAC,EAAE,CAAC;YACjE,OAAO,CAAC,GAAG,CACT,iCAAiC,GAAG,CAAC,OAAO,CAAC,aAAa,EAAE,WAAW,CAAC,EAAE,CAC3E,CAAC;YACF,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC,GAAG,CAAC,CAAC;YAClC,aAAa,GAAG,MAAM,CAAC;YACvB,MAAM,CAAC,gBAAgB,CAAC,MAAM,EAAE,GAAG,EAAE;gBACnC,OAAO,CAAC,GAAG,CAAC,2BAA2B,CAAC,CAAC;gBACzC,EAAE,GAAG,MAAM,CAAC;gBACZ,aAAa,GAAG,IAAI,CAAC;gBACrB,cAAc,GAAG,IAAI,CAAC;gBACtB,iBAAiB,GAAG,CAAC,CAAC;gBACtB,SAAS,EAAE,CAAC;gBACZ,OAAO,EAAE,CAAC;YACZ,CAAC,CAAC,CAAC;YACH,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,CAAC,CAAC,EAAE,EAAE;gBACrC,mEAAmE;gBACnE,kEAAkE;gBAClE,6DAA6D;gBAC7D,IAAI,aAAa,KAAK,MAAM;oBAAE,OAAO;gBACrC,OAAO,CAAC,KAAK,CAAC,kCAAkC,EAAE,CAAC,CAAC,CAAC;gBACrD,cAAc,GAAG,IAAI,CAAC;gBACtB,MAAM,CAAC,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC,CAAC;YAC5D,CAAC,CAAC,CAAC;YACH,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,CAAC,CAAC,EAAE,EAAE;gBACrC,OAAO,CAAC,GAAG,CAAC,+BAA+B,CAAC,CAAC,IAAI,WAAW,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;gBACxE,qEAAqE;gBACrE,qEAAqE;gBACrE,IAAI,EAAE,KAAK,MAAM,IAAI,aAAa,KAAK,MAAM;oBAAE,OAAO;gBACtD,IAAI,EAAE,KAAK,MAAM;oBAAE,EAAE,GAAG,IAAI,CAAC;gBAC7B,IAAI,aAAa,KAAK,MAAM;oBAAE,aAAa,GAAG,IAAI,CAAC;gBACnD,cAAc,GAAG,IAAI,CAAC;gBACtB,QAAQ,EAAE,CAAC;gBACX,IAAI,gBAAgB;oBAAE,OAAO;gBAC7B,oEAAoE;gBACpE,sEAAsE;gBACtE,yBAAyB;gBACzB,QAAQ,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC;gBACjC,IAAI,QAAQ,GAAG,CAAC;oBAAE,iBAAiB,EAAE,CAAC;YACxC,CAAC,CAAC,CAAC;YACH,MAAM,CAAC,gBAAgB,CAAC,SAAS,EAAE,CAAC,KAAmB,EAAE,EAAE;gBACzD,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,IAAc,CAA4B,CAAC;gBACxE,MAAM,IAAI,GAAG,GAAG,CAAC,IAAc,CAAC;gBAChC,uEAAuE;gBACvE,MAAM,IAAI,GACR,GAAG,CAAC,IAAI,KAAK,SAAS;oBACpB,CAAC,CAAC,GAAG,CAAC,IAAI;oBACV,CAAC,CAAC,CAAC,GAAG,EAAE;wBACJ,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,GAAG,IAAI,EAAE,GAAG,GAAG,CAAC;wBAClC,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;oBAClD,CAAC,CAAC,EAAE,CAAC;gBACX,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;YACvB,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QACH,OAAO,cAAc,CAAC;IACxB,CAAC;IACD,SAAS,UAAU,CAAC,IAAY,EAAE,IAAa;QAC7C,IAAI,EAAE,EAAE,UAAU,KAAK,SAAS,CAAC,IAAI,EAAE,CAAC;YACtC,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;QAC1C,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,KAAK,CACX,gCAAgC,IAAI,SAAS,EAAE,CAAC,CAAC,CAAC,cAAc,EAAE,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,MAAM,EAAE,CAC3F,CAAC;QACJ,CAAC;IACH,CAAC;IACD,SAAS,WAAW;QAClB,gBAAgB,GAAG,IAAI,CAAC;QACxB,eAAe,EAAE,CAAC;QAClB,QAAQ,EAAE,CAAC;QACX,wEAAwE;QACxE,mEAAmE;QACnE,IAAI,aAAa,EAAE,CAAC;YAClB,aAAa,CAAC,KAAK,EAAE,CAAC;YACtB,aAAa,GAAG,IAAI,CAAC;QACvB,CAAC;QACD,IAAI,EAAE,EAAE,CAAC;YACP,EAAE,CAAC,KAAK,EAAE,CAAC;YACX,EAAE,GAAG,IAAI,CAAC;QACZ,CAAC;QACD,cAAc,GAAG,IAAI,CAAC;IACxB,CAAC;IAED,gFAAgF;IAChF,IAAI,MAAM,GAAyB,IAAI,CAAC;IACxC,IAAI,aAAa,GAAyB,IAAI,CAAC;IAE/C,SAAS,YAAY;QACnB,IAAI,MAAM;YAAE,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;QACrC,IAAI,aAAa;YAAE,OAAO,aAAa,CAAC;QACxC,aAAa,GAAG,MAAM,CAAC,OAAO;aAC3B,UAAU,EAAE;aACZ,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE;YACV,CAAC,CAAC,SAAS,CAAC,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC;YAClD,MAAM,GAAG,CAAC,CAAC;YACX,aAAa,GAAG,IAAI,CAAC;QACvB,CAAC,CAAC;aACD,KAAK,CAAC,CAAC,CAAU,EAAE,EAAE;YACpB,aAAa,GAAG,IAAI,CAAC;YACrB,MAAM,CAAC,CAAC;QACV,CAAC,CAAC,CAAC;QACL,OAAO,aAAa,CAAC;IACvB,CAAC;IAED,+EAA+E;IAC/E,uEAAuE;IACvE,IAAI,SAAS,GAAmB,IAAI,CAAC;IAErC,KAAK,UAAU,MAAM;QACnB,IAAI,SAAS,KAAK,IAAI;YAAE,SAAS,GAAG,iBAAiB,EAAE,CAAC;QACxD,IAAI,SAAS,EAAE,CAAC;YACd,IAAI,CAAC;gBACH,MAAM,YAAY,EAAE,CAAC;gBACrB,OAAO;YACT,CAAC;YAAC,OAAO,CAAC,EAAE,CAAC;gBACX,IAAI,CAAC,oBAAoB,CAAC,CAAC,CAAC;oBAAE,MAAM,CAAC,CAAC;gBACtC,0EAA0E;gBAC1E,OAAO,CAAC,IAAI,CACV,yEAAyE,CAC1E,CAAC;gBACF,SAAS,GAAG,KAAK,CAAC;YACpB,CAAC;QACH,CAAC;QACD,MAAM,YAAY,EAAE,CAAC;IACvB,CAAC;IACD,SAAS,IAAI,CAAC,IAAY,EAAE,IAAa;QACvC,IAAI,SAAS,IAAI,MAAM;YAAE,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;;YAC5C,UAAU,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IAC9B,CAAC;IACD,SAAS,KAAK;QACZ,IAAI,MAAM,EAAE,CAAC;YACX,KAAK,MAAM,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;YACpC,MAAM,GAAG,IAAI,CAAC;YACd,aAAa,GAAG,IAAI,CAAC;QACvB,CAAC;QACD,WAAW,EAAE,CAAC;IAChB,CAAC;IAED,SAAS,SAAS;QAChB,IAAI,SAAS,EAAE,CAAC;YACd,YAAY,CAAC,SAAS,CAAC,CAAC;YACxB,SAAS,GAAG,IAAI,CAAC;QACnB,CAAC;IACH,CAAC;IACD,SAAS,SAAS;QAChB,SAAS,EAAE,CAAC;QACZ,SAAS,GAAG,UAAU,CAAC,GAAG,EAAE;YAC1B,SAAS,GAAG,IAAI,CAAC;YACjB,IAAI,QAAQ,KAAK,CAAC;gBAAE,KAAK,EAAE,CAAC;QAC9B,CAAC,EAAE,eAAe,CAAC,CAAC;IACtB,CAAC;IAED,OAAO;QACL,IAAI,CAAC,IAAI,EAAE,IAAI;YACb,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QACnB,CAAC;QACD,EAAE,CAAC,KAAK,EAAE,OAAO;YACf,OAAO,SAAS,CAAC,KAAK,EAAE,OAAkC,CAAC,CAAC;QAC9D,CAAC;QACD,OAAO,CAAC,OAAO;YACb,OAAO,SAAS,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;QACzC,CAAC;QACD,KAAK,CAAC,OAAO;YACX,SAAS,EAAE,CAAC;YACZ,QAAQ,EAAE,CAAC;YACX,MAAM,MAAM,EAAE,CAAC;QACjB,CAAC;QACD,OAAO;YACL,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,QAAQ,GAAG,CAAC,CAAC,CAAC;YACrC,IAAI,QAAQ,KAAK,CAAC;gBAAE,SAAS,EAAE,CAAC;QAClC,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ConsolePanel.d.ts","sourceRoot":"","sources":["../../../../src/inspect/ui/panels/ConsolePanel.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"ConsolePanel.d.ts","sourceRoot":"","sources":["../../../../src/inspect/ui/panels/ConsolePanel.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAwBrD,wBAAgB,YAAY,CAAC,EAC3B,MAAM,EACN,QAAQ,GACT,EAAE;IACD,MAAM,EAAE,aAAa,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;CAClB,+BAsHA"}
|
|
@@ -15,12 +15,24 @@ export function ConsolePanel({ client, targetId, }) {
|
|
|
15
15
|
const [repl, setRepl] = useState([]);
|
|
16
16
|
const [input, setInput] = useState('');
|
|
17
17
|
const scrollRef = useRef(null);
|
|
18
|
+
// ⚠️ NOT `seq` — THAT IS NOT UNIQUE HERE. `seq` counts inside the page's
|
|
19
|
+
// console agent, and a fresh document gets a fresh agent, so it RESTARTS AT 1
|
|
20
|
+
// on every navigation. This list spans navigations (the target is a tab, and
|
|
21
|
+
// the tab did not change), so keying rows by seq gave two `c1`s: React warned
|
|
22
|
+
// that children "may be duplicated and/or omitted" and then omitted them —
|
|
23
|
+
// reported as "I cannot scroll to the end of the console", because the end was
|
|
24
|
+
// what stopped rendering. This counter is monotonic for the life of the list,
|
|
25
|
+
// which is the scope the keys have to be unique in.
|
|
26
|
+
const uid = useRef(0);
|
|
18
27
|
useEffect(() => {
|
|
19
28
|
let live = true;
|
|
20
29
|
setEntries([]);
|
|
21
30
|
setRepl([]);
|
|
22
|
-
|
|
23
|
-
|
|
31
|
+
const row = (e) => ({ uid: uid.current++, e });
|
|
32
|
+
void client
|
|
33
|
+
.consoleBuffer(targetId)
|
|
34
|
+
.then((b) => live && setEntries(b.map(row)));
|
|
35
|
+
const off = client.onConsole(targetId, (e) => setEntries((cur) => [...cur, row(e)].slice(-2000)));
|
|
24
36
|
return () => {
|
|
25
37
|
live = false;
|
|
26
38
|
off();
|
|
@@ -48,7 +60,7 @@ export function ConsolePanel({ client, targetId, }) {
|
|
|
48
60
|
: [...cur, { kind: 'error', text: res.exception?.text ?? 'error' }]);
|
|
49
61
|
};
|
|
50
62
|
const expand = (objectId) => client.expand(targetId, objectId);
|
|
51
|
-
return (_jsxs("div", { className: "ins-panel", children: [_jsxs("div", { className: "ins-scroll", ref: scrollRef, children: [entries.length === 0 && repl.length === 0 && (_jsxs("div", { className: "ins-empty", children: [_jsx("b", { children: "Console" }), "console output streams here \u00B7 type below to evaluate"] })), entries.map((e) => (_jsxs("div", { className: "ins-log-row", "data-level": e.level, children: [_jsx("span", { className: "ins-log-gutter", children: GUTTER[e.level] ?? '·' }), _jsx("span", { className: "ins-log-body", children: e.text }), e.source?.url && (_jsxs("span", { className: "ins-log-src", children: [shortUrl(e.source.url), ":", e.source.line] }))] }, `c${
|
|
63
|
+
return (_jsxs("div", { className: "ins-panel", children: [_jsxs("div", { className: "ins-scroll", ref: scrollRef, children: [entries.length === 0 && repl.length === 0 && (_jsxs("div", { className: "ins-empty", children: [_jsx("b", { children: "Console" }), "console output streams here \u00B7 type below to evaluate"] })), entries.map(({ uid, e }) => (_jsxs("div", { className: "ins-log-row", "data-level": e.level, children: [_jsx("span", { className: "ins-log-gutter", children: GUTTER[e.level] ?? '·' }), _jsx("span", { className: "ins-log-body", children: e.text }), e.source?.url && (_jsxs("span", { className: "ins-log-src", children: [shortUrl(e.source.url), ":", e.source.line] }))] }, `c${uid}`))), repl.map((line, i) => (_jsxs("div", { className: "ins-log-row", "data-level": line.kind === 'error' ? 'error' : 'log', children: [_jsx("span", { className: "ins-log-gutter", children: line.kind === 'input' ? '›' : line.kind === 'error' ? '✕' : '‹' }), _jsx("span", { className: "ins-log-body", children: line.value ? (_jsx(RemoteValueView, { value: line.value, expand: expand, defaultOpen: true })) : (line.text) })] }, `r${i}`)))] }), _jsx("form", { className: "ins-repl", onSubmit: (e) => {
|
|
52
64
|
e.preventDefault();
|
|
53
65
|
void run();
|
|
54
66
|
}, children: _jsx("input", { value: input, onChange: (e) => setInput(e.target.value), placeholder: "evaluate expression\u2026", spellCheck: false, autoComplete: "off" }) })] }));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ConsolePanel.js","sourceRoot":"","sources":["../../../../src/inspect/ui/panels/ConsolePanel.tsx"],"names":[],"mappings":";AAAA,4EAA4E;AAC5E,gFAAgF;AAEhF,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAGpD,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAExD,MAAM,MAAM,GAA2B;IACrC,GAAG,EAAE,GAAG;IACR,IAAI,EAAE,GAAG;IACT,IAAI,EAAE,GAAG;IACT,KAAK,EAAE,GAAG;IACV,KAAK,EAAE,GAAG;CACX,CAAC;
|
|
1
|
+
{"version":3,"file":"ConsolePanel.js","sourceRoot":"","sources":["../../../../src/inspect/ui/panels/ConsolePanel.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ConsolePanel.test.d.ts","sourceRoot":"","sources":["../../../../src/inspect/ui/panels/ConsolePanel.test.tsx"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
// @vitest-environment jsdom
|
|
3
|
+
import { cleanup, render, act } from '@testing-library/react';
|
|
4
|
+
import { afterEach, describe, expect, it, vi } from 'vitest';
|
|
5
|
+
import { ConsolePanel } from './ConsolePanel.js';
|
|
6
|
+
afterEach(cleanup);
|
|
7
|
+
const entry = (seq, text) => ({ seq, level: 'log', text, ts: 0, args: [] });
|
|
8
|
+
/** A client whose live console stream we drive by hand. */
|
|
9
|
+
function fakeClient(buffer = []) {
|
|
10
|
+
let emit = () => { };
|
|
11
|
+
const client = {
|
|
12
|
+
consoleBuffer: vi.fn(async () => buffer),
|
|
13
|
+
onConsole: vi.fn((_t, h) => {
|
|
14
|
+
emit = h;
|
|
15
|
+
return () => { };
|
|
16
|
+
}),
|
|
17
|
+
eval: vi.fn(),
|
|
18
|
+
expand: vi.fn(),
|
|
19
|
+
};
|
|
20
|
+
return { client, push: (e) => act(() => emit(e)) };
|
|
21
|
+
}
|
|
22
|
+
describe('ConsolePanel', () => {
|
|
23
|
+
/**
|
|
24
|
+
* `seq` is a closure counter inside the page's console agent, so it RESTARTS
|
|
25
|
+
* AT 1 on every navigation — a fresh document gets a fresh agent. The panel
|
|
26
|
+
* keeps accumulating across those navigations (the target id, a tab, never
|
|
27
|
+
* changed), so a keyed-by-seq list ends up with two `c1`s, React warns that
|
|
28
|
+
* children "may be duplicated and/or omitted", and the tail of the log stops
|
|
29
|
+
* rendering: "I cannot scroll to the end of the console."
|
|
30
|
+
*/
|
|
31
|
+
it('renders every entry after a navigation resets the agent seq', async () => {
|
|
32
|
+
const warn = vi.spyOn(console, 'error').mockImplementation(() => { });
|
|
33
|
+
const { client, push } = fakeClient([entry(1, 'before-nav')]);
|
|
34
|
+
const { container } = render(_jsx(ConsolePanel, { client: client, targetId: "tab-1" }));
|
|
35
|
+
await act(async () => { });
|
|
36
|
+
// The page navigates; the new document's agent starts counting from 1 again.
|
|
37
|
+
push(entry(1, 'after-nav-a'));
|
|
38
|
+
push(entry(2, 'after-nav-b'));
|
|
39
|
+
const rows = Array.from(container.querySelectorAll('.ins-log-row'), (r) => r.querySelector('.ins-log-body')?.textContent);
|
|
40
|
+
expect(rows).toEqual(['before-nav', 'after-nav-a', 'after-nav-b']);
|
|
41
|
+
// React reports duplicate keys through console.error — none may be raised.
|
|
42
|
+
const dupWarning = warn.mock.calls
|
|
43
|
+
.flat()
|
|
44
|
+
.some((a) => String(a).includes('same key'));
|
|
45
|
+
expect(dupWarning, 'React warned about duplicate keys').toBe(false);
|
|
46
|
+
warn.mockRestore();
|
|
47
|
+
});
|
|
48
|
+
it('keeps the newest entry last so the pinned scroll lands on it', async () => {
|
|
49
|
+
const { client, push } = fakeClient([entry(7, 'old')]);
|
|
50
|
+
const { container } = render(_jsx(ConsolePanel, { client: client, targetId: "tab-1" }));
|
|
51
|
+
await act(async () => { });
|
|
52
|
+
push(entry(1, 'newest'));
|
|
53
|
+
const rows = Array.from(container.querySelectorAll('.ins-log-body'), (r) => r.textContent);
|
|
54
|
+
expect(rows.at(-1)).toBe('newest');
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
//# sourceMappingURL=ConsolePanel.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ConsolePanel.test.js","sourceRoot":"","sources":["../../../../src/inspect/ui/panels/ConsolePanel.test.tsx"],"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.985",
|
|
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.985";
|
|
@@ -29,6 +29,12 @@ interface TTSOptions {
|
|
|
29
29
|
|
|
30
30
|
let streamCounter = 0;
|
|
31
31
|
|
|
32
|
+
// If no tts frame (chunk/viseme/word/done) arrives within this window after
|
|
33
|
+
// tts:start (or since the last frame), the stream is dead — the socket dropped
|
|
34
|
+
// silently or the server hung. Fail loudly instead of leaving `playing` true
|
|
35
|
+
// forever with the rAF loop spinning and the socket held.
|
|
36
|
+
const WATCHDOG_MS = 30_000;
|
|
37
|
+
|
|
32
38
|
export function useTTS(socket: UglyBotSocket) {
|
|
33
39
|
const [playing, setPlaying] = useState(false);
|
|
34
40
|
const [currentWord, setCurrentWord] = useState('');
|
|
@@ -41,6 +47,7 @@ export function useTTS(socket: UglyBotSocket) {
|
|
|
41
47
|
const streamIdRef = useRef<string | null>(null);
|
|
42
48
|
const unsubsRef = useRef<(() => void)[]>([]);
|
|
43
49
|
const rafRef = useRef<number | null>(null);
|
|
50
|
+
const watchdogRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
44
51
|
// Lightweight diagnostics (exposed via getStatus) — frames received + events
|
|
45
52
|
// dispatched, so apps/e2e can see exactly where TTS breaks.
|
|
46
53
|
const statsRef = useRef({
|
|
@@ -74,6 +81,13 @@ export function useTTS(socket: UglyBotSocket) {
|
|
|
74
81
|
}
|
|
75
82
|
}
|
|
76
83
|
|
|
84
|
+
function clearWatchdog() {
|
|
85
|
+
if (watchdogRef.current) {
|
|
86
|
+
clearTimeout(watchdogRef.current);
|
|
87
|
+
watchdogRef.current = null;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
77
91
|
// Create + resume the AudioContext from inside a user gesture (e.g. the
|
|
78
92
|
// user's send-click). Browsers (esp. Safari) only unlock audio when resume()
|
|
79
93
|
// runs within a gesture; the TTS reply itself plays later, async, so without
|
|
@@ -124,6 +138,7 @@ export function useTTS(socket: UglyBotSocket) {
|
|
|
124
138
|
if (streamIdRef.current !== streamId) return;
|
|
125
139
|
streamIdRef.current = null;
|
|
126
140
|
stopRaf();
|
|
141
|
+
clearWatchdog();
|
|
127
142
|
// Flush any remaining caption indices so the text fully reveals.
|
|
128
143
|
while (wi < words.length)
|
|
129
144
|
options?.onSubtitleIndex?.(words[wi++]!.charEnd);
|
|
@@ -133,6 +148,18 @@ export function useTTS(socket: UglyBotSocket) {
|
|
|
133
148
|
socket.release();
|
|
134
149
|
};
|
|
135
150
|
|
|
151
|
+
// Re-armed by every incoming tts frame; if it ever fires, the stream
|
|
152
|
+
// went silent mid-playback and we fail it like a tts:error.
|
|
153
|
+
const armWatchdog = () => {
|
|
154
|
+
clearWatchdog();
|
|
155
|
+
watchdogRef.current = setTimeout(() => {
|
|
156
|
+
watchdogRef.current = null;
|
|
157
|
+
if (streamIdRef.current !== streamId) return;
|
|
158
|
+
setError('TTS stream timed out — no response from server');
|
|
159
|
+
finish();
|
|
160
|
+
}, WATCHDOG_MS);
|
|
161
|
+
};
|
|
162
|
+
|
|
136
163
|
const tick = () => {
|
|
137
164
|
if (streamIdRef.current !== streamId) return;
|
|
138
165
|
const t = player.getPlaybackTimeMs();
|
|
@@ -170,9 +197,20 @@ export function useTTS(socket: UglyBotSocket) {
|
|
|
170
197
|
|
|
171
198
|
cleanupListeners();
|
|
172
199
|
stopRaf();
|
|
200
|
+
const unsubClose = socket.onClose?.(() => {
|
|
201
|
+
// The transport dropped mid-stream: no more frames will arrive (the
|
|
202
|
+
// stream is dead server-side and is not replayed after a reconnect).
|
|
203
|
+
// Fail like a tts:error instead of hanging in `playing` forever. A
|
|
204
|
+
// stream that already got tts:done is just draining its audio queue —
|
|
205
|
+
// let the rAF loop finish it normally.
|
|
206
|
+
if (streamIdRef.current !== streamId || serverDone) return;
|
|
207
|
+
setError('connection lost');
|
|
208
|
+
finish();
|
|
209
|
+
});
|
|
173
210
|
unsubsRef.current.push(
|
|
174
211
|
socket.on('tts:chunk', (data) => {
|
|
175
212
|
if (data.streamId !== streamId) return;
|
|
213
|
+
armWatchdog();
|
|
176
214
|
statsRef.current.chunks++;
|
|
177
215
|
void player.feedChunk(data.audio);
|
|
178
216
|
if (data.visemes) {
|
|
@@ -185,6 +223,7 @@ export function useTTS(socket: UglyBotSocket) {
|
|
|
185
223
|
}),
|
|
186
224
|
socket.on('tts:viseme', (data) => {
|
|
187
225
|
if (data.streamId !== streamId) return;
|
|
226
|
+
armWatchdog();
|
|
188
227
|
for (const v of data.visemes) {
|
|
189
228
|
visemes.push(v);
|
|
190
229
|
statsRef.current.visemes++;
|
|
@@ -193,6 +232,7 @@ export function useTTS(socket: UglyBotSocket) {
|
|
|
193
232
|
}),
|
|
194
233
|
socket.on('tts:word', (data) => {
|
|
195
234
|
if (data.streamId !== streamId) return;
|
|
235
|
+
armWatchdog();
|
|
196
236
|
statsRef.current.words++;
|
|
197
237
|
setCurrentWord(data.word);
|
|
198
238
|
const ce = data.charEndIndex;
|
|
@@ -204,14 +244,19 @@ export function useTTS(socket: UglyBotSocket) {
|
|
|
204
244
|
socket.on('tts:done', (data) => {
|
|
205
245
|
if (data.streamId !== streamId) return;
|
|
206
246
|
serverDone = true;
|
|
247
|
+
// The stream ended cleanly; the audio tail is driven by the rAF
|
|
248
|
+
// loop, so silence from here on is expected, not a failure.
|
|
249
|
+
clearWatchdog();
|
|
207
250
|
}),
|
|
208
251
|
socket.on('tts:error', (data) => {
|
|
209
252
|
if (data.streamId !== streamId) return;
|
|
210
253
|
setError(data.message);
|
|
211
254
|
finish();
|
|
212
255
|
}),
|
|
256
|
+
...(unsubClose ? [unsubClose] : []),
|
|
213
257
|
);
|
|
214
258
|
rafRef.current = requestAnimationFrame(tick);
|
|
259
|
+
armWatchdog();
|
|
215
260
|
|
|
216
261
|
socket.send('tts:start', {
|
|
217
262
|
streamId,
|
|
@@ -229,6 +274,7 @@ export function useTTS(socket: UglyBotSocket) {
|
|
|
229
274
|
}
|
|
230
275
|
streamIdRef.current = null;
|
|
231
276
|
stopRaf();
|
|
277
|
+
clearWatchdog();
|
|
232
278
|
playerRef.current?.stop();
|
|
233
279
|
setPlaying(false);
|
|
234
280
|
cleanupListeners();
|
|
@@ -241,6 +287,7 @@ export function useTTS(socket: UglyBotSocket) {
|
|
|
241
287
|
socket.send('tts:cancel', { streamId: streamIdRef.current });
|
|
242
288
|
}
|
|
243
289
|
stopRaf();
|
|
290
|
+
clearWatchdog();
|
|
244
291
|
cleanupListeners();
|
|
245
292
|
socket.release();
|
|
246
293
|
playerRef.current?.close().catch(() => {});
|
|
@@ -15,11 +15,33 @@ export interface UglyBotSocket {
|
|
|
15
15
|
event: K,
|
|
16
16
|
handler: (data: MessageData<UglyBotMessages[K]>) => void,
|
|
17
17
|
): Unsubscribe;
|
|
18
|
+
/**
|
|
19
|
+
* Fired when the direct WebSocket transport drops unexpectedly (not after a
|
|
20
|
+
* deliberate release-driven close). Any in-flight streams on it are dead —
|
|
21
|
+
* listeners should fail them rather than wait for frames that never come.
|
|
22
|
+
* The native host transport manages its own socket and never fires this.
|
|
23
|
+
* Optional so hand-rolled socket stubs keep type-checking.
|
|
24
|
+
*/
|
|
25
|
+
onClose?(handler: () => void): Unsubscribe;
|
|
18
26
|
acquire(): Promise<void>;
|
|
19
27
|
release(): void;
|
|
20
28
|
}
|
|
21
29
|
|
|
22
30
|
const IDLE_TIMEOUT_MS = 10_000;
|
|
31
|
+
// Keep-alive cadence: well under the ~60s the edge lets a frame-less socket
|
|
32
|
+
// live (createSocket.ts uses the same idiom at 30s), so an acquired-but-idle
|
|
33
|
+
// TTS/STT socket stops being recycled out from under an active stream.
|
|
34
|
+
const PING_INTERVAL_MS = 25_000;
|
|
35
|
+
// Must byte-match the edge auto-response request registered in
|
|
36
|
+
// src/server/adapter/workers/createWorkersApp.ts (PING_FRAME /
|
|
37
|
+
// setWebSocketAutoResponse): exactly `{"type":"ping","data":{}}`. The edge
|
|
38
|
+
// then answers with a pong without waking the Durable Object; a non-matching
|
|
39
|
+
// frame still gets answered by the `msg.type === 'ping'` fallback there.
|
|
40
|
+
const PING_FRAME = JSON.stringify({ type: 'ping', data: {} });
|
|
41
|
+
// Reconnect backoff after an unexpected drop — createSocket.ts's idiom
|
|
42
|
+
// (1s doubling, capped), at a smaller scale.
|
|
43
|
+
const RECONNECT_BASE_MS = 1_000;
|
|
44
|
+
const RECONNECT_MAX_MS = 30_000;
|
|
23
45
|
|
|
24
46
|
/** True when a native host rejected `uglybot.openStream` because it doesn't
|
|
25
47
|
* implement the channel (older shell) — fall back to a direct ugly.bot socket. */
|
|
@@ -40,18 +62,81 @@ export function createUglyBotSocket(
|
|
|
40
62
|
// Listeners are transport-agnostic: both the native and the direct transport
|
|
41
63
|
// dispatch incoming `{type,data}` frames here.
|
|
42
64
|
const listeners = new Map<string, Set<(data: unknown) => void>>();
|
|
65
|
+
function subscribe(
|
|
66
|
+
event: string,
|
|
67
|
+
handler: (data: unknown) => void,
|
|
68
|
+
): Unsubscribe {
|
|
69
|
+
let set = listeners.get(event);
|
|
70
|
+
if (!set) {
|
|
71
|
+
set = new Set();
|
|
72
|
+
listeners.set(event, set);
|
|
73
|
+
}
|
|
74
|
+
set.add(handler);
|
|
75
|
+
return () => {
|
|
76
|
+
set.delete(handler);
|
|
77
|
+
if (set.size === 0) listeners.delete(event);
|
|
78
|
+
};
|
|
79
|
+
}
|
|
43
80
|
function dispatch(type: string, data: unknown) {
|
|
44
81
|
const set = listeners.get(type);
|
|
45
82
|
if (set) for (const handler of set) handler(data);
|
|
46
83
|
}
|
|
84
|
+
// Reserved pseudo-event for transport drops — not a wire message, so it is
|
|
85
|
+
// not part of UglyBotMessages and is only reachable via `onClose`.
|
|
86
|
+
const CLOSE_EVENT = 'close';
|
|
47
87
|
|
|
48
88
|
// ─── Direct ugly.bot WebSocket transport (plain browser / no shell) ──────────
|
|
49
89
|
let ws: WebSocket | null = null;
|
|
90
|
+
// Socket whose handshake is still in flight; lets the error/close handlers
|
|
91
|
+
// tell "the pending connect failed" from "a stale event on an old socket".
|
|
92
|
+
let pendingSocket: WebSocket | null = null;
|
|
50
93
|
let connectPromise: Promise<void> | null = null;
|
|
94
|
+
let pingTimer: ReturnType<typeof setInterval> | null = null;
|
|
95
|
+
let reconnectTimer: ReturnType<typeof setTimeout> | null = null;
|
|
96
|
+
let reconnectAttempts = 0;
|
|
97
|
+
// Set by directClose() so the ensuing close event isn't mistaken for a drop
|
|
98
|
+
// (which would fire onClose + schedule a reconnect). Re-armed per connect.
|
|
99
|
+
let intentionalClose = false;
|
|
100
|
+
|
|
101
|
+
function stopPing() {
|
|
102
|
+
if (pingTimer) {
|
|
103
|
+
clearInterval(pingTimer);
|
|
104
|
+
pingTimer = null;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
function startPing() {
|
|
108
|
+
stopPing();
|
|
109
|
+
pingTimer = setInterval(() => {
|
|
110
|
+
if (refCount === 0 || ws?.readyState !== WebSocket.OPEN) return;
|
|
111
|
+
ws.send(PING_FRAME);
|
|
112
|
+
}, PING_INTERVAL_MS);
|
|
113
|
+
}
|
|
114
|
+
function cancelReconnect() {
|
|
115
|
+
if (reconnectTimer) {
|
|
116
|
+
clearTimeout(reconnectTimer);
|
|
117
|
+
reconnectTimer = null;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
// Unexpected drop while still acquired: retry with capped exponential
|
|
121
|
+
// backoff. In-flight streams are NOT replayed — consumers were already
|
|
122
|
+
// notified via onClose and should have failed them.
|
|
123
|
+
function scheduleReconnect() {
|
|
124
|
+
cancelReconnect();
|
|
125
|
+
const delay = Math.min(
|
|
126
|
+
RECONNECT_MAX_MS,
|
|
127
|
+
RECONNECT_BASE_MS * Math.pow(2, reconnectAttempts),
|
|
128
|
+
);
|
|
129
|
+
reconnectTimer = setTimeout(() => {
|
|
130
|
+
reconnectTimer = null;
|
|
131
|
+
reconnectAttempts++;
|
|
132
|
+
void directEnsure().catch(() => {});
|
|
133
|
+
}, delay);
|
|
134
|
+
}
|
|
51
135
|
|
|
52
136
|
function directEnsure(): Promise<void> {
|
|
53
137
|
if (ws?.readyState === WebSocket.OPEN) return Promise.resolve();
|
|
54
138
|
if (connectPromise) return connectPromise;
|
|
139
|
+
intentionalClose = false;
|
|
55
140
|
connectPromise = new Promise<void>((resolve, reject) => {
|
|
56
141
|
const httpBase = baseUrl ?? 'https://ugly.bot';
|
|
57
142
|
const wsBase = httpBase.replace(/^http/, 'ws');
|
|
@@ -60,21 +145,40 @@ export function createUglyBotSocket(
|
|
|
60
145
|
`[UglyBotSocket] connecting to ${url.replace(/token=[^&]+/, 'token=***')}`,
|
|
61
146
|
);
|
|
62
147
|
const socket = new WebSocket(url);
|
|
148
|
+
pendingSocket = socket;
|
|
63
149
|
socket.addEventListener('open', () => {
|
|
64
150
|
console.log('[UglyBotSocket] connected');
|
|
65
151
|
ws = socket;
|
|
152
|
+
pendingSocket = null;
|
|
66
153
|
connectPromise = null;
|
|
154
|
+
reconnectAttempts = 0;
|
|
155
|
+
startPing();
|
|
67
156
|
resolve();
|
|
68
157
|
});
|
|
69
158
|
socket.addEventListener('error', (e) => {
|
|
159
|
+
// An error on an ESTABLISHED or STALE socket is always followed by
|
|
160
|
+
// 'close', which owns drop handling (onClose + reconnect) — don't
|
|
161
|
+
// double-report, and never reject a NEWER connect's promise.
|
|
162
|
+
if (pendingSocket !== socket) return;
|
|
70
163
|
console.error('[UglyBotSocket] connection error', e);
|
|
71
164
|
connectPromise = null;
|
|
72
165
|
reject(new Error('ugly.bot WebSocket connection failed'));
|
|
73
166
|
});
|
|
74
167
|
socket.addEventListener('close', (e) => {
|
|
75
168
|
console.log(`[UglyBotSocket] closed code=${e.code} reason=${e.reason}`);
|
|
169
|
+
// Stale close (a newer direct connection already replaced this one):
|
|
170
|
+
// it must not fail streams or clobber the new connect's bookkeeping.
|
|
171
|
+
if (ws !== socket && pendingSocket !== socket) return;
|
|
76
172
|
if (ws === socket) ws = null;
|
|
173
|
+
if (pendingSocket === socket) pendingSocket = null;
|
|
77
174
|
connectPromise = null;
|
|
175
|
+
stopPing();
|
|
176
|
+
if (intentionalClose) return;
|
|
177
|
+
// Notify BEFORE checking refCount: a listener that fails its stream
|
|
178
|
+
// will release(), and if that drops the count to zero there is no one
|
|
179
|
+
// left to reconnect for.
|
|
180
|
+
dispatch(CLOSE_EVENT, undefined);
|
|
181
|
+
if (refCount > 0) scheduleReconnect();
|
|
78
182
|
});
|
|
79
183
|
socket.addEventListener('message', (event: MessageEvent) => {
|
|
80
184
|
const msg = JSON.parse(event.data as string) as Record<string, unknown>;
|
|
@@ -102,6 +206,15 @@ export function createUglyBotSocket(
|
|
|
102
206
|
}
|
|
103
207
|
}
|
|
104
208
|
function directClose() {
|
|
209
|
+
intentionalClose = true;
|
|
210
|
+
cancelReconnect();
|
|
211
|
+
stopPing();
|
|
212
|
+
// A release can land while a (re)connect handshake is still in flight —
|
|
213
|
+
// abort it too, or the orphan socket opens with no one holding it.
|
|
214
|
+
if (pendingSocket) {
|
|
215
|
+
pendingSocket.close();
|
|
216
|
+
pendingSocket = null;
|
|
217
|
+
}
|
|
105
218
|
if (ws) {
|
|
106
219
|
ws.close();
|
|
107
220
|
ws = null;
|
|
@@ -183,16 +296,10 @@ export function createUglyBotSocket(
|
|
|
183
296
|
send(type, data);
|
|
184
297
|
},
|
|
185
298
|
on(event, handler) {
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
}
|
|
191
|
-
set.add(handler as (data: unknown) => void);
|
|
192
|
-
return () => {
|
|
193
|
-
set.delete(handler as (data: unknown) => void);
|
|
194
|
-
if (set.size === 0) listeners.delete(event);
|
|
195
|
-
};
|
|
299
|
+
return subscribe(event, handler as (data: unknown) => void);
|
|
300
|
+
},
|
|
301
|
+
onClose(handler) {
|
|
302
|
+
return subscribe(CLOSE_EVENT, handler);
|
|
196
303
|
},
|
|
197
304
|
async acquire(): Promise<void> {
|
|
198
305
|
clearIdle();
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
// @vitest-environment jsdom
|
|
2
|
+
import { cleanup, render, act } from '@testing-library/react';
|
|
3
|
+
import { afterEach, describe, expect, it, vi } from 'vitest';
|
|
4
|
+
import { ConsolePanel } from './ConsolePanel.js';
|
|
5
|
+
import type { InspectClient } from '../../client.js';
|
|
6
|
+
import type { ConsoleEntry } from '../../protocol.js';
|
|
7
|
+
|
|
8
|
+
afterEach(cleanup);
|
|
9
|
+
|
|
10
|
+
const entry = (seq: number, text: string): ConsoleEntry =>
|
|
11
|
+
({ seq, level: 'log', text, ts: 0, args: [] }) as unknown as ConsoleEntry;
|
|
12
|
+
|
|
13
|
+
/** A client whose live console stream we drive by hand. */
|
|
14
|
+
function fakeClient(buffer: ConsoleEntry[] = []) {
|
|
15
|
+
let emit: (e: ConsoleEntry) => void = () => {};
|
|
16
|
+
const client = {
|
|
17
|
+
consoleBuffer: vi.fn(async () => buffer),
|
|
18
|
+
onConsole: vi.fn((_t: string, h: (e: ConsoleEntry) => void) => {
|
|
19
|
+
emit = h;
|
|
20
|
+
return () => {};
|
|
21
|
+
}),
|
|
22
|
+
eval: vi.fn(),
|
|
23
|
+
expand: vi.fn(),
|
|
24
|
+
} as unknown as InspectClient;
|
|
25
|
+
return { client, push: (e: ConsoleEntry) => act(() => emit(e)) };
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
describe('ConsolePanel', () => {
|
|
29
|
+
/**
|
|
30
|
+
* `seq` is a closure counter inside the page's console agent, so it RESTARTS
|
|
31
|
+
* AT 1 on every navigation — a fresh document gets a fresh agent. The panel
|
|
32
|
+
* keeps accumulating across those navigations (the target id, a tab, never
|
|
33
|
+
* changed), so a keyed-by-seq list ends up with two `c1`s, React warns that
|
|
34
|
+
* children "may be duplicated and/or omitted", and the tail of the log stops
|
|
35
|
+
* rendering: "I cannot scroll to the end of the console."
|
|
36
|
+
*/
|
|
37
|
+
it('renders every entry after a navigation resets the agent seq', async () => {
|
|
38
|
+
const warn = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
39
|
+
const { client, push } = fakeClient([entry(1, 'before-nav')]);
|
|
40
|
+
const { container } = render(
|
|
41
|
+
<ConsolePanel client={client} targetId="tab-1" />,
|
|
42
|
+
);
|
|
43
|
+
await act(async () => {});
|
|
44
|
+
|
|
45
|
+
// The page navigates; the new document's agent starts counting from 1 again.
|
|
46
|
+
push(entry(1, 'after-nav-a'));
|
|
47
|
+
push(entry(2, 'after-nav-b'));
|
|
48
|
+
|
|
49
|
+
const rows = Array.from(
|
|
50
|
+
container.querySelectorAll('.ins-log-row'),
|
|
51
|
+
(r) => r.querySelector('.ins-log-body')?.textContent,
|
|
52
|
+
);
|
|
53
|
+
expect(rows).toEqual(['before-nav', 'after-nav-a', 'after-nav-b']);
|
|
54
|
+
|
|
55
|
+
// React reports duplicate keys through console.error — none may be raised.
|
|
56
|
+
const dupWarning = warn.mock.calls
|
|
57
|
+
.flat()
|
|
58
|
+
.some((a) => String(a).includes('same key'));
|
|
59
|
+
expect(dupWarning, 'React warned about duplicate keys').toBe(false);
|
|
60
|
+
warn.mockRestore();
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('keeps the newest entry last so the pinned scroll lands on it', async () => {
|
|
64
|
+
const { client, push } = fakeClient([entry(7, 'old')]);
|
|
65
|
+
const { container } = render(
|
|
66
|
+
<ConsolePanel client={client} targetId="tab-1" />,
|
|
67
|
+
);
|
|
68
|
+
await act(async () => {});
|
|
69
|
+
push(entry(1, 'newest'));
|
|
70
|
+
const rows = Array.from(
|
|
71
|
+
container.querySelectorAll('.ins-log-body'),
|
|
72
|
+
(r) => r.textContent,
|
|
73
|
+
);
|
|
74
|
+
expect(rows.at(-1)).toBe('newest');
|
|
75
|
+
});
|
|
76
|
+
});
|
|
@@ -20,6 +20,12 @@ interface ReplLine {
|
|
|
20
20
|
value?: RemoteValue | undefined;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
/** A captured entry plus a key that is unique in THIS list — see `uid` below. */
|
|
24
|
+
interface Row {
|
|
25
|
+
uid: number;
|
|
26
|
+
e: ConsoleEntry;
|
|
27
|
+
}
|
|
28
|
+
|
|
23
29
|
export function ConsolePanel({
|
|
24
30
|
client,
|
|
25
31
|
targetId,
|
|
@@ -27,18 +33,30 @@ export function ConsolePanel({
|
|
|
27
33
|
client: InspectClient;
|
|
28
34
|
targetId: string;
|
|
29
35
|
}) {
|
|
30
|
-
const [entries, setEntries] = useState<
|
|
36
|
+
const [entries, setEntries] = useState<Row[]>([]);
|
|
31
37
|
const [repl, setRepl] = useState<ReplLine[]>([]);
|
|
32
38
|
const [input, setInput] = useState('');
|
|
33
39
|
const scrollRef = useRef<HTMLDivElement>(null);
|
|
40
|
+
// ⚠️ NOT `seq` — THAT IS NOT UNIQUE HERE. `seq` counts inside the page's
|
|
41
|
+
// console agent, and a fresh document gets a fresh agent, so it RESTARTS AT 1
|
|
42
|
+
// on every navigation. This list spans navigations (the target is a tab, and
|
|
43
|
+
// the tab did not change), so keying rows by seq gave two `c1`s: React warned
|
|
44
|
+
// that children "may be duplicated and/or omitted" and then omitted them —
|
|
45
|
+
// reported as "I cannot scroll to the end of the console", because the end was
|
|
46
|
+
// what stopped rendering. This counter is monotonic for the life of the list,
|
|
47
|
+
// which is the scope the keys have to be unique in.
|
|
48
|
+
const uid = useRef(0);
|
|
34
49
|
|
|
35
50
|
useEffect(() => {
|
|
36
51
|
let live = true;
|
|
37
52
|
setEntries([]);
|
|
38
53
|
setRepl([]);
|
|
39
|
-
|
|
54
|
+
const row = (e: ConsoleEntry): Row => ({ uid: uid.current++, e });
|
|
55
|
+
void client
|
|
56
|
+
.consoleBuffer(targetId)
|
|
57
|
+
.then((b) => live && setEntries(b.map(row)));
|
|
40
58
|
const off = client.onConsole(targetId, (e) =>
|
|
41
|
-
setEntries((cur) => [...cur, e].slice(-2000)),
|
|
59
|
+
setEntries((cur) => [...cur, row(e)].slice(-2000)),
|
|
42
60
|
);
|
|
43
61
|
return () => {
|
|
44
62
|
live = false;
|
|
@@ -81,8 +99,8 @@ export function ConsolePanel({
|
|
|
81
99
|
console output streams here · type below to evaluate
|
|
82
100
|
</div>
|
|
83
101
|
)}
|
|
84
|
-
{entries.map((e) => (
|
|
85
|
-
<div className="ins-log-row" data-level={e.level} key={`c${
|
|
102
|
+
{entries.map(({ uid, e }) => (
|
|
103
|
+
<div className="ins-log-row" data-level={e.level} key={`c${uid}`}>
|
|
86
104
|
<span className="ins-log-gutter">{GUTTER[e.level] ?? '·'}</span>
|
|
87
105
|
<span className="ins-log-body">{e.text}</span>
|
|
88
106
|
{e.source?.url && (
|