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.
- package/cli.mjs +177 -30
- 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
|
|
1140
|
-
//
|
|
1141
|
-
//
|
|
1142
|
-
//
|
|
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((
|
|
1147
|
+
.then((bodyStr) => {
|
|
1148
|
+
let data;
|
|
1146
1149
|
try {
|
|
1147
|
-
|
|
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;
|