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.
- package/cli.mjs +62 -75
- 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 +
|
|
1140
|
-
//
|
|
1141
|
-
//
|
|
1142
|
-
//
|
|
1143
|
-
//
|
|
1144
|
-
//
|
|
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.
|
|
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(
|
|
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
|
-
//
|
|
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
|
-
|
|
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(` ${
|
|
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
|
-
//
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
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 });
|