osborn 0.8.8 → 0.8.9
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/index.js +41 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -227,6 +227,47 @@ function startApiServer(workingDir, port) {
|
|
|
227
227
|
setTimeout(() => process.exit(0), 150);
|
|
228
228
|
return;
|
|
229
229
|
}
|
|
230
|
+
// GET /events — Server-Sent Events heartbeat for cloud-sandbox keepalive.
|
|
231
|
+
//
|
|
232
|
+
// This endpoint is the single thing preventing Sprites' CRIU-based
|
|
233
|
+
// hibernation from freezing osborn's Node.js event loop and dropping our
|
|
234
|
+
// LiveKit WebSocket mid-session. Short HTTP pings don't work: Sprites'
|
|
235
|
+
// warm state serves /health responses from a process snapshot without
|
|
236
|
+
// actually resuming the event loop, so background timers (including
|
|
237
|
+
// LiveKit heartbeats) stop firing after a few seconds. That causes the
|
|
238
|
+
// LiveKit server to drop osborn's participant, delete the room, and
|
|
239
|
+
// leave any future user joins stuck at "Connecting..." forever.
|
|
240
|
+
//
|
|
241
|
+
// An OPEN long-lived TCP connection keeps the sprite in 'running' state.
|
|
242
|
+
// The frontend opens this endpoint on chat page mount and holds it open
|
|
243
|
+
// for the entire voice session. While open, osborn's event loop ticks
|
|
244
|
+
// continuously, LiveKit heartbeats fire, and the room stays alive.
|
|
245
|
+
//
|
|
246
|
+
// For local (non-cloud) dev, this endpoint is harmless — it just idles
|
|
247
|
+
// on a client that may never connect. Zero cost when unused.
|
|
248
|
+
if (req.method === 'GET' && url.pathname === '/events') {
|
|
249
|
+
res.writeHead(200, {
|
|
250
|
+
'Content-Type': 'text/event-stream',
|
|
251
|
+
'Cache-Control': 'no-cache',
|
|
252
|
+
'Connection': 'keep-alive',
|
|
253
|
+
// Disable proxy buffering (nginx-style) so each ping is flushed
|
|
254
|
+
// through Sprites' reverse proxy immediately rather than batched.
|
|
255
|
+
'X-Accel-Buffering': 'no',
|
|
256
|
+
});
|
|
257
|
+
res.write(`: sprite-keepalive connected at ${new Date().toISOString()}\n\n`);
|
|
258
|
+
const heartbeat = setInterval(() => {
|
|
259
|
+
try {
|
|
260
|
+
res.write(`: ping ${Date.now()}\n\n`);
|
|
261
|
+
}
|
|
262
|
+
catch { }
|
|
263
|
+
}, 10_000);
|
|
264
|
+
req.on('close', () => {
|
|
265
|
+
clearInterval(heartbeat);
|
|
266
|
+
console.log('[events] SSE client disconnected');
|
|
267
|
+
});
|
|
268
|
+
console.log('[events] SSE client connected');
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
230
271
|
res.writeHead(404, { 'Content-Type': 'application/json' });
|
|
231
272
|
res.end(JSON.stringify({ error: 'Not found' }));
|
|
232
273
|
});
|