bloby-bot 0.48.2 → 0.48.3
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/package.json +1 -1
- package/supervisor/index.ts +44 -7
package/package.json
CHANGED
package/supervisor/index.ts
CHANGED
|
@@ -441,7 +441,21 @@ export async function startSupervisor() {
|
|
|
441
441
|
const proxy = http.request(
|
|
442
442
|
{ host: '127.0.0.1', port: backendPort, path: backendPath, method: req.method, headers: req.headers },
|
|
443
443
|
(proxyRes) => {
|
|
444
|
+
const ct = String(proxyRes.headers['content-type'] || '');
|
|
445
|
+
const isSse = ct.includes('text/event-stream');
|
|
446
|
+
if (isSse) {
|
|
447
|
+
console.log(`[app-proxy] SSE upstream status=${proxyRes.statusCode} ct="${ct}" url=${backendPath}`);
|
|
448
|
+
}
|
|
444
449
|
res.writeHead(proxyRes.statusCode!, proxyRes.headers);
|
|
450
|
+
if (isSse) {
|
|
451
|
+
try { res.socket?.setNoDelay(true); } catch {}
|
|
452
|
+
proxyRes.on('data', (chunk: Buffer) => {
|
|
453
|
+
console.log(`[app-proxy] SSE chunk bytes=${chunk.length} preview=${JSON.stringify(chunk.toString('utf-8').slice(0, 80))}`);
|
|
454
|
+
});
|
|
455
|
+
proxyRes.on('end', () => console.log(`[app-proxy] SSE upstream END`));
|
|
456
|
+
proxyRes.on('error', (e: any) => console.log(`[app-proxy] SSE upstream ERROR ${e.message}`));
|
|
457
|
+
res.on('close', () => console.log(`[app-proxy] SSE res CLOSE`));
|
|
458
|
+
}
|
|
445
459
|
proxyRes.pipe(res);
|
|
446
460
|
},
|
|
447
461
|
);
|
|
@@ -1243,25 +1257,33 @@ mint();
|
|
|
1243
1257
|
// GET /api/agent/chat/stream — Server-Sent Events, every chat event (bot:*, chat:*)
|
|
1244
1258
|
if (req.method === 'GET' && agentPath === '/api/agent/chat/stream') {
|
|
1245
1259
|
const clientId = urlObj.searchParams.get('clientId') || undefined;
|
|
1260
|
+
const subId = crypto.randomBytes(8).toString('hex');
|
|
1261
|
+
console.log(`[sse-handler] OPEN sub=${subId} clientId=${clientId} remote=${req.socket.remoteAddress}`);
|
|
1262
|
+
|
|
1246
1263
|
res.setHeader('Content-Type', 'text/event-stream');
|
|
1247
1264
|
res.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate');
|
|
1248
1265
|
res.setHeader('Connection', 'keep-alive');
|
|
1249
1266
|
res.setHeader('X-Accel-Buffering', 'no');
|
|
1250
1267
|
res.writeHead(200);
|
|
1251
|
-
res.
|
|
1268
|
+
try { res.socket?.setNoDelay(true); } catch {}
|
|
1269
|
+
const wrote = res.write(': connected\n\n');
|
|
1270
|
+
console.log(`[sse-handler] wrote initial comment sub=${subId} writeOk=${wrote}`);
|
|
1252
1271
|
|
|
1253
1272
|
const sub: ChatSubscriber = {
|
|
1254
|
-
id:
|
|
1273
|
+
id: subId,
|
|
1255
1274
|
clientId,
|
|
1256
1275
|
send: (type, data) => {
|
|
1257
|
-
if (res.writableEnded)
|
|
1258
|
-
|
|
1276
|
+
if (res.writableEnded) {
|
|
1277
|
+
console.log(`[sse-handler] skip write (ended) sub=${subId} type=${type}`);
|
|
1278
|
+
return;
|
|
1279
|
+
}
|
|
1280
|
+
const ok = res.write(`event: ${type}\ndata: ${JSON.stringify(data)}\n\n`);
|
|
1281
|
+
console.log(`[sse-handler] write sub=${subId} type=${type} ok=${ok} bytes=${(type.length + JSON.stringify(data).length + 10)}`);
|
|
1259
1282
|
},
|
|
1260
1283
|
close: () => { try { res.end(); } catch {} },
|
|
1261
1284
|
};
|
|
1262
1285
|
chatSubscribers.add(sub);
|
|
1263
1286
|
|
|
1264
|
-
// Replay current streaming state so a late-joining workspace catches up mid-turn
|
|
1265
1287
|
if (agentQueryActive && currentStreamConvId) {
|
|
1266
1288
|
sub.send('chat:state', {
|
|
1267
1289
|
streaming: true,
|
|
@@ -1273,12 +1295,20 @@ mint();
|
|
|
1273
1295
|
const keepAlive = setInterval(() => {
|
|
1274
1296
|
if (res.writableEnded) return;
|
|
1275
1297
|
res.write(': ping\n\n');
|
|
1298
|
+
console.log(`[sse-handler] ping sub=${subId}`);
|
|
1276
1299
|
}, 25_000);
|
|
1277
1300
|
|
|
1278
1301
|
req.on('close', () => {
|
|
1302
|
+
console.log(`[sse-handler] CLOSE sub=${subId} reason=req-close`);
|
|
1279
1303
|
clearInterval(keepAlive);
|
|
1280
1304
|
chatSubscribers.delete(sub);
|
|
1281
1305
|
});
|
|
1306
|
+
res.on('close', () => {
|
|
1307
|
+
console.log(`[sse-handler] CLOSE sub=${subId} reason=res-close`);
|
|
1308
|
+
});
|
|
1309
|
+
res.on('error', (err: any) => {
|
|
1310
|
+
console.log(`[sse-handler] ERROR sub=${subId} ${err?.message}`);
|
|
1311
|
+
});
|
|
1282
1312
|
return;
|
|
1283
1313
|
}
|
|
1284
1314
|
|
|
@@ -1716,11 +1746,18 @@ mint();
|
|
|
1716
1746
|
* subscribers. The workspace mirror sees the exact same event stream the widget does. */
|
|
1717
1747
|
function broadcastBloby(type: string, data: any = {}) {
|
|
1718
1748
|
const msg = JSON.stringify({ type, data });
|
|
1749
|
+
let wsCount = 0;
|
|
1719
1750
|
for (const client of blobyWss.clients) {
|
|
1720
|
-
if (client.readyState === WebSocket.OPEN) client.send(msg);
|
|
1751
|
+
if (client.readyState === WebSocket.OPEN) { client.send(msg); wsCount++; }
|
|
1721
1752
|
}
|
|
1753
|
+
let sseCount = 0;
|
|
1722
1754
|
for (const sub of chatSubscribers) {
|
|
1723
|
-
try { sub.send(type, data); } catch {
|
|
1755
|
+
try { sub.send(type, data); sseCount++; } catch (e: any) {
|
|
1756
|
+
console.log(`[sse-broadcast] send failed sub=${sub.id}: ${e.message}`);
|
|
1757
|
+
}
|
|
1758
|
+
}
|
|
1759
|
+
if (type.startsWith('bot:') || type.startsWith('chat:')) {
|
|
1760
|
+
console.log(`[sse-broadcast] type=${type} ws=${wsCount} sse=${sseCount} subs=${chatSubscribers.size}`);
|
|
1724
1761
|
}
|
|
1725
1762
|
}
|
|
1726
1763
|
|