openclaw-navigator 5.6.1 → 5.6.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.
Files changed (2) hide show
  1. package/cli.mjs +62 -75
  2. package/package.json +1 -1
package/cli.mjs CHANGED
@@ -1136,12 +1136,12 @@ function handleRequest(req, res) {
1136
1136
  return;
1137
1137
  }
1138
1138
 
1139
- // ── Chat send: store locally + relay to OC Gateway for processing ──────
1140
- // HYBRID approach:
1141
- // 1. Store user message in bridge (for MCP tools to read via history)
1142
- // 2. Relay to OC Gateway (port 18789) so the AI agent processes it
1143
- // 3. Gateway may return response inline (JSON/SSE) store + broadcast
1144
- // 4. Or agent responds async via navigator_chat_respond MCP tool
1139
+ // ── Chat send: store locally + respond immediately ─────────────────────
1140
+ // 1. Store user message in bridge memory (for MCP tools to read)
1141
+ // 2. Respond to Navigator immediately (don't block on gateway)
1142
+ // 3. In background: try relaying to gateway for AI processing
1143
+ // - If gateway responds store assistant message + broadcast via WS
1144
+ // - If gateway hangs/fails → agent will respond via MCP tools later
1145
1145
  if (path === "/api/sessions/send" && req.method === "POST") {
1146
1146
  readBody(req)
1147
1147
  .then((bodyStr) => {
@@ -1167,25 +1167,22 @@ function handleRequest(req, res) {
1167
1167
  content: message,
1168
1168
  timestamp: Date.now(),
1169
1169
  });
1170
- broadcastToWS({
1171
- type: "chat.user",
1172
- text: message,
1173
- content: message,
1174
- sessionKey,
1175
- role: "user",
1176
- timestamp: Date.now(),
1177
- });
1178
1170
  console.log(` ${DIM}Chat send [${sessionKey}]: ${message.substring(0, 60)}...${RESET}`);
1179
1171
 
1180
- // 2. Relay to OC Gateway for AI processing
1172
+ // 2. Respond immediately don't block Navigator
1173
+ sendJSON(res, 200, { ok: true, stored: true, messageCount: session.messages.length });
1174
+
1175
+ // 3. Background: relay to gateway for AI processing
1176
+ const proxyBody = JSON.stringify({ message, sessionKey });
1181
1177
  const proxyOpts = {
1182
1178
  hostname: "127.0.0.1",
1183
1179
  port: ocGatewayPort,
1184
1180
  path: `/api/sessions/send`,
1185
1181
  method: "POST",
1182
+ timeout: 60000, // 60s — agent may take a while to respond
1186
1183
  headers: {
1187
1184
  "content-type": "application/json",
1188
- "content-length": Buffer.byteLength(bodyStr),
1185
+ "content-length": Buffer.byteLength(proxyBody),
1189
1186
  },
1190
1187
  };
1191
1188
 
@@ -1194,10 +1191,9 @@ function handleRequest(req, res) {
1194
1191
  const isSSE = contentType.includes("text/event-stream");
1195
1192
 
1196
1193
  if (isSSE) {
1197
- // ── SSE response: collect stream, broadcast chunks, return JSON ──
1194
+ // SSE response: collect stream, broadcast chunks via WebSocket
1198
1195
  let fullText = "";
1199
1196
  let buffer = "";
1200
-
1201
1197
  proxyRes.setEncoding("utf-8");
1202
1198
  proxyRes.on("data", (chunk) => {
1203
1199
  buffer += chunk;
@@ -1227,7 +1223,6 @@ function handleRequest(req, res) {
1227
1223
  }
1228
1224
  }
1229
1225
  });
1230
-
1231
1226
  proxyRes.on("end", () => {
1232
1227
  if (buffer.startsWith("data: ")) {
1233
1228
  const raw = buffer.slice(6).trim();
@@ -1241,12 +1236,7 @@ function handleRequest(req, res) {
1241
1236
  }
1242
1237
  }
1243
1238
  if (fullText) {
1244
- // Store assistant response
1245
- session.messages.push({
1246
- role: "assistant",
1247
- content: fullText,
1248
- timestamp: Date.now(),
1249
- });
1239
+ session.messages.push({ role: "assistant", content: fullText, timestamp: Date.now() });
1250
1240
  broadcastToWS({
1251
1241
  type: "chat.final",
1252
1242
  text: fullText,
@@ -1255,69 +1245,51 @@ function handleRequest(req, res) {
1255
1245
  role: "assistant",
1256
1246
  timestamp: Date.now(),
1257
1247
  });
1258
- console.log(` ${DIM}Gateway SSE→WS: ${fullText.substring(0, 80)}...${RESET}`);
1248
+ console.log(` ${GREEN}✓${RESET} Gateway SSE response: ${fullText.substring(0, 80)}...`);
1259
1249
  }
1260
- sendJSON(res, 200, {
1261
- ok: true,
1262
- stored: true,
1263
- response: fullText || null,
1264
- source: "gateway-sse",
1265
- });
1266
- });
1267
-
1268
- proxyRes.on("error", () => {
1269
- sendJSON(res, 200, { ok: true, stored: true, response: null, source: "gateway-sse-error" });
1270
1250
  });
1251
+ proxyRes.on("error", () => {});
1271
1252
  } else {
1272
- // ── JSON response from gateway ──
1253
+ // JSON response from gateway
1273
1254
  const chunks = [];
1274
1255
  proxyRes.on("data", (c) => chunks.push(c));
1275
1256
  proxyRes.on("end", () => {
1276
1257
  const body = Buffer.concat(chunks).toString("utf-8");
1277
- let jsonBody;
1278
1258
  try {
1279
- jsonBody = JSON.parse(body);
1259
+ const jsonBody = JSON.parse(body);
1260
+ const inlineResponse =
1261
+ jsonBody.response || jsonBody.message || jsonBody.answer || jsonBody.text || "";
1262
+ if (inlineResponse) {
1263
+ session.messages.push({ role: "assistant", content: inlineResponse, timestamp: Date.now() });
1264
+ broadcastToWS({
1265
+ type: "chat.final",
1266
+ text: inlineResponse,
1267
+ content: inlineResponse,
1268
+ sessionKey,
1269
+ role: "assistant",
1270
+ timestamp: Date.now(),
1271
+ });
1272
+ console.log(` ${GREEN}✓${RESET} Gateway JSON response: ${inlineResponse.substring(0, 80)}...`);
1273
+ } else {
1274
+ console.log(` ${DIM}Gateway returned JSON with no response field — waiting for MCP${RESET}`);
1275
+ }
1280
1276
  } catch {
1281
- // Non-JSON — message is stored, agent will respond via MCP
1282
- sendJSON(res, 200, { ok: true, stored: true, response: null, source: "gateway-raw" });
1283
- return;
1284
- }
1285
- // Check if gateway returned an inline AI response
1286
- const inlineResponse =
1287
- jsonBody.response || jsonBody.message || jsonBody.answer || jsonBody.text || "";
1288
- if (inlineResponse) {
1289
- session.messages.push({
1290
- role: "assistant",
1291
- content: inlineResponse,
1292
- timestamp: Date.now(),
1293
- });
1294
- broadcastToWS({
1295
- type: "chat.final",
1296
- text: inlineResponse,
1297
- content: inlineResponse,
1298
- sessionKey,
1299
- role: "assistant",
1300
- timestamp: Date.now(),
1301
- });
1302
- console.log(` ${DIM}Gateway inline→WS: ${inlineResponse.substring(0, 80)}...${RESET}`);
1277
+ console.log(` ${DIM}Gateway returned non-JSON — waiting for MCP${RESET}`);
1303
1278
  }
1304
- sendJSON(res, 200, {
1305
- ok: true,
1306
- stored: true,
1307
- response: inlineResponse || null,
1308
- source: "gateway-json",
1309
- });
1310
1279
  });
1311
1280
  }
1312
1281
  });
1313
1282
 
1283
+ proxyReq.on("timeout", () => {
1284
+ proxyReq.destroy();
1285
+ console.log(` ${DIM}Gateway relay timed out — agent will respond via MCP${RESET}`);
1286
+ });
1287
+
1314
1288
  proxyReq.on("error", (err) => {
1315
- // Gateway not reachable message is still stored, agent can use MCP tools
1316
- console.log(` ${DIM}Gateway not reachable (${err.message}) — message stored for MCP polling${RESET}`);
1317
- sendJSON(res, 200, { ok: true, stored: true, response: null, source: "local-only" });
1289
+ console.log(` ${DIM}Gateway relay failed (${err.message}) agent will respond via MCP${RESET}`);
1318
1290
  });
1319
1291
 
1320
- proxyReq.write(bodyStr);
1292
+ proxyReq.write(proxyBody);
1321
1293
  proxyReq.end();
1322
1294
  })
1323
1295
  .catch(() => sendJSON(res, 400, { ok: false, error: "Bad request body" }));
@@ -1391,6 +1363,10 @@ function handleRequest(req, res) {
1391
1363
  {
1392
1364
  const targetURL = `${path}${url.search}`;
1393
1365
  const incomingHost = req.headers.host || "localhost";
1366
+ // Log API calls for diagnostics (helps debug web UI chat)
1367
+ if (path.startsWith("/api/")) {
1368
+ console.log(` ${DIM}→ Proxy ${req.method} ${path} → localhost:${ocUIPort}${RESET}`);
1369
+ }
1394
1370
 
1395
1371
  // Encourage JSON responses from the web UI's API routes
1396
1372
  const proxyHeaders = {
@@ -1401,8 +1377,10 @@ function handleRequest(req, res) {
1401
1377
  "x-forwarded-for": req.socket.remoteAddress || "127.0.0.1",
1402
1378
  };
1403
1379
 
1404
- // For /api/* requests, prefer JSON over SSE
1405
- if (path.startsWith("/api/")) {
1380
+ // For /api/* requests, prefer JSON over SSE — BUT not for streaming chat endpoints
1381
+ // which need text/event-stream to get SSE responses from the BFF
1382
+ const isStreamingReq = path.startsWith("/api/chat") || path.startsWith("/api/stream");
1383
+ if (path.startsWith("/api/") && !isStreamingReq) {
1406
1384
  proxyHeaders["accept"] = "application/json, text/plain, */*";
1407
1385
  }
1408
1386
 
@@ -1417,6 +1395,12 @@ function handleRequest(req, res) {
1417
1395
  const proxyReq = httpRequest(proxyOpts, (proxyRes) => {
1418
1396
  const headers = { ...proxyRes.headers };
1419
1397
 
1398
+ // Log response info for API calls (helps debug web UI chat)
1399
+ if (path.startsWith("/api/")) {
1400
+ const ct = (proxyRes.headers["content-type"] || "unknown").split(";")[0];
1401
+ console.log(` ${DIM}← ${proxyRes.statusCode} ${ct} for ${path}${RESET}`);
1402
+ }
1403
+
1420
1404
  // CORS
1421
1405
  headers["access-control-allow-origin"] = "*";
1422
1406
  headers["access-control-allow-methods"] = "GET, POST, PUT, DELETE, OPTIONS";
@@ -1578,8 +1562,11 @@ function handleRequest(req, res) {
1578
1562
  proxyRes.pipe(res, { end: true });
1579
1563
  });
1580
1564
 
1581
- proxyReq.on("error", () => {
1582
- sendJSON(res, 404, { ok: false, error: "Not found" });
1565
+ proxyReq.on("error", (err) => {
1566
+ if (path.startsWith("/api/")) {
1567
+ console.log(` ${DIM}✗ Proxy error for ${path}: ${err.message}${RESET}`);
1568
+ }
1569
+ sendJSON(res, 502, { ok: false, error: `Web UI (port ${ocUIPort}) not reachable: ${err.message}` });
1583
1570
  });
1584
1571
 
1585
1572
  req.pipe(proxyReq, { end: true });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openclaw-navigator",
3
- "version": "5.6.1",
3
+ "version": "5.6.3",
4
4
  "description": "One-command bridge + tunnel for the Navigator browser — works on any machine, any OS",
5
5
  "keywords": [
6
6
  "browser",