openclaw-navigator 5.5.2 → 5.5.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/cli.mjs +74 -6
- package/package.json +1 -1
package/cli.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* openclaw-navigator v5.5.
|
|
4
|
+
* openclaw-navigator v5.5.3
|
|
5
5
|
*
|
|
6
6
|
* One-command bridge + tunnel for the Navigator browser.
|
|
7
7
|
* Starts a local bridge, creates a Cloudflare tunnel automatically,
|
|
@@ -1389,14 +1389,16 @@ function handleRequest(req, res) {
|
|
|
1389
1389
|
);
|
|
1390
1390
|
}
|
|
1391
1391
|
|
|
1392
|
-
// ── SSE→JSON conversion for /api/* endpoints
|
|
1393
|
-
//
|
|
1394
|
-
//
|
|
1395
|
-
//
|
|
1392
|
+
// ── SSE→JSON conversion for non-streaming /api/* endpoints ──────
|
|
1393
|
+
// Some API endpoints (e.g. /api/agents) return SSE when the frontend
|
|
1394
|
+
// expects JSON. We convert those to JSON. But STREAMING endpoints
|
|
1395
|
+
// like /api/chat must pass through as SSE so the frontend can read
|
|
1396
|
+
// the real-time token stream via EventSource/fetch streaming.
|
|
1396
1397
|
const contentType = (proxyRes.headers["content-type"] || "").toLowerCase();
|
|
1397
1398
|
const isSSE = contentType.includes("text/event-stream");
|
|
1399
|
+
const isStreamingEndpoint = path.startsWith("/api/chat") || path.startsWith("/api/stream");
|
|
1398
1400
|
|
|
1399
|
-
if (isSSE && path.startsWith("/api/")) {
|
|
1401
|
+
if (isSSE && path.startsWith("/api/") && !isStreamingEndpoint) {
|
|
1400
1402
|
console.log(` ${DIM}SSE→JSON: converting ${path}${RESET}`);
|
|
1401
1403
|
let buffer = "";
|
|
1402
1404
|
const items = [];
|
|
@@ -1454,6 +1456,72 @@ function handleRequest(req, res) {
|
|
|
1454
1456
|
return;
|
|
1455
1457
|
}
|
|
1456
1458
|
|
|
1459
|
+
// ── SSE passthrough for streaming endpoints (/api/chat) ──────────
|
|
1460
|
+
// Let the SSE stream through to the browser AND tap it to broadcast
|
|
1461
|
+
// chunks via our WebSocket (so Navigator's sidepane chat gets them).
|
|
1462
|
+
if (isSSE && isStreamingEndpoint) {
|
|
1463
|
+
console.log(` ${DIM}SSE passthrough + WS tap: ${path}${RESET}`);
|
|
1464
|
+
res.writeHead(proxyRes.statusCode ?? 200, headers);
|
|
1465
|
+
|
|
1466
|
+
let fullText = "";
|
|
1467
|
+
proxyRes.on("data", (chunk) => {
|
|
1468
|
+
// Forward the chunk to the browser immediately
|
|
1469
|
+
res.write(chunk);
|
|
1470
|
+
|
|
1471
|
+
// Also parse and broadcast to WebSocket
|
|
1472
|
+
const text = chunk.toString("utf-8");
|
|
1473
|
+
for (const line of text.split("\n")) {
|
|
1474
|
+
if (line.startsWith("data: ")) {
|
|
1475
|
+
const raw = line.slice(6).trim();
|
|
1476
|
+
if (raw === "[DONE]" || !raw) {
|
|
1477
|
+
continue;
|
|
1478
|
+
}
|
|
1479
|
+
try {
|
|
1480
|
+
const evt = JSON.parse(raw);
|
|
1481
|
+
const delta =
|
|
1482
|
+
evt.choices?.[0]?.delta?.content ||
|
|
1483
|
+
evt.delta?.text ||
|
|
1484
|
+
evt.text ||
|
|
1485
|
+
evt.content ||
|
|
1486
|
+
"";
|
|
1487
|
+
if (delta) {
|
|
1488
|
+
fullText += delta;
|
|
1489
|
+
broadcastToWS({
|
|
1490
|
+
type: "chat.delta",
|
|
1491
|
+
text: fullText,
|
|
1492
|
+
delta,
|
|
1493
|
+
sessionKey: "main",
|
|
1494
|
+
timestamp: Date.now(),
|
|
1495
|
+
});
|
|
1496
|
+
}
|
|
1497
|
+
} catch {
|
|
1498
|
+
/* non-JSON SSE event — skip */
|
|
1499
|
+
}
|
|
1500
|
+
}
|
|
1501
|
+
}
|
|
1502
|
+
});
|
|
1503
|
+
|
|
1504
|
+
proxyRes.on("end", () => {
|
|
1505
|
+
// Stream ended — broadcast final message via WebSocket
|
|
1506
|
+
if (fullText) {
|
|
1507
|
+
broadcastToWS({
|
|
1508
|
+
type: "chat.final",
|
|
1509
|
+
text: fullText,
|
|
1510
|
+
content: fullText,
|
|
1511
|
+
sessionKey: "main",
|
|
1512
|
+
role: "assistant",
|
|
1513
|
+
timestamp: Date.now(),
|
|
1514
|
+
});
|
|
1515
|
+
}
|
|
1516
|
+
res.end();
|
|
1517
|
+
});
|
|
1518
|
+
|
|
1519
|
+
proxyRes.on("error", () => {
|
|
1520
|
+
res.end();
|
|
1521
|
+
});
|
|
1522
|
+
return;
|
|
1523
|
+
}
|
|
1524
|
+
|
|
1457
1525
|
// Non-SSE responses — stream through as-is
|
|
1458
1526
|
res.writeHead(proxyRes.statusCode ?? 502, headers);
|
|
1459
1527
|
proxyRes.pipe(res, { end: true });
|