openclaw-navigator 5.7.6 → 5.7.7
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 +39 -15
- package/package.json +1 -1
package/cli.mjs
CHANGED
|
@@ -1613,38 +1613,62 @@ function handleRequest(req, res) {
|
|
|
1613
1613
|
return;
|
|
1614
1614
|
}
|
|
1615
1615
|
|
|
1616
|
-
// ── SSE
|
|
1617
|
-
//
|
|
1618
|
-
//
|
|
1616
|
+
// ── SSE → NDJSON for streaming endpoints (/api/chat) ─────────────
|
|
1617
|
+
// The OC web UI frontend does fetch() + JSON.parse() on each line,
|
|
1618
|
+
// NOT proper EventSource parsing. So it chokes on "data: {...}"
|
|
1619
|
+
// (the "data: " prefix isn't valid JSON). We strip the prefix and
|
|
1620
|
+
// forward clean JSON lines (NDJSON), one per write, flushed immediately.
|
|
1619
1621
|
if (isSSE && isStreamingEndpoint) {
|
|
1620
|
-
console.log(` ${DIM}SSE
|
|
1622
|
+
console.log(` ${DIM}SSE→NDJSON: ${path}${RESET}`);
|
|
1621
1623
|
|
|
1622
|
-
//
|
|
1624
|
+
// Send as NDJSON (application/x-ndjson) so the frontend gets
|
|
1625
|
+
// one clean JSON object per line, no SSE framing.
|
|
1626
|
+
headers["content-type"] = "application/x-ndjson";
|
|
1623
1627
|
headers["cache-control"] = "no-cache";
|
|
1624
1628
|
headers["connection"] = "keep-alive";
|
|
1625
|
-
|
|
1626
|
-
delete headers["
|
|
1629
|
+
delete headers["content-length"];
|
|
1630
|
+
delete headers["transfer-encoding"];
|
|
1627
1631
|
|
|
1628
|
-
res.writeHead(
|
|
1629
|
-
|
|
1630
|
-
// Disable buffering at every layer
|
|
1632
|
+
res.writeHead(200, headers);
|
|
1631
1633
|
if (res.socket) res.socket.setNoDelay(true);
|
|
1632
1634
|
|
|
1633
|
-
|
|
1635
|
+
let sseLineBuf = "";
|
|
1636
|
+
proxyRes.setEncoding("utf-8");
|
|
1634
1637
|
proxyRes.on("data", (chunk) => {
|
|
1635
|
-
|
|
1636
|
-
//
|
|
1638
|
+
sseLineBuf += chunk;
|
|
1639
|
+
// Process complete lines
|
|
1640
|
+
const lines = sseLineBuf.split("\n");
|
|
1641
|
+
sseLineBuf = lines.pop() || ""; // keep incomplete line in buffer
|
|
1642
|
+
for (const line of lines) {
|
|
1643
|
+
if (line.startsWith("data: ")) {
|
|
1644
|
+
const payload = line.slice(6).trim();
|
|
1645
|
+
if (payload === "[DONE]") {
|
|
1646
|
+
res.write("data: [DONE]\n\n");
|
|
1647
|
+
} else if (payload) {
|
|
1648
|
+
// Write clean JSON + newline — one object per write
|
|
1649
|
+
res.write(payload + "\n");
|
|
1650
|
+
}
|
|
1651
|
+
}
|
|
1652
|
+
// Skip empty lines, "event:", "id:", "retry:" etc.
|
|
1653
|
+
}
|
|
1637
1654
|
if (typeof res.flush === "function") res.flush();
|
|
1638
1655
|
});
|
|
1639
1656
|
|
|
1640
1657
|
proxyRes.on("end", () => {
|
|
1658
|
+
// Flush remaining buffer
|
|
1659
|
+
if (sseLineBuf.startsWith("data: ")) {
|
|
1660
|
+
const payload = sseLineBuf.slice(6).trim();
|
|
1661
|
+
if (payload && payload !== "[DONE]") {
|
|
1662
|
+
res.write(payload + "\n");
|
|
1663
|
+
}
|
|
1664
|
+
}
|
|
1641
1665
|
res.end();
|
|
1642
|
-
console.log(` ${GREEN}✓${RESET} SSE stream completed for ${path}`);
|
|
1666
|
+
console.log(` ${GREEN}✓${RESET} SSE→NDJSON stream completed for ${path}`);
|
|
1643
1667
|
});
|
|
1644
1668
|
|
|
1645
1669
|
proxyRes.on("error", (err) => {
|
|
1646
1670
|
console.log(` ${DIM}SSE stream error: ${err.message}${RESET}`);
|
|
1647
|
-
|
|
1671
|
+
res.end();
|
|
1648
1672
|
});
|
|
1649
1673
|
return;
|
|
1650
1674
|
}
|