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.
Files changed (2) hide show
  1. package/cli.mjs +39 -15
  2. 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 passthrough for streaming endpoints (/api/chat) ────────────
1617
- // Pass SSE through as-is the web UI frontend handles SSE natively.
1618
- // Disable response buffering so each SSE event reaches the client immediately.
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 passthrough: ${path}${RESET}`);
1622
+ console.log(` ${DIM}SSE→NDJSON: ${path}${RESET}`);
1621
1623
 
1622
- // Ensure SSE headers are clean for the browser
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
- // Keep content-type as text/event-stream
1626
- delete headers["content-length"]; // SSE is chunked
1629
+ delete headers["content-length"];
1630
+ delete headers["transfer-encoding"];
1627
1631
 
1628
- res.writeHead(proxyRes.statusCode ?? 200, headers);
1629
-
1630
- // Disable buffering at every layer
1632
+ res.writeHead(200, headers);
1631
1633
  if (res.socket) res.socket.setNoDelay(true);
1632
1634
 
1633
- // Pipe SSE events directly — no transformation
1635
+ let sseLineBuf = "";
1636
+ proxyRes.setEncoding("utf-8");
1634
1637
  proxyRes.on("data", (chunk) => {
1635
- res.write(chunk);
1636
- // Flush after each chunk to prevent TCP buffering
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
- sendJSON(res, 502, { ok: false, error: "Stream error" });
1671
+ res.end();
1648
1672
  });
1649
1673
  return;
1650
1674
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openclaw-navigator",
3
- "version": "5.7.6",
3
+ "version": "5.7.7",
4
4
  "description": "One-command bridge + tunnel for the Navigator browser — works on any machine, any OS",
5
5
  "keywords": [
6
6
  "browser",