@agent-e/server 1.6.8 → 1.6.9
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/cli.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
|
};
|
|
@@ -972,14 +972,30 @@ function setSecurityHeaders(res) {
|
|
|
972
972
|
res.setHeader("X-Frame-Options", "DENY");
|
|
973
973
|
res.setHeader("Referrer-Policy", "strict-origin-when-cross-origin");
|
|
974
974
|
}
|
|
975
|
-
function setCorsHeaders(res,
|
|
975
|
+
function setCorsHeaders(res, allowedOrigin, requestOrigin) {
|
|
976
976
|
setSecurityHeaders(res);
|
|
977
|
+
const origin = allowedOrigin === "*" ? "*" : requestOrigin === allowedOrigin ? allowedOrigin : allowedOrigin;
|
|
977
978
|
res.setHeader("Access-Control-Allow-Origin", origin);
|
|
978
979
|
res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
|
|
979
|
-
res.setHeader("Access-Control-Allow-Headers", "Content-Type");
|
|
980
|
+
res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
|
|
980
981
|
}
|
|
981
|
-
function
|
|
982
|
-
|
|
982
|
+
function sanitizeJson(obj) {
|
|
983
|
+
if (obj === null || typeof obj !== "object") return obj;
|
|
984
|
+
if (Array.isArray(obj)) return obj.map(sanitizeJson);
|
|
985
|
+
const clean = {};
|
|
986
|
+
for (const [key, val] of Object.entries(obj)) {
|
|
987
|
+
if (key === "__proto__" || key === "constructor" || key === "prototype") continue;
|
|
988
|
+
clean[key] = sanitizeJson(val);
|
|
989
|
+
}
|
|
990
|
+
return clean;
|
|
991
|
+
}
|
|
992
|
+
function checkAuth(req, apiKey) {
|
|
993
|
+
if (!apiKey) return true;
|
|
994
|
+
const header = req.headers["authorization"];
|
|
995
|
+
return header === `Bearer ${apiKey}`;
|
|
996
|
+
}
|
|
997
|
+
function json(res, status, data, origin, reqOrigin) {
|
|
998
|
+
setCorsHeaders(res, origin, reqOrigin);
|
|
983
999
|
res.writeHead(status, { "Content-Type": "application/json" });
|
|
984
1000
|
res.end(JSON.stringify(data));
|
|
985
1001
|
}
|
|
@@ -1003,6 +1019,7 @@ function readBody(req) {
|
|
|
1003
1019
|
}
|
|
1004
1020
|
function createRouteHandler(server2) {
|
|
1005
1021
|
const cors = server2.corsOrigin;
|
|
1022
|
+
const apiKey = server2.apiKey;
|
|
1006
1023
|
return async (req, res) => {
|
|
1007
1024
|
const url = new URL(req.url ?? "/", `http://${req.headers.host ?? "localhost"}`);
|
|
1008
1025
|
const path = url.pathname;
|
|
@@ -1015,10 +1032,14 @@ function createRouteHandler(server2) {
|
|
|
1015
1032
|
}
|
|
1016
1033
|
try {
|
|
1017
1034
|
if (path === "/tick" && method === "POST") {
|
|
1035
|
+
if (!checkAuth(req, apiKey)) {
|
|
1036
|
+
json(res, 401, { error: "Unauthorized" }, cors);
|
|
1037
|
+
return;
|
|
1038
|
+
}
|
|
1018
1039
|
const body = await readBody(req);
|
|
1019
1040
|
let parsed;
|
|
1020
1041
|
try {
|
|
1021
|
-
parsed = JSON.parse(body);
|
|
1042
|
+
parsed = sanitizeJson(JSON.parse(body));
|
|
1022
1043
|
} catch {
|
|
1023
1044
|
json(res, 400, { error: "Invalid JSON" }, cors);
|
|
1024
1045
|
return;
|
|
@@ -1089,10 +1110,14 @@ function createRouteHandler(server2) {
|
|
|
1089
1110
|
return;
|
|
1090
1111
|
}
|
|
1091
1112
|
if (path === "/config" && method === "POST") {
|
|
1113
|
+
if (!checkAuth(req, apiKey)) {
|
|
1114
|
+
json(res, 401, { error: "Unauthorized" }, cors);
|
|
1115
|
+
return;
|
|
1116
|
+
}
|
|
1092
1117
|
const body = await readBody(req);
|
|
1093
1118
|
let parsed;
|
|
1094
1119
|
try {
|
|
1095
|
-
parsed = JSON.parse(body);
|
|
1120
|
+
parsed = sanitizeJson(JSON.parse(body));
|
|
1096
1121
|
} catch {
|
|
1097
1122
|
json(res, 400, { error: "Invalid JSON" }, cors);
|
|
1098
1123
|
return;
|
|
@@ -1109,6 +1134,7 @@ function createRouteHandler(server2) {
|
|
|
1109
1134
|
}
|
|
1110
1135
|
}
|
|
1111
1136
|
if (Array.isArray(config["constrain"])) {
|
|
1137
|
+
const validated = [];
|
|
1112
1138
|
for (const c of config["constrain"]) {
|
|
1113
1139
|
if (c && typeof c === "object" && typeof c["param"] === "string" && typeof c["min"] === "number" && typeof c["max"] === "number") {
|
|
1114
1140
|
const constraint = c;
|
|
@@ -1120,9 +1146,12 @@ function createRouteHandler(server2) {
|
|
|
1120
1146
|
json(res, 400, { error: "Constraint min cannot exceed max" }, cors);
|
|
1121
1147
|
return;
|
|
1122
1148
|
}
|
|
1123
|
-
|
|
1149
|
+
validated.push(constraint);
|
|
1124
1150
|
}
|
|
1125
1151
|
}
|
|
1152
|
+
for (const constraint of validated) {
|
|
1153
|
+
server2.constrain(constraint.param, { min: constraint.min, max: constraint.max });
|
|
1154
|
+
}
|
|
1126
1155
|
}
|
|
1127
1156
|
if (config["mode"] === "autonomous" || config["mode"] === "advisor") {
|
|
1128
1157
|
server2.setMode(config["mode"]);
|
|
@@ -1144,10 +1173,14 @@ function createRouteHandler(server2) {
|
|
|
1144
1173
|
return;
|
|
1145
1174
|
}
|
|
1146
1175
|
if (path === "/diagnose" && method === "POST") {
|
|
1176
|
+
if (!checkAuth(req, apiKey)) {
|
|
1177
|
+
json(res, 401, { error: "Unauthorized" }, cors);
|
|
1178
|
+
return;
|
|
1179
|
+
}
|
|
1147
1180
|
const body = await readBody(req);
|
|
1148
1181
|
let parsed;
|
|
1149
1182
|
try {
|
|
1150
|
-
parsed = JSON.parse(body);
|
|
1183
|
+
parsed = sanitizeJson(JSON.parse(body));
|
|
1151
1184
|
} catch {
|
|
1152
1185
|
json(res, 400, { error: "Invalid JSON" }, cors);
|
|
1153
1186
|
return;
|
|
@@ -1198,10 +1231,14 @@ function createRouteHandler(server2) {
|
|
|
1198
1231
|
return;
|
|
1199
1232
|
}
|
|
1200
1233
|
if (path === "/approve" && method === "POST") {
|
|
1234
|
+
if (!checkAuth(req, apiKey)) {
|
|
1235
|
+
json(res, 401, { error: "Unauthorized" }, cors);
|
|
1236
|
+
return;
|
|
1237
|
+
}
|
|
1201
1238
|
const body = await readBody(req);
|
|
1202
1239
|
let parsed;
|
|
1203
1240
|
try {
|
|
1204
|
-
parsed = JSON.parse(body);
|
|
1241
|
+
parsed = sanitizeJson(JSON.parse(body));
|
|
1205
1242
|
} catch {
|
|
1206
1243
|
json(res, 400, { error: "Invalid JSON" }, cors);
|
|
1207
1244
|
return;
|
|
@@ -1237,10 +1274,14 @@ function createRouteHandler(server2) {
|
|
|
1237
1274
|
return;
|
|
1238
1275
|
}
|
|
1239
1276
|
if (path === "/reject" && method === "POST") {
|
|
1277
|
+
if (!checkAuth(req, apiKey)) {
|
|
1278
|
+
json(res, 401, { error: "Unauthorized" }, cors);
|
|
1279
|
+
return;
|
|
1280
|
+
}
|
|
1240
1281
|
const body = await readBody(req);
|
|
1241
1282
|
let parsed;
|
|
1242
1283
|
try {
|
|
1243
|
-
parsed = JSON.parse(body);
|
|
1284
|
+
parsed = sanitizeJson(JSON.parse(body));
|
|
1244
1285
|
} catch {
|
|
1245
1286
|
json(res, 400, { error: "Invalid JSON" }, cors);
|
|
1246
1287
|
return;
|
|
@@ -1299,6 +1340,17 @@ function send(ws, data) {
|
|
|
1299
1340
|
}
|
|
1300
1341
|
var MAX_WS_PAYLOAD = 1048576;
|
|
1301
1342
|
var MAX_WS_CONNECTIONS = 100;
|
|
1343
|
+
var MIN_TICK_INTERVAL_MS = 100;
|
|
1344
|
+
function sanitizeJson2(obj) {
|
|
1345
|
+
if (obj === null || typeof obj !== "object") return obj;
|
|
1346
|
+
if (Array.isArray(obj)) return obj.map(sanitizeJson2);
|
|
1347
|
+
const clean = {};
|
|
1348
|
+
for (const [key, val] of Object.entries(obj)) {
|
|
1349
|
+
if (key === "__proto__" || key === "constructor" || key === "prototype") continue;
|
|
1350
|
+
clean[key] = sanitizeJson2(val);
|
|
1351
|
+
}
|
|
1352
|
+
return clean;
|
|
1353
|
+
}
|
|
1302
1354
|
function createWebSocketHandler(httpServer, server2) {
|
|
1303
1355
|
const wss = new import_ws.WebSocketServer({ server: httpServer, maxPayload: MAX_WS_PAYLOAD });
|
|
1304
1356
|
const aliveMap = /* @__PURE__ */ new WeakMap();
|
|
@@ -1314,13 +1366,22 @@ function createWebSocketHandler(httpServer, server2) {
|
|
|
1314
1366
|
}
|
|
1315
1367
|
}
|
|
1316
1368
|
}, 3e4);
|
|
1317
|
-
wss.on("connection", (ws) => {
|
|
1369
|
+
wss.on("connection", (ws, req) => {
|
|
1318
1370
|
if (wss.clients.size > MAX_WS_CONNECTIONS) {
|
|
1319
1371
|
ws.close(1013, "Server at capacity");
|
|
1320
1372
|
return;
|
|
1321
1373
|
}
|
|
1374
|
+
if (server2.apiKey) {
|
|
1375
|
+
const url = new URL(req.url ?? "/", `http://${req.headers.host ?? "localhost"}`);
|
|
1376
|
+
const token = url.searchParams.get("token") ?? req.headers["authorization"]?.replace("Bearer ", "");
|
|
1377
|
+
if (token !== server2.apiKey) {
|
|
1378
|
+
ws.close(1008, "Unauthorized");
|
|
1379
|
+
return;
|
|
1380
|
+
}
|
|
1381
|
+
}
|
|
1322
1382
|
console.log("[AgentE Server] Client connected");
|
|
1323
1383
|
aliveMap.set(ws, true);
|
|
1384
|
+
let lastTickTime = 0;
|
|
1324
1385
|
ws.on("pong", () => {
|
|
1325
1386
|
aliveMap.set(ws, true);
|
|
1326
1387
|
});
|
|
@@ -1330,7 +1391,7 @@ function createWebSocketHandler(httpServer, server2) {
|
|
|
1330
1391
|
ws.on("message", async (raw) => {
|
|
1331
1392
|
let msg;
|
|
1332
1393
|
try {
|
|
1333
|
-
msg = JSON.parse(raw.toString());
|
|
1394
|
+
msg = sanitizeJson2(JSON.parse(raw.toString()));
|
|
1334
1395
|
} catch {
|
|
1335
1396
|
send(ws, { type: "error", message: "Malformed JSON" });
|
|
1336
1397
|
return;
|
|
@@ -1341,6 +1402,12 @@ function createWebSocketHandler(httpServer, server2) {
|
|
|
1341
1402
|
}
|
|
1342
1403
|
switch (msg.type) {
|
|
1343
1404
|
case "tick": {
|
|
1405
|
+
const now = Date.now();
|
|
1406
|
+
if (now - lastTickTime < MIN_TICK_INTERVAL_MS) {
|
|
1407
|
+
send(ws, { type: "error", message: "Rate limited \u2014 min 100ms between ticks" });
|
|
1408
|
+
break;
|
|
1409
|
+
}
|
|
1410
|
+
lastTickTime = now;
|
|
1344
1411
|
const state = msg["state"];
|
|
1345
1412
|
const events = msg["events"];
|
|
1346
1413
|
if (server2.validateState) {
|
|
@@ -1420,7 +1487,7 @@ function createWebSocketHandler(httpServer, server2) {
|
|
|
1420
1487
|
break;
|
|
1421
1488
|
}
|
|
1422
1489
|
default:
|
|
1423
|
-
send(ws, { type: "error", message: `Unknown message type: "${msg.type}"` });
|
|
1490
|
+
send(ws, { type: "error", message: `Unknown message type: "${String(msg.type).slice(0, 100)}"` });
|
|
1424
1491
|
}
|
|
1425
1492
|
});
|
|
1426
1493
|
});
|
|
@@ -1450,7 +1517,8 @@ var AgentEServer = class {
|
|
|
1450
1517
|
this.startedAt = Date.now();
|
|
1451
1518
|
this.wsHandle = null;
|
|
1452
1519
|
this.port = config.port ?? 3100;
|
|
1453
|
-
this.host = config.host ?? "
|
|
1520
|
+
this.host = config.host ?? "127.0.0.1";
|
|
1521
|
+
this.apiKey = config.apiKey;
|
|
1454
1522
|
this.validateState = config.validateState ?? true;
|
|
1455
1523
|
this.corsOrigin = config.corsOrigin ?? "http://localhost:3100";
|
|
1456
1524
|
this.serveDashboard = config.serveDashboard ?? true;
|