@agent-e/server 1.6.8 → 1.6.10
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/dist/AgentEServer-WOYIMZWG.mjs +7 -0
- package/dist/{chunk-T66KRKT4.mjs → chunk-MH5XTBNY.mjs} +86 -18
- package/dist/chunk-MH5XTBNY.mjs.map +1 -0
- package/dist/cli.js +85 -17
- package/dist/cli.js.map +1 -1
- package/dist/cli.mjs +1 -1
- package/dist/index.d.mts +3 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +86 -18
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2 -2
- package/package.json +1 -1
- package/dist/AgentEServer-745ZSFYB.mjs +0 -7
- package/dist/chunk-T66KRKT4.mjs.map +0 -1
- /package/dist/{AgentEServer-745ZSFYB.mjs.map → AgentEServer-WOYIMZWG.mjs.map} +0 -0
package/dist/index.js
CHANGED
|
@@ -569,7 +569,7 @@ function getDashboardHtml() {
|
|
|
569
569
|
const $app = document.getElementById('app');
|
|
570
570
|
|
|
571
571
|
// \u2500\u2500 Helpers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
572
|
-
function esc(s) { return String(s).replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"').replace(/'/g,'''); }
|
|
572
|
+
function esc(s) { return String(s).replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"').replace(/'/g,''').replace(/\\\\/g,'\'); }
|
|
573
573
|
function pad(n, w) { return String(n).padStart(w || 4, ' '); }
|
|
574
574
|
function fmt(n) { return typeof n === 'number' ? n.toFixed(3) : '\u2014'; }
|
|
575
575
|
function pct(n) { return typeof n === 'number' ? (n * 100).toFixed(0) + '%' : '\u2014'; }
|
|
@@ -941,7 +941,7 @@ function getDashboardHtml() {
|
|
|
941
941
|
window._approve = function(id) {
|
|
942
942
|
postJSON('/approve', { decisionId: id }).then(function(data) {
|
|
943
943
|
if (data.ok) {
|
|
944
|
-
addTerminalLine('<span class="t-tick">[Advisor]</span> <span class="t-ok">\\u2705 Approved ' + id + '</span>');
|
|
944
|
+
addTerminalLine('<span class="t-tick">[Advisor]</span> <span class="t-ok">\\u2705 Approved ' + esc(id) + '</span>');
|
|
945
945
|
}
|
|
946
946
|
}).catch(function() {});
|
|
947
947
|
};
|
|
@@ -950,7 +950,7 @@ function getDashboardHtml() {
|
|
|
950
950
|
var reason = prompt('Rejection reason (optional):');
|
|
951
951
|
postJSON('/reject', { decisionId: id, reason: reason || undefined }).then(function(data) {
|
|
952
952
|
if (data.ok) {
|
|
953
|
-
addTerminalLine('<span class="t-tick">[Advisor]</span> <span class="t-fail">\\u274c Rejected ' + id + '</span>');
|
|
953
|
+
addTerminalLine('<span class="t-tick">[Advisor]</span> <span class="t-fail">\\u274c Rejected ' + esc(id) + '</span>');
|
|
954
954
|
}
|
|
955
955
|
}).catch(function() {});
|
|
956
956
|
};
|
|
@@ -977,14 +977,30 @@ function setSecurityHeaders(res) {
|
|
|
977
977
|
res.setHeader("X-Frame-Options", "DENY");
|
|
978
978
|
res.setHeader("Referrer-Policy", "strict-origin-when-cross-origin");
|
|
979
979
|
}
|
|
980
|
-
function setCorsHeaders(res,
|
|
980
|
+
function setCorsHeaders(res, allowedOrigin, requestOrigin) {
|
|
981
981
|
setSecurityHeaders(res);
|
|
982
|
+
const origin = allowedOrigin === "*" ? "*" : requestOrigin === allowedOrigin ? allowedOrigin : allowedOrigin;
|
|
982
983
|
res.setHeader("Access-Control-Allow-Origin", origin);
|
|
983
984
|
res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
|
|
984
|
-
res.setHeader("Access-Control-Allow-Headers", "Content-Type");
|
|
985
|
+
res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
|
|
985
986
|
}
|
|
986
|
-
function
|
|
987
|
-
|
|
987
|
+
function sanitizeJson(obj) {
|
|
988
|
+
if (obj === null || typeof obj !== "object") return obj;
|
|
989
|
+
if (Array.isArray(obj)) return obj.map(sanitizeJson);
|
|
990
|
+
const clean = {};
|
|
991
|
+
for (const [key, val] of Object.entries(obj)) {
|
|
992
|
+
if (key === "__proto__" || key === "constructor" || key === "prototype") continue;
|
|
993
|
+
clean[key] = sanitizeJson(val);
|
|
994
|
+
}
|
|
995
|
+
return clean;
|
|
996
|
+
}
|
|
997
|
+
function checkAuth(req, apiKey) {
|
|
998
|
+
if (!apiKey) return true;
|
|
999
|
+
const header = req.headers["authorization"];
|
|
1000
|
+
return header === `Bearer ${apiKey}`;
|
|
1001
|
+
}
|
|
1002
|
+
function json(res, status, data, origin, reqOrigin) {
|
|
1003
|
+
setCorsHeaders(res, origin, reqOrigin);
|
|
988
1004
|
res.writeHead(status, { "Content-Type": "application/json" });
|
|
989
1005
|
res.end(JSON.stringify(data));
|
|
990
1006
|
}
|
|
@@ -1007,6 +1023,7 @@ function readBody(req) {
|
|
|
1007
1023
|
}
|
|
1008
1024
|
function createRouteHandler(server) {
|
|
1009
1025
|
const cors = server.corsOrigin;
|
|
1026
|
+
const apiKey = server.apiKey;
|
|
1010
1027
|
return async (req, res) => {
|
|
1011
1028
|
const url = new URL(req.url ?? "/", `http://${req.headers.host ?? "localhost"}`);
|
|
1012
1029
|
const path = url.pathname;
|
|
@@ -1019,10 +1036,14 @@ function createRouteHandler(server) {
|
|
|
1019
1036
|
}
|
|
1020
1037
|
try {
|
|
1021
1038
|
if (path === "/tick" && method === "POST") {
|
|
1039
|
+
if (!checkAuth(req, apiKey)) {
|
|
1040
|
+
json(res, 401, { error: "Unauthorized" }, cors);
|
|
1041
|
+
return;
|
|
1042
|
+
}
|
|
1022
1043
|
const body = await readBody(req);
|
|
1023
1044
|
let parsed;
|
|
1024
1045
|
try {
|
|
1025
|
-
parsed = JSON.parse(body);
|
|
1046
|
+
parsed = sanitizeJson(JSON.parse(body));
|
|
1026
1047
|
} catch {
|
|
1027
1048
|
json(res, 400, { error: "Invalid JSON" }, cors);
|
|
1028
1049
|
return;
|
|
@@ -1093,10 +1114,14 @@ function createRouteHandler(server) {
|
|
|
1093
1114
|
return;
|
|
1094
1115
|
}
|
|
1095
1116
|
if (path === "/config" && method === "POST") {
|
|
1117
|
+
if (!checkAuth(req, apiKey)) {
|
|
1118
|
+
json(res, 401, { error: "Unauthorized" }, cors);
|
|
1119
|
+
return;
|
|
1120
|
+
}
|
|
1096
1121
|
const body = await readBody(req);
|
|
1097
1122
|
let parsed;
|
|
1098
1123
|
try {
|
|
1099
|
-
parsed = JSON.parse(body);
|
|
1124
|
+
parsed = sanitizeJson(JSON.parse(body));
|
|
1100
1125
|
} catch {
|
|
1101
1126
|
json(res, 400, { error: "Invalid JSON" }, cors);
|
|
1102
1127
|
return;
|
|
@@ -1113,6 +1138,7 @@ function createRouteHandler(server) {
|
|
|
1113
1138
|
}
|
|
1114
1139
|
}
|
|
1115
1140
|
if (Array.isArray(config["constrain"])) {
|
|
1141
|
+
const validated = [];
|
|
1116
1142
|
for (const c of config["constrain"]) {
|
|
1117
1143
|
if (c && typeof c === "object" && typeof c["param"] === "string" && typeof c["min"] === "number" && typeof c["max"] === "number") {
|
|
1118
1144
|
const constraint = c;
|
|
@@ -1124,9 +1150,12 @@ function createRouteHandler(server) {
|
|
|
1124
1150
|
json(res, 400, { error: "Constraint min cannot exceed max" }, cors);
|
|
1125
1151
|
return;
|
|
1126
1152
|
}
|
|
1127
|
-
|
|
1153
|
+
validated.push(constraint);
|
|
1128
1154
|
}
|
|
1129
1155
|
}
|
|
1156
|
+
for (const constraint of validated) {
|
|
1157
|
+
server.constrain(constraint.param, { min: constraint.min, max: constraint.max });
|
|
1158
|
+
}
|
|
1130
1159
|
}
|
|
1131
1160
|
if (config["mode"] === "autonomous" || config["mode"] === "advisor") {
|
|
1132
1161
|
server.setMode(config["mode"]);
|
|
@@ -1148,10 +1177,14 @@ function createRouteHandler(server) {
|
|
|
1148
1177
|
return;
|
|
1149
1178
|
}
|
|
1150
1179
|
if (path === "/diagnose" && method === "POST") {
|
|
1180
|
+
if (!checkAuth(req, apiKey)) {
|
|
1181
|
+
json(res, 401, { error: "Unauthorized" }, cors);
|
|
1182
|
+
return;
|
|
1183
|
+
}
|
|
1151
1184
|
const body = await readBody(req);
|
|
1152
1185
|
let parsed;
|
|
1153
1186
|
try {
|
|
1154
|
-
parsed = JSON.parse(body);
|
|
1187
|
+
parsed = sanitizeJson(JSON.parse(body));
|
|
1155
1188
|
} catch {
|
|
1156
1189
|
json(res, 400, { error: "Invalid JSON" }, cors);
|
|
1157
1190
|
return;
|
|
@@ -1202,10 +1235,14 @@ function createRouteHandler(server) {
|
|
|
1202
1235
|
return;
|
|
1203
1236
|
}
|
|
1204
1237
|
if (path === "/approve" && method === "POST") {
|
|
1238
|
+
if (!checkAuth(req, apiKey)) {
|
|
1239
|
+
json(res, 401, { error: "Unauthorized" }, cors);
|
|
1240
|
+
return;
|
|
1241
|
+
}
|
|
1205
1242
|
const body = await readBody(req);
|
|
1206
1243
|
let parsed;
|
|
1207
1244
|
try {
|
|
1208
|
-
parsed = JSON.parse(body);
|
|
1245
|
+
parsed = sanitizeJson(JSON.parse(body));
|
|
1209
1246
|
} catch {
|
|
1210
1247
|
json(res, 400, { error: "Invalid JSON" }, cors);
|
|
1211
1248
|
return;
|
|
@@ -1241,10 +1278,14 @@ function createRouteHandler(server) {
|
|
|
1241
1278
|
return;
|
|
1242
1279
|
}
|
|
1243
1280
|
if (path === "/reject" && method === "POST") {
|
|
1281
|
+
if (!checkAuth(req, apiKey)) {
|
|
1282
|
+
json(res, 401, { error: "Unauthorized" }, cors);
|
|
1283
|
+
return;
|
|
1284
|
+
}
|
|
1244
1285
|
const body = await readBody(req);
|
|
1245
1286
|
let parsed;
|
|
1246
1287
|
try {
|
|
1247
|
-
parsed = JSON.parse(body);
|
|
1288
|
+
parsed = sanitizeJson(JSON.parse(body));
|
|
1248
1289
|
} catch {
|
|
1249
1290
|
json(res, 400, { error: "Invalid JSON" }, cors);
|
|
1250
1291
|
return;
|
|
@@ -1308,6 +1349,16 @@ function send(ws, data) {
|
|
|
1308
1349
|
ws.send(JSON.stringify(data));
|
|
1309
1350
|
}
|
|
1310
1351
|
}
|
|
1352
|
+
function sanitizeJson2(obj) {
|
|
1353
|
+
if (obj === null || typeof obj !== "object") return obj;
|
|
1354
|
+
if (Array.isArray(obj)) return obj.map(sanitizeJson2);
|
|
1355
|
+
const clean = {};
|
|
1356
|
+
for (const [key, val] of Object.entries(obj)) {
|
|
1357
|
+
if (key === "__proto__" || key === "constructor" || key === "prototype") continue;
|
|
1358
|
+
clean[key] = sanitizeJson2(val);
|
|
1359
|
+
}
|
|
1360
|
+
return clean;
|
|
1361
|
+
}
|
|
1311
1362
|
function createWebSocketHandler(httpServer, server) {
|
|
1312
1363
|
const wss = new import_ws.WebSocketServer({ server: httpServer, maxPayload: MAX_WS_PAYLOAD });
|
|
1313
1364
|
const aliveMap = /* @__PURE__ */ new WeakMap();
|
|
@@ -1323,13 +1374,22 @@ function createWebSocketHandler(httpServer, server) {
|
|
|
1323
1374
|
}
|
|
1324
1375
|
}
|
|
1325
1376
|
}, 3e4);
|
|
1326
|
-
wss.on("connection", (ws) => {
|
|
1377
|
+
wss.on("connection", (ws, req) => {
|
|
1327
1378
|
if (wss.clients.size > MAX_WS_CONNECTIONS) {
|
|
1328
1379
|
ws.close(1013, "Server at capacity");
|
|
1329
1380
|
return;
|
|
1330
1381
|
}
|
|
1382
|
+
if (server.apiKey) {
|
|
1383
|
+
const url = new URL(req.url ?? "/", `http://${req.headers.host ?? "localhost"}`);
|
|
1384
|
+
const token = url.searchParams.get("token") ?? req.headers["authorization"]?.replace("Bearer ", "");
|
|
1385
|
+
if (token !== server.apiKey) {
|
|
1386
|
+
ws.close(1008, "Unauthorized");
|
|
1387
|
+
return;
|
|
1388
|
+
}
|
|
1389
|
+
}
|
|
1331
1390
|
console.log("[AgentE Server] Client connected");
|
|
1332
1391
|
aliveMap.set(ws, true);
|
|
1392
|
+
let lastTickTime = 0;
|
|
1333
1393
|
ws.on("pong", () => {
|
|
1334
1394
|
aliveMap.set(ws, true);
|
|
1335
1395
|
});
|
|
@@ -1339,7 +1399,7 @@ function createWebSocketHandler(httpServer, server) {
|
|
|
1339
1399
|
ws.on("message", async (raw) => {
|
|
1340
1400
|
let msg;
|
|
1341
1401
|
try {
|
|
1342
|
-
msg = JSON.parse(raw.toString());
|
|
1402
|
+
msg = sanitizeJson2(JSON.parse(raw.toString()));
|
|
1343
1403
|
} catch {
|
|
1344
1404
|
send(ws, { type: "error", message: "Malformed JSON" });
|
|
1345
1405
|
return;
|
|
@@ -1350,6 +1410,12 @@ function createWebSocketHandler(httpServer, server) {
|
|
|
1350
1410
|
}
|
|
1351
1411
|
switch (msg.type) {
|
|
1352
1412
|
case "tick": {
|
|
1413
|
+
const now = Date.now();
|
|
1414
|
+
if (now - lastTickTime < MIN_TICK_INTERVAL_MS) {
|
|
1415
|
+
send(ws, { type: "error", message: "Rate limited \u2014 min 100ms between ticks" });
|
|
1416
|
+
break;
|
|
1417
|
+
}
|
|
1418
|
+
lastTickTime = now;
|
|
1353
1419
|
const state = msg["state"];
|
|
1354
1420
|
const events = msg["events"];
|
|
1355
1421
|
if (server.validateState) {
|
|
@@ -1429,7 +1495,7 @@ function createWebSocketHandler(httpServer, server) {
|
|
|
1429
1495
|
break;
|
|
1430
1496
|
}
|
|
1431
1497
|
default:
|
|
1432
|
-
send(ws, { type: "error", message: `Unknown message type: "${msg.type}"` });
|
|
1498
|
+
send(ws, { type: "error", message: `Unknown message type: "${String(msg.type).slice(0, 100)}"` });
|
|
1433
1499
|
}
|
|
1434
1500
|
});
|
|
1435
1501
|
});
|
|
@@ -1449,7 +1515,7 @@ function createWebSocketHandler(httpServer, server) {
|
|
|
1449
1515
|
broadcast
|
|
1450
1516
|
};
|
|
1451
1517
|
}
|
|
1452
|
-
var import_ws, import_core2, MAX_WS_PAYLOAD, MAX_WS_CONNECTIONS;
|
|
1518
|
+
var import_ws, import_core2, MAX_WS_PAYLOAD, MAX_WS_CONNECTIONS, MIN_TICK_INTERVAL_MS;
|
|
1453
1519
|
var init_websocket = __esm({
|
|
1454
1520
|
"src/websocket.ts"() {
|
|
1455
1521
|
"use strict";
|
|
@@ -1457,6 +1523,7 @@ var init_websocket = __esm({
|
|
|
1457
1523
|
import_core2 = require("@agent-e/core");
|
|
1458
1524
|
MAX_WS_PAYLOAD = 1048576;
|
|
1459
1525
|
MAX_WS_CONNECTIONS = 100;
|
|
1526
|
+
MIN_TICK_INTERVAL_MS = 100;
|
|
1460
1527
|
}
|
|
1461
1528
|
});
|
|
1462
1529
|
|
|
@@ -1481,7 +1548,8 @@ var init_AgentEServer = __esm({
|
|
|
1481
1548
|
this.startedAt = Date.now();
|
|
1482
1549
|
this.wsHandle = null;
|
|
1483
1550
|
this.port = config.port ?? 3100;
|
|
1484
|
-
this.host = config.host ?? "
|
|
1551
|
+
this.host = config.host ?? "127.0.0.1";
|
|
1552
|
+
this.apiKey = config.apiKey;
|
|
1485
1553
|
this.validateState = config.validateState ?? true;
|
|
1486
1554
|
this.corsOrigin = config.corsOrigin ?? "http://localhost:3100";
|
|
1487
1555
|
this.serveDashboard = config.serveDashboard ?? true;
|