@rubytech/create-realagent 1.0.684 → 1.0.686

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 (36) hide show
  1. package/dist/index.js +49 -203
  2. package/dist/pinned-binaries.js +10 -41
  3. package/dist/uninstall.js +47 -19
  4. package/package.json +1 -1
  5. package/payload/platform/config/brand.json +1 -0
  6. package/payload/platform/plugins/cloudflare/scripts/setup-tunnel.sh +35 -9
  7. package/payload/platform/plugins/docs/PLUGIN.md +2 -0
  8. package/payload/platform/plugins/docs/references/cloudflare.md +1 -1
  9. package/payload/platform/plugins/docs/references/deployment.md +19 -6
  10. package/payload/platform/plugins/docs/references/graph.md +38 -0
  11. package/payload/platform/plugins/docs/references/platform.md +10 -7
  12. package/payload/platform/plugins/docs/references/troubleshooting.md +23 -13
  13. package/payload/platform/scripts/vnc.sh +7 -7
  14. package/payload/platform/templates/systemd/{maxy-edge.service → edge.service.template} +9 -6
  15. package/payload/server/maxy-edge.js +7 -369
  16. package/payload/server/public/assets/admin-BqLtaMVu.js +352 -0
  17. package/payload/server/public/assets/{data-DUSyrydY.js → data-BZ7v-zug.js} +1 -1
  18. package/payload/server/public/assets/{file-CDJ6dUV3.js → file-CScYkZq5.js} +1 -1
  19. package/payload/server/public/assets/graph-tjXdtwk-.js +50 -0
  20. package/payload/server/public/assets/{house-CNP_bwvT.js → house-CdFRNujU.js} +1 -1
  21. package/payload/server/public/assets/{jsx-runtime-BFFQvkdQ.css → jsx-runtime-Og0q7dXg.css} +1 -1
  22. package/payload/server/public/assets/{public-sHoAccvb.js → public-CrkQJek6.js} +2 -2
  23. package/payload/server/public/assets/{share-2-DBcb9j6E.js → share-2-Ev-D4Lm9.js} +1 -1
  24. package/payload/server/public/assets/{useVoiceRecorder-CtSgpc95.js → useVoiceRecorder-DyDXH7EA.js} +2 -2
  25. package/payload/server/public/assets/{x-CTVJaC_u.js → x-D5W7ddgP.js} +1 -1
  26. package/payload/server/public/data.html +6 -6
  27. package/payload/server/public/graph.html +6 -6
  28. package/payload/server/public/index.html +7 -8
  29. package/payload/server/public/public.html +4 -4
  30. package/payload/server/server.js +830 -258
  31. package/payload/platform/templates/dotfiles/.tmux.conf +0 -1
  32. package/payload/platform/templates/systemd/maxy-ttyd.service +0 -25
  33. package/payload/server/public/assets/admin-WQxJgaus.js +0 -362
  34. package/payload/server/public/assets/admin-kHJ-D0s7.css +0 -1
  35. package/payload/server/public/assets/graph-CWcYp5bE.js +0 -50
  36. /package/payload/server/public/assets/{jsx-runtime-BVKWELH6.js → jsx-runtime-CHqDsKlc.js} +0 -0
@@ -1203,14 +1203,14 @@ var Hono = class _Hono {
1203
1203
  * app.route("/api", app2) // GET /api/user
1204
1204
  * ```
1205
1205
  */
1206
- route(path2, app35) {
1206
+ route(path2, app39) {
1207
1207
  const subApp = this.basePath(path2);
1208
- app35.routes.map((r) => {
1208
+ app39.routes.map((r) => {
1209
1209
  let handler;
1210
- if (app35.errorHandler === errorHandler) {
1210
+ if (app39.errorHandler === errorHandler) {
1211
1211
  handler = r.handler;
1212
1212
  } else {
1213
- handler = async (c, next) => (await compose([], app35.errorHandler)(c, () => r.handler(c, next))).res;
1213
+ handler = async (c, next) => (await compose([], app39.errorHandler)(c, () => r.handler(c, next))).res;
1214
1214
  handler[COMPOSED_HANDLER] = r.handler;
1215
1215
  }
1216
1216
  subApp.#addRoute(r.method, r.path, handler);
@@ -2393,13 +2393,13 @@ function writeFromReadableStreamDefaultReader(reader, writable, currentReadPromi
2393
2393
  }
2394
2394
  }
2395
2395
  }
2396
- function writeFromReadableStream(stream, writable) {
2397
- if (stream.locked) {
2396
+ function writeFromReadableStream(stream2, writable) {
2397
+ if (stream2.locked) {
2398
2398
  throw new TypeError("ReadableStream is locked.");
2399
2399
  } else if (writable.destroyed) {
2400
2400
  return;
2401
2401
  }
2402
- return writeFromReadableStreamDefaultReader(stream.getReader(), writable);
2402
+ return writeFromReadableStreamDefaultReader(stream2.getReader(), writable);
2403
2403
  }
2404
2404
  var buildOutgoingHttpHeaders = (headers) => {
2405
2405
  const res = {};
@@ -2528,7 +2528,7 @@ var responseViaResponseObject = async (res, outgoing, options = {}) => {
2528
2528
  });
2529
2529
  if (!chunk) {
2530
2530
  if (i === 1) {
2531
- await new Promise((resolve28) => setTimeout(resolve28));
2531
+ await new Promise((resolve29) => setTimeout(resolve29));
2532
2532
  maxReadCount = 3;
2533
2533
  continue;
2534
2534
  }
@@ -2761,24 +2761,24 @@ var pr54206Applied = () => {
2761
2761
  return major >= 23 || major === 22 && minor >= 7 || major === 20 && minor >= 18;
2762
2762
  };
2763
2763
  var useReadableToWeb = pr54206Applied();
2764
- var createStreamBody = (stream) => {
2764
+ var createStreamBody = (stream2) => {
2765
2765
  if (useReadableToWeb) {
2766
- return Readable2.toWeb(stream);
2766
+ return Readable2.toWeb(stream2);
2767
2767
  }
2768
2768
  const body = new ReadableStream({
2769
2769
  start(controller) {
2770
- stream.on("data", (chunk) => {
2770
+ stream2.on("data", (chunk) => {
2771
2771
  controller.enqueue(chunk);
2772
2772
  });
2773
- stream.on("error", (err) => {
2773
+ stream2.on("error", (err) => {
2774
2774
  controller.error(err);
2775
2775
  });
2776
- stream.on("end", () => {
2776
+ stream2.on("end", () => {
2777
2777
  controller.close();
2778
2778
  });
2779
2779
  },
2780
2780
  cancel() {
2781
- stream.destroy();
2781
+ stream2.destroy();
2782
2782
  }
2783
2783
  });
2784
2784
  return body;
@@ -2883,10 +2883,10 @@ var serveStatic = (options = { root: "" }) => {
2883
2883
  end = size - 1;
2884
2884
  }
2885
2885
  const chunksize = end - start + 1;
2886
- const stream = createReadStream(path2, { start, end });
2886
+ const stream2 = createReadStream(path2, { start, end });
2887
2887
  c.header("Content-Length", chunksize.toString());
2888
2888
  c.header("Content-Range", `bytes ${start}-${end}/${stats.size}`);
2889
- result = c.body(createStreamBody(stream), 206);
2889
+ result = c.body(createStreamBody(stream2), 206);
2890
2890
  }
2891
2891
  await options.onFound?.(path2, c);
2892
2892
  return result;
@@ -2894,9 +2894,9 @@ var serveStatic = (options = { root: "" }) => {
2894
2894
  };
2895
2895
 
2896
2896
  // server/index.ts
2897
- import { readFileSync as readFileSync25, existsSync as existsSync23, watchFile } from "fs";
2898
- import { resolve as resolve27, join as join12, basename as basename7 } from "path";
2899
- import { homedir as homedir4 } from "os";
2897
+ import { readFileSync as readFileSync26, existsSync as existsSync25, watchFile } from "fs";
2898
+ import { resolve as resolve28, join as join13, basename as basename7 } from "path";
2899
+ import { homedir as homedir5 } from "os";
2900
2900
 
2901
2901
  // app/lib/agent-slug-pattern.ts
2902
2902
  var AGENT_SLUG_PATTERN = /^\/([a-z][a-z0-9-]{2,49})$/;
@@ -3992,8 +3992,8 @@ async function persistMessage(conversationId, role, content, accountId, tokens,
3992
3992
  const prev = persistMessageLocks.get(conversationId);
3993
3993
  const waited = prev !== void 0;
3994
3994
  let release;
3995
- const mine = new Promise((resolve28) => {
3996
- release = resolve28;
3995
+ const mine = new Promise((resolve29) => {
3996
+ release = resolve29;
3997
3997
  });
3998
3998
  const chained = (prev ?? Promise.resolve()).then(() => mine);
3999
3999
  persistMessageLocks.set(conversationId, chained);
@@ -4252,7 +4252,7 @@ ${userContent}`;
4252
4252
  "dontAsk",
4253
4253
  prompt
4254
4254
  ];
4255
- return new Promise((resolve28) => {
4255
+ return new Promise((resolve29) => {
4256
4256
  let stdout = "";
4257
4257
  let stderr = "";
4258
4258
  const spawnFn = _spawnOverride ?? spawn;
@@ -4270,35 +4270,35 @@ ${userContent}`;
4270
4270
  const timer = setTimeout(() => {
4271
4271
  proc.kill("SIGTERM");
4272
4272
  console.error("[persist] autoLabel: haiku subprocess timed out");
4273
- resolve28(null);
4273
+ resolve29(null);
4274
4274
  }, SESSION_LABEL_TIMEOUT_MS);
4275
4275
  proc.on("error", (err) => {
4276
4276
  clearTimeout(timer);
4277
4277
  console.error(`[persist] autoLabel: subprocess error \u2014 ${err.message}`);
4278
- resolve28(null);
4278
+ resolve29(null);
4279
4279
  });
4280
4280
  proc.on("close", (code) => {
4281
4281
  clearTimeout(timer);
4282
4282
  if (code !== 0) {
4283
4283
  console.error(`[persist] autoLabel: subprocess exited code=${code}${stderr ? ` stderr=${stderr.trim().slice(0, 200)}` : ""}`);
4284
- resolve28(null);
4284
+ resolve29(null);
4285
4285
  return;
4286
4286
  }
4287
4287
  const text = stdout.trim();
4288
4288
  if (!text) {
4289
4289
  console.error("[persist] autoLabel: haiku returned empty response");
4290
- resolve28(null);
4290
+ resolve29(null);
4291
4291
  return;
4292
4292
  }
4293
4293
  if (text === "SKIP") {
4294
4294
  console.error("[persist] autoLabel: haiku returned SKIP \u2014 messages too vague");
4295
- resolve28(null);
4295
+ resolve29(null);
4296
4296
  return;
4297
4297
  }
4298
4298
  const words = text.split(/\s+/).slice(0, SESSION_LABEL_MAX_WORDS);
4299
4299
  const label = words.join(" ");
4300
4300
  console.error(`[persist] autoLabel: haiku response="${label}"`);
4301
- resolve28(label);
4301
+ resolve29(label);
4302
4302
  });
4303
4303
  });
4304
4304
  }
@@ -5909,9 +5909,9 @@ function agentLogStream(name, accountDir, conversationId) {
5909
5909
  mkdirSync4(logDir, { recursive: true });
5910
5910
  purgeOldLogs(logDir, `${name}-`);
5911
5911
  const logPath2 = resolve5(logDir, `${name}-${conversationId}.log`);
5912
- const stream = createWriteStream(logPath2, { flags: "a" });
5913
- registerStreamLog(stream, { path: logPath2, conversationId, name });
5914
- return stream;
5912
+ const stream2 = createWriteStream(logPath2, { flags: "a" });
5913
+ registerStreamLog(stream2, { path: logPath2, conversationId, name });
5914
+ return stream2;
5915
5915
  }
5916
5916
  function preConversationLogStream(name, accountDir) {
5917
5917
  const logDir = resolve5(accountDir, "logs");
@@ -5919,15 +5919,15 @@ function preConversationLogStream(name, accountDir) {
5919
5919
  const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
5920
5920
  purgeOldLogs(logDir, `preconversation-${name}-`);
5921
5921
  const logPath2 = resolve5(logDir, `preconversation-${name}-${date}.log`);
5922
- const stream = createWriteStream(logPath2, { flags: "a" });
5923
- registerStreamLog(stream, { path: logPath2, conversationId: null, name: `preconversation-${name}` });
5924
- return stream;
5922
+ const stream2 = createWriteStream(logPath2, { flags: "a" });
5923
+ registerStreamLog(stream2, { path: logPath2, conversationId: null, name: `preconversation-${name}` });
5924
+ return stream2;
5925
5925
  }
5926
5926
  var openStreamLogs = /* @__PURE__ */ new Map();
5927
- function registerStreamLog(stream, entry) {
5928
- openStreamLogs.set(stream, entry);
5929
- stream.once("close", () => {
5930
- openStreamLogs.delete(stream);
5927
+ function registerStreamLog(stream2, entry) {
5928
+ openStreamLogs.set(stream2, entry);
5929
+ stream2.once("close", () => {
5930
+ openStreamLogs.delete(stream2);
5931
5931
  });
5932
5932
  }
5933
5933
  function sigtermFlushStreamLogs(reason, source) {
@@ -7777,7 +7777,7 @@ async function fetchMemoryContext(accountId, query, sessionKey, options) {
7777
7777
  return null;
7778
7778
  }
7779
7779
  const startMs = Date.now();
7780
- return new Promise((resolve28) => {
7780
+ return new Promise((resolve29) => {
7781
7781
  const proc = spawn2(process.execPath, [serverPath], {
7782
7782
  env: {
7783
7783
  ...process.env,
@@ -7806,7 +7806,7 @@ async function fetchMemoryContext(accountId, query, sessionKey, options) {
7806
7806
  } else {
7807
7807
  console.error(`[fetchMemoryContext] failed: ${reason} (${elapsed}ms)${stderrBuf ? ` stderr: ${stderrBuf.slice(0, 500)}` : ""}`);
7808
7808
  }
7809
- resolve28(value);
7809
+ resolve29(value);
7810
7810
  };
7811
7811
  proc.stdout.on("data", (chunk) => {
7812
7812
  buffer += chunk.toString();
@@ -9882,7 +9882,7 @@ User messages are prefixed with the sender's name in brackets. Address participa
9882
9882
  try {
9883
9883
  while (turnCount < MAX_TOOL_TURNS) {
9884
9884
  turnCount++;
9885
- const stream = client.messages.stream({
9885
+ const stream2 = client.messages.stream({
9886
9886
  model: publicModel,
9887
9887
  max_tokens: 1024,
9888
9888
  system,
@@ -9924,13 +9924,13 @@ User messages are prefixed with the sender's name in brackets. Address participa
9924
9924
  }
9925
9925
  }]
9926
9926
  });
9927
- for await (const event of stream) {
9927
+ for await (const event of stream2) {
9928
9928
  if (event.type === "content_block_delta" && event.delta.type === "text_delta") {
9929
9929
  fullText += event.delta.text;
9930
9930
  yield { type: "text", content: event.delta.text };
9931
9931
  }
9932
9932
  }
9933
- const finalMessage = await stream.finalMessage();
9933
+ const finalMessage = await stream2.finalMessage();
9934
9934
  if (finalMessage.stop_reason !== "tool_use") break;
9935
9935
  const toolUseBlocks = [];
9936
9936
  for (const block of finalMessage.content) {
@@ -12536,7 +12536,7 @@ var credsSaveQueue = Promise.resolve();
12536
12536
  async function drainCredsSaveQueue(timeoutMs = 5e3) {
12537
12537
  console.error(`${TAG5} draining credential save queue\u2026`);
12538
12538
  const timer = new Promise(
12539
- (resolve28) => setTimeout(() => resolve28("timeout"), timeoutMs)
12539
+ (resolve29) => setTimeout(() => resolve29("timeout"), timeoutMs)
12540
12540
  );
12541
12541
  const result = await Promise.race([
12542
12542
  credsSaveQueue.then(() => "drained"),
@@ -12664,11 +12664,11 @@ async function createWaSocket(opts) {
12664
12664
  return sock;
12665
12665
  }
12666
12666
  async function waitForConnection(sock) {
12667
- return new Promise((resolve28, reject) => {
12667
+ return new Promise((resolve29, reject) => {
12668
12668
  const handler = (update) => {
12669
12669
  if (update.connection === "open") {
12670
12670
  sock.ev.off("connection.update", handler);
12671
- resolve28();
12671
+ resolve29();
12672
12672
  }
12673
12673
  if (update.connection === "close") {
12674
12674
  sock.ev.off("connection.update", handler);
@@ -12782,14 +12782,14 @@ ${inspected}`;
12782
12782
  return inspect2(err, INSPECT_OPTS2);
12783
12783
  }
12784
12784
  function withTimeout(label, promise, timeoutMs) {
12785
- return new Promise((resolve28, reject) => {
12785
+ return new Promise((resolve29, reject) => {
12786
12786
  const timer = setTimeout(() => {
12787
12787
  reject(new Error(`${label} timed out after ${timeoutMs}ms`));
12788
12788
  }, timeoutMs);
12789
12789
  promise.then(
12790
12790
  (value) => {
12791
12791
  clearTimeout(timer);
12792
- resolve28(value);
12792
+ resolve29(value);
12793
12793
  },
12794
12794
  (err) => {
12795
12795
  clearTimeout(timer);
@@ -13334,9 +13334,9 @@ function getDownloadableContent(message) {
13334
13334
  if (msg.stickerMessage) return { downloadable: msg.stickerMessage, mediaType: "sticker" };
13335
13335
  return void 0;
13336
13336
  }
13337
- async function streamToBuffer(stream) {
13337
+ async function streamToBuffer(stream2) {
13338
13338
  const chunks = [];
13339
- for await (const chunk of stream) {
13339
+ for await (const chunk of stream2) {
13340
13340
  chunks.push(Buffer.from(chunk));
13341
13341
  }
13342
13342
  return Buffer.concat(chunks);
@@ -13367,8 +13367,8 @@ async function downloadInboundMedia(msg, sock, opts) {
13367
13367
  const downloadable = getDownloadableContent(content);
13368
13368
  if (downloadable) {
13369
13369
  try {
13370
- const stream = await downloadContentFromMessage(downloadable.downloadable, downloadable.mediaType);
13371
- buffer = await streamToBuffer(stream);
13370
+ const stream2 = await downloadContentFromMessage(downloadable.downloadable, downloadable.mediaType);
13371
+ buffer = await streamToBuffer(stream2);
13372
13372
  } catch (fallbackErr) {
13373
13373
  console.error(`${TAG9} direct download fallback failed: ${String(fallbackErr)}`);
13374
13374
  }
@@ -13999,15 +13999,15 @@ async function connectWithReconnect(conn) {
13999
13999
  );
14000
14000
  }
14001
14001
  if (decision.reason === "short-lived" || cycleError) {
14002
- const delay = computeBackoff(Math.max(1, decision.nextAttempts));
14002
+ const delay2 = computeBackoff(Math.max(1, decision.nextAttempts));
14003
14003
  console.error(
14004
- `${TAG13} reconnecting account=${conn.accountId} in ${delay}ms (attempt ${decision.nextAttempts}/${maxAttempts})`
14004
+ `${TAG13} reconnecting account=${conn.accountId} in ${delay2}ms (attempt ${decision.nextAttempts}/${maxAttempts})`
14005
14005
  );
14006
- await new Promise((resolve28) => {
14007
- const timer = setTimeout(resolve28, delay);
14006
+ await new Promise((resolve29) => {
14007
+ const timer = setTimeout(resolve29, delay2);
14008
14008
  conn.abortController.signal.addEventListener("abort", () => {
14009
14009
  clearTimeout(timer);
14010
- resolve28();
14010
+ resolve29();
14011
14011
  }, { once: true });
14012
14012
  });
14013
14013
  }
@@ -14015,16 +14015,16 @@ async function connectWithReconnect(conn) {
14015
14015
  }
14016
14016
  }
14017
14017
  function waitForDisconnectEvent(conn) {
14018
- return new Promise((resolve28) => {
14018
+ return new Promise((resolve29) => {
14019
14019
  if (!conn.sock) {
14020
- resolve28();
14020
+ resolve29();
14021
14021
  return;
14022
14022
  }
14023
14023
  const sock = conn.sock;
14024
14024
  const handler = (update) => {
14025
14025
  if (update.connection === "close") {
14026
14026
  sock.ev.off("connection.update", handler);
14027
- resolve28();
14027
+ resolve29();
14028
14028
  }
14029
14029
  };
14030
14030
  sock.ev.on("connection.update", handler);
@@ -14241,8 +14241,8 @@ async function handleInboundMessage(conn, msg) {
14241
14241
  const conversationKey = isGroup ? remoteJid : senderPhone;
14242
14242
  const debounceKey = `${conn.accountId}:${conversationKey}:${senderPhone}`;
14243
14243
  let resolvePending;
14244
- const sttPending = new Promise((resolve28) => {
14245
- resolvePending = resolve28;
14244
+ const sttPending = new Promise((resolve29) => {
14245
+ resolvePending = resolve29;
14246
14246
  });
14247
14247
  if (conn.debouncer) conn.debouncer.registerPending(debounceKey, sttPending);
14248
14248
  try {
@@ -14355,20 +14355,20 @@ async function probeApiKey() {
14355
14355
  return result.status;
14356
14356
  }
14357
14357
  function checkPort(port2, timeoutMs = 500) {
14358
- return new Promise((resolve28) => {
14358
+ return new Promise((resolve29) => {
14359
14359
  const socket = createConnection2(port2, "127.0.0.1");
14360
14360
  socket.setTimeout(timeoutMs);
14361
14361
  socket.once("connect", () => {
14362
14362
  socket.destroy();
14363
- resolve28(true);
14363
+ resolve29(true);
14364
14364
  });
14365
14365
  socket.once("error", () => {
14366
14366
  socket.destroy();
14367
- resolve28(false);
14367
+ resolve29(false);
14368
14368
  });
14369
14369
  socket.once("timeout", () => {
14370
14370
  socket.destroy();
14371
- resolve28(false);
14371
+ resolve29(false);
14372
14372
  });
14373
14373
  });
14374
14374
  }
@@ -15338,7 +15338,7 @@ app3.post("/", async (c) => {
15338
15338
  `[chat-route] session=${session_key.slice(0, 8)} gateway=discard reason=${gatewayResult.screening.reason}`
15339
15339
  );
15340
15340
  const encoder2 = new TextEncoder();
15341
- const stream = new ReadableStream({
15341
+ const stream2 = new ReadableStream({
15342
15342
  start(controller) {
15343
15343
  controller.enqueue(
15344
15344
  encoder2.encode(`data: ${JSON.stringify({ text: "I'm sorry, I can't help with that request." })}
@@ -15349,7 +15349,7 @@ app3.post("/", async (c) => {
15349
15349
  controller.close();
15350
15350
  }
15351
15351
  });
15352
- return new Response(stream, {
15352
+ return new Response(stream2, {
15353
15353
  headers: { "Content-Type": "text/event-stream", "Cache-Control": "no-cache", Connection: "keep-alive" }
15354
15354
  });
15355
15355
  }
@@ -16518,12 +16518,12 @@ async function loginConnectionLoop(accountId, login) {
16518
16518
  return;
16519
16519
  }
16520
16520
  attempt++;
16521
- const delay = LOGIN_RECONNECT_DELAYS[attempt - 1] ?? 8e3;
16521
+ const delay2 = LOGIN_RECONNECT_DELAYS[attempt - 1] ?? 8e3;
16522
16522
  console.error(
16523
- `${TAG17} status=${classification.statusCode ?? "unknown"} restart required \u2014 reconnecting with saved creds (attempt ${attempt}/${LOGIN_MAX_RECONNECTS}) delay=${delay}ms`
16523
+ `${TAG17} status=${classification.statusCode ?? "unknown"} restart required \u2014 reconnecting with saved creds (attempt ${attempt}/${LOGIN_MAX_RECONNECTS}) delay=${delay2}ms`
16524
16524
  );
16525
16525
  closeSocket(current.sock);
16526
- await new Promise((r) => setTimeout(r, delay));
16526
+ await new Promise((r) => setTimeout(r, delay2));
16527
16527
  const afterDelay = activeLogins.get(accountId);
16528
16528
  if (afterDelay?.id !== login.id) return;
16529
16529
  try {
@@ -16570,8 +16570,8 @@ async function startLogin(opts) {
16570
16570
  resetActiveLogin(accountId);
16571
16571
  let resolveQr = null;
16572
16572
  let rejectQr = null;
16573
- const qrPromise = new Promise((resolve28, reject) => {
16574
- resolveQr = resolve28;
16573
+ const qrPromise = new Promise((resolve29, reject) => {
16574
+ resolveQr = resolve29;
16575
16575
  rejectQr = reject;
16576
16576
  });
16577
16577
  const qrTimer = setTimeout(
@@ -17721,8 +17721,8 @@ function startScriptStreamTailer(opts) {
17721
17721
  buffer = "";
17722
17722
  }
17723
17723
  await new Promise((res, rej) => {
17724
- const stream = createReadStream2(path2, { start: offset, end: size - 1 });
17725
- stream.on("data", (chunk) => {
17724
+ const stream2 = createReadStream2(path2, { start: offset, end: size - 1 });
17725
+ stream2.on("data", (chunk) => {
17726
17726
  buffer += typeof chunk === "string" ? chunk : utf8.write(chunk);
17727
17727
  let idx;
17728
17728
  while ((idx = buffer.indexOf("\n")) !== -1) {
@@ -17731,11 +17731,11 @@ function startScriptStreamTailer(opts) {
17731
17731
  if (line.length > 0) processLine(line);
17732
17732
  }
17733
17733
  });
17734
- stream.on("end", () => {
17734
+ stream2.on("end", () => {
17735
17735
  offset = size;
17736
17736
  res();
17737
17737
  });
17738
- stream.on("error", rej);
17738
+ stream2.on("error", rej);
17739
17739
  });
17740
17740
  } catch (err) {
17741
17741
  if (onError) onError(err instanceof Error ? err : new Error(String(err)));
@@ -18057,7 +18057,7 @@ app11.post("/", requireAdminSession, async (c) => {
18057
18057
  `);
18058
18058
  lifecycleLog.end();
18059
18059
  const encoder2 = new TextEncoder();
18060
- const stream = new ReadableStream({
18060
+ const stream2 = new ReadableStream({
18061
18061
  start(controller) {
18062
18062
  controller.enqueue(encoder2.encode(`data: ${JSON.stringify({ type: "done" })}
18063
18063
 
@@ -18065,7 +18065,7 @@ app11.post("/", requireAdminSession, async (c) => {
18065
18065
  controller.close();
18066
18066
  }
18067
18067
  });
18068
- return new Response(stream, {
18068
+ return new Response(stream2, {
18069
18069
  headers: { "Content-Type": "text/event-stream", "Cache-Control": "no-cache", Connection: "keep-alive" }
18070
18070
  });
18071
18071
  }
@@ -18212,7 +18212,7 @@ var app12 = new Hono2();
18212
18212
  app12.post("/", requireAdminSession, async (c) => {
18213
18213
  const sessionKey = c.var.sessionKey;
18214
18214
  const encoder = new TextEncoder();
18215
- const stream = new ReadableStream({
18215
+ const stream2 = new ReadableStream({
18216
18216
  async start(controller) {
18217
18217
  const iter = compactSession(sessionKey);
18218
18218
  let step = await iter.next();
@@ -18228,7 +18228,7 @@ app12.post("/", requireAdminSession, async (c) => {
18228
18228
  controller.close();
18229
18229
  }
18230
18230
  });
18231
- return new Response(stream, {
18231
+ return new Response(stream2, {
18232
18232
  headers: {
18233
18233
  "Content-Type": "text/event-stream",
18234
18234
  "Cache-Control": "no-cache",
@@ -18951,9 +18951,9 @@ app22.post("/", async (c) => {
18951
18951
  var events_default = app22;
18952
18952
 
18953
18953
  // server/routes/admin/cloudflare.ts
18954
- import { homedir as homedir3 } from "os";
18955
- import { resolve as resolve24 } from "path";
18956
- import { readFileSync as readFileSync24 } from "fs";
18954
+ import { homedir as homedir4 } from "os";
18955
+ import { resolve as resolve25 } from "path";
18956
+ import { readFileSync as readFileSync25 } from "fs";
18957
18957
 
18958
18958
  // app/lib/dns-label.ts
18959
18959
  var VALID_LABEL = /^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$/;
@@ -18991,9 +18991,13 @@ function addAliasDomain(hostname2) {
18991
18991
  writeFileSync14(ALIAS_DOMAINS_PATH, JSON.stringify([...existing], null, 2) + "\n", "utf-8");
18992
18992
  }
18993
18993
 
18994
- // server/routes/admin/cloudflare.ts
18995
- var SETUP_TIMEOUT_MS = 10 * 60 * 1e3;
18996
- var DOMAINS_TIMEOUT_MS = 40 * 1e3;
18994
+ // server/lib/action-runner.ts
18995
+ import { spawn as spawn5 } from "child_process";
18996
+ import { createReadStream as createReadStream3, existsSync as existsSync23, mkdirSync as mkdirSync13, readdirSync as readdirSync7, statSync as statSync10, unlinkSync as unlinkSync5, watch } from "fs";
18997
+ import { homedir as homedir3 } from "os";
18998
+ import { join as join11, resolve as resolve24 } from "path";
18999
+ import { readFileSync as readFileSync24 } from "fs";
19000
+ import { setTimeout as delay } from "timers/promises";
18997
19001
  function loadBrandInfo() {
18998
19002
  const platformRoot2 = process.env.MAXY_PLATFORM_ROOT ?? resolve24(process.cwd(), "..");
18999
19003
  const brandPath = resolve24(platformRoot2, "config", "brand.json");
@@ -19006,6 +19010,333 @@ function loadBrandInfo() {
19006
19010
  return { hostname: "maxy", configDir: ".maxy" };
19007
19011
  }
19008
19012
  }
19013
+ function actionLogDir() {
19014
+ const { configDir: configDir2 } = loadBrandInfo();
19015
+ return join11(homedir3(), configDir2, "logs", "actions");
19016
+ }
19017
+ function actionLogPath(actionId) {
19018
+ return join11(actionLogDir(), `${actionId}.log`);
19019
+ }
19020
+ var WHITELIST = {
19021
+ upgrade: {
19022
+ build: ({ sudoPassword }) => {
19023
+ const { hostname: hostname2 } = loadBrandInfo();
19024
+ const pkg = `@rubytech/create-${hostname2}`;
19025
+ const cmd = [
19026
+ // prime: read pw from stdin, silently cache
19027
+ 'printf %s "$SUDO_PASSWORD"',
19028
+ "|",
19029
+ "sudo -S -v 2>/dev/null",
19030
+ "&&",
19031
+ // run installer — it will re-prompt only if the cache expires
19032
+ `npx -y ${pkg}@latest`
19033
+ ].join(" ");
19034
+ return {
19035
+ argv: ["bash", "-c", cmd],
19036
+ env: sudoPassword ? { SUDO_PASSWORD: sudoPassword } : void 0
19037
+ };
19038
+ }
19039
+ },
19040
+ "cloudflare-setup": {
19041
+ build: ({ positional }) => {
19042
+ if (!positional || positional.length < 3) {
19043
+ throw new Error("cloudflare-setup requires [brand, port, ...hostnames]");
19044
+ }
19045
+ const scriptPath = join11(homedir3(), "setup-tunnel.sh");
19046
+ if (!existsSync23(scriptPath)) {
19047
+ throw new Error(
19048
+ `cloudflare-setup: ~/setup-tunnel.sh not found. Re-run the installer to repair the symlink.`
19049
+ );
19050
+ }
19051
+ return { argv: [scriptPath, ...positional] };
19052
+ }
19053
+ }
19054
+ };
19055
+ function isKnownAction(name) {
19056
+ return Object.prototype.hasOwnProperty.call(WHITELIST, name);
19057
+ }
19058
+ function generateActionId(name) {
19059
+ const ts = Date.now().toString(36);
19060
+ const nonce = Math.floor(Math.random() * 4294967295).toString(16).padStart(8, "0");
19061
+ return `${name}-${ts}-${nonce}`;
19062
+ }
19063
+ function cleanupStaleLogs() {
19064
+ const dir = actionLogDir();
19065
+ if (!existsSync23(dir)) return;
19066
+ const cutoff = Date.now() - 7 * 24 * 60 * 60 * 1e3;
19067
+ for (const entry of readdirSync7(dir)) {
19068
+ const full = join11(dir, entry);
19069
+ try {
19070
+ const st = statSync10(full);
19071
+ if (st.isFile() && st.mtimeMs < cutoff) unlinkSync5(full);
19072
+ } catch {
19073
+ }
19074
+ }
19075
+ }
19076
+ async function isActionActive(actionName) {
19077
+ return new Promise((resolveP) => {
19078
+ const proc = spawn5(
19079
+ "systemctl",
19080
+ ["--user", "list-units", "--no-legend", "--state=active", `maxy-action-${actionName}-*`],
19081
+ { stdio: ["ignore", "pipe", "pipe"] }
19082
+ );
19083
+ let out = "";
19084
+ proc.stdout.on("data", (chunk) => {
19085
+ out += chunk.toString();
19086
+ });
19087
+ proc.on("close", () => resolveP(out.trim().length > 0));
19088
+ proc.on("error", () => resolveP(false));
19089
+ });
19090
+ }
19091
+ async function launchAction(name, args) {
19092
+ const actionId = generateActionId(name);
19093
+ const dir = actionLogDir();
19094
+ mkdirSync13(dir, { recursive: true });
19095
+ cleanupStaleLogs();
19096
+ const logPath2 = actionLogPath(actionId);
19097
+ const unit = `maxy-action-${actionId}`;
19098
+ const built = WHITELIST[name].build(args);
19099
+ const systemdArgs = [
19100
+ "--user",
19101
+ `--unit=${unit}`,
19102
+ "--collect",
19103
+ "--property=RemainAfterExit=false",
19104
+ `--property=StandardOutput=append:${logPath2}`,
19105
+ `--property=StandardError=append:${logPath2}`,
19106
+ `--description=Maxy action ${name} (${actionId})`
19107
+ ];
19108
+ if (built.env) {
19109
+ for (const [k, v] of Object.entries(built.env)) {
19110
+ systemdArgs.push(`--setenv=${k}=${v}`);
19111
+ }
19112
+ }
19113
+ systemdArgs.push("--", ...built.argv);
19114
+ await new Promise((resolveP, rejectP) => {
19115
+ const proc = spawn5("systemd-run", systemdArgs, { stdio: ["ignore", "pipe", "pipe"] });
19116
+ let stderr = "";
19117
+ proc.stderr.on("data", (chunk) => {
19118
+ stderr += chunk.toString();
19119
+ });
19120
+ proc.on("close", (code) => {
19121
+ if (code === 0) resolveP();
19122
+ else rejectP(new Error(`systemd-run exited ${code}: ${stderr.trim().slice(0, 400)}`));
19123
+ });
19124
+ proc.on("error", rejectP);
19125
+ });
19126
+ console.log(`[action-runner] launched action=${name} id=${actionId} unit=${unit} log=${logPath2}`);
19127
+ return { actionId, unit, logPath: logPath2 };
19128
+ }
19129
+ async function getUnitStatus(unit) {
19130
+ return new Promise((resolveP) => {
19131
+ const proc = spawn5(
19132
+ "systemctl",
19133
+ ["--user", "show", unit, "--property=ActiveState,SubState,MainPID,ExecMainStatus,ExecMainCode"],
19134
+ { stdio: ["ignore", "pipe", "pipe"] }
19135
+ );
19136
+ let out = "";
19137
+ proc.stdout.on("data", (chunk) => {
19138
+ out += chunk.toString();
19139
+ });
19140
+ proc.on("close", () => {
19141
+ const kv = {};
19142
+ for (const line of out.split("\n")) {
19143
+ const eq = line.indexOf("=");
19144
+ if (eq > 0) kv[line.slice(0, eq)] = line.slice(eq + 1);
19145
+ }
19146
+ if (!kv.ActiveState) return resolveP(null);
19147
+ resolveP({
19148
+ activeState: kv.ActiveState,
19149
+ subState: kv.SubState ?? "",
19150
+ mainPid: kv.MainPID ? Number(kv.MainPID) || null : null,
19151
+ execMainStatus: kv.ExecMainStatus ? Number(kv.ExecMainStatus) : null,
19152
+ execMainSignal: null
19153
+ // systemd reports signal in SIGNAL (SIGTERM etc.), deferred — code is enough for our contract
19154
+ });
19155
+ });
19156
+ proc.on("error", () => resolveP(null));
19157
+ });
19158
+ }
19159
+ var HEARTBEAT_INTERVAL_MS = 5e3;
19160
+ var PHASE_BRACKETED = /\[(\d+)\/(\d+)\]/;
19161
+ var PHASE_STREAM_LOG = /step=([a-z0-9-]+)/;
19162
+ function extractPhase(text) {
19163
+ const m1 = text.match(PHASE_BRACKETED);
19164
+ if (m1) return m1[0];
19165
+ const m2 = text.match(PHASE_STREAM_LOG);
19166
+ if (m2) return m2[1];
19167
+ return null;
19168
+ }
19169
+ async function* streamActionEvents(opts) {
19170
+ const { actionId, unit, signal } = opts;
19171
+ const logPath2 = actionLogPath(actionId);
19172
+ const startedAt = Date.now();
19173
+ let lastLineAt = startedAt;
19174
+ let lastPhase = null;
19175
+ let byteOffset = opts.fromByteOffset ?? 0;
19176
+ if (existsSync23(logPath2)) {
19177
+ const snapshot = statSync10(logPath2).size;
19178
+ if (byteOffset < snapshot) {
19179
+ for await (const ev of readLogLines(logPath2, byteOffset, snapshot)) {
19180
+ const phase = extractPhase(ev.text);
19181
+ if (phase) lastPhase = phase;
19182
+ lastLineAt = ev.ts;
19183
+ byteOffset = ev.byteOffset;
19184
+ yield ev;
19185
+ if (signal.aborted) return;
19186
+ }
19187
+ }
19188
+ }
19189
+ let watching = true;
19190
+ let hbTimer = null;
19191
+ const queue = [];
19192
+ let resolveNext = null;
19193
+ function push(ev) {
19194
+ queue.push(ev);
19195
+ if (resolveNext) {
19196
+ const r = resolveNext;
19197
+ resolveNext = null;
19198
+ r();
19199
+ }
19200
+ }
19201
+ const watcher = existsSync23(logPath2) ? watch(logPath2, async () => {
19202
+ if (!watching) return;
19203
+ try {
19204
+ const size = statSync10(logPath2).size;
19205
+ if (size <= byteOffset) return;
19206
+ for await (const ev of readLogLines(logPath2, byteOffset, size)) {
19207
+ const phase = extractPhase(ev.text);
19208
+ if (phase) lastPhase = phase;
19209
+ lastLineAt = ev.ts;
19210
+ byteOffset = ev.byteOffset;
19211
+ push(ev);
19212
+ }
19213
+ } catch (err) {
19214
+ console.error(`[action-runner] watcher error action=${actionId}: ${err}`);
19215
+ }
19216
+ }) : null;
19217
+ hbTimer = setInterval(async () => {
19218
+ const status = await getUnitStatus(unit);
19219
+ const now = Date.now();
19220
+ push({
19221
+ type: "heartbeat",
19222
+ ts: now,
19223
+ elapsed_since_last_line_ms: now - lastLineAt,
19224
+ systemd_state: status ? status.activeState : "unknown",
19225
+ pid: status ? status.mainPid : null,
19226
+ last_phase: lastPhase
19227
+ });
19228
+ if (status && status.activeState !== "active" && status.activeState !== "activating") {
19229
+ const finalStatus = await getUnitStatus(unit);
19230
+ push({
19231
+ type: "exit",
19232
+ ts: Date.now(),
19233
+ code: finalStatus?.execMainStatus ?? null,
19234
+ signal: null,
19235
+ duration_ms: Date.now() - startedAt
19236
+ });
19237
+ watching = false;
19238
+ }
19239
+ }, HEARTBEAT_INTERVAL_MS);
19240
+ try {
19241
+ while (watching && !signal.aborted) {
19242
+ if (queue.length === 0) {
19243
+ await new Promise((r) => {
19244
+ resolveNext = r;
19245
+ });
19246
+ }
19247
+ while (queue.length > 0) {
19248
+ const ev = queue.shift();
19249
+ yield ev;
19250
+ if (ev.type === "exit") {
19251
+ watching = false;
19252
+ break;
19253
+ }
19254
+ }
19255
+ }
19256
+ } finally {
19257
+ if (hbTimer) clearInterval(hbTimer);
19258
+ watcher?.close();
19259
+ }
19260
+ }
19261
+ async function* readLogLines(path2, fromOffset, toOffset) {
19262
+ const stream2 = createReadStream3(path2, { start: fromOffset, end: toOffset - 1, encoding: "utf-8" });
19263
+ let buffer = "";
19264
+ let offset = fromOffset;
19265
+ for await (const chunk of stream2) {
19266
+ buffer += chunk;
19267
+ let nl;
19268
+ while ((nl = buffer.indexOf("\n")) !== -1) {
19269
+ const text = buffer.slice(0, nl);
19270
+ offset += Buffer.byteLength(text, "utf-8") + 1;
19271
+ buffer = buffer.slice(nl + 1);
19272
+ yield { type: "line", ts: Date.now(), stream: "stdout", text, byteOffset: offset };
19273
+ }
19274
+ }
19275
+ }
19276
+ var RATE_LIMIT_WINDOW_MS = 6e4;
19277
+ var RATE_LIMIT_MAX_ATTEMPTS = 5;
19278
+ var rateLimitBuckets = /* @__PURE__ */ new Map();
19279
+ function rateLimitCheck(ip) {
19280
+ const now = Date.now();
19281
+ const bucket = rateLimitBuckets.get(ip);
19282
+ if (!bucket || now - bucket.windowStart > RATE_LIMIT_WINDOW_MS) {
19283
+ rateLimitBuckets.set(ip, { count: 1, windowStart: now });
19284
+ return "ok";
19285
+ }
19286
+ bucket.count += 1;
19287
+ if (bucket.count > RATE_LIMIT_MAX_ATTEMPTS) return "limited";
19288
+ return "ok";
19289
+ }
19290
+ async function primeSudo(password, clientIp) {
19291
+ if (!password) return "invalid";
19292
+ if (rateLimitCheck(clientIp) === "limited") return "limited";
19293
+ return new Promise((resolveP) => {
19294
+ const proc = spawn5("sudo", ["-S", "-v"], { stdio: ["pipe", "ignore", "pipe"] });
19295
+ let stderr = "";
19296
+ proc.stderr.on("data", (chunk) => {
19297
+ stderr += chunk.toString();
19298
+ });
19299
+ proc.on("close", (code) => {
19300
+ if (code === 0) return resolveP("ok");
19301
+ if (/incorrect password|authentication failure/i.test(stderr)) return resolveP("invalid");
19302
+ console.error(`[action-runner] sudo-prime failed code=${code} stderr=${stderr.slice(0, 200)}`);
19303
+ resolveP("error");
19304
+ });
19305
+ proc.on("error", (err) => {
19306
+ console.error(`[action-runner] sudo-prime spawn error: ${err}`);
19307
+ resolveP("error");
19308
+ });
19309
+ proc.stdin.write(`${password}
19310
+ `);
19311
+ proc.stdin.end();
19312
+ });
19313
+ }
19314
+ async function waitForExit(unit, timeoutMs = 10 * 60 * 1e3) {
19315
+ const deadline = Date.now() + timeoutMs;
19316
+ while (Date.now() < deadline) {
19317
+ const status = await getUnitStatus(unit);
19318
+ if (!status) return null;
19319
+ if (status.activeState !== "active" && status.activeState !== "activating") return status;
19320
+ await delay(500);
19321
+ }
19322
+ return null;
19323
+ }
19324
+
19325
+ // server/routes/admin/cloudflare.ts
19326
+ var SETUP_TIMEOUT_MS = 10 * 60 * 1e3;
19327
+ var DOMAINS_TIMEOUT_MS = 40 * 1e3;
19328
+ function loadBrandInfo2() {
19329
+ const platformRoot2 = process.env.MAXY_PLATFORM_ROOT ?? resolve25(process.cwd(), "..");
19330
+ const brandPath = resolve25(platformRoot2, "config", "brand.json");
19331
+ try {
19332
+ const parsed = JSON.parse(readFileSync25(brandPath, "utf-8"));
19333
+ const hostname2 = typeof parsed.hostname === "string" && parsed.hostname ? parsed.hostname : "maxy";
19334
+ const configDir2 = typeof parsed.configDir === "string" && parsed.configDir ? parsed.configDir : ".maxy";
19335
+ return { hostname: hostname2, configDir: configDir2 };
19336
+ } catch {
19337
+ return { hostname: "maxy", configDir: ".maxy" };
19338
+ }
19339
+ }
19009
19340
  function resolvePort() {
19010
19341
  const raw2 = process.env.PORT;
19011
19342
  if (!raw2) throw new Error("PORT env var is not set \u2014 refusing to guess");
@@ -19100,8 +19431,8 @@ app23.get("/domains", requireAdminSession, async (c) => {
19100
19431
  if (!correlationId) return err("session", "No active conversation for session \u2014 refresh chat.");
19101
19432
  streamLogPath = streamLogPathFor(accountId, correlationId).streamLogPath;
19102
19433
  log2(`phase=stream-log-resolved path=${streamLogPath}`);
19103
- const brand = loadBrandInfo();
19104
- const scriptPath = resolve24(homedir3(), "list-cf-domains.sh");
19434
+ const brand = loadBrandInfo2();
19435
+ const scriptPath = resolve25(homedir4(), "list-cf-domains.sh");
19105
19436
  const result = await runFormSpawn({
19106
19437
  scriptPath,
19107
19438
  args: [brand.hostname],
@@ -19186,8 +19517,8 @@ app23.post("/setup", requireAdminSession, async (c) => {
19186
19517
  };
19187
19518
  return c.json(body2, 200);
19188
19519
  }
19189
- function ok(result2) {
19190
- return c.json(result2, 200);
19520
+ function ok(result) {
19521
+ return c.json(result, 200);
19191
19522
  }
19192
19523
  let body;
19193
19524
  try {
@@ -19219,93 +19550,90 @@ app23.post("/setup", requireAdminSession, async (c) => {
19219
19550
  let brand;
19220
19551
  let port2;
19221
19552
  try {
19222
- brand = loadBrandInfo();
19553
+ brand = loadBrandInfo2();
19223
19554
  port2 = resolvePort();
19224
19555
  } catch (e) {
19225
19556
  return err("script", `Server misconfigured: ${e instanceof Error ? e.message : String(e)}`);
19226
19557
  }
19227
19558
  streamLogPath = streamLogPathFor(accountId, correlationId).streamLogPath;
19228
19559
  log2(`phase=stream-log-resolved path=${streamLogPath}`);
19229
- const scriptPath = resolve24(homedir3(), "setup-tunnel.sh");
19230
19560
  const args = [brand.hostname, String(port2), adminFqdn];
19231
19561
  if (publicFqdn) args.push(publicFqdn);
19232
19562
  if (apex) args.push(apex);
19233
- const result = await runFormSpawn({
19234
- scriptPath,
19235
- args,
19236
- streamLogPath,
19237
- correlationId,
19238
- timeoutMs: SETUP_TIMEOUT_MS,
19239
- log: log2,
19240
- logErr,
19241
- broadcast: broadcastToConversation
19242
- });
19243
- const combined = `${result.stdout}${result.stderr ? `
19244
- ---
19245
- ${result.stderr}` : ""}`;
19246
- if (result.code !== 0) {
19247
- const reason = result.timedOut ? `Timed out after ${SETUP_TIMEOUT_MS / 1e3}s` : `Exited with code ${result.code ?? "null"}${result.signal ? ` (signal ${result.signal})` : ""}`;
19248
- return err("script", reason, combined);
19249
- }
19250
- const candidates = [publicFqdn, apex].filter((h) => typeof h === "string" && h.length > 0);
19251
- const aliasesWritten = [];
19252
- for (const host of candidates) {
19253
- if (host === adminFqdn) {
19254
- log2(`phase=alias-domain-write host=${host} result=skipped-admin`);
19255
- continue;
19256
- }
19257
- if (host.startsWith("public.")) {
19258
- log2(`phase=alias-domain-write host=${host} result=skipped-public-prefix`);
19259
- continue;
19563
+ if (await isActionActive("cloudflare-setup")) {
19564
+ return err("request", "Another Cloudflare setup is already running. Wait for it to finish before starting a new one.");
19565
+ }
19566
+ let actionId;
19567
+ let unit;
19568
+ try {
19569
+ const launched = await launchAction("cloudflare-setup", { positional: args });
19570
+ actionId = launched.actionId;
19571
+ unit = launched.unit;
19572
+ } catch (e) {
19573
+ return err("script", `Failed to launch cloudflare-setup action: ${e instanceof Error ? e.message : String(e)}`);
19574
+ }
19575
+ log2(`phase=action-launched id=${actionId} unit=${unit}`);
19576
+ void (async () => {
19577
+ const status = await waitForExit(unit, SETUP_TIMEOUT_MS);
19578
+ if (!status || status.execMainStatus !== 0) {
19579
+ logErr(`phase=post-exit-skipped reason=${status ? `exit=${status.execMainStatus}` : "unit-gone"} id=${actionId}`);
19580
+ return;
19260
19581
  }
19261
- try {
19262
- addAliasDomain(host);
19263
- aliasesWritten.push(host);
19264
- log2(`phase=alias-domain-write host=${host} result=written`);
19265
- } catch (e) {
19266
- return err(
19267
- "script",
19268
- `Tunnel created but alias-domains.json write failed for ${host}: ${e instanceof Error ? e.message : String(e)}`,
19269
- combined
19270
- );
19582
+ const candidates = [publicFqdn, apex].filter((h) => typeof h === "string" && h.length > 0);
19583
+ for (const host of candidates) {
19584
+ if (host === adminFqdn) {
19585
+ log2(`phase=alias-domain-write host=${host} result=skipped-admin`);
19586
+ continue;
19587
+ }
19588
+ if (host.startsWith("public.")) {
19589
+ log2(`phase=alias-domain-write host=${host} result=skipped-public-prefix`);
19590
+ continue;
19591
+ }
19592
+ try {
19593
+ addAliasDomain(host);
19594
+ log2(`phase=alias-domain-write host=${host} result=written`);
19595
+ } catch (e) {
19596
+ logErr(`phase=alias-domain-write host=${host} result=error reason="${e instanceof Error ? e.message : String(e)}"`);
19597
+ }
19271
19598
  }
19272
- }
19599
+ log2(`phase=done action=${actionId}`);
19600
+ })().catch((e) => logErr(`post-exit handler threw: ${e}`));
19273
19601
  const total = Date.now() - started;
19274
- log2(`phase=done total_ms=${total} aliases=${aliasesWritten.length}`);
19602
+ log2(`phase=response-sent action_id=${actionId} total_ms=${total}`);
19275
19603
  writeRouteMilestone(
19276
19604
  streamLogPath,
19277
19605
  "cloudflare-setup",
19278
- `phase=done total_ms=${total} aliases=${aliasesWritten.length}`
19606
+ `phase=action-launched id=${actionId}`
19279
19607
  );
19280
19608
  const success = {
19281
19609
  ok: true,
19282
- output: combined,
19610
+ output: `Action launched \u2014 see live log in the form panel.
19611
+ actionId: ${actionId}`,
19283
19612
  adminFqdn,
19284
19613
  publicFqdn,
19285
19614
  apex,
19286
- aliasesWritten
19615
+ aliasesWritten: [],
19616
+ actionId
19287
19617
  };
19288
- const resp = ok(success);
19289
- log2(`phase=response-sent`);
19290
- return resp;
19618
+ return ok(success);
19291
19619
  });
19292
19620
  var cloudflare_default = app23;
19293
19621
 
19294
19622
  // server/routes/admin/files.ts
19295
- import { createReadStream as createReadStream3 } from "fs";
19623
+ import { createReadStream as createReadStream4 } from "fs";
19296
19624
  import { readdir as readdir2, readFile as readFile4, stat as stat4, mkdir as mkdir3, writeFile as writeFile4, unlink as unlink2 } from "fs/promises";
19297
19625
  import { realpathSync as realpathSync4 } from "fs";
19298
- import { basename as basename6, dirname as dirname10, join as join11, resolve as resolve26, sep as sep2 } from "path";
19626
+ import { basename as basename6, dirname as dirname10, join as join12, resolve as resolve27, sep as sep2 } from "path";
19299
19627
  import { Readable as Readable3 } from "stream";
19300
19628
 
19301
19629
  // app/lib/data-path.ts
19302
19630
  import { realpathSync as realpathSync3 } from "fs";
19303
- import { resolve as resolve25, normalize, sep, relative } from "path";
19304
- var PLATFORM_ROOT9 = process.env.MAXY_PLATFORM_ROOT ?? resolve25(process.cwd(), "../platform");
19305
- var DATA_ROOT = resolve25(PLATFORM_ROOT9, "..", "data");
19631
+ import { resolve as resolve26, normalize, sep, relative } from "path";
19632
+ var PLATFORM_ROOT9 = process.env.MAXY_PLATFORM_ROOT ?? resolve26(process.cwd(), "../platform");
19633
+ var DATA_ROOT = resolve26(PLATFORM_ROOT9, "..", "data");
19306
19634
  function resolveDataPath(raw2) {
19307
19635
  const cleaned = normalize("/" + (raw2 ?? "").replace(/\\/g, "/")).replace(/^\/+/, "");
19308
- const absolute = resolve25(DATA_ROOT, cleaned);
19636
+ const absolute = resolve26(DATA_ROOT, cleaned);
19309
19637
  let dataRootReal;
19310
19638
  try {
19311
19639
  dataRootReal = realpathSync3(DATA_ROOT);
@@ -19653,7 +19981,7 @@ async function cascadeDeleteDocument(params) {
19653
19981
  var UUID_RE3 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
19654
19982
  async function readMeta(absDir, baseName) {
19655
19983
  try {
19656
- const raw2 = await readFile4(join11(absDir, `${baseName}.meta.json`), "utf8");
19984
+ const raw2 = await readFile4(join12(absDir, `${baseName}.meta.json`), "utf8");
19657
19985
  const parsed = JSON.parse(raw2);
19658
19986
  if (typeof parsed?.filename === "string") {
19659
19987
  return { filename: parsed.filename, mimeType: typeof parsed.mimeType === "string" ? parsed.mimeType : void 0 };
@@ -19664,7 +19992,7 @@ async function readMeta(absDir, baseName) {
19664
19992
  }
19665
19993
  async function readAccountNames() {
19666
19994
  const map = /* @__PURE__ */ new Map();
19667
- const accountsDir = resolve26(DATA_ROOT, "accounts");
19995
+ const accountsDir = resolve27(DATA_ROOT, "accounts");
19668
19996
  let names;
19669
19997
  try {
19670
19998
  names = await readdir2(accountsDir);
@@ -19673,7 +20001,7 @@ async function readAccountNames() {
19673
20001
  }
19674
20002
  for (const name of names) {
19675
20003
  if (!UUID_RE3.test(name)) continue;
19676
- const configPath2 = resolve26(accountsDir, name, "account.json");
20004
+ const configPath2 = resolve27(accountsDir, name, "account.json");
19677
20005
  try {
19678
20006
  const raw2 = await readFile4(configPath2, "utf8");
19679
20007
  const parsed = JSON.parse(raw2);
@@ -19691,7 +20019,7 @@ async function readAccountNames() {
19691
20019
  }
19692
20020
  async function enrich(absolute, entry, accountNames) {
19693
20021
  if (entry.kind === "directory" && UUID_RE3.test(entry.name)) {
19694
- const meta = await readMeta(join11(absolute, entry.name), entry.name);
20022
+ const meta = await readMeta(join12(absolute, entry.name), entry.name);
19695
20023
  if (meta?.filename) {
19696
20024
  entry.displayName = meta.filename;
19697
20025
  entry.mimeType = meta.mimeType;
@@ -19750,7 +20078,7 @@ app24.get("/", requireAdminSession, async (c) => {
19750
20078
  continue;
19751
20079
  }
19752
20080
  try {
19753
- const entryPath = join11(absolute, name);
20081
+ const entryPath = join12(absolute, name);
19754
20082
  const s = await stat4(entryPath);
19755
20083
  entries.push({
19756
20084
  name,
@@ -19807,7 +20135,7 @@ app24.get("/download", requireAdminSession, async (c) => {
19807
20135
  }
19808
20136
  const filename = basename6(absolute);
19809
20137
  const mimeType = detectMimeType(absolute);
19810
- const nodeStream = createReadStream3(absolute);
20138
+ const nodeStream = createReadStream4(absolute);
19811
20139
  const webStream = Readable3.toWeb(nodeStream);
19812
20140
  console.error(`[data] file-download path="${relPath}" size=${info.size}`);
19813
20141
  const safeQuotedName = filename.replace(/[\r\n"\\]/g, "_");
@@ -19864,8 +20192,8 @@ app24.post("/upload", requireAdminSession, async (c) => {
19864
20192
  }
19865
20193
  const safeName = basename6(file.name).replace(/[\0/\\]/g, "_");
19866
20194
  const finalName = `${Date.now()}-${safeName}`;
19867
- const destDir = resolve26(DATA_ROOT, "uploads", accountId);
19868
- const destPath = resolve26(destDir, finalName);
20195
+ const destDir = resolve27(DATA_ROOT, "uploads", accountId);
20196
+ const destPath = resolve27(destDir, finalName);
19869
20197
  try {
19870
20198
  await mkdir3(destDir, { recursive: true });
19871
20199
  const dataRootReal = realpathSync4(DATA_ROOT);
@@ -19923,7 +20251,7 @@ app24.delete("/", requireAdminSession, async (c) => {
19923
20251
  }
19924
20252
  const dot = base.lastIndexOf(".");
19925
20253
  const stem = dot === -1 ? base : base.slice(0, dot);
19926
- const sidecarPath = UUID_RE3.test(stem) && base !== `${stem}.meta.json` ? join11(dirname10(absolute), `${stem}.meta.json`) : null;
20254
+ const sidecarPath = UUID_RE3.test(stem) && base !== `${stem}.meta.json` ? join12(dirname10(absolute), `${stem}.meta.json`) : null;
19927
20255
  await unlink2(absolute);
19928
20256
  if (sidecarPath) {
19929
20257
  try {
@@ -20200,7 +20528,8 @@ async function handleDefault(c, accountId) {
20200
20528
  );
20201
20529
  }
20202
20530
  const warnedClasses = /* @__PURE__ */ new Set();
20203
- const nodes = result.rawNodes.map((n) => pruneNode(n, warnedClasses));
20531
+ const conversationWarnings = { loggedMissingMessageCount: false };
20532
+ const nodes = result.rawNodes.map((n) => pruneNode(n, warnedClasses, conversationWarnings));
20204
20533
  const edges = result.rawEdges.filter((e) => e && e.id != null);
20205
20534
  const trashedSuffix = includeTrashed ? " includeTrashed=1" : "";
20206
20535
  console.error(
@@ -20244,7 +20573,8 @@ async function handleNeighbourhood(c, accountId) {
20244
20573
  return c.json({ error: "Node not found" }, 404);
20245
20574
  }
20246
20575
  const warnedClasses = /* @__PURE__ */ new Set();
20247
- const nodes = rawNodes.map((n) => pruneNode(n, warnedClasses));
20576
+ const conversationWarnings = { loggedMissingMessageCount: false };
20577
+ const nodes = rawNodes.map((n) => pruneNode(n, warnedClasses, conversationWarnings));
20248
20578
  const edges = rawEdges.filter((e) => e && e.id != null);
20249
20579
  console.error(
20250
20580
  `[graph-page] load mode=neighbourhood account=${accountId} elementId=${elementId} nodes=${nodes.length} edges=${edges.length} ms=${elapsed}`
@@ -20270,6 +20600,11 @@ var DEFAULT_COUNT_CYPHER = `
20270
20600
  AND n.deletedAt IS NULL
20271
20601
  RETURN count(n) AS matched
20272
20602
  `;
20603
+ var CONVERSATION_PROPS_PROJECTION = `CASE
20604
+ WHEN 'Conversation' IN labels(x)
20605
+ THEN properties(x) + {messageCount: size([(x)<-[:PART_OF]-(m:Message) WHERE NOT m:Trashed AND m.deletedAt IS NULL | m])}
20606
+ ELSE properties(x)
20607
+ END`;
20273
20608
  var DEFAULT_FETCH_CYPHER = `
20274
20609
  MATCH (n)
20275
20610
  WHERE n.accountId = $accountId
@@ -20288,7 +20623,7 @@ var DEFAULT_FETCH_CYPHER = `
20288
20623
  type: type(r)
20289
20624
  } END) AS rawEdges
20290
20625
  RETURN
20291
- [x IN nodes | {id: elementId(x), labels: labels(x), properties: properties(x)}] AS nodes,
20626
+ [x IN nodes | {id: elementId(x), labels: labels(x), properties: ${CONVERSATION_PROPS_PROJECTION}}] AS nodes,
20292
20627
  [e IN rawEdges WHERE e IS NOT NULL] AS edges
20293
20628
  `;
20294
20629
  var DEFAULT_COUNT_CYPHER_INCLUDE_TRASHED = `
@@ -20315,7 +20650,7 @@ var DEFAULT_FETCH_CYPHER_INCLUDE_TRASHED = `
20315
20650
  type: type(r)
20316
20651
  } END) AS rawEdges
20317
20652
  RETURN
20318
- [x IN nodes | {id: elementId(x), labels: labels(x), properties: properties(x)}] AS nodes,
20653
+ [x IN nodes | {id: elementId(x), labels: labels(x), properties: ${CONVERSATION_PROPS_PROJECTION}}] AS nodes,
20319
20654
  [e IN rawEdges WHERE e IS NOT NULL] AS edges
20320
20655
  `;
20321
20656
  var NEIGHBOURHOOD_CYPHER = `
@@ -20342,7 +20677,7 @@ var NEIGHBOURHOOD_CYPHER = `
20342
20677
  type: type(r)
20343
20678
  } END) AS rawEdges
20344
20679
  RETURN
20345
- [x IN windowNodes | {id: elementId(x), labels: labels(x), properties: properties(x)}] AS nodes,
20680
+ [x IN windowNodes | {id: elementId(x), labels: labels(x), properties: ${CONVERSATION_PROPS_PROJECTION}}] AS nodes,
20346
20681
  [e IN rawEdges WHERE e IS NOT NULL] AS edges
20347
20682
  `;
20348
20683
  var KNOWN_DRIVER_CLASS_NAMES = /* @__PURE__ */ new Set([
@@ -20384,7 +20719,7 @@ function convertNeo4jValue(value, warnedClasses) {
20384
20719
  }
20385
20720
  return value;
20386
20721
  }
20387
- function pruneNode(node, warnedClasses) {
20722
+ function pruneNode(node, warnedClasses, conversationWarnings) {
20388
20723
  const properties = {};
20389
20724
  for (const [key, value] of Object.entries(node.properties ?? {})) {
20390
20725
  if (STRIPPED_PROPERTIES.has(key)) continue;
@@ -20392,6 +20727,12 @@ function pruneNode(node, warnedClasses) {
20392
20727
  }
20393
20728
  const trashed = (node.labels ?? []).includes("Trashed");
20394
20729
  const labels = (node.labels ?? []).filter((l) => l !== "Trashed");
20730
+ if (labels.includes("Conversation") && typeof properties.messageCount !== "number" && !conversationWarnings.loggedMissingMessageCount) {
20731
+ console.error(
20732
+ `[graph-subgraph] conversation-message-count missing elementId=${node.id} labels=${labels.join(",")}`
20733
+ );
20734
+ conversationWarnings.loggedMissingMessageCount = true;
20735
+ }
20395
20736
  return trashed ? { id: node.id, labels, properties, trashed: true } : { id: node.id, labels, properties };
20396
20737
  }
20397
20738
  var graph_subgraph_default = app26;
@@ -20781,43 +21122,274 @@ app32.get("/", requireAdminSession, async (c) => {
20781
21122
  });
20782
21123
  var adherence_default = app32;
20783
21124
 
20784
- // server/routes/admin/index.ts
21125
+ // server/routes/admin/actions/run.ts
20785
21126
  var app33 = new Hono2();
20786
- app33.route("/session", session_default2);
20787
- app33.route("/chat", chat_default2);
20788
- app33.route("/compact", compact_default);
20789
- app33.route("/logs", logs_default);
20790
- app33.route("/claude-info", claude_info_default);
20791
- app33.route("/attachment", attachment_default);
20792
- app33.route("/account", account_default);
20793
- app33.route("/agents", agents_default);
20794
- app33.route("/version", version_default);
20795
- app33.route("/sessions", sessions_default);
20796
- app33.route("/browser", browser_default);
20797
- app33.route("/device-browser", device_browser_default);
20798
- app33.route("/events", events_default);
20799
- app33.route("/cloudflare", cloudflare_default);
20800
- app33.route("/files", files_default);
20801
- app33.route("/graph-search", graph_search_default);
20802
- app33.route("/graph-subgraph", graph_subgraph_default);
20803
- app33.route("/graph-delete", graph_delete_default);
20804
- app33.route("/graph-restore", graph_restore_default);
20805
- app33.route("/graph-labels-in-graph", graph_labels_in_graph_default);
20806
- app33.route("/graph-default-view", graph_default_view_default);
20807
- app33.route("/file-attach", file_attach_default);
20808
- app33.route("/adherence", adherence_default);
20809
- var admin_default = app33;
21127
+ app33.post("/:name", requireAdminSession, async (c) => {
21128
+ const name = c.req.param("name");
21129
+ if (!isKnownAction(name)) {
21130
+ return c.json({ error: `unknown action: ${name}` }, 400);
21131
+ }
21132
+ const actionName = name;
21133
+ if (await isActionActive(actionName)) {
21134
+ return c.json({ error: `action ${actionName} is already running`, code: "already-active" }, 409);
21135
+ }
21136
+ let body = {};
21137
+ try {
21138
+ body = await c.req.json();
21139
+ } catch {
21140
+ }
21141
+ const launchArgs = {};
21142
+ if (Array.isArray(body.positional)) {
21143
+ if (!body.positional.every((x) => typeof x === "string")) {
21144
+ return c.json({ error: "positional args must be strings" }, 400);
21145
+ }
21146
+ launchArgs.positional = body.positional;
21147
+ }
21148
+ if (typeof body.sudoPassword === "string" && body.sudoPassword) {
21149
+ launchArgs.sudoPassword = body.sudoPassword;
21150
+ }
21151
+ try {
21152
+ const result = await launchAction(actionName, launchArgs);
21153
+ return c.json({ ok: true, actionId: result.actionId, unit: result.unit, logPath: result.logPath });
21154
+ } catch (err) {
21155
+ const message = err instanceof Error ? err.message : String(err);
21156
+ console.error(`[admin/actions/run] launch failed action=${actionName}: ${message}`);
21157
+ return c.json({ error: `launch failed: ${message}` }, 500);
21158
+ }
21159
+ });
21160
+ var run_default = app33;
21161
+
21162
+ // node_modules/hono/dist/utils/stream.js
21163
+ var StreamingApi = class {
21164
+ writer;
21165
+ encoder;
21166
+ writable;
21167
+ abortSubscribers = [];
21168
+ responseReadable;
21169
+ /**
21170
+ * Whether the stream has been aborted.
21171
+ */
21172
+ aborted = false;
21173
+ /**
21174
+ * Whether the stream has been closed normally.
21175
+ */
21176
+ closed = false;
21177
+ constructor(writable, _readable) {
21178
+ this.writable = writable;
21179
+ this.writer = writable.getWriter();
21180
+ this.encoder = new TextEncoder();
21181
+ const reader = _readable.getReader();
21182
+ this.abortSubscribers.push(async () => {
21183
+ await reader.cancel();
21184
+ });
21185
+ this.responseReadable = new ReadableStream({
21186
+ async pull(controller) {
21187
+ const { done, value } = await reader.read();
21188
+ done ? controller.close() : controller.enqueue(value);
21189
+ },
21190
+ cancel: () => {
21191
+ this.abort();
21192
+ }
21193
+ });
21194
+ }
21195
+ async write(input) {
21196
+ try {
21197
+ if (typeof input === "string") {
21198
+ input = this.encoder.encode(input);
21199
+ }
21200
+ await this.writer.write(input);
21201
+ } catch {
21202
+ }
21203
+ return this;
21204
+ }
21205
+ async writeln(input) {
21206
+ await this.write(input + "\n");
21207
+ return this;
21208
+ }
21209
+ sleep(ms) {
21210
+ return new Promise((res) => setTimeout(res, ms));
21211
+ }
21212
+ async close() {
21213
+ try {
21214
+ await this.writer.close();
21215
+ } catch {
21216
+ }
21217
+ this.closed = true;
21218
+ }
21219
+ async pipe(body) {
21220
+ this.writer.releaseLock();
21221
+ await body.pipeTo(this.writable, { preventClose: true });
21222
+ this.writer = this.writable.getWriter();
21223
+ }
21224
+ onAbort(listener) {
21225
+ this.abortSubscribers.push(listener);
21226
+ }
21227
+ /**
21228
+ * Abort the stream.
21229
+ * You can call this method when stream is aborted by external event.
21230
+ */
21231
+ abort() {
21232
+ if (!this.aborted) {
21233
+ this.aborted = true;
21234
+ this.abortSubscribers.forEach((subscriber) => subscriber());
21235
+ }
21236
+ }
21237
+ };
21238
+
21239
+ // node_modules/hono/dist/helper/streaming/utils.js
21240
+ var isOldBunVersion = () => {
21241
+ const version = typeof Bun !== "undefined" ? Bun.version : void 0;
21242
+ if (version === void 0) {
21243
+ return false;
21244
+ }
21245
+ const result = version.startsWith("1.1") || version.startsWith("1.0") || version.startsWith("0.");
21246
+ isOldBunVersion = () => result;
21247
+ return result;
21248
+ };
21249
+
21250
+ // node_modules/hono/dist/helper/streaming/stream.js
21251
+ var contextStash = /* @__PURE__ */ new WeakMap();
21252
+ var stream = (c, cb, onError) => {
21253
+ const { readable, writable } = new TransformStream();
21254
+ const stream2 = new StreamingApi(writable, readable);
21255
+ if (isOldBunVersion()) {
21256
+ c.req.raw.signal.addEventListener("abort", () => {
21257
+ if (!stream2.closed) {
21258
+ stream2.abort();
21259
+ }
21260
+ });
21261
+ }
21262
+ contextStash.set(stream2.responseReadable, c);
21263
+ (async () => {
21264
+ try {
21265
+ await cb(stream2);
21266
+ } catch (e) {
21267
+ if (e === void 0) {
21268
+ } else if (e instanceof Error && onError) {
21269
+ await onError(e, stream2);
21270
+ } else {
21271
+ console.error(e);
21272
+ }
21273
+ } finally {
21274
+ stream2.close();
21275
+ }
21276
+ })();
21277
+ return c.newResponse(stream2.responseReadable);
21278
+ };
21279
+
21280
+ // server/routes/admin/actions/stream.ts
21281
+ import { existsSync as existsSync24 } from "fs";
21282
+ var app34 = new Hono2();
21283
+ var ACTION_ID_RE = /^[a-z0-9-]+-[a-z0-9]+-[a-f0-9]{8}$/;
21284
+ app34.get("/:id/stream", requireAdminSession, async (c) => {
21285
+ const id = c.req.param("id");
21286
+ if (!ACTION_ID_RE.test(id)) {
21287
+ return c.json({ error: "invalid action id" }, 400);
21288
+ }
21289
+ const logPath2 = actionLogPath(id);
21290
+ if (!existsSync24(logPath2)) {
21291
+ return c.json({ error: "action not found (log missing)" }, 404);
21292
+ }
21293
+ const lastEventId = c.req.header("Last-Event-ID") ?? c.req.query("from") ?? "";
21294
+ const fromByteOffset = lastEventId ? Math.max(0, Number(lastEventId) || 0) : 0;
21295
+ const unit = `maxy-action-${id}`;
21296
+ const abort = new AbortController();
21297
+ c.req.raw.signal.addEventListener("abort", () => abort.abort());
21298
+ return stream(c, async (s) => {
21299
+ s.onAbort(() => abort.abort());
21300
+ c.header("Content-Type", "text/event-stream");
21301
+ c.header("Cache-Control", "no-cache, no-transform");
21302
+ c.header("Connection", "keep-alive");
21303
+ await s.write(": ok\n\n");
21304
+ try {
21305
+ for await (const ev of streamActionEvents({ actionId: id, unit, fromByteOffset, signal: abort.signal })) {
21306
+ const id_ = ev.type === "line" ? String(ev.byteOffset) : "";
21307
+ const payload = JSON.stringify(ev);
21308
+ const frame = `${id_ ? `id: ${id_}
21309
+ ` : ""}event: ${ev.type}
21310
+ data: ${payload}
21311
+
21312
+ `;
21313
+ await s.write(frame);
21314
+ }
21315
+ } catch (err) {
21316
+ console.error(`[admin/actions/stream] action=${id} error: ${err instanceof Error ? err.message : err}`);
21317
+ }
21318
+ });
21319
+ });
21320
+ var stream_default = app34;
21321
+
21322
+ // server/routes/admin/actions/sudo-prime.ts
21323
+ var app35 = new Hono2();
21324
+ app35.post("/", requireAdminSession, async (c) => {
21325
+ let body = {};
21326
+ try {
21327
+ body = await c.req.json();
21328
+ } catch {
21329
+ }
21330
+ if (typeof body.password !== "string" || !body.password) {
21331
+ return c.json({ error: "password required" }, 400);
21332
+ }
21333
+ const clientIp = c.var.clientIp ?? "unknown";
21334
+ const result = await primeSudo(body.password, clientIp);
21335
+ switch (result) {
21336
+ case "ok":
21337
+ return c.body(null, 204);
21338
+ case "invalid":
21339
+ return c.json({ error: "invalid password" }, 401);
21340
+ case "limited":
21341
+ return c.json({ error: "too many attempts; wait a minute" }, 429);
21342
+ case "error":
21343
+ return c.json({ error: "sudo prime failed \u2014 check server logs" }, 500);
21344
+ }
21345
+ });
21346
+ var sudo_prime_default = app35;
21347
+
21348
+ // server/routes/admin/actions/index.ts
21349
+ var app36 = new Hono2();
21350
+ app36.route("/sudo-prime", sudo_prime_default);
21351
+ app36.route("/", stream_default);
21352
+ app36.route("/", run_default);
21353
+ var actions_default = app36;
21354
+
21355
+ // server/routes/admin/index.ts
21356
+ var app37 = new Hono2();
21357
+ app37.route("/session", session_default2);
21358
+ app37.route("/chat", chat_default2);
21359
+ app37.route("/compact", compact_default);
21360
+ app37.route("/logs", logs_default);
21361
+ app37.route("/claude-info", claude_info_default);
21362
+ app37.route("/attachment", attachment_default);
21363
+ app37.route("/account", account_default);
21364
+ app37.route("/agents", agents_default);
21365
+ app37.route("/version", version_default);
21366
+ app37.route("/sessions", sessions_default);
21367
+ app37.route("/browser", browser_default);
21368
+ app37.route("/device-browser", device_browser_default);
21369
+ app37.route("/events", events_default);
21370
+ app37.route("/cloudflare", cloudflare_default);
21371
+ app37.route("/files", files_default);
21372
+ app37.route("/graph-search", graph_search_default);
21373
+ app37.route("/graph-subgraph", graph_subgraph_default);
21374
+ app37.route("/graph-delete", graph_delete_default);
21375
+ app37.route("/graph-restore", graph_restore_default);
21376
+ app37.route("/graph-labels-in-graph", graph_labels_in_graph_default);
21377
+ app37.route("/graph-default-view", graph_default_view_default);
21378
+ app37.route("/file-attach", file_attach_default);
21379
+ app37.route("/adherence", adherence_default);
21380
+ app37.route("/actions", actions_default);
21381
+ var admin_default = app37;
20810
21382
 
20811
21383
  // server/index.ts
20812
21384
  var PLATFORM_ROOT10 = process.env.MAXY_PLATFORM_ROOT || "";
20813
- var BRAND_JSON_PATH = PLATFORM_ROOT10 ? join12(PLATFORM_ROOT10, "config", "brand.json") : "";
21385
+ var BRAND_JSON_PATH = PLATFORM_ROOT10 ? join13(PLATFORM_ROOT10, "config", "brand.json") : "";
20814
21386
  var BRAND = { productName: "Maxy", hostname: "maxy", configDir: ".maxy", domain: "getmaxy.com" };
20815
- if (BRAND_JSON_PATH && !existsSync23(BRAND_JSON_PATH)) {
21387
+ if (BRAND_JSON_PATH && !existsSync25(BRAND_JSON_PATH)) {
20816
21388
  console.error(`[brand] WARNING: brand.json not found at ${BRAND_JSON_PATH} \u2014 using Maxy defaults`);
20817
21389
  }
20818
- if (BRAND_JSON_PATH && existsSync23(BRAND_JSON_PATH)) {
21390
+ if (BRAND_JSON_PATH && existsSync25(BRAND_JSON_PATH)) {
20819
21391
  try {
20820
- const parsed = JSON.parse(readFileSync25(BRAND_JSON_PATH, "utf-8"));
21392
+ const parsed = JSON.parse(readFileSync26(BRAND_JSON_PATH, "utf-8"));
20821
21393
  BRAND = { ...BRAND, ...parsed };
20822
21394
  } catch (err) {
20823
21395
  console.error(`[brand] Failed to parse brand.json: ${err.message}`);
@@ -20836,11 +21408,11 @@ var brandLoginOpts = {
20836
21408
  bodyFont: BRAND.defaultFonts?.body,
20837
21409
  logoContainsName: !!BRAND.logoContainsName
20838
21410
  };
20839
- var ALIAS_DOMAINS_PATH2 = join12(homedir4(), BRAND.configDir, "alias-domains.json");
21411
+ var ALIAS_DOMAINS_PATH2 = join13(homedir5(), BRAND.configDir, "alias-domains.json");
20840
21412
  function loadAliasDomains() {
20841
21413
  try {
20842
- if (!existsSync23(ALIAS_DOMAINS_PATH2)) return null;
20843
- const parsed = JSON.parse(readFileSync25(ALIAS_DOMAINS_PATH2, "utf-8"));
21414
+ if (!existsSync25(ALIAS_DOMAINS_PATH2)) return null;
21415
+ const parsed = JSON.parse(readFileSync26(ALIAS_DOMAINS_PATH2, "utf-8"));
20844
21416
  if (!Array.isArray(parsed)) {
20845
21417
  console.error("[alias-domains] malformed alias-domains.json \u2014 expected array");
20846
21418
  return null;
@@ -20864,9 +21436,9 @@ watchFile(ALIAS_DOMAINS_PATH2, { interval: 2e3 }, () => {
20864
21436
  function isPublicHost(host) {
20865
21437
  return host.startsWith("public.") || aliasDomains.has(host);
20866
21438
  }
20867
- var app34 = new Hono2();
20868
- app34.use("*", clientIpMiddleware);
20869
- app34.use("*", async (c, next) => {
21439
+ var app38 = new Hono2();
21440
+ app38.use("*", clientIpMiddleware);
21441
+ app38.use("*", async (c, next) => {
20870
21442
  await next();
20871
21443
  c.header("X-Content-Type-Options", "nosniff");
20872
21444
  c.header("Referrer-Policy", "strict-origin-when-cross-origin");
@@ -20889,7 +21461,7 @@ var PUBLIC_ALLOWED_PREFIXES = [
20889
21461
  "/g/"
20890
21462
  ];
20891
21463
  var PUBLIC_ALLOWED_EXACT = ["/favicon.ico"];
20892
- app34.use("*", async (c, next) => {
21464
+ app38.use("*", async (c, next) => {
20893
21465
  const host = (c.req.header("host") ?? "").split(":")[0];
20894
21466
  if (!isPublicHost(host)) {
20895
21467
  await next();
@@ -20929,7 +21501,7 @@ function resolveRemoteAuthOpts() {
20929
21501
  return brandLoginOpts;
20930
21502
  }
20931
21503
  var MAX_LOGIN_BODY = 8 * 1024;
20932
- app34.post("/__remote-auth/login", async (c) => {
21504
+ app38.post("/__remote-auth/login", async (c) => {
20933
21505
  const clientIp = c.var.clientIp || "unknown";
20934
21506
  const rateLimited = checkRateLimit(clientIp);
20935
21507
  if (rateLimited) {
@@ -20965,7 +21537,7 @@ app34.post("/__remote-auth/login", async (c) => {
20965
21537
  }
20966
21538
  });
20967
21539
  });
20968
- app34.get("/__remote-auth/logout", () => {
21540
+ app38.get("/__remote-auth/logout", () => {
20969
21541
  return new Response(null, {
20970
21542
  status: 302,
20971
21543
  headers: {
@@ -20975,7 +21547,7 @@ app34.get("/__remote-auth/logout", () => {
20975
21547
  }
20976
21548
  });
20977
21549
  });
20978
- app34.post("/__remote-auth/change-password", async (c) => {
21550
+ app38.post("/__remote-auth/change-password", async (c) => {
20979
21551
  const clientIp = c.var.clientIp || "unknown";
20980
21552
  const rateLimited = checkRateLimit(clientIp);
20981
21553
  if (rateLimited) {
@@ -21024,13 +21596,13 @@ app34.post("/__remote-auth/change-password", async (c) => {
21024
21596
  return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(), mode: "change", changeError: "Failed to save password", redirect }), 200);
21025
21597
  }
21026
21598
  });
21027
- app34.get("/__remote-auth/setup", (c) => {
21599
+ app38.get("/__remote-auth/setup", (c) => {
21028
21600
  if (isRemoteAuthConfigured()) {
21029
21601
  return c.redirect("/");
21030
21602
  }
21031
21603
  return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(), mode: "setup" }), 200);
21032
21604
  });
21033
- app34.post("/__remote-auth/set-initial-password", async (c) => {
21605
+ app38.post("/__remote-auth/set-initial-password", async (c) => {
21034
21606
  if (isRemoteAuthConfigured()) {
21035
21607
  return c.redirect("/");
21036
21608
  }
@@ -21066,10 +21638,10 @@ app34.post("/__remote-auth/set-initial-password", async (c) => {
21066
21638
  return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(), mode: "setup", setupError: "Failed to save password. Please try again." }), 200);
21067
21639
  }
21068
21640
  });
21069
- app34.get("/api/remote-auth/status", (c) => {
21641
+ app38.get("/api/remote-auth/status", (c) => {
21070
21642
  return c.json({ configured: isRemoteAuthConfigured() });
21071
21643
  });
21072
- app34.post("/api/remote-auth/set-password", async (c) => {
21644
+ app38.post("/api/remote-auth/set-password", async (c) => {
21073
21645
  let body;
21074
21646
  try {
21075
21647
  body = await c.req.json();
@@ -21099,9 +21671,9 @@ app34.post("/api/remote-auth/set-password", async (c) => {
21099
21671
  return c.json({ error: "Failed to save password" }, 500);
21100
21672
  }
21101
21673
  });
21102
- app34.route("/api/_client-error", client_error_default);
21674
+ app38.route("/api/_client-error", client_error_default);
21103
21675
  console.log("[client-error-route] mounted");
21104
- app34.use("*", async (c, next) => {
21676
+ app38.use("*", async (c, next) => {
21105
21677
  const host = (c.req.header("host") ?? "").split(":")[0];
21106
21678
  const path2 = c.req.path;
21107
21679
  if (path2 === "/favicon.ico" || path2.startsWith("/assets/") || path2.startsWith("/brand/")) {
@@ -21131,15 +21703,15 @@ app34.use("*", async (c, next) => {
21131
21703
  console.error(`[remote-auth] login required ip=${clientIp} path=${path2}`);
21132
21704
  return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(), redirect: path2 }), 200);
21133
21705
  });
21134
- app34.route("/api/health", health_default);
21135
- app34.route("/api/session", session_default);
21136
- app34.route("/api/chat", chat_default);
21137
- app34.route("/api/group", group_default);
21138
- app34.route("/api/access", access_default);
21139
- app34.route("/api/telegram", telegram_default);
21140
- app34.route("/api/whatsapp", whatsapp_default);
21141
- app34.route("/api/onboarding", onboarding_default);
21142
- app34.route("/api/admin", admin_default);
21706
+ app38.route("/api/health", health_default);
21707
+ app38.route("/api/session", session_default);
21708
+ app38.route("/api/chat", chat_default);
21709
+ app38.route("/api/group", group_default);
21710
+ app38.route("/api/access", access_default);
21711
+ app38.route("/api/telegram", telegram_default);
21712
+ app38.route("/api/whatsapp", whatsapp_default);
21713
+ app38.route("/api/onboarding", onboarding_default);
21714
+ app38.route("/api/admin", admin_default);
21143
21715
  var SAFE_SLUG_RE = /^[a-z][a-z0-9-]{2,49}$/;
21144
21716
  var SAFE_FILENAME_RE = /^[a-z0-9_][a-z0-9_.-]{0,99}$/i;
21145
21717
  var IMAGE_MIME = {
@@ -21151,7 +21723,7 @@ var IMAGE_MIME = {
21151
21723
  ".svg": "image/svg+xml",
21152
21724
  ".ico": "image/x-icon"
21153
21725
  };
21154
- app34.get("/agent-assets/:slug/:filename", (c) => {
21726
+ app38.get("/agent-assets/:slug/:filename", (c) => {
21155
21727
  const slug = c.req.param("slug");
21156
21728
  const filename = c.req.param("filename");
21157
21729
  if (!SAFE_SLUG_RE.test(slug)) {
@@ -21167,26 +21739,26 @@ app34.get("/agent-assets/:slug/:filename", (c) => {
21167
21739
  console.error(`[agent-assets] no-account slug=${slug} file=${filename}`);
21168
21740
  return c.text("Not found", 404);
21169
21741
  }
21170
- const filePath = resolve27(account.accountDir, "agents", slug, "assets", filename);
21171
- const expectedDir = resolve27(account.accountDir, "agents", slug, "assets");
21742
+ const filePath = resolve28(account.accountDir, "agents", slug, "assets", filename);
21743
+ const expectedDir = resolve28(account.accountDir, "agents", slug, "assets");
21172
21744
  if (!filePath.startsWith(expectedDir + "/")) {
21173
21745
  console.error(`[agent-assets] path-traversal-rejected slug=${slug} file=${filename}`);
21174
21746
  return c.text("Forbidden", 403);
21175
21747
  }
21176
- if (!existsSync23(filePath)) {
21748
+ if (!existsSync25(filePath)) {
21177
21749
  console.error(`[agent-assets] serve slug=${slug} file=${filename} status=404`);
21178
21750
  return c.text("Not found", 404);
21179
21751
  }
21180
21752
  const ext = "." + filename.split(".").pop()?.toLowerCase();
21181
21753
  const contentType = IMAGE_MIME[ext] || "application/octet-stream";
21182
21754
  console.log(`[agent-assets] serve slug=${slug} file=${filename} status=200`);
21183
- const body = readFileSync25(filePath);
21755
+ const body = readFileSync26(filePath);
21184
21756
  return c.body(body, 200, {
21185
21757
  "Content-Type": contentType,
21186
21758
  "Cache-Control": "public, max-age=3600"
21187
21759
  });
21188
21760
  });
21189
- app34.get("/generated/:filename", (c) => {
21761
+ app38.get("/generated/:filename", (c) => {
21190
21762
  const filename = c.req.param("filename");
21191
21763
  if (!SAFE_FILENAME_RE.test(filename) || filename.includes("..")) {
21192
21764
  console.error(`[generated] serve file=${filename} status=403`);
@@ -21197,20 +21769,20 @@ app34.get("/generated/:filename", (c) => {
21197
21769
  console.error(`[generated] serve file=${filename} status=404`);
21198
21770
  return c.text("Not found", 404);
21199
21771
  }
21200
- const filePath = resolve27(account.accountDir, "generated", filename);
21201
- const expectedDir = resolve27(account.accountDir, "generated");
21772
+ const filePath = resolve28(account.accountDir, "generated", filename);
21773
+ const expectedDir = resolve28(account.accountDir, "generated");
21202
21774
  if (!filePath.startsWith(expectedDir + "/")) {
21203
21775
  console.error(`[generated] serve file=${filename} status=403`);
21204
21776
  return c.text("Forbidden", 403);
21205
21777
  }
21206
- if (!existsSync23(filePath)) {
21778
+ if (!existsSync25(filePath)) {
21207
21779
  console.error(`[generated] serve file=${filename} status=404`);
21208
21780
  return c.text("Not found", 404);
21209
21781
  }
21210
21782
  const ext = "." + filename.split(".").pop()?.toLowerCase();
21211
21783
  const contentType = IMAGE_MIME[ext] || "application/octet-stream";
21212
21784
  console.log(`[generated] serve file=${filename} status=200`);
21213
- const body = readFileSync25(filePath);
21785
+ const body = readFileSync26(filePath);
21214
21786
  return c.body(body, 200, {
21215
21787
  "Content-Type": contentType,
21216
21788
  "Cache-Control": "public, max-age=86400"
@@ -21219,9 +21791,9 @@ app34.get("/generated/:filename", (c) => {
21219
21791
  var htmlCache = /* @__PURE__ */ new Map();
21220
21792
  var brandLogoPath = "/brand/maxy-monochrome.png";
21221
21793
  var brandIconPath = "/brand/maxy-monochrome.png";
21222
- if (BRAND_JSON_PATH && existsSync23(BRAND_JSON_PATH)) {
21794
+ if (BRAND_JSON_PATH && existsSync25(BRAND_JSON_PATH)) {
21223
21795
  try {
21224
- const fullBrand = JSON.parse(readFileSync25(BRAND_JSON_PATH, "utf-8"));
21796
+ const fullBrand = JSON.parse(readFileSync26(BRAND_JSON_PATH, "utf-8"));
21225
21797
  if (fullBrand.assets?.logo) brandLogoPath = `/brand/${fullBrand.assets.logo}`;
21226
21798
  brandIconPath = fullBrand.assets?.icon ? `/brand/${fullBrand.assets.icon}` : brandLogoPath;
21227
21799
  } catch {
@@ -21238,9 +21810,9 @@ var brandScript = `<script>window.__BRAND__=${JSON.stringify({
21238
21810
  function readInstalledVersion() {
21239
21811
  try {
21240
21812
  if (!PLATFORM_ROOT10) return "unknown";
21241
- const versionFile = join12(PLATFORM_ROOT10, "config", `.${BRAND.hostname}-version`);
21242
- if (!existsSync23(versionFile)) return "unknown";
21243
- const content = readFileSync25(versionFile, "utf-8").trim();
21813
+ const versionFile = join13(PLATFORM_ROOT10, "config", `.${BRAND.hostname}-version`);
21814
+ if (!existsSync25(versionFile)) return "unknown";
21815
+ const content = readFileSync26(versionFile, "utf-8").trim();
21244
21816
  return content || "unknown";
21245
21817
  } catch {
21246
21818
  return "unknown";
@@ -21281,7 +21853,7 @@ var clientErrorReporterScript = `<script>
21281
21853
  function cachedHtml(file) {
21282
21854
  let html = htmlCache.get(file);
21283
21855
  if (!html) {
21284
- html = readFileSync25(resolve27(process.cwd(), "public", file), "utf-8");
21856
+ html = readFileSync26(resolve28(process.cwd(), "public", file), "utf-8");
21285
21857
  html = html.replace("<title>Maxy</title>", `<title>${escapeHtml(BRAND.productName)}</title>`);
21286
21858
  html = html.replace('href="/favicon.ico"', `href="${escapeHtml(brandFaviconPath)}"`);
21287
21859
  const headInjection = file === "index.html" ? `${brandScript}
@@ -21296,26 +21868,26 @@ ${clientErrorReporterScript}
21296
21868
  }
21297
21869
  var brandedHtmlCache = /* @__PURE__ */ new Map();
21298
21870
  function loadBrandingCache(agentSlug) {
21299
- const configDir2 = join12(homedir4(), BRAND.configDir);
21871
+ const configDir2 = join13(homedir5(), BRAND.configDir);
21300
21872
  try {
21301
- const accountJsonPath = join12(configDir2, "account.json");
21302
- if (!existsSync23(accountJsonPath)) return null;
21303
- const account = JSON.parse(readFileSync25(accountJsonPath, "utf-8"));
21873
+ const accountJsonPath = join13(configDir2, "account.json");
21874
+ if (!existsSync25(accountJsonPath)) return null;
21875
+ const account = JSON.parse(readFileSync26(accountJsonPath, "utf-8"));
21304
21876
  const accountId = account.accountId;
21305
21877
  if (!accountId) return null;
21306
- const cachePath = join12(configDir2, "branding-cache", accountId, `${agentSlug}.json`);
21307
- if (!existsSync23(cachePath)) return null;
21308
- return JSON.parse(readFileSync25(cachePath, "utf-8"));
21878
+ const cachePath = join13(configDir2, "branding-cache", accountId, `${agentSlug}.json`);
21879
+ if (!existsSync25(cachePath)) return null;
21880
+ return JSON.parse(readFileSync26(cachePath, "utf-8"));
21309
21881
  } catch {
21310
21882
  return null;
21311
21883
  }
21312
21884
  }
21313
21885
  function resolveDefaultSlug() {
21314
21886
  try {
21315
- const configDir2 = join12(homedir4(), BRAND.configDir);
21316
- const accountJsonPath = join12(configDir2, "account.json");
21317
- if (!existsSync23(accountJsonPath)) return null;
21318
- const account = JSON.parse(readFileSync25(accountJsonPath, "utf-8"));
21887
+ const configDir2 = join13(homedir5(), BRAND.configDir);
21888
+ const accountJsonPath = join13(configDir2, "account.json");
21889
+ if (!existsSync25(accountJsonPath)) return null;
21890
+ const account = JSON.parse(readFileSync26(accountJsonPath, "utf-8"));
21319
21891
  return account.defaultAgent || null;
21320
21892
  } catch {
21321
21893
  return null;
@@ -21351,7 +21923,7 @@ function brandedPublicHtml(agentSlug) {
21351
21923
  function escapeHtml(s) {
21352
21924
  return s.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
21353
21925
  }
21354
- app34.get("/", (c) => {
21926
+ app38.get("/", (c) => {
21355
21927
  const host = (c.req.header("host") ?? "").split(":")[0];
21356
21928
  if (isPublicHost(host)) {
21357
21929
  const defaultSlug = resolveDefaultSlug();
@@ -21359,12 +21931,12 @@ app34.get("/", (c) => {
21359
21931
  }
21360
21932
  return c.html(cachedHtml("index.html"));
21361
21933
  });
21362
- app34.get("/public", (c) => {
21934
+ app38.get("/public", (c) => {
21363
21935
  const host = (c.req.header("host") ?? "").split(":")[0];
21364
21936
  if (isPublicHost(host)) return c.text("Not found", 404);
21365
21937
  return c.html(cachedHtml("public.html"));
21366
21938
  });
21367
- app34.get("/chat", (c) => {
21939
+ app38.get("/chat", (c) => {
21368
21940
  const host = (c.req.header("host") ?? "").split(":")[0];
21369
21941
  if (isPublicHost(host)) return c.text("Not found", 404);
21370
21942
  return c.html(cachedHtml("public.html"));
@@ -21383,12 +21955,12 @@ async function logViewerFetch(c, next) {
21383
21955
  duration_ms: Date.now() - start
21384
21956
  });
21385
21957
  }
21386
- app34.use("/vnc-viewer.html", logViewerFetch);
21387
- app34.use("/vnc-popout.html", logViewerFetch);
21388
- app34.get("/vnc-popout.html", (c) => {
21958
+ app38.use("/vnc-viewer.html", logViewerFetch);
21959
+ app38.use("/vnc-popout.html", logViewerFetch);
21960
+ app38.get("/vnc-popout.html", (c) => {
21389
21961
  let html = htmlCache.get("vnc-popout.html");
21390
21962
  if (!html) {
21391
- html = readFileSync25(resolve27(process.cwd(), "public", "vnc-popout.html"), "utf-8");
21963
+ html = readFileSync26(resolve28(process.cwd(), "public", "vnc-popout.html"), "utf-8");
21392
21964
  const name = escapeHtml(BRAND.productName);
21393
21965
  html = html.replace("<title>Browser \u2014 Maxy</title>", `<title>${name}</title>`);
21394
21966
  html = html.replace("</head>", ` ${brandScript}
@@ -21398,7 +21970,7 @@ app34.get("/vnc-popout.html", (c) => {
21398
21970
  }
21399
21971
  return c.html(html);
21400
21972
  });
21401
- app34.post("/api/vnc/client-event", async (c) => {
21973
+ app38.post("/api/vnc/client-event", async (c) => {
21402
21974
  let body;
21403
21975
  try {
21404
21976
  body = await c.req.json();
@@ -21419,20 +21991,20 @@ app34.post("/api/vnc/client-event", async (c) => {
21419
21991
  });
21420
21992
  return c.json({ ok: true });
21421
21993
  });
21422
- app34.get("/g/:slug", (c) => {
21994
+ app38.get("/g/:slug", (c) => {
21423
21995
  return c.html(brandedPublicHtml());
21424
21996
  });
21425
- app34.get("/graph", (c) => {
21997
+ app38.get("/graph", (c) => {
21426
21998
  const host = (c.req.header("host") ?? "").split(":")[0];
21427
21999
  if (isPublicHost(host)) return c.text("Not found", 404);
21428
22000
  return c.html(cachedHtml("graph.html"));
21429
22001
  });
21430
- app34.get("/data", (c) => {
22002
+ app38.get("/data", (c) => {
21431
22003
  const host = (c.req.header("host") ?? "").split(":")[0];
21432
22004
  if (isPublicHost(host)) return c.text("Not found", 404);
21433
22005
  return c.html(cachedHtml("data.html"));
21434
22006
  });
21435
- app34.get("/:slug", async (c, next) => {
22007
+ app38.get("/:slug", async (c, next) => {
21436
22008
  const slug = c.req.param("slug");
21437
22009
  if (AGENT_SLUG_PATTERN.test(`/${slug}`)) {
21438
22010
  const branding = loadBrandingCache(slug);
@@ -21441,10 +22013,10 @@ app34.get("/:slug", async (c, next) => {
21441
22013
  }
21442
22014
  await next();
21443
22015
  });
21444
- app34.use("/*", serveStatic({ root: "./public" }));
22016
+ app38.use("/*", serveStatic({ root: "./public" }));
21445
22017
  var port = parseInt(process.env.PORT ?? "19199", 10);
21446
22018
  var hostname = process.env.HOSTNAME ?? "127.0.0.1";
21447
- var httpServer = serve({ fetch: app34.fetch, port, hostname });
22019
+ var httpServer = serve({ fetch: app38.fetch, port, hostname });
21448
22020
  console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
21449
22021
  var SUBAPP_MANIFEST = [
21450
22022
  { prefix: "/api/health", file: "server/routes/health.ts", subapp: health_default },
@@ -21464,7 +22036,7 @@ for (const m of SUBAPP_MANIFEST) {
21464
22036
  }
21465
22037
  try {
21466
22038
  const registered = [];
21467
- for (const r of app34.routes ?? []) {
22039
+ for (const r of app38.routes ?? []) {
21468
22040
  if (typeof r.path !== "string" || r.path.includes(":") || r.path.includes("*")) continue;
21469
22041
  if (AGENT_SLUG_PATTERN.test(r.path)) {
21470
22042
  registered.push({ method: (r.method ?? "ALL").toUpperCase(), path: r.path });
@@ -21478,8 +22050,8 @@ try {
21478
22050
  (async () => {
21479
22051
  try {
21480
22052
  let userId = "";
21481
- if (existsSync23(USERS_FILE)) {
21482
- const users = JSON.parse(readFileSync25(USERS_FILE, "utf-8").trim() || "[]");
22053
+ if (existsSync25(USERS_FILE)) {
22054
+ const users = JSON.parse(readFileSync26(USERS_FILE, "utf-8").trim() || "[]");
21483
22055
  userId = users[0]?.userId ?? "";
21484
22056
  }
21485
22057
  await backfillNullUserIdConversations(userId);
@@ -21505,7 +22077,7 @@ if (bootAccountConfig?.whatsapp) {
21505
22077
  }
21506
22078
  init({
21507
22079
  configDir: configDirForWhatsApp,
21508
- platformRoot: resolve27(process.env.MAXY_PLATFORM_ROOT ?? join12(__dirname, "..")),
22080
+ platformRoot: resolve28(process.env.MAXY_PLATFORM_ROOT ?? join13(__dirname, "..")),
21509
22081
  accountConfig: bootAccountConfig,
21510
22082
  onMessage: async (msg) => {
21511
22083
  try {