openclaw-navigator 5.6.0 → 5.6.1

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 +177 -30
  2. package/package.json +1 -1
package/cli.mjs CHANGED
@@ -1136,42 +1136,189 @@ 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 + 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
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;
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;
1174
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
+ broadcastToWS({
1171
+ type: "chat.user",
1172
+ text: message,
1173
+ content: message,
1174
+ sessionKey,
1175
+ role: "user",
1176
+ timestamp: Date.now(),
1177
+ });
1178
+ console.log(` ${DIM}Chat send [${sessionKey}]: ${message.substring(0, 60)}...${RESET}`);
1179
+
1180
+ // 2. Relay to OC Gateway for AI processing
1181
+ const proxyOpts = {
1182
+ hostname: "127.0.0.1",
1183
+ port: ocGatewayPort,
1184
+ path: `/api/sessions/send`,
1185
+ method: "POST",
1186
+ headers: {
1187
+ "content-type": "application/json",
1188
+ "content-length": Buffer.byteLength(bodyStr),
1189
+ },
1190
+ };
1191
+
1192
+ const proxyReq = httpRequest(proxyOpts, (proxyRes) => {
1193
+ const contentType = (proxyRes.headers["content-type"] || "").toLowerCase();
1194
+ const isSSE = contentType.includes("text/event-stream");
1195
+
1196
+ if (isSSE) {
1197
+ // ── SSE response: collect stream, broadcast chunks, return JSON ──
1198
+ let fullText = "";
1199
+ let buffer = "";
1200
+
1201
+ proxyRes.setEncoding("utf-8");
1202
+ proxyRes.on("data", (chunk) => {
1203
+ buffer += chunk;
1204
+ const lines = buffer.split("\n");
1205
+ buffer = lines.pop() || "";
1206
+ for (const line of lines) {
1207
+ if (line.startsWith("data: ")) {
1208
+ const raw = line.slice(6).trim();
1209
+ if (raw === "[DONE]") continue;
1210
+ try {
1211
+ const evt = JSON.parse(raw);
1212
+ const text =
1213
+ evt.text || evt.content || evt.delta?.text || evt.delta?.content || "";
1214
+ if (text) {
1215
+ fullText += text;
1216
+ broadcastToWS({
1217
+ type: "chat.delta",
1218
+ text: fullText,
1219
+ delta: text,
1220
+ sessionKey,
1221
+ timestamp: Date.now(),
1222
+ });
1223
+ }
1224
+ } catch {
1225
+ if (raw && raw !== "[DONE]") fullText += raw;
1226
+ }
1227
+ }
1228
+ }
1229
+ });
1230
+
1231
+ proxyRes.on("end", () => {
1232
+ if (buffer.startsWith("data: ")) {
1233
+ const raw = buffer.slice(6).trim();
1234
+ if (raw && raw !== "[DONE]") {
1235
+ try {
1236
+ const evt = JSON.parse(raw);
1237
+ fullText += evt.text || evt.content || evt.delta?.text || "";
1238
+ } catch {
1239
+ fullText += raw;
1240
+ }
1241
+ }
1242
+ }
1243
+ if (fullText) {
1244
+ // Store assistant response
1245
+ session.messages.push({
1246
+ role: "assistant",
1247
+ content: fullText,
1248
+ timestamp: Date.now(),
1249
+ });
1250
+ broadcastToWS({
1251
+ type: "chat.final",
1252
+ text: fullText,
1253
+ content: fullText,
1254
+ sessionKey,
1255
+ role: "assistant",
1256
+ timestamp: Date.now(),
1257
+ });
1258
+ console.log(` ${DIM}Gateway SSE→WS: ${fullText.substring(0, 80)}...${RESET}`);
1259
+ }
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
+ });
1271
+ } else {
1272
+ // ── JSON response from gateway ──
1273
+ const chunks = [];
1274
+ proxyRes.on("data", (c) => chunks.push(c));
1275
+ proxyRes.on("end", () => {
1276
+ const body = Buffer.concat(chunks).toString("utf-8");
1277
+ let jsonBody;
1278
+ try {
1279
+ jsonBody = JSON.parse(body);
1280
+ } 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}`);
1303
+ }
1304
+ sendJSON(res, 200, {
1305
+ ok: true,
1306
+ stored: true,
1307
+ response: inlineResponse || null,
1308
+ source: "gateway-json",
1309
+ });
1310
+ });
1311
+ }
1312
+ });
1313
+
1314
+ 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" });
1318
+ });
1319
+
1320
+ proxyReq.write(bodyStr);
1321
+ proxyReq.end();
1175
1322
  })
1176
1323
  .catch(() => sendJSON(res, 400, { ok: false, error: "Bad request body" }));
1177
1324
  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.1",
4
4
  "description": "One-command bridge + tunnel for the Navigator browser — works on any machine, any OS",
5
5
  "keywords": [
6
6
  "browser",