anycodex 0.0.14 → 0.0.15
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/bin.js +387 -371
- package/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -22,7 +22,7 @@ import fs5 from "fs";
|
|
|
22
22
|
import readline from "readline";
|
|
23
23
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
24
24
|
|
|
25
|
-
// ../server/dist/chunk-
|
|
25
|
+
// ../server/dist/chunk-AQFLUXUT.js
|
|
26
26
|
import http from "http";
|
|
27
27
|
import { fileURLToPath } from "url";
|
|
28
28
|
import path3 from "path";
|
|
@@ -459,34 +459,30 @@ function simpleGlobMatch(filename, pattern) {
|
|
|
459
459
|
}
|
|
460
460
|
var ANYCODE_DIR = path3.join(os.homedir(), ".anycode");
|
|
461
461
|
var DB_PATH = path3.join(ANYCODE_DIR, "data.db");
|
|
462
|
-
var userSettings =
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
462
|
+
var userSettings = {};
|
|
463
|
+
var PROVIDER = "";
|
|
464
|
+
var MODEL = "";
|
|
465
|
+
var API_KEY = "";
|
|
466
|
+
var BASE_URL = "";
|
|
467
|
+
var PORT = 3210;
|
|
468
|
+
var PREVIEW_PORT = 3211;
|
|
469
|
+
function loadConfig() {
|
|
470
|
+
PROVIDER = process.env.PROVIDER ?? "";
|
|
471
|
+
MODEL = process.env.MODEL ?? userSettings.MODEL ?? "claude-sonnet-4-20250514";
|
|
472
|
+
API_KEY = process.env.API_KEY ?? userSettings.API_KEY ?? "";
|
|
473
|
+
BASE_URL = process.env.BASE_URL ?? userSettings.BASE_URL ?? "";
|
|
474
|
+
PORT = parseInt(process.env.PORT ?? "3210", 10);
|
|
475
|
+
PREVIEW_PORT = parseInt(process.env.PREVIEW_PORT ?? String(PORT + 1), 10);
|
|
476
|
+
if (!PROVIDER || !MODEL || !BASE_URL) {
|
|
477
|
+
console.error("\u274C Missing PROVIDER, MODEL, BASE_URL");
|
|
478
|
+
process.exit(1);
|
|
479
|
+
}
|
|
480
|
+
if (!API_KEY) {
|
|
481
|
+
console.error("\u274C Missing API_KEY");
|
|
482
|
+
console.error("Run 'anycode start' to configure, or set API_KEY env var.");
|
|
483
|
+
process.exit(1);
|
|
467
484
|
}
|
|
468
|
-
}
|
|
469
|
-
var PROVIDER = process.env.PROVIDER ?? "";
|
|
470
|
-
var MODEL = process.env.MODEL ?? userSettings.MODEL ?? "claude-sonnet-4-20250514";
|
|
471
|
-
var API_KEY = process.env.API_KEY ?? userSettings.API_KEY;
|
|
472
|
-
var BASE_URL = process.env.BASE_URL ?? userSettings.BASE_URL;
|
|
473
|
-
var PORT = parseInt(process.env.PORT ?? "3210", 10);
|
|
474
|
-
var PREVIEW_PORT = parseInt(process.env.PREVIEW_PORT ?? String(PORT + 1), 10);
|
|
475
|
-
if (!PROVIDER || !MODEL || !BASE_URL) {
|
|
476
|
-
console.error("\u274C Missing PROVIDER, MODEL, BASE_URL");
|
|
477
|
-
process.exit();
|
|
478
|
-
}
|
|
479
|
-
if (!API_KEY) {
|
|
480
|
-
console.error("\u274C Missing API_KEY");
|
|
481
|
-
console.error("Run 'anycode start' to configure, or set API_KEY env var.");
|
|
482
|
-
process.exit(1);
|
|
483
|
-
}
|
|
484
|
-
process.on("uncaughtException", (err) => {
|
|
485
|
-
console.error("\u26A0 Uncaught exception:", err.message);
|
|
486
|
-
});
|
|
487
|
-
process.on("unhandledRejection", (reason) => {
|
|
488
|
-
console.error("\u26A0 Unhandled rejection:", reason instanceof Error ? reason.message : reason);
|
|
489
|
-
});
|
|
485
|
+
}
|
|
490
486
|
function makePaths() {
|
|
491
487
|
const dataPath = path3.join(ANYCODE_DIR, "data");
|
|
492
488
|
fs4.mkdirSync(dataPath, { recursive: true });
|
|
@@ -1184,76 +1180,79 @@ function getOrCreatePreviewProvider(sessionId) {
|
|
|
1184
1180
|
}
|
|
1185
1181
|
return pp;
|
|
1186
1182
|
}
|
|
1187
|
-
var previewServer
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
res.
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
});
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1183
|
+
var previewServer;
|
|
1184
|
+
function createPreviewServer() {
|
|
1185
|
+
previewServer = http.createServer((req, res) => {
|
|
1186
|
+
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
1187
|
+
res.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
|
|
1188
|
+
res.setHeader("Access-Control-Allow-Headers", "*");
|
|
1189
|
+
if (req.method === "OPTIONS") {
|
|
1190
|
+
res.writeHead(204);
|
|
1191
|
+
res.end();
|
|
1192
|
+
return;
|
|
1193
|
+
}
|
|
1194
|
+
if (!previewTarget) {
|
|
1195
|
+
res.writeHead(502, { "Content-Type": "text/plain" });
|
|
1196
|
+
res.end("No preview target configured");
|
|
1197
|
+
return;
|
|
1198
|
+
}
|
|
1199
|
+
try {
|
|
1200
|
+
const targetUrl = previewTarget + (req.url || "/");
|
|
1201
|
+
const parsed = new URL(targetUrl);
|
|
1202
|
+
const options = {
|
|
1203
|
+
hostname: parsed.hostname,
|
|
1204
|
+
port: parsed.port,
|
|
1205
|
+
path: parsed.pathname + parsed.search,
|
|
1206
|
+
method: req.method,
|
|
1207
|
+
headers: { ...req.headers, host: parsed.host }
|
|
1208
|
+
};
|
|
1209
|
+
const proxyReq = http.request(options, (proxyRes) => {
|
|
1210
|
+
res.writeHead(proxyRes.statusCode || 502, proxyRes.headers);
|
|
1211
|
+
proxyRes.pipe(res);
|
|
1212
|
+
});
|
|
1213
|
+
proxyReq.on("error", (err) => {
|
|
1214
|
+
if (!res.headersSent) res.writeHead(502, { "Content-Type": "text/plain" });
|
|
1215
|
+
res.end(`Preview proxy error: ${err.message}`);
|
|
1216
|
+
});
|
|
1217
|
+
req.pipe(proxyReq);
|
|
1218
|
+
} catch (err) {
|
|
1219
|
+
res.writeHead(502, { "Content-Type": "text/plain" });
|
|
1220
|
+
res.end(`Invalid proxy target: ${err.message}`);
|
|
1221
|
+
}
|
|
1222
|
+
});
|
|
1223
|
+
previewServer.on("upgrade", (req, socket, head) => {
|
|
1224
|
+
if (!previewTarget) {
|
|
1225
|
+
socket.destroy();
|
|
1226
|
+
return;
|
|
1227
|
+
}
|
|
1228
|
+
try {
|
|
1229
|
+
const parsed = new URL(previewTarget);
|
|
1230
|
+
const targetWs = `ws://${parsed.hostname}:${parsed.port}${req.url || "/"}`;
|
|
1231
|
+
const wsTarget = new URL(targetWs);
|
|
1232
|
+
const options = {
|
|
1233
|
+
hostname: wsTarget.hostname,
|
|
1234
|
+
port: wsTarget.port,
|
|
1235
|
+
path: wsTarget.pathname + wsTarget.search,
|
|
1236
|
+
method: "GET",
|
|
1237
|
+
headers: { ...req.headers, host: wsTarget.host }
|
|
1238
|
+
};
|
|
1239
|
+
const proxyReq = http.request(options);
|
|
1240
|
+
proxyReq.on("upgrade", (_proxyRes, proxySocket, proxyHead) => {
|
|
1241
|
+
socket.write(
|
|
1242
|
+
"HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\n" + Object.entries(_proxyRes.headers).filter(([k]) => !["upgrade", "connection"].includes(k.toLowerCase())).map(([k, v]) => `${k}: ${v}`).join("\r\n") + "\r\n\r\n"
|
|
1243
|
+
);
|
|
1244
|
+
if (proxyHead.length > 0) socket.write(proxyHead);
|
|
1245
|
+
proxySocket.pipe(socket);
|
|
1246
|
+
socket.pipe(proxySocket);
|
|
1247
|
+
});
|
|
1248
|
+
proxyReq.on("error", () => socket.destroy());
|
|
1249
|
+
socket.on("error", () => proxyReq.destroy());
|
|
1250
|
+
proxyReq.end();
|
|
1251
|
+
} catch {
|
|
1252
|
+
socket.destroy();
|
|
1253
|
+
}
|
|
1254
|
+
});
|
|
1255
|
+
}
|
|
1257
1256
|
function adminHTML() {
|
|
1258
1257
|
return (
|
|
1259
1258
|
/* html */
|
|
@@ -1374,7 +1373,7 @@ function resolveAppDist() {
|
|
|
1374
1373
|
}
|
|
1375
1374
|
return bundled;
|
|
1376
1375
|
}
|
|
1377
|
-
var APP_DIST =
|
|
1376
|
+
var APP_DIST = "";
|
|
1378
1377
|
function serveStatic(req, res) {
|
|
1379
1378
|
const url = req.url || "/";
|
|
1380
1379
|
const filePath = path3.join(APP_DIST, url);
|
|
@@ -1399,331 +1398,348 @@ function serveAppIndex(res) {
|
|
|
1399
1398
|
}
|
|
1400
1399
|
return false;
|
|
1401
1400
|
}
|
|
1402
|
-
var server
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
res.
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
if (req.method === "POST" && req.url === "/api/poll/connect") {
|
|
1412
|
-
let body = "";
|
|
1413
|
-
for await (const chunk of req) body += chunk;
|
|
1414
|
-
const { sessionId } = body ? JSON.parse(body) : {};
|
|
1415
|
-
const session = sessionId ? getSession(sessionId) : void 0;
|
|
1416
|
-
if (!session) {
|
|
1417
|
-
res.writeHead(404, { "Content-Type": "application/json" });
|
|
1418
|
-
res.end(JSON.stringify({ error: "Session not found" }));
|
|
1401
|
+
var server;
|
|
1402
|
+
function createMainServer() {
|
|
1403
|
+
server = http.createServer(async (req, res) => {
|
|
1404
|
+
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
1405
|
+
res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
|
|
1406
|
+
res.setHeader("Access-Control-Allow-Headers", "Content-Type");
|
|
1407
|
+
if (req.method === "OPTIONS") {
|
|
1408
|
+
res.writeHead(204);
|
|
1409
|
+
res.end();
|
|
1419
1410
|
return;
|
|
1420
1411
|
}
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
res.
|
|
1412
|
+
if (req.method === "POST" && req.url === "/api/poll/connect") {
|
|
1413
|
+
let body = "";
|
|
1414
|
+
for await (const chunk of req) body += chunk;
|
|
1415
|
+
const { sessionId } = body ? JSON.parse(body) : {};
|
|
1416
|
+
const session = sessionId ? getSession(sessionId) : void 0;
|
|
1417
|
+
if (!session) {
|
|
1418
|
+
res.writeHead(404, { "Content-Type": "application/json" });
|
|
1419
|
+
res.end(JSON.stringify({ error: "Session not found" }));
|
|
1420
|
+
return;
|
|
1421
|
+
}
|
|
1422
|
+
const channelId = Math.random().toString(36).slice(2) + Date.now().toString(36);
|
|
1423
|
+
const client = new PollingClient(channelId, sessionId);
|
|
1424
|
+
pollingClients.set(channelId, client);
|
|
1425
|
+
getSessionClients(sessionId).add(client);
|
|
1426
|
+
console.log(`\u{1F50C} Poll client connected to session ${sessionId} (channel=${channelId})`);
|
|
1427
|
+
sendStateTo(sessionId, client);
|
|
1428
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
1429
|
+
res.end(JSON.stringify({ channelId }));
|
|
1438
1430
|
return;
|
|
1439
1431
|
}
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
res.end(JSON.stringify({ error: "Channel not found" }));
|
|
1432
|
+
if (req.method === "GET" && req.url?.startsWith("/api/poll?")) {
|
|
1433
|
+
const url = new URL(req.url, `http://localhost:${PORT}`);
|
|
1434
|
+
const channelId = url.searchParams.get("channelId");
|
|
1435
|
+
const client = channelId ? pollingClients.get(channelId) : void 0;
|
|
1436
|
+
if (!client || client.readyState !== 1) {
|
|
1437
|
+
res.writeHead(404, { "Content-Type": "application/json" });
|
|
1438
|
+
res.end(JSON.stringify({ error: "Channel not found" }));
|
|
1439
|
+
return;
|
|
1440
|
+
}
|
|
1441
|
+
client.hold(res);
|
|
1451
1442
|
return;
|
|
1452
1443
|
}
|
|
1453
|
-
|
|
1454
|
-
handleClientMessage(client.sessionId, client, data).catch(() => {
|
|
1455
|
-
});
|
|
1456
|
-
res.writeHead(200, { "Content-Type": "application/json" });
|
|
1457
|
-
res.end(JSON.stringify({ ok: true }));
|
|
1458
|
-
return;
|
|
1459
|
-
}
|
|
1460
|
-
if (req.method === "POST" && req.url === "/api/poll/close") {
|
|
1461
|
-
let body = "";
|
|
1462
|
-
for await (const chunk of req) body += chunk;
|
|
1463
|
-
const { channelId } = body ? JSON.parse(body) : {};
|
|
1464
|
-
const client = channelId ? pollingClients.get(channelId) : void 0;
|
|
1465
|
-
if (client) {
|
|
1466
|
-
client.close();
|
|
1467
|
-
removeClient(client.sessionId, client);
|
|
1468
|
-
pollingClients.delete(channelId);
|
|
1469
|
-
console.log(`\u{1F50C} Poll client disconnected (channel=${channelId})`);
|
|
1470
|
-
}
|
|
1471
|
-
res.writeHead(200, { "Content-Type": "application/json" });
|
|
1472
|
-
res.end(JSON.stringify({ ok: true }));
|
|
1473
|
-
return;
|
|
1474
|
-
}
|
|
1475
|
-
if (req.method === "POST" && req.url === "/api/sessions") {
|
|
1476
|
-
(async () => {
|
|
1444
|
+
if (req.method === "POST" && req.url === "/api/poll/send") {
|
|
1477
1445
|
let body = "";
|
|
1478
1446
|
for await (const chunk of req) body += chunk;
|
|
1479
|
-
const {
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
res.
|
|
1447
|
+
const { channelId, data } = body ? JSON.parse(body) : {};
|
|
1448
|
+
const client = channelId ? pollingClients.get(channelId) : void 0;
|
|
1449
|
+
if (!client || client.readyState !== 1) {
|
|
1450
|
+
res.writeHead(404, { "Content-Type": "application/json" });
|
|
1451
|
+
res.end(JSON.stringify({ error: "Channel not found" }));
|
|
1483
1452
|
return;
|
|
1484
1453
|
}
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
res.end(JSON.stringify({ id: entry.id, directory: entry.directory }));
|
|
1488
|
-
}).catch((err) => {
|
|
1489
|
-
res.writeHead(500, { "Content-Type": "application/json" });
|
|
1490
|
-
res.end(JSON.stringify({ error: err.message }));
|
|
1454
|
+
client.lastActivity = Date.now();
|
|
1455
|
+
handleClientMessage(client.sessionId, client, data).catch(() => {
|
|
1491
1456
|
});
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1457
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
1458
|
+
res.end(JSON.stringify({ ok: true }));
|
|
1459
|
+
return;
|
|
1460
|
+
}
|
|
1461
|
+
if (req.method === "POST" && req.url === "/api/poll/close") {
|
|
1462
|
+
let body = "";
|
|
1463
|
+
for await (const chunk of req) body += chunk;
|
|
1464
|
+
const { channelId } = body ? JSON.parse(body) : {};
|
|
1465
|
+
const client = channelId ? pollingClients.get(channelId) : void 0;
|
|
1466
|
+
if (client) {
|
|
1467
|
+
client.close();
|
|
1468
|
+
removeClient(client.sessionId, client);
|
|
1469
|
+
pollingClients.delete(channelId);
|
|
1470
|
+
console.log(`\u{1F50C} Poll client disconnected (channel=${channelId})`);
|
|
1471
|
+
}
|
|
1472
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
1473
|
+
res.end(JSON.stringify({ ok: true }));
|
|
1474
|
+
return;
|
|
1475
|
+
}
|
|
1476
|
+
if (req.method === "POST" && req.url === "/api/sessions") {
|
|
1477
|
+
(async () => {
|
|
1478
|
+
let body = "";
|
|
1479
|
+
for await (const chunk of req) body += chunk;
|
|
1480
|
+
const { userId } = body ? JSON.parse(body) : {};
|
|
1481
|
+
if (!userId) {
|
|
1482
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
1483
|
+
res.end(JSON.stringify({ error: "userId is required" }));
|
|
1484
|
+
return;
|
|
1485
|
+
}
|
|
1486
|
+
getOrCreateSession(userId).then((entry) => {
|
|
1487
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
1488
|
+
res.end(JSON.stringify({ id: entry.id, directory: entry.directory }));
|
|
1489
|
+
}).catch((err) => {
|
|
1490
|
+
res.writeHead(500, { "Content-Type": "application/json" });
|
|
1491
|
+
res.end(JSON.stringify({ error: err.message }));
|
|
1492
|
+
});
|
|
1493
|
+
})();
|
|
1511
1494
|
return;
|
|
1512
1495
|
}
|
|
1513
|
-
|
|
1514
|
-
const
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
directory: e.directory,
|
|
1519
|
-
createdAt: e.createdAt,
|
|
1520
|
-
isDefault: defaultMap.get(e.id) ?? false
|
|
1496
|
+
if (req.method === "GET" && req.url === "/api/sessions") {
|
|
1497
|
+
const list = Array.from(sessions.values()).map((s) => ({
|
|
1498
|
+
id: s.id,
|
|
1499
|
+
directory: s.directory,
|
|
1500
|
+
createdAt: s.createdAt
|
|
1521
1501
|
}));
|
|
1522
1502
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
1523
1503
|
res.end(JSON.stringify(list));
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
}
|
|
1530
|
-
if (req.method === "POST" && req.url === "/api/windows") {
|
|
1531
|
-
(async () => {
|
|
1532
|
-
let body = "";
|
|
1533
|
-
for await (const chunk of req) body += chunk;
|
|
1534
|
-
const { userId } = body ? JSON.parse(body) : {};
|
|
1504
|
+
return;
|
|
1505
|
+
}
|
|
1506
|
+
if (req.method === "GET" && req.url?.startsWith("/api/windows")) {
|
|
1507
|
+
const url = new URL(req.url, `http://localhost:${PORT}`);
|
|
1508
|
+
const userId = url.searchParams.get("userId");
|
|
1535
1509
|
if (!userId) {
|
|
1536
1510
|
res.writeHead(400, { "Content-Type": "application/json" });
|
|
1537
1511
|
res.end(JSON.stringify({ error: "userId is required" }));
|
|
1538
1512
|
return;
|
|
1539
1513
|
}
|
|
1540
|
-
|
|
1514
|
+
getAllWindows(userId).then((entries) => {
|
|
1515
|
+
const rows = db.findMany("user_session", { filter: { op: "eq", field: "user_id", value: userId } });
|
|
1516
|
+
const defaultMap = new Map(rows.map((r) => [r.session_id, r.is_default === 1]));
|
|
1517
|
+
const list = entries.map((e) => ({
|
|
1518
|
+
id: e.id,
|
|
1519
|
+
directory: e.directory,
|
|
1520
|
+
createdAt: e.createdAt,
|
|
1521
|
+
isDefault: defaultMap.get(e.id) ?? false
|
|
1522
|
+
}));
|
|
1541
1523
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
1542
|
-
res.end(JSON.stringify(
|
|
1524
|
+
res.end(JSON.stringify(list));
|
|
1543
1525
|
}).catch((err) => {
|
|
1544
1526
|
res.writeHead(500, { "Content-Type": "application/json" });
|
|
1545
1527
|
res.end(JSON.stringify({ error: err.message }));
|
|
1546
1528
|
});
|
|
1547
|
-
})();
|
|
1548
|
-
return;
|
|
1549
|
-
}
|
|
1550
|
-
const windowDeleteMatch = req.url?.match(/^\/api\/windows\/([^/?]+)$/);
|
|
1551
|
-
if (req.method === "DELETE" && windowDeleteMatch) {
|
|
1552
|
-
const ok = deleteWindow(windowDeleteMatch[1]);
|
|
1553
|
-
if (ok) {
|
|
1554
|
-
res.writeHead(200, { "Content-Type": "application/json" });
|
|
1555
|
-
res.end(JSON.stringify({ ok: true }));
|
|
1556
|
-
} else {
|
|
1557
|
-
res.writeHead(400, { "Content-Type": "application/json" });
|
|
1558
|
-
res.end(JSON.stringify({ error: "Cannot delete default window or window not found" }));
|
|
1559
|
-
}
|
|
1560
|
-
return;
|
|
1561
|
-
}
|
|
1562
|
-
const sessionMatch = req.url?.match(/^\/api\/sessions\/([^/?]+)(?:\/([a-z]+))?/);
|
|
1563
|
-
if (req.method === "GET" && sessionMatch) {
|
|
1564
|
-
const session = getSession(sessionMatch[1]);
|
|
1565
|
-
if (!session) {
|
|
1566
|
-
res.writeHead(404, { "Content-Type": "application/json" });
|
|
1567
|
-
res.end(JSON.stringify({ error: "Session not found" }));
|
|
1568
1529
|
return;
|
|
1569
1530
|
}
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1531
|
+
if (req.method === "POST" && req.url === "/api/windows") {
|
|
1532
|
+
(async () => {
|
|
1533
|
+
let body = "";
|
|
1534
|
+
for await (const chunk of req) body += chunk;
|
|
1535
|
+
const { userId } = body ? JSON.parse(body) : {};
|
|
1536
|
+
if (!userId) {
|
|
1537
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
1538
|
+
res.end(JSON.stringify({ error: "userId is required" }));
|
|
1539
|
+
return;
|
|
1540
|
+
}
|
|
1541
|
+
createNewWindow(userId, false).then((entry) => {
|
|
1542
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
1543
|
+
res.end(JSON.stringify({ id: entry.id, directory: entry.directory, isDefault: false }));
|
|
1544
|
+
}).catch((err) => {
|
|
1545
|
+
res.writeHead(500, { "Content-Type": "application/json" });
|
|
1546
|
+
res.end(JSON.stringify({ error: err.message }));
|
|
1547
|
+
});
|
|
1548
|
+
})();
|
|
1581
1549
|
return;
|
|
1582
1550
|
}
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
const
|
|
1586
|
-
if (
|
|
1551
|
+
const windowDeleteMatch = req.url?.match(/^\/api\/windows\/([^/?]+)$/);
|
|
1552
|
+
if (req.method === "DELETE" && windowDeleteMatch) {
|
|
1553
|
+
const ok = deleteWindow(windowDeleteMatch[1]);
|
|
1554
|
+
if (ok) {
|
|
1587
1555
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
1588
|
-
res.end(JSON.stringify({
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
if (!target.startsWith(path3.resolve(dir))) {
|
|
1593
|
-
res.writeHead(403, { "Content-Type": "application/json" });
|
|
1594
|
-
res.end(JSON.stringify({ error: "Forbidden" }));
|
|
1595
|
-
return;
|
|
1556
|
+
res.end(JSON.stringify({ ok: true }));
|
|
1557
|
+
} else {
|
|
1558
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
1559
|
+
res.end(JSON.stringify({ error: "Cannot delete default window or window not found" }));
|
|
1596
1560
|
}
|
|
1597
|
-
const entries = await listDir(target);
|
|
1598
|
-
res.writeHead(200, { "Content-Type": "application/json" });
|
|
1599
|
-
res.end(JSON.stringify({ entries }));
|
|
1600
1561
|
return;
|
|
1601
1562
|
}
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
const
|
|
1605
|
-
if (!
|
|
1606
|
-
res.writeHead(
|
|
1607
|
-
res.end(JSON.stringify({
|
|
1563
|
+
const sessionMatch = req.url?.match(/^\/api\/sessions\/([^/?]+)(?:\/([a-z]+))?/);
|
|
1564
|
+
if (req.method === "GET" && sessionMatch) {
|
|
1565
|
+
const session = getSession(sessionMatch[1]);
|
|
1566
|
+
if (!session) {
|
|
1567
|
+
res.writeHead(404, { "Content-Type": "application/json" });
|
|
1568
|
+
res.end(JSON.stringify({ error: "Session not found" }));
|
|
1608
1569
|
return;
|
|
1609
1570
|
}
|
|
1610
|
-
const
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1571
|
+
const sub = sessionMatch[2];
|
|
1572
|
+
const url = new URL(req.url, `http://localhost:${PORT}`);
|
|
1573
|
+
if (sub === "state") {
|
|
1574
|
+
const dir = session.directory;
|
|
1575
|
+
const [topLevel, changes] = await Promise.all([
|
|
1576
|
+
dir ? listDir(dir) : Promise.resolve([]),
|
|
1577
|
+
dir ? getGitChanges(dir) : Promise.resolve([])
|
|
1578
|
+
]);
|
|
1579
|
+
const hasPreview = previewSessionId === session.id && previewTarget;
|
|
1580
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
1581
|
+
res.end(JSON.stringify({ directory: dir, topLevel, changes, previewPort: hasPreview ? PREVIEW_PORT : null }));
|
|
1614
1582
|
return;
|
|
1615
1583
|
}
|
|
1616
|
-
|
|
1617
|
-
const
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1584
|
+
if (sub === "ls") {
|
|
1585
|
+
const subPath = url.searchParams.get("path") || "";
|
|
1586
|
+
const dir = session.directory;
|
|
1587
|
+
if (!dir) {
|
|
1588
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
1589
|
+
res.end(JSON.stringify({ entries: [] }));
|
|
1590
|
+
return;
|
|
1591
|
+
}
|
|
1592
|
+
const target = path3.resolve(dir, subPath);
|
|
1593
|
+
if (!target.startsWith(path3.resolve(dir))) {
|
|
1594
|
+
res.writeHead(403, { "Content-Type": "application/json" });
|
|
1595
|
+
res.end(JSON.stringify({ error: "Forbidden" }));
|
|
1596
|
+
return;
|
|
1597
|
+
}
|
|
1598
|
+
const entries = await listDir(target);
|
|
1621
1599
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
1622
|
-
res.end(JSON.stringify({
|
|
1600
|
+
res.end(JSON.stringify({ entries }));
|
|
1601
|
+
return;
|
|
1623
1602
|
}
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1603
|
+
if (sub === "file") {
|
|
1604
|
+
const filePath = url.searchParams.get("path") || "";
|
|
1605
|
+
const dir = session.directory;
|
|
1606
|
+
if (!dir) {
|
|
1607
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
1608
|
+
res.end(JSON.stringify({ content: null, error: "No directory" }));
|
|
1609
|
+
return;
|
|
1610
|
+
}
|
|
1611
|
+
const target = path3.resolve(dir, filePath);
|
|
1612
|
+
if (!target.startsWith(path3.resolve(dir))) {
|
|
1613
|
+
res.writeHead(403, { "Content-Type": "application/json" });
|
|
1614
|
+
res.end(JSON.stringify({ error: "Forbidden" }));
|
|
1615
|
+
return;
|
|
1616
|
+
}
|
|
1617
|
+
try {
|
|
1618
|
+
const content = await fsPromises.readFile(target, "utf-8");
|
|
1619
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
1620
|
+
res.end(JSON.stringify({ content }));
|
|
1621
|
+
} catch {
|
|
1622
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
1623
|
+
res.end(JSON.stringify({ content: null, error: "\u8BFB\u53D6\u5931\u8D25" }));
|
|
1624
|
+
}
|
|
1632
1625
|
return;
|
|
1633
1626
|
}
|
|
1634
|
-
|
|
1635
|
-
const
|
|
1636
|
-
const
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
{
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1627
|
+
if (sub === "diff") {
|
|
1628
|
+
const filePath = url.searchParams.get("path") || "";
|
|
1629
|
+
const dir = session.directory;
|
|
1630
|
+
if (!dir) {
|
|
1631
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
1632
|
+
res.end(JSON.stringify({ added: [], removed: [] }));
|
|
1633
|
+
return;
|
|
1634
|
+
}
|
|
1635
|
+
try {
|
|
1636
|
+
const added = [];
|
|
1637
|
+
const removed = [];
|
|
1638
|
+
let result = await gitProvider.run(
|
|
1639
|
+
["diff", "--unified=0", "--", filePath],
|
|
1644
1640
|
{ cwd: dir }
|
|
1645
1641
|
);
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
const
|
|
1653
|
-
const
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1642
|
+
if (result.exitCode !== 0 || !result.text().trim()) {
|
|
1643
|
+
result = await gitProvider.run(
|
|
1644
|
+
["diff", "--unified=0", "--cached", "--", filePath],
|
|
1645
|
+
{ cwd: dir }
|
|
1646
|
+
);
|
|
1647
|
+
}
|
|
1648
|
+
const diffText = result.text();
|
|
1649
|
+
const hunkRe = /^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/gm;
|
|
1650
|
+
let m;
|
|
1651
|
+
while (m = hunkRe.exec(diffText)) {
|
|
1652
|
+
const oldStart = parseInt(m[1], 10);
|
|
1653
|
+
const oldCount = parseInt(m[2] ?? "1", 10);
|
|
1654
|
+
const newStart = parseInt(m[3], 10);
|
|
1655
|
+
const newCount = parseInt(m[4] ?? "1", 10);
|
|
1656
|
+
for (let i = 0; i < oldCount; i++) removed.push(oldStart + i);
|
|
1657
|
+
for (let i = 0; i < newCount; i++) added.push(newStart + i);
|
|
1658
|
+
}
|
|
1659
|
+
if (!diffText.trim()) {
|
|
1660
|
+
try {
|
|
1661
|
+
const target = path3.resolve(dir, filePath);
|
|
1662
|
+
if (target.startsWith(path3.resolve(dir))) {
|
|
1663
|
+
const content = await fsPromises.readFile(target, "utf-8");
|
|
1664
|
+
const lineCount = content.split("\n").length;
|
|
1665
|
+
for (let i = 1; i <= lineCount; i++) added.push(i);
|
|
1666
|
+
}
|
|
1667
|
+
} catch {
|
|
1665
1668
|
}
|
|
1666
|
-
} catch {
|
|
1667
1669
|
}
|
|
1670
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
1671
|
+
res.end(JSON.stringify({ added, removed }));
|
|
1672
|
+
} catch {
|
|
1673
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
1674
|
+
res.end(JSON.stringify({ added: [], removed: [] }));
|
|
1668
1675
|
}
|
|
1669
|
-
|
|
1670
|
-
res.end(JSON.stringify({ added, removed }));
|
|
1671
|
-
} catch {
|
|
1672
|
-
res.writeHead(200, { "Content-Type": "application/json" });
|
|
1673
|
-
res.end(JSON.stringify({ added: [], removed: [] }));
|
|
1676
|
+
return;
|
|
1674
1677
|
}
|
|
1678
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
1679
|
+
res.end(JSON.stringify({
|
|
1680
|
+
id: session.id,
|
|
1681
|
+
directory: session.directory,
|
|
1682
|
+
createdAt: session.createdAt
|
|
1683
|
+
}));
|
|
1675
1684
|
return;
|
|
1676
1685
|
}
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
const list = Array.from(sessions.values()).map((s) => ({
|
|
1687
|
-
id: s.id,
|
|
1688
|
-
directory: s.directory,
|
|
1689
|
-
stats: s.agent.getStats(),
|
|
1690
|
-
sessionId: s.agent.sessionId
|
|
1691
|
-
}));
|
|
1692
|
-
res.writeHead(200, { "Content-Type": "application/json" });
|
|
1693
|
-
res.end(JSON.stringify({ sessions: list }));
|
|
1694
|
-
return;
|
|
1695
|
-
}
|
|
1696
|
-
if (req.method === "GET" && req.url?.startsWith("/api/messages")) {
|
|
1697
|
-
const url = new URL(req.url, `http://localhost:${PORT}`);
|
|
1698
|
-
const sessionId = url.searchParams.get("sessionId");
|
|
1699
|
-
const session = sessionId ? getSession(sessionId) : void 0;
|
|
1700
|
-
if (!session) {
|
|
1701
|
-
res.writeHead(404, { "Content-Type": "application/json" });
|
|
1702
|
-
res.end(JSON.stringify({ error: "Session not found" }));
|
|
1686
|
+
if (req.method === "GET" && req.url === "/api/status") {
|
|
1687
|
+
const list = Array.from(sessions.values()).map((s) => ({
|
|
1688
|
+
id: s.id,
|
|
1689
|
+
directory: s.directory,
|
|
1690
|
+
stats: s.agent.getStats(),
|
|
1691
|
+
sessionId: s.agent.sessionId
|
|
1692
|
+
}));
|
|
1693
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
1694
|
+
res.end(JSON.stringify({ sessions: list }));
|
|
1703
1695
|
return;
|
|
1704
1696
|
}
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1697
|
+
if (req.method === "GET" && req.url?.startsWith("/api/messages")) {
|
|
1698
|
+
const url = new URL(req.url, `http://localhost:${PORT}`);
|
|
1699
|
+
const sessionId = url.searchParams.get("sessionId");
|
|
1700
|
+
const session = sessionId ? getSession(sessionId) : void 0;
|
|
1701
|
+
if (!session) {
|
|
1702
|
+
res.writeHead(404, { "Content-Type": "application/json" });
|
|
1703
|
+
res.end(JSON.stringify({ error: "Session not found" }));
|
|
1704
|
+
return;
|
|
1705
|
+
}
|
|
1706
|
+
session.agent.getSessionMessages({ limit: 30 }).then((messages) => {
|
|
1707
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
1708
|
+
res.end(JSON.stringify(messages));
|
|
1709
|
+
}).catch((err) => {
|
|
1710
|
+
res.writeHead(500, { "Content-Type": "application/json" });
|
|
1711
|
+
res.end(JSON.stringify({ error: err.message }));
|
|
1712
|
+
});
|
|
1713
|
+
return;
|
|
1714
|
+
}
|
|
1715
|
+
if (req.method === "GET" && req.url === "/admin") {
|
|
1716
|
+
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
1717
|
+
res.end(adminHTML());
|
|
1718
|
+
return;
|
|
1719
|
+
}
|
|
1720
|
+
if (req.method === "GET") {
|
|
1721
|
+
if (serveStatic(req, res)) return;
|
|
1722
|
+
if (serveAppIndex(res)) return;
|
|
1723
|
+
}
|
|
1724
|
+
res.writeHead(404, { "Content-Type": "application/json" });
|
|
1725
|
+
res.end(JSON.stringify({ error: "Not found" }));
|
|
1726
|
+
});
|
|
1727
|
+
}
|
|
1726
1728
|
async function startServer() {
|
|
1729
|
+
try {
|
|
1730
|
+
userSettings = JSON.parse(fs4.readFileSync(path3.join(ANYCODE_DIR, "settings.json"), "utf-8"));
|
|
1731
|
+
} catch {
|
|
1732
|
+
}
|
|
1733
|
+
loadConfig();
|
|
1734
|
+
process.on("uncaughtException", (err) => {
|
|
1735
|
+
console.error("\u26A0 Uncaught exception:", err.message);
|
|
1736
|
+
});
|
|
1737
|
+
process.on("unhandledRejection", (reason) => {
|
|
1738
|
+
console.error("\u26A0 Unhandled rejection:", reason instanceof Error ? reason.message : reason);
|
|
1739
|
+
});
|
|
1740
|
+
APP_DIST = resolveAppDist();
|
|
1741
|
+
createPreviewServer();
|
|
1742
|
+
createMainServer();
|
|
1727
1743
|
console.log("\u{1F680} Starting @any-code/server\u2026");
|
|
1728
1744
|
sharedStorage = new SqlJsStorage(DB_PATH);
|
|
1729
1745
|
const migrations = Database.getMigrations();
|