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.
@@ -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,CAeZ"}
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"}
@@ -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;
@@ -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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"}
@@ -1,2 +1,2 @@
1
- export declare const CLI_VERSION = "0.1.983";
1
+ export declare const CLI_VERSION = "0.1.985";
2
2
  //# sourceMappingURL=version.d.ts.map
@@ -1,3 +1,3 @@
1
1
  // Auto-generated by prebuild — do not edit manually
2
- export const CLI_VERSION = "0.1.983";
2
+ export const CLI_VERSION = "0.1.985";
3
3
  //# sourceMappingURL=version.js.map
@@ -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;AAID,wBAAgB,MAAM,CAAC,MAAM,EAAE,aAAa;iBA4D3B,MAAM,YAAY,UAAU;;;;;;;;;;;;;;;;;EAwK5C"}
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;AAaD,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE,MAAM,GACf,aAAa,CA2Kf"}
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
- let set = listeners.get(event);
158
- if (!set) {
159
- set = new Set();
160
- listeners.set(event, set);
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":"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"}
1
+ {"version":3,"file":"uglyBotSocket.js","sourceRoot":"","sources":["../../src/client/uglyBotSocket.ts"],"names":[],"mappings":"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"}
@@ -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;AAkBrD,wBAAgB,YAAY,CAAC,EAC3B,MAAM,EACN,QAAQ,GACT,EAAE;IACD,MAAM,EAAE,aAAa,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;CAClB,+BA0GA"}
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
- void client.consoleBuffer(targetId).then((b) => live && setEntries(b));
23
- const off = client.onConsole(targetId, (e) => setEntries((cur) => [...cur, e].slice(-2000)));
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${e.seq}`))), 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) => {
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":"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"}
1
+ {"version":3,"file":"ConsolePanel.js","sourceRoot":"","sources":["../../../../src/inspect/ui/panels/ConsolePanel.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=ConsolePanel.test.d.ts.map
@@ -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.983",
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": [
@@ -1,2 +1,2 @@
1
1
  // Auto-generated by prebuild — do not edit manually
2
- export const CLI_VERSION = "0.1.983";
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
- let set = listeners.get(event);
187
- if (!set) {
188
- set = new Set();
189
- listeners.set(event, set);
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<ConsoleEntry[]>([]);
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
- void client.consoleBuffer(targetId).then((b) => live && setEntries(b));
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${e.seq}`}>
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 && (