anycodex 0.0.14 → 0.0.16

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/dist/bin.js +449 -440
  2. 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-W3A4E2Q5.js
25
+ // ../server/dist/chunk-YOFLJZXT.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 = (() => {
462
+ function loadConfig() {
463
+ let userSettings = {};
463
464
  try {
464
- return JSON.parse(fs4.readFileSync(path3.join(ANYCODE_DIR, "settings.json"), "utf-8"));
465
+ userSettings = JSON.parse(fs4.readFileSync(path3.join(ANYCODE_DIR, "settings.json"), "utf-8"));
465
466
  } catch {
466
- return {};
467
467
  }
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
- });
468
+ const provider = process.env.PROVIDER ?? "";
469
+ const model = process.env.MODEL ?? userSettings.MODEL ?? "claude-sonnet-4-20250514";
470
+ const apiKey = process.env.API_KEY ?? userSettings.API_KEY ?? "";
471
+ const baseUrl = process.env.BASE_URL ?? userSettings.BASE_URL ?? "";
472
+ const port = parseInt(process.env.PORT ?? "3210", 10);
473
+ const previewPort = parseInt(process.env.PREVIEW_PORT ?? String(port + 1), 10);
474
+ if (!provider || !model || !baseUrl) {
475
+ console.error("\u274C Missing PROVIDER, MODEL, BASE_URL");
476
+ process.exit(1);
477
+ }
478
+ if (!apiKey) {
479
+ console.error("\u274C Missing API_KEY");
480
+ console.error("Run 'anycode start' to configure, or set API_KEY env var.");
481
+ process.exit(1);
482
+ }
483
+ const appDist = resolveAppDist();
484
+ return { provider, model, apiKey, baseUrl, port, previewPort, appDist, userSettings };
485
+ }
490
486
  function makePaths() {
491
487
  const dataPath = path3.join(ANYCODE_DIR, "data");
492
488
  fs4.mkdirSync(dataPath, { recursive: true });
@@ -550,10 +546,9 @@ var NodeGitProvider = class {
550
546
  }
551
547
  };
552
548
  var sessions = /* @__PURE__ */ new Map();
553
- var PROVIDER_ID = PROVIDER;
554
549
  var sharedStorage;
555
550
  var db;
556
- function createAgentConfig(directory, sessionId, terminal, preview) {
551
+ function createAgentConfig(cfg, directory, sessionId, terminal, preview) {
557
552
  return {
558
553
  directory,
559
554
  fs: new NodeFS(),
@@ -566,28 +561,26 @@ function createAgentConfig(directory, sessionId, terminal, preview) {
566
561
  ...terminal ? { terminal } : {},
567
562
  ...preview ? { preview } : {},
568
563
  provider: {
569
- id: PROVIDER_ID,
570
- apiKey: API_KEY,
571
- model: MODEL,
572
- ...BASE_URL ? { baseUrl: BASE_URL } : {}
564
+ id: cfg.provider,
565
+ apiKey: cfg.apiKey,
566
+ model: cfg.model,
567
+ ...cfg.baseUrl ? { baseUrl: cfg.baseUrl } : {}
573
568
  },
574
- settings: userSettings,
569
+ settings: cfg.userSettings,
575
570
  config: {
576
- model: `${PROVIDER_ID}/${MODEL}`,
577
- small_model: `${PROVIDER_ID}/${MODEL}`,
571
+ model: `${cfg.provider}/${cfg.model}`,
572
+ small_model: `${cfg.provider}/${cfg.model}`,
578
573
  provider: {
579
- [PROVIDER_ID]: {
580
- // Use @ai-sdk/anthropic for Claude models — it natively handles
581
- // thinking blocks and signatures. Other models use openai-compatible.
582
- npm: /claude/i.test(MODEL) ? "@ai-sdk/anthropic" : "@ai-sdk/openai-compatible",
583
- ...BASE_URL ? { api: BASE_URL } : {},
574
+ [cfg.provider]: {
575
+ npm: /claude/i.test(cfg.model) ? "@ai-sdk/anthropic" : "@ai-sdk/openai-compatible",
576
+ ...cfg.baseUrl ? { api: cfg.baseUrl } : {},
584
577
  options: {
585
- apiKey: API_KEY,
586
- ...BASE_URL ? { baseURL: BASE_URL } : {}
578
+ apiKey: cfg.apiKey,
579
+ ...cfg.baseUrl ? { baseURL: cfg.baseUrl } : {}
587
580
  },
588
581
  models: {
589
- [MODEL]: {
590
- name: MODEL,
582
+ [cfg.model]: {
583
+ name: cfg.model,
591
584
  attachment: true,
592
585
  tool_call: true,
593
586
  temperature: true,
@@ -601,7 +594,7 @@ function createAgentConfig(directory, sessionId, terminal, preview) {
601
594
  }
602
595
  };
603
596
  }
604
- function registerSession(id, agent, directory, createdAt) {
597
+ function registerSession(cfg, id, agent, directory, createdAt) {
605
598
  const entry = { id, agent, directory, createdAt };
606
599
  sessions.set(id, entry);
607
600
  agent.on("directory.set", (data) => {
@@ -613,36 +606,36 @@ function registerSession(id, agent, directory, createdAt) {
613
606
  }
614
607
  db.update("user_session", { op: "eq", field: "session_id", value: id }, { directory: dir });
615
608
  console.log(`\u{1F4C2} Session ${id} directory set to: ${dir}`);
616
- pushState(id);
617
- watchDirectory(id, dir);
609
+ pushState(cfg, id);
610
+ watchDirectory(cfg, id, dir);
618
611
  });
619
612
  return entry;
620
613
  }
621
- async function resumeSession(row) {
614
+ async function resumeSession(cfg, row) {
622
615
  const sessionId = row.session_id;
623
616
  const cached = sessions.get(sessionId);
624
617
  if (cached) return cached;
625
618
  const dir = row.directory || "";
626
619
  const tp = getOrCreateTerminalProvider(sessionId);
627
- const pp = getOrCreatePreviewProvider(sessionId);
628
- const agent = new CodeAgent(createAgentConfig(dir, sessionId, tp, pp));
620
+ const pp = getOrCreatePreviewProvider(cfg, sessionId);
621
+ const agent = new CodeAgent(createAgentConfig(cfg, dir, sessionId, tp, pp));
629
622
  await agent.init();
630
- const entry = registerSession(sessionId, agent, dir, row.time_created);
623
+ const entry = registerSession(cfg, sessionId, agent, dir, row.time_created);
631
624
  if (dir) {
632
625
  try {
633
626
  agent.setWorkingDirectory(dir);
634
627
  } catch {
635
628
  }
636
- watchDirectory(sessionId, dir);
629
+ watchDirectory(cfg, sessionId, dir);
637
630
  }
638
631
  console.log(`\u267B\uFE0F Session ${sessionId} resumed`);
639
632
  return entry;
640
633
  }
641
- async function createNewWindow(userId, isDefault = false) {
634
+ async function createNewWindow(cfg, userId, isDefault = false) {
642
635
  const tempId = `temp-${Date.now()}`;
643
636
  const tp = getOrCreateTerminalProvider(tempId);
644
- const pp = getOrCreatePreviewProvider(tempId);
645
- const agent = new CodeAgent(createAgentConfig("", void 0, tp, pp));
637
+ const pp = getOrCreatePreviewProvider(cfg, tempId);
638
+ const agent = new CodeAgent(createAgentConfig(cfg, "", void 0, tp, pp));
646
639
  await agent.init();
647
640
  const sessionId = agent.sessionId;
648
641
  const now = Date.now();
@@ -652,7 +645,7 @@ async function createNewWindow(userId, isDefault = false) {
652
645
  previewProviders.set(sessionId, pp);
653
646
  tp.sessionId = sessionId;
654
647
  pp.sessionId = sessionId;
655
- const entry = registerSession(sessionId, agent, "", now);
648
+ const entry = registerSession(cfg, sessionId, agent, "", now);
656
649
  db.insert("user_session", {
657
650
  user_id: userId,
658
651
  session_id: sessionId,
@@ -663,22 +656,22 @@ async function createNewWindow(userId, isDefault = false) {
663
656
  console.log(`\u2705 Window ${sessionId} created for user ${userId}${isDefault ? " (default)" : ""}`);
664
657
  return entry;
665
658
  }
666
- async function getOrCreateSession(userId) {
659
+ async function getOrCreateSession(cfg, userId) {
667
660
  const rows = db.findMany("user_session", { filter: { op: "eq", field: "user_id", value: userId } });
668
661
  const defaultRow = rows.find((r) => r.is_default === 1) || rows[0];
669
662
  if (defaultRow) {
670
663
  if (defaultRow.is_default !== 1) {
671
664
  db.update("user_session", { op: "eq", field: "session_id", value: defaultRow.session_id }, { is_default: 1 });
672
665
  }
673
- return resumeSession(defaultRow);
666
+ return resumeSession(cfg, defaultRow);
674
667
  }
675
- return createNewWindow(userId, true);
668
+ return createNewWindow(cfg, userId, true);
676
669
  }
677
- async function getAllWindows(userId) {
670
+ async function getAllWindows(cfg, userId) {
678
671
  const rows = db.findMany("user_session", { filter: { op: "eq", field: "user_id", value: userId } });
679
672
  const entries = [];
680
673
  for (const row of rows) {
681
- entries.push(await resumeSession(row));
674
+ entries.push(await resumeSession(cfg, row));
682
675
  }
683
676
  return entries;
684
677
  }
@@ -741,12 +734,12 @@ var sessionClients = /* @__PURE__ */ new Map();
741
734
  var sessionChatAbort = /* @__PURE__ */ new Map();
742
735
  var lastStateJson = /* @__PURE__ */ new Map();
743
736
  var statePushTimers = /* @__PURE__ */ new Map();
744
- function scheduleStatePush(sessionId, delayMs = 300) {
737
+ function scheduleStatePush(cfg, sessionId, delayMs = 300) {
745
738
  const existing = statePushTimers.get(sessionId);
746
739
  if (existing) clearTimeout(existing);
747
740
  const timer = setTimeout(() => {
748
741
  statePushTimers.delete(sessionId);
749
- void pushState(sessionId);
742
+ void pushState(cfg, sessionId);
750
743
  }, delayMs);
751
744
  statePushTimers.set(sessionId, timer);
752
745
  }
@@ -905,11 +898,11 @@ ${message}` : message;
905
898
  }
906
899
  }
907
900
  var watchers = /* @__PURE__ */ new Map();
908
- function watchDirectory(sessionId, dir) {
901
+ function watchDirectory(cfg, sessionId, dir) {
909
902
  const existing = watchers.get(sessionId);
910
903
  if (existing) existing.close();
911
904
  const debouncedPush = () => {
912
- scheduleStatePush(sessionId, 500);
905
+ scheduleStatePush(cfg, sessionId, 500);
913
906
  };
914
907
  try {
915
908
  const watcher = fs4.watch(dir, { recursive: true }, (eventType, filename) => {
@@ -922,7 +915,7 @@ function watchDirectory(sessionId, dir) {
922
915
  console.error(`\u274C fs.watch failed for ${dir}:`, err);
923
916
  }
924
917
  }
925
- async function buildState(sessionId) {
918
+ async function buildState(cfg, sessionId) {
926
919
  const session = getSession(sessionId);
927
920
  if (!session) return null;
928
921
  const dir = session.directory;
@@ -933,12 +926,12 @@ async function buildState(sessionId) {
933
926
  directory: dir,
934
927
  changes,
935
928
  topLevel,
936
- previewPort: previewSessionId === sessionId && previewTarget ? PREVIEW_PORT : null
929
+ previewPort: previewSessionId === sessionId && previewTarget ? cfg.previewPort : null
937
930
  };
938
931
  }
939
- async function pushState(sessionId) {
932
+ async function pushState(cfg, sessionId) {
940
933
  try {
941
- const payload = await buildState(sessionId);
934
+ const payload = await buildState(cfg, sessionId);
942
935
  if (!payload) return;
943
936
  const json = JSON.stringify(payload);
944
937
  const prev = lastStateJson.get(sessionId);
@@ -955,11 +948,11 @@ async function pushState(sessionId) {
955
948
  console.error(`\u274C pushState error:`, err);
956
949
  }
957
950
  }
958
- async function sendStateTo(sessionId, client) {
951
+ async function sendStateTo(cfg, sessionId, client) {
959
952
  try {
960
953
  let json = lastStateJson.get(sessionId);
961
954
  if (!json) {
962
- const payload = await buildState(sessionId);
955
+ const payload = await buildState(cfg, sessionId);
963
956
  if (!payload) return;
964
957
  json = JSON.stringify(payload);
965
958
  lastStateJson.set(sessionId, json);
@@ -1162,99 +1155,104 @@ var previewTarget = null;
1162
1155
  var previewSessionId = null;
1163
1156
  var NodePreviewProvider = class {
1164
1157
  sessionId;
1165
- constructor(sessionId) {
1158
+ cfg;
1159
+ constructor(cfg, sessionId) {
1160
+ this.cfg = cfg;
1166
1161
  this.sessionId = sessionId;
1167
1162
  }
1168
1163
  setPreviewTarget(forwardedLocalUrl) {
1169
1164
  previewTarget = forwardedLocalUrl.replace(/\/+$/, "");
1170
1165
  previewSessionId = this.sessionId;
1171
- console.log(`\u{1F517} Preview proxy: :${PREVIEW_PORT} \u2192 ${previewTarget} (session ${this.sessionId})`);
1166
+ console.log(`\u{1F517} Preview proxy: :${this.cfg.previewPort} \u2192 ${previewTarget} (session ${this.sessionId})`);
1172
1167
  broadcast(this.sessionId, {
1173
1168
  type: "preview",
1174
- port: PREVIEW_PORT
1169
+ port: this.cfg.previewPort
1175
1170
  });
1176
1171
  }
1177
1172
  };
1178
1173
  var previewProviders = /* @__PURE__ */ new Map();
1179
- function getOrCreatePreviewProvider(sessionId) {
1174
+ function getOrCreatePreviewProvider(cfg, sessionId) {
1180
1175
  let pp = previewProviders.get(sessionId);
1181
1176
  if (!pp) {
1182
- pp = new NodePreviewProvider(sessionId);
1177
+ pp = new NodePreviewProvider(cfg, sessionId);
1183
1178
  previewProviders.set(sessionId, pp);
1184
1179
  }
1185
1180
  return pp;
1186
1181
  }
1187
- var previewServer = http.createServer((req, res) => {
1188
- res.setHeader("Access-Control-Allow-Origin", "*");
1189
- res.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
1190
- res.setHeader("Access-Control-Allow-Headers", "*");
1191
- if (req.method === "OPTIONS") {
1192
- res.writeHead(204);
1193
- res.end();
1194
- return;
1195
- }
1196
- if (!previewTarget) {
1197
- res.writeHead(502, { "Content-Type": "text/plain" });
1198
- res.end("No preview target configured");
1199
- return;
1200
- }
1201
- try {
1202
- const targetUrl = previewTarget + (req.url || "/");
1203
- const parsed = new URL(targetUrl);
1204
- const options = {
1205
- hostname: parsed.hostname,
1206
- port: parsed.port,
1207
- path: parsed.pathname + parsed.search,
1208
- method: req.method,
1209
- headers: { ...req.headers, host: parsed.host }
1210
- };
1211
- const proxyReq = http.request(options, (proxyRes) => {
1212
- res.writeHead(proxyRes.statusCode || 502, proxyRes.headers);
1213
- proxyRes.pipe(res);
1214
- });
1215
- proxyReq.on("error", (err) => {
1216
- if (!res.headersSent) res.writeHead(502, { "Content-Type": "text/plain" });
1217
- res.end(`Preview proxy error: ${err.message}`);
1218
- });
1219
- req.pipe(proxyReq);
1220
- } catch (err) {
1221
- res.writeHead(502, { "Content-Type": "text/plain" });
1222
- res.end(`Invalid proxy target: ${err.message}`);
1223
- }
1224
- });
1225
- previewServer.on("upgrade", (req, socket, head) => {
1226
- if (!previewTarget) {
1227
- socket.destroy();
1228
- return;
1229
- }
1230
- try {
1231
- const parsed = new URL(previewTarget);
1232
- const targetWs = `ws://${parsed.hostname}:${parsed.port}${req.url || "/"}`;
1233
- const wsTarget = new URL(targetWs);
1234
- const options = {
1235
- hostname: wsTarget.hostname,
1236
- port: wsTarget.port,
1237
- path: wsTarget.pathname + wsTarget.search,
1238
- method: "GET",
1239
- headers: { ...req.headers, host: wsTarget.host }
1240
- };
1241
- const proxyReq = http.request(options);
1242
- proxyReq.on("upgrade", (_proxyRes, proxySocket, proxyHead) => {
1243
- socket.write(
1244
- "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"
1245
- );
1246
- if (proxyHead.length > 0) socket.write(proxyHead);
1247
- proxySocket.pipe(socket);
1248
- socket.pipe(proxySocket);
1249
- });
1250
- proxyReq.on("error", () => socket.destroy());
1251
- socket.on("error", () => proxyReq.destroy());
1252
- proxyReq.end();
1253
- } catch {
1254
- socket.destroy();
1255
- }
1256
- });
1257
- function adminHTML() {
1182
+ function createPreviewServer(cfg) {
1183
+ const previewServer = http.createServer((req, res) => {
1184
+ res.setHeader("Access-Control-Allow-Origin", "*");
1185
+ res.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
1186
+ res.setHeader("Access-Control-Allow-Headers", "*");
1187
+ if (req.method === "OPTIONS") {
1188
+ res.writeHead(204);
1189
+ res.end();
1190
+ return;
1191
+ }
1192
+ if (!previewTarget) {
1193
+ res.writeHead(502, { "Content-Type": "text/plain" });
1194
+ res.end("No preview target configured");
1195
+ return;
1196
+ }
1197
+ try {
1198
+ const targetUrl = previewTarget + (req.url || "/");
1199
+ const parsed = new URL(targetUrl);
1200
+ const options = {
1201
+ hostname: parsed.hostname,
1202
+ port: parsed.port,
1203
+ path: parsed.pathname + parsed.search,
1204
+ method: req.method,
1205
+ headers: { ...req.headers, host: parsed.host }
1206
+ };
1207
+ const proxyReq = http.request(options, (proxyRes) => {
1208
+ res.writeHead(proxyRes.statusCode || 502, proxyRes.headers);
1209
+ proxyRes.pipe(res);
1210
+ });
1211
+ proxyReq.on("error", (err) => {
1212
+ if (!res.headersSent) res.writeHead(502, { "Content-Type": "text/plain" });
1213
+ res.end(`Preview proxy error: ${err.message}`);
1214
+ });
1215
+ req.pipe(proxyReq);
1216
+ } catch (err) {
1217
+ res.writeHead(502, { "Content-Type": "text/plain" });
1218
+ res.end(`Invalid proxy target: ${err.message}`);
1219
+ }
1220
+ });
1221
+ previewServer.on("upgrade", (req, socket, head) => {
1222
+ if (!previewTarget) {
1223
+ socket.destroy();
1224
+ return;
1225
+ }
1226
+ try {
1227
+ const parsed = new URL(previewTarget);
1228
+ const targetWs = `ws://${parsed.hostname}:${parsed.port}${req.url || "/"}`;
1229
+ const wsTarget = new URL(targetWs);
1230
+ const options = {
1231
+ hostname: wsTarget.hostname,
1232
+ port: wsTarget.port,
1233
+ path: wsTarget.pathname + wsTarget.search,
1234
+ method: "GET",
1235
+ headers: { ...req.headers, host: wsTarget.host }
1236
+ };
1237
+ const proxyReq = http.request(options);
1238
+ proxyReq.on("upgrade", (_proxyRes, proxySocket, proxyHead) => {
1239
+ socket.write(
1240
+ "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"
1241
+ );
1242
+ if (proxyHead.length > 0) socket.write(proxyHead);
1243
+ proxySocket.pipe(socket);
1244
+ socket.pipe(proxySocket);
1245
+ });
1246
+ proxyReq.on("error", () => socket.destroy());
1247
+ socket.on("error", () => proxyReq.destroy());
1248
+ proxyReq.end();
1249
+ } catch {
1250
+ socket.destroy();
1251
+ }
1252
+ });
1253
+ return previewServer;
1254
+ }
1255
+ function adminHTML(cfg) {
1258
1256
  return (
1259
1257
  /* html */
1260
1258
  `<!DOCTYPE html>
@@ -1305,9 +1303,9 @@ function adminHTML() {
1305
1303
  <h1><span class="dot"></span> AnyCode Server</h1>
1306
1304
  <div class="card">
1307
1305
  <h2>\u2699 Configuration</h2>
1308
- <div class="row"><span class="label">Provider</span><span class="value">${PROVIDER}</span></div>
1309
- <div class="row"><span class="label">Model</span><span class="value">${MODEL}</span></div>
1310
- <div class="row"><span class="label">Port</span><span class="value">${PORT}</span></div>
1306
+ <div class="row"><span class="label">Provider</span><span class="value">${cfg.provider}</span></div>
1307
+ <div class="row"><span class="label">Model</span><span class="value">${cfg.model}</span></div>
1308
+ <div class="row"><span class="label">Port</span><span class="value">${cfg.port}</span></div>
1311
1309
  <div class="row"><span class="label">Sessions</span><span class="value" id="session-count">0</span></div>
1312
1310
  </div>
1313
1311
  <div class="card">
@@ -1374,10 +1372,9 @@ function resolveAppDist() {
1374
1372
  }
1375
1373
  return bundled;
1376
1374
  }
1377
- var APP_DIST = resolveAppDist();
1378
- function serveStatic(req, res) {
1375
+ function serveStatic(cfg, req, res) {
1379
1376
  const url = req.url || "/";
1380
- const filePath = path3.join(APP_DIST, url);
1377
+ const filePath = path3.join(cfg.appDist, url);
1381
1378
  if (fs4.existsSync(filePath) && fs4.statSync(filePath).isFile()) {
1382
1379
  const ext = path3.extname(filePath);
1383
1380
  res.writeHead(200, { "Content-Type": MIME_TYPES[ext] || "application/octet-stream" });
@@ -1386,10 +1383,10 @@ function serveStatic(req, res) {
1386
1383
  }
1387
1384
  return false;
1388
1385
  }
1389
- function serveAppIndex(res) {
1390
- const indexPath = path3.join(APP_DIST, "index.html");
1386
+ function serveAppIndex(cfg, res) {
1387
+ const indexPath = path3.join(cfg.appDist, "index.html");
1391
1388
  if (fs4.existsSync(indexPath)) {
1392
- const webSocket = userSettings.webSocket === true;
1389
+ const webSocket = cfg.userSettings.webSocket === true;
1393
1390
  const configScript = `<script>window.__ANYCODE_CONFIG__=${JSON.stringify({ webSocket })}</script>`;
1394
1391
  let html = fs4.readFileSync(indexPath, "utf-8");
1395
1392
  html = html.replace("</head>", `${configScript}</head>`);
@@ -1399,331 +1396,343 @@ function serveAppIndex(res) {
1399
1396
  }
1400
1397
  return false;
1401
1398
  }
1402
- var server = http.createServer(async (req, res) => {
1403
- res.setHeader("Access-Control-Allow-Origin", "*");
1404
- res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
1405
- res.setHeader("Access-Control-Allow-Headers", "Content-Type");
1406
- if (req.method === "OPTIONS") {
1407
- res.writeHead(204);
1408
- res.end();
1409
- return;
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" }));
1399
+ function createMainServer(cfg) {
1400
+ const server = http.createServer(async (req, res) => {
1401
+ res.setHeader("Access-Control-Allow-Origin", "*");
1402
+ res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
1403
+ res.setHeader("Access-Control-Allow-Headers", "Content-Type");
1404
+ if (req.method === "OPTIONS") {
1405
+ res.writeHead(204);
1406
+ res.end();
1419
1407
  return;
1420
1408
  }
1421
- const channelId = Math.random().toString(36).slice(2) + Date.now().toString(36);
1422
- const client = new PollingClient(channelId, sessionId);
1423
- pollingClients.set(channelId, client);
1424
- getSessionClients(sessionId).add(client);
1425
- console.log(`\u{1F50C} Poll client connected to session ${sessionId} (channel=${channelId})`);
1426
- sendStateTo(sessionId, client);
1427
- res.writeHead(200, { "Content-Type": "application/json" });
1428
- res.end(JSON.stringify({ channelId }));
1429
- return;
1430
- }
1431
- if (req.method === "GET" && req.url?.startsWith("/api/poll?")) {
1432
- const url = new URL(req.url, `http://localhost:${PORT}`);
1433
- const channelId = url.searchParams.get("channelId");
1434
- const client = channelId ? pollingClients.get(channelId) : void 0;
1435
- if (!client || client.readyState !== 1) {
1436
- res.writeHead(404, { "Content-Type": "application/json" });
1437
- res.end(JSON.stringify({ error: "Channel not found" }));
1409
+ if (req.method === "POST" && req.url === "/api/poll/connect") {
1410
+ let body = "";
1411
+ for await (const chunk of req) body += chunk;
1412
+ const { sessionId } = body ? JSON.parse(body) : {};
1413
+ const session = sessionId ? getSession(sessionId) : void 0;
1414
+ if (!session) {
1415
+ res.writeHead(404, { "Content-Type": "application/json" });
1416
+ res.end(JSON.stringify({ error: "Session not found" }));
1417
+ return;
1418
+ }
1419
+ const channelId = Math.random().toString(36).slice(2) + Date.now().toString(36);
1420
+ const client = new PollingClient(channelId, sessionId);
1421
+ pollingClients.set(channelId, client);
1422
+ getSessionClients(sessionId).add(client);
1423
+ console.log(`\u{1F50C} Poll client connected to session ${sessionId} (channel=${channelId})`);
1424
+ sendStateTo(cfg, sessionId, client);
1425
+ res.writeHead(200, { "Content-Type": "application/json" });
1426
+ res.end(JSON.stringify({ channelId }));
1438
1427
  return;
1439
1428
  }
1440
- client.hold(res);
1441
- return;
1442
- }
1443
- if (req.method === "POST" && req.url === "/api/poll/send") {
1444
- let body = "";
1445
- for await (const chunk of req) body += chunk;
1446
- const { channelId, data } = body ? JSON.parse(body) : {};
1447
- const client = channelId ? pollingClients.get(channelId) : void 0;
1448
- if (!client || client.readyState !== 1) {
1449
- res.writeHead(404, { "Content-Type": "application/json" });
1450
- res.end(JSON.stringify({ error: "Channel not found" }));
1429
+ if (req.method === "GET" && req.url?.startsWith("/api/poll?")) {
1430
+ const url = new URL(req.url, `http://localhost:${cfg.port}`);
1431
+ const channelId = url.searchParams.get("channelId");
1432
+ const client = channelId ? pollingClients.get(channelId) : void 0;
1433
+ if (!client || client.readyState !== 1) {
1434
+ res.writeHead(404, { "Content-Type": "application/json" });
1435
+ res.end(JSON.stringify({ error: "Channel not found" }));
1436
+ return;
1437
+ }
1438
+ client.hold(res);
1451
1439
  return;
1452
1440
  }
1453
- client.lastActivity = Date.now();
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 () => {
1441
+ if (req.method === "POST" && req.url === "/api/poll/send") {
1477
1442
  let body = "";
1478
1443
  for await (const chunk of req) body += chunk;
1479
- const { userId } = body ? JSON.parse(body) : {};
1480
- if (!userId) {
1481
- res.writeHead(400, { "Content-Type": "application/json" });
1482
- res.end(JSON.stringify({ error: "userId is required" }));
1444
+ const { channelId, data } = body ? JSON.parse(body) : {};
1445
+ const client = channelId ? pollingClients.get(channelId) : void 0;
1446
+ if (!client || client.readyState !== 1) {
1447
+ res.writeHead(404, { "Content-Type": "application/json" });
1448
+ res.end(JSON.stringify({ error: "Channel not found" }));
1483
1449
  return;
1484
1450
  }
1485
- getOrCreateSession(userId).then((entry) => {
1486
- res.writeHead(200, { "Content-Type": "application/json" });
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 }));
1451
+ client.lastActivity = Date.now();
1452
+ handleClientMessage(client.sessionId, client, data).catch(() => {
1491
1453
  });
1492
- })();
1493
- return;
1494
- }
1495
- if (req.method === "GET" && req.url === "/api/sessions") {
1496
- const list = Array.from(sessions.values()).map((s) => ({
1497
- id: s.id,
1498
- directory: s.directory,
1499
- createdAt: s.createdAt
1500
- }));
1501
- res.writeHead(200, { "Content-Type": "application/json" });
1502
- res.end(JSON.stringify(list));
1503
- return;
1504
- }
1505
- if (req.method === "GET" && req.url?.startsWith("/api/windows")) {
1506
- const url = new URL(req.url, `http://localhost:${PORT}`);
1507
- const userId = url.searchParams.get("userId");
1508
- if (!userId) {
1509
- res.writeHead(400, { "Content-Type": "application/json" });
1510
- res.end(JSON.stringify({ error: "userId is required" }));
1454
+ res.writeHead(200, { "Content-Type": "application/json" });
1455
+ res.end(JSON.stringify({ ok: true }));
1456
+ return;
1457
+ }
1458
+ if (req.method === "POST" && req.url === "/api/poll/close") {
1459
+ let body = "";
1460
+ for await (const chunk of req) body += chunk;
1461
+ const { channelId } = body ? JSON.parse(body) : {};
1462
+ const client = channelId ? pollingClients.get(channelId) : void 0;
1463
+ if (client) {
1464
+ client.close();
1465
+ removeClient(client.sessionId, client);
1466
+ pollingClients.delete(channelId);
1467
+ console.log(`\u{1F50C} Poll client disconnected (channel=${channelId})`);
1468
+ }
1469
+ res.writeHead(200, { "Content-Type": "application/json" });
1470
+ res.end(JSON.stringify({ ok: true }));
1511
1471
  return;
1512
1472
  }
1513
- getAllWindows(userId).then((entries) => {
1514
- const rows = db.findMany("user_session", { filter: { op: "eq", field: "user_id", value: userId } });
1515
- const defaultMap = new Map(rows.map((r) => [r.session_id, r.is_default === 1]));
1516
- const list = entries.map((e) => ({
1517
- id: e.id,
1518
- directory: e.directory,
1519
- createdAt: e.createdAt,
1520
- isDefault: defaultMap.get(e.id) ?? false
1473
+ if (req.method === "POST" && req.url === "/api/sessions") {
1474
+ (async () => {
1475
+ let body = "";
1476
+ for await (const chunk of req) body += chunk;
1477
+ const { userId } = body ? JSON.parse(body) : {};
1478
+ if (!userId) {
1479
+ res.writeHead(400, { "Content-Type": "application/json" });
1480
+ res.end(JSON.stringify({ error: "userId is required" }));
1481
+ return;
1482
+ }
1483
+ getOrCreateSession(cfg, userId).then((entry) => {
1484
+ res.writeHead(200, { "Content-Type": "application/json" });
1485
+ res.end(JSON.stringify({ id: entry.id, directory: entry.directory }));
1486
+ }).catch((err) => {
1487
+ res.writeHead(500, { "Content-Type": "application/json" });
1488
+ res.end(JSON.stringify({ error: err.message }));
1489
+ });
1490
+ })();
1491
+ return;
1492
+ }
1493
+ if (req.method === "GET" && req.url === "/api/sessions") {
1494
+ const list = Array.from(sessions.values()).map((s) => ({
1495
+ id: s.id,
1496
+ directory: s.directory,
1497
+ createdAt: s.createdAt
1521
1498
  }));
1522
1499
  res.writeHead(200, { "Content-Type": "application/json" });
1523
1500
  res.end(JSON.stringify(list));
1524
- }).catch((err) => {
1525
- res.writeHead(500, { "Content-Type": "application/json" });
1526
- res.end(JSON.stringify({ error: err.message }));
1527
- });
1528
- return;
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) : {};
1501
+ return;
1502
+ }
1503
+ if (req.method === "GET" && req.url?.startsWith("/api/windows")) {
1504
+ const url = new URL(req.url, `http://localhost:${cfg.port}`);
1505
+ const userId = url.searchParams.get("userId");
1535
1506
  if (!userId) {
1536
1507
  res.writeHead(400, { "Content-Type": "application/json" });
1537
1508
  res.end(JSON.stringify({ error: "userId is required" }));
1538
1509
  return;
1539
1510
  }
1540
- createNewWindow(userId, false).then((entry) => {
1511
+ getAllWindows(cfg, userId).then((entries) => {
1512
+ const rows = db.findMany("user_session", { filter: { op: "eq", field: "user_id", value: userId } });
1513
+ const defaultMap = new Map(rows.map((r) => [r.session_id, r.is_default === 1]));
1514
+ const list = entries.map((e) => ({
1515
+ id: e.id,
1516
+ directory: e.directory,
1517
+ createdAt: e.createdAt,
1518
+ isDefault: defaultMap.get(e.id) ?? false
1519
+ }));
1541
1520
  res.writeHead(200, { "Content-Type": "application/json" });
1542
- res.end(JSON.stringify({ id: entry.id, directory: entry.directory, isDefault: false }));
1521
+ res.end(JSON.stringify(list));
1543
1522
  }).catch((err) => {
1544
1523
  res.writeHead(500, { "Content-Type": "application/json" });
1545
1524
  res.end(JSON.stringify({ error: err.message }));
1546
1525
  });
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
1526
  return;
1569
1527
  }
1570
- const sub = sessionMatch[2];
1571
- const url = new URL(req.url, `http://localhost:${PORT}`);
1572
- if (sub === "state") {
1573
- const dir = session.directory;
1574
- const [topLevel, changes] = await Promise.all([
1575
- dir ? listDir(dir) : Promise.resolve([]),
1576
- dir ? getGitChanges(dir) : Promise.resolve([])
1577
- ]);
1578
- const hasPreview = previewSessionId === session.id && previewTarget;
1579
- res.writeHead(200, { "Content-Type": "application/json" });
1580
- res.end(JSON.stringify({ directory: dir, topLevel, changes, previewPort: hasPreview ? PREVIEW_PORT : null }));
1528
+ if (req.method === "POST" && req.url === "/api/windows") {
1529
+ (async () => {
1530
+ let body = "";
1531
+ for await (const chunk of req) body += chunk;
1532
+ const { userId } = body ? JSON.parse(body) : {};
1533
+ if (!userId) {
1534
+ res.writeHead(400, { "Content-Type": "application/json" });
1535
+ res.end(JSON.stringify({ error: "userId is required" }));
1536
+ return;
1537
+ }
1538
+ createNewWindow(cfg, userId, false).then((entry) => {
1539
+ res.writeHead(200, { "Content-Type": "application/json" });
1540
+ res.end(JSON.stringify({ id: entry.id, directory: entry.directory, isDefault: false }));
1541
+ }).catch((err) => {
1542
+ res.writeHead(500, { "Content-Type": "application/json" });
1543
+ res.end(JSON.stringify({ error: err.message }));
1544
+ });
1545
+ })();
1581
1546
  return;
1582
1547
  }
1583
- if (sub === "ls") {
1584
- const subPath = url.searchParams.get("path") || "";
1585
- const dir = session.directory;
1586
- if (!dir) {
1548
+ const windowDeleteMatch = req.url?.match(/^\/api\/windows\/([^/?]+)$/);
1549
+ if (req.method === "DELETE" && windowDeleteMatch) {
1550
+ const ok = deleteWindow(windowDeleteMatch[1]);
1551
+ if (ok) {
1587
1552
  res.writeHead(200, { "Content-Type": "application/json" });
1588
- res.end(JSON.stringify({ entries: [] }));
1589
- return;
1590
- }
1591
- const target = path3.resolve(dir, subPath);
1592
- if (!target.startsWith(path3.resolve(dir))) {
1593
- res.writeHead(403, { "Content-Type": "application/json" });
1594
- res.end(JSON.stringify({ error: "Forbidden" }));
1595
- return;
1553
+ res.end(JSON.stringify({ ok: true }));
1554
+ } else {
1555
+ res.writeHead(400, { "Content-Type": "application/json" });
1556
+ res.end(JSON.stringify({ error: "Cannot delete default window or window not found" }));
1596
1557
  }
1597
- const entries = await listDir(target);
1598
- res.writeHead(200, { "Content-Type": "application/json" });
1599
- res.end(JSON.stringify({ entries }));
1600
1558
  return;
1601
1559
  }
1602
- if (sub === "file") {
1603
- const filePath = url.searchParams.get("path") || "";
1604
- const dir = session.directory;
1605
- if (!dir) {
1606
- res.writeHead(200, { "Content-Type": "application/json" });
1607
- res.end(JSON.stringify({ content: null, error: "No directory" }));
1560
+ const sessionMatch = req.url?.match(/^\/api\/sessions\/([^/?]+)(?:\/([a-z]+))?/);
1561
+ if (req.method === "GET" && sessionMatch) {
1562
+ const session = getSession(sessionMatch[1]);
1563
+ if (!session) {
1564
+ res.writeHead(404, { "Content-Type": "application/json" });
1565
+ res.end(JSON.stringify({ error: "Session not found" }));
1608
1566
  return;
1609
1567
  }
1610
- const target = path3.resolve(dir, filePath);
1611
- if (!target.startsWith(path3.resolve(dir))) {
1612
- res.writeHead(403, { "Content-Type": "application/json" });
1613
- res.end(JSON.stringify({ error: "Forbidden" }));
1568
+ const sub = sessionMatch[2];
1569
+ const url = new URL(req.url, `http://localhost:${cfg.port}`);
1570
+ if (sub === "state") {
1571
+ const dir = session.directory;
1572
+ const [topLevel, changes] = await Promise.all([
1573
+ dir ? listDir(dir) : Promise.resolve([]),
1574
+ dir ? getGitChanges(dir) : Promise.resolve([])
1575
+ ]);
1576
+ const hasPreview = previewSessionId === session.id && previewTarget;
1577
+ res.writeHead(200, { "Content-Type": "application/json" });
1578
+ res.end(JSON.stringify({ directory: dir, topLevel, changes, previewPort: hasPreview ? cfg.previewPort : null }));
1614
1579
  return;
1615
1580
  }
1616
- try {
1617
- const content = await fsPromises.readFile(target, "utf-8");
1618
- res.writeHead(200, { "Content-Type": "application/json" });
1619
- res.end(JSON.stringify({ content }));
1620
- } catch {
1581
+ if (sub === "ls") {
1582
+ const subPath = url.searchParams.get("path") || "";
1583
+ const dir = session.directory;
1584
+ if (!dir) {
1585
+ res.writeHead(200, { "Content-Type": "application/json" });
1586
+ res.end(JSON.stringify({ entries: [] }));
1587
+ return;
1588
+ }
1589
+ const target = path3.resolve(dir, subPath);
1590
+ if (!target.startsWith(path3.resolve(dir))) {
1591
+ res.writeHead(403, { "Content-Type": "application/json" });
1592
+ res.end(JSON.stringify({ error: "Forbidden" }));
1593
+ return;
1594
+ }
1595
+ const entries = await listDir(target);
1621
1596
  res.writeHead(200, { "Content-Type": "application/json" });
1622
- res.end(JSON.stringify({ content: null, error: "\u8BFB\u53D6\u5931\u8D25" }));
1597
+ res.end(JSON.stringify({ entries }));
1598
+ return;
1623
1599
  }
1624
- return;
1625
- }
1626
- if (sub === "diff") {
1627
- const filePath = url.searchParams.get("path") || "";
1628
- const dir = session.directory;
1629
- if (!dir) {
1630
- res.writeHead(200, { "Content-Type": "application/json" });
1631
- res.end(JSON.stringify({ added: [], removed: [] }));
1600
+ if (sub === "file") {
1601
+ const filePath = url.searchParams.get("path") || "";
1602
+ const dir = session.directory;
1603
+ if (!dir) {
1604
+ res.writeHead(200, { "Content-Type": "application/json" });
1605
+ res.end(JSON.stringify({ content: null, error: "No directory" }));
1606
+ return;
1607
+ }
1608
+ const target = path3.resolve(dir, filePath);
1609
+ if (!target.startsWith(path3.resolve(dir))) {
1610
+ res.writeHead(403, { "Content-Type": "application/json" });
1611
+ res.end(JSON.stringify({ error: "Forbidden" }));
1612
+ return;
1613
+ }
1614
+ try {
1615
+ const content = await fsPromises.readFile(target, "utf-8");
1616
+ res.writeHead(200, { "Content-Type": "application/json" });
1617
+ res.end(JSON.stringify({ content }));
1618
+ } catch {
1619
+ res.writeHead(200, { "Content-Type": "application/json" });
1620
+ res.end(JSON.stringify({ content: null, error: "\u8BFB\u53D6\u5931\u8D25" }));
1621
+ }
1632
1622
  return;
1633
1623
  }
1634
- try {
1635
- const added = [];
1636
- const removed = [];
1637
- let result = await gitProvider.run(
1638
- ["diff", "--unified=0", "--", filePath],
1639
- { cwd: dir }
1640
- );
1641
- if (result.exitCode !== 0 || !result.text().trim()) {
1642
- result = await gitProvider.run(
1643
- ["diff", "--unified=0", "--cached", "--", filePath],
1624
+ if (sub === "diff") {
1625
+ const filePath = url.searchParams.get("path") || "";
1626
+ const dir = session.directory;
1627
+ if (!dir) {
1628
+ res.writeHead(200, { "Content-Type": "application/json" });
1629
+ res.end(JSON.stringify({ added: [], removed: [] }));
1630
+ return;
1631
+ }
1632
+ try {
1633
+ const added = [];
1634
+ const removed = [];
1635
+ let result = await gitProvider.run(
1636
+ ["diff", "--unified=0", "--", filePath],
1644
1637
  { cwd: dir }
1645
1638
  );
1646
- }
1647
- const diffText = result.text();
1648
- const hunkRe = /^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/gm;
1649
- let m;
1650
- while (m = hunkRe.exec(diffText)) {
1651
- const oldStart = parseInt(m[1], 10);
1652
- const oldCount = parseInt(m[2] ?? "1", 10);
1653
- const newStart = parseInt(m[3], 10);
1654
- const newCount = parseInt(m[4] ?? "1", 10);
1655
- for (let i = 0; i < oldCount; i++) removed.push(oldStart + i);
1656
- for (let i = 0; i < newCount; i++) added.push(newStart + i);
1657
- }
1658
- if (!diffText.trim()) {
1659
- try {
1660
- const target = path3.resolve(dir, filePath);
1661
- if (target.startsWith(path3.resolve(dir))) {
1662
- const content = await fsPromises.readFile(target, "utf-8");
1663
- const lineCount = content.split("\n").length;
1664
- for (let i = 1; i <= lineCount; i++) added.push(i);
1639
+ if (result.exitCode !== 0 || !result.text().trim()) {
1640
+ result = await gitProvider.run(
1641
+ ["diff", "--unified=0", "--cached", "--", filePath],
1642
+ { cwd: dir }
1643
+ );
1644
+ }
1645
+ const diffText = result.text();
1646
+ const hunkRe = /^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/gm;
1647
+ let m;
1648
+ while (m = hunkRe.exec(diffText)) {
1649
+ const oldStart = parseInt(m[1], 10);
1650
+ const oldCount = parseInt(m[2] ?? "1", 10);
1651
+ const newStart = parseInt(m[3], 10);
1652
+ const newCount = parseInt(m[4] ?? "1", 10);
1653
+ for (let i = 0; i < oldCount; i++) removed.push(oldStart + i);
1654
+ for (let i = 0; i < newCount; i++) added.push(newStart + i);
1655
+ }
1656
+ if (!diffText.trim()) {
1657
+ try {
1658
+ const target = path3.resolve(dir, filePath);
1659
+ if (target.startsWith(path3.resolve(dir))) {
1660
+ const content = await fsPromises.readFile(target, "utf-8");
1661
+ const lineCount = content.split("\n").length;
1662
+ for (let i = 1; i <= lineCount; i++) added.push(i);
1663
+ }
1664
+ } catch {
1665
1665
  }
1666
- } catch {
1667
1666
  }
1667
+ res.writeHead(200, { "Content-Type": "application/json" });
1668
+ res.end(JSON.stringify({ added, removed }));
1669
+ } catch {
1670
+ res.writeHead(200, { "Content-Type": "application/json" });
1671
+ res.end(JSON.stringify({ added: [], removed: [] }));
1668
1672
  }
1669
- res.writeHead(200, { "Content-Type": "application/json" });
1670
- res.end(JSON.stringify({ added, removed }));
1671
- } catch {
1672
- res.writeHead(200, { "Content-Type": "application/json" });
1673
- res.end(JSON.stringify({ added: [], removed: [] }));
1673
+ return;
1674
1674
  }
1675
+ res.writeHead(200, { "Content-Type": "application/json" });
1676
+ res.end(JSON.stringify({
1677
+ id: session.id,
1678
+ directory: session.directory,
1679
+ createdAt: session.createdAt
1680
+ }));
1675
1681
  return;
1676
1682
  }
1677
- res.writeHead(200, { "Content-Type": "application/json" });
1678
- res.end(JSON.stringify({
1679
- id: session.id,
1680
- directory: session.directory,
1681
- createdAt: session.createdAt
1682
- }));
1683
- return;
1684
- }
1685
- if (req.method === "GET" && req.url === "/api/status") {
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" }));
1683
+ if (req.method === "GET" && req.url === "/api/status") {
1684
+ const list = Array.from(sessions.values()).map((s) => ({
1685
+ id: s.id,
1686
+ directory: s.directory,
1687
+ stats: s.agent.getStats(),
1688
+ sessionId: s.agent.sessionId
1689
+ }));
1690
+ res.writeHead(200, { "Content-Type": "application/json" });
1691
+ res.end(JSON.stringify({ sessions: list }));
1703
1692
  return;
1704
1693
  }
1705
- session.agent.getSessionMessages({ limit: 30 }).then((messages) => {
1706
- res.writeHead(200, { "Content-Type": "application/json" });
1707
- res.end(JSON.stringify(messages));
1708
- }).catch((err) => {
1709
- res.writeHead(500, { "Content-Type": "application/json" });
1710
- res.end(JSON.stringify({ error: err.message }));
1711
- });
1712
- return;
1713
- }
1714
- if (req.method === "GET" && req.url === "/admin") {
1715
- res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
1716
- res.end(adminHTML());
1717
- return;
1718
- }
1719
- if (req.method === "GET") {
1720
- if (serveStatic(req, res)) return;
1721
- if (serveAppIndex(res)) return;
1722
- }
1723
- res.writeHead(404, { "Content-Type": "application/json" });
1724
- res.end(JSON.stringify({ error: "Not found" }));
1725
- });
1694
+ if (req.method === "GET" && req.url?.startsWith("/api/messages")) {
1695
+ const url = new URL(req.url, `http://localhost:${cfg.port}`);
1696
+ const sessionId = url.searchParams.get("sessionId");
1697
+ const session = sessionId ? getSession(sessionId) : void 0;
1698
+ if (!session) {
1699
+ res.writeHead(404, { "Content-Type": "application/json" });
1700
+ res.end(JSON.stringify({ error: "Session not found" }));
1701
+ return;
1702
+ }
1703
+ session.agent.getSessionMessages({ limit: 30 }).then((messages) => {
1704
+ res.writeHead(200, { "Content-Type": "application/json" });
1705
+ res.end(JSON.stringify(messages));
1706
+ }).catch((err) => {
1707
+ res.writeHead(500, { "Content-Type": "application/json" });
1708
+ res.end(JSON.stringify({ error: err.message }));
1709
+ });
1710
+ return;
1711
+ }
1712
+ if (req.method === "GET" && req.url === "/admin") {
1713
+ res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
1714
+ res.end(adminHTML(cfg));
1715
+ return;
1716
+ }
1717
+ if (req.method === "GET") {
1718
+ if (serveStatic(cfg, req, res)) return;
1719
+ if (serveAppIndex(cfg, res)) return;
1720
+ }
1721
+ res.writeHead(404, { "Content-Type": "application/json" });
1722
+ res.end(JSON.stringify({ error: "Not found" }));
1723
+ });
1724
+ return server;
1725
+ }
1726
1726
  async function startServer() {
1727
+ const cfg = loadConfig();
1728
+ process.on("uncaughtException", (err) => {
1729
+ console.error("\u26A0 Uncaught exception:", err.message);
1730
+ });
1731
+ process.on("unhandledRejection", (reason) => {
1732
+ console.error("\u26A0 Unhandled rejection:", reason instanceof Error ? reason.message : reason);
1733
+ });
1734
+ const previewServer = createPreviewServer(cfg);
1735
+ const server = createMainServer(cfg);
1727
1736
  console.log("\u{1F680} Starting @any-code/server\u2026");
1728
1737
  sharedStorage = new SqlJsStorage(DB_PATH);
1729
1738
  const migrations = Database.getMigrations();
@@ -1764,7 +1773,7 @@ async function startServer() {
1764
1773
  )
1765
1774
  `);
1766
1775
  }
1767
- const appDistExists = fs4.existsSync(APP_DIST);
1776
+ const appDistExists = fs4.existsSync(cfg.appDist);
1768
1777
  setInterval(() => {
1769
1778
  const now = Date.now();
1770
1779
  for (const [id, client] of pollingClients) {
@@ -1778,7 +1787,7 @@ async function startServer() {
1778
1787
  }, 3e4);
1779
1788
  const wss = new WebSocketServer({ server });
1780
1789
  wss.on("connection", (ws, req) => {
1781
- const url = new URL(req.url || "/", `http://localhost:${PORT}`);
1790
+ const url = new URL(req.url || "/", `http://localhost:${cfg.port}`);
1782
1791
  const sessionId = url.searchParams.get("sessionId");
1783
1792
  if (!sessionId || !getSession(sessionId)) {
1784
1793
  ws.close(4001, "Invalid session");
@@ -1791,7 +1800,7 @@ async function startServer() {
1791
1800
  const clients = getSessionClients(sessionId);
1792
1801
  clients.add(ws);
1793
1802
  console.log(`\u{1F50C} WS client connected to session ${sessionId} (${clients.size} total)`);
1794
- sendStateTo(sessionId, ws);
1803
+ sendStateTo(cfg, sessionId, ws);
1795
1804
  ws.on("message", async (raw) => {
1796
1805
  try {
1797
1806
  const msg = JSON.parse(raw.toString());
@@ -1805,20 +1814,20 @@ async function startServer() {
1805
1814
  });
1806
1815
  });
1807
1816
  const HOST = process.env.HOST ?? "0.0.0.0";
1808
- previewServer.listen(PREVIEW_PORT, HOST, () => {
1809
- console.log(`\u{1F441} Preview proxy: http://${HOST}:${PREVIEW_PORT}`);
1817
+ previewServer.listen(cfg.previewPort, HOST, () => {
1818
+ console.log(`\u{1F441} Preview proxy: http://${HOST}:${cfg.previewPort}`);
1810
1819
  });
1811
- server.listen(PORT, HOST, () => {
1812
- console.log(`\u{1F310} http://${HOST}:${PORT}`);
1813
- console.log(`\u{1F916} Provider: ${PROVIDER} / ${MODEL}`);
1814
- console.log(`\u{1F5A5} Admin: http://${HOST}:${PORT}/admin`);
1820
+ server.listen(cfg.port, HOST, () => {
1821
+ console.log(`\u{1F310} http://${HOST}:${cfg.port}`);
1822
+ console.log(`\u{1F916} Provider: ${cfg.provider} / ${cfg.model}`);
1823
+ console.log(`\u{1F5A5} Admin: http://${HOST}:${cfg.port}/admin`);
1815
1824
  if (appDistExists) {
1816
- console.log(`\u{1F4F1} App: http://${HOST}:${PORT}`);
1825
+ console.log(`\u{1F4F1} App: http://${HOST}:${cfg.port}`);
1817
1826
  } else {
1818
- console.log(`\u26A0 App dist not found at ${APP_DIST} \u2014 run 'pnpm --filter @any-code/app build' first`);
1827
+ console.log(`\u26A0 App dist not found at ${cfg.appDist} \u2014 run 'pnpm --filter @any-code/app build' first`);
1819
1828
  }
1820
1829
  console.log(`\u{1F4CB} Sessions: POST /api/sessions to create`);
1821
- console.log(`\u{1F50C} WebSocket: ws://${HOST}:${PORT}?sessionId=xxx`);
1830
+ console.log(`\u{1F50C} WebSocket: ws://${HOST}:${cfg.port}?sessionId=xxx`);
1822
1831
  });
1823
1832
  }
1824
1833
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "anycodex",
3
- "version": "0.0.14",
3
+ "version": "0.0.16",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist"