openclaw-navigator 5.6.0 → 5.6.2

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 +149 -30
  2. package/package.json +1 -1
package/cli.mjs CHANGED
@@ -1136,42 +1136,161 @@ function handleRequest(req, res) {
1136
1136
  return;
1137
1137
  }
1138
1138
 
1139
- // ── Chat send: store user message + broadcast via WebSocket ────────────
1140
- // The bridge IS the chat backend messages are stored here in-memory.
1141
- // The OC agent reads them via navigator_get_chat_messages MCP tool,
1142
- // processes them, and pushes responses via navigator_chat_respond.
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
1143
1145
  if (path === "/api/sessions/send" && req.method === "POST") {
1144
1146
  readBody(req)
1145
- .then((body) => {
1147
+ .then((bodyStr) => {
1148
+ let data;
1146
1149
  try {
1147
- const data = JSON.parse(body);
1148
- const message = data.message || data.content || data.text || "";
1149
- const sessionKey = data.sessionKey || "main";
1150
- if (!message) {
1151
- sendJSON(res, 400, { ok: false, error: "Missing 'message'" });
1152
- return;
1153
- }
1154
- // Store user message in local session
1155
- const session = getChatSession(sessionKey);
1156
- session.messages.push({
1157
- role: "user",
1158
- content: message,
1159
- timestamp: Date.now(),
1160
- });
1161
- // Broadcast via WebSocket so any listening UI gets it
1162
- broadcastToWS({
1163
- type: "chat.user",
1164
- text: message,
1165
- content: message,
1166
- sessionKey,
1167
- role: "user",
1168
- timestamp: Date.now(),
1169
- });
1170
- console.log(` ${DIM}Chat send [${sessionKey}]: ${message.substring(0, 60)}...${RESET}`);
1171
- sendJSON(res, 200, { ok: true, stored: true, messageCount: session.messages.length });
1150
+ data = JSON.parse(bodyStr);
1172
1151
  } catch {
1173
1152
  sendJSON(res, 400, { ok: false, error: "Invalid JSON" });
1153
+ return;
1174
1154
  }
1155
+
1156
+ const message = data.message || data.content || data.text || "";
1157
+ const sessionKey = data.sessionKey || "main";
1158
+ if (!message) {
1159
+ sendJSON(res, 400, { ok: false, error: "Missing 'message'" });
1160
+ return;
1161
+ }
1162
+
1163
+ // 1. Store user message locally
1164
+ const session = getChatSession(sessionKey);
1165
+ session.messages.push({
1166
+ role: "user",
1167
+ content: message,
1168
+ timestamp: Date.now(),
1169
+ });
1170
+ console.log(` ${DIM}Chat send [${sessionKey}]: ${message.substring(0, 60)}...${RESET}`);
1171
+
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 });
1177
+ const proxyOpts = {
1178
+ hostname: "127.0.0.1",
1179
+ port: ocGatewayPort,
1180
+ path: `/api/sessions/send`,
1181
+ method: "POST",
1182
+ timeout: 60000, // 60s — agent may take a while to respond
1183
+ headers: {
1184
+ "content-type": "application/json",
1185
+ "content-length": Buffer.byteLength(proxyBody),
1186
+ },
1187
+ };
1188
+
1189
+ const proxyReq = httpRequest(proxyOpts, (proxyRes) => {
1190
+ const contentType = (proxyRes.headers["content-type"] || "").toLowerCase();
1191
+ const isSSE = contentType.includes("text/event-stream");
1192
+
1193
+ if (isSSE) {
1194
+ // SSE response: collect stream, broadcast chunks via WebSocket
1195
+ let fullText = "";
1196
+ let buffer = "";
1197
+ proxyRes.setEncoding("utf-8");
1198
+ proxyRes.on("data", (chunk) => {
1199
+ buffer += chunk;
1200
+ const lines = buffer.split("\n");
1201
+ buffer = lines.pop() || "";
1202
+ for (const line of lines) {
1203
+ if (line.startsWith("data: ")) {
1204
+ const raw = line.slice(6).trim();
1205
+ if (raw === "[DONE]") continue;
1206
+ try {
1207
+ const evt = JSON.parse(raw);
1208
+ const text =
1209
+ evt.text || evt.content || evt.delta?.text || evt.delta?.content || "";
1210
+ if (text) {
1211
+ fullText += text;
1212
+ broadcastToWS({
1213
+ type: "chat.delta",
1214
+ text: fullText,
1215
+ delta: text,
1216
+ sessionKey,
1217
+ timestamp: Date.now(),
1218
+ });
1219
+ }
1220
+ } catch {
1221
+ if (raw && raw !== "[DONE]") fullText += raw;
1222
+ }
1223
+ }
1224
+ }
1225
+ });
1226
+ proxyRes.on("end", () => {
1227
+ if (buffer.startsWith("data: ")) {
1228
+ const raw = buffer.slice(6).trim();
1229
+ if (raw && raw !== "[DONE]") {
1230
+ try {
1231
+ const evt = JSON.parse(raw);
1232
+ fullText += evt.text || evt.content || evt.delta?.text || "";
1233
+ } catch {
1234
+ fullText += raw;
1235
+ }
1236
+ }
1237
+ }
1238
+ if (fullText) {
1239
+ session.messages.push({ role: "assistant", content: fullText, timestamp: Date.now() });
1240
+ broadcastToWS({
1241
+ type: "chat.final",
1242
+ text: fullText,
1243
+ content: fullText,
1244
+ sessionKey,
1245
+ role: "assistant",
1246
+ timestamp: Date.now(),
1247
+ });
1248
+ console.log(` ${GREEN}✓${RESET} Gateway SSE response: ${fullText.substring(0, 80)}...`);
1249
+ }
1250
+ });
1251
+ proxyRes.on("error", () => {});
1252
+ } else {
1253
+ // JSON response from gateway
1254
+ const chunks = [];
1255
+ proxyRes.on("data", (c) => chunks.push(c));
1256
+ proxyRes.on("end", () => {
1257
+ const body = Buffer.concat(chunks).toString("utf-8");
1258
+ try {
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
+ }
1276
+ } catch {
1277
+ console.log(` ${DIM}Gateway returned non-JSON — waiting for MCP${RESET}`);
1278
+ }
1279
+ });
1280
+ }
1281
+ });
1282
+
1283
+ proxyReq.on("timeout", () => {
1284
+ proxyReq.destroy();
1285
+ console.log(` ${DIM}Gateway relay timed out — agent will respond via MCP${RESET}`);
1286
+ });
1287
+
1288
+ proxyReq.on("error", (err) => {
1289
+ console.log(` ${DIM}Gateway relay failed (${err.message}) — agent will respond via MCP${RESET}`);
1290
+ });
1291
+
1292
+ proxyReq.write(proxyBody);
1293
+ proxyReq.end();
1175
1294
  })
1176
1295
  .catch(() => sendJSON(res, 400, { ok: false, error: "Bad request body" }));
1177
1296
  return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openclaw-navigator",
3
- "version": "5.6.0",
3
+ "version": "5.6.2",
4
4
  "description": "One-command bridge + tunnel for the Navigator browser — works on any machine, any OS",
5
5
  "keywords": [
6
6
  "browser",