@rubytech/create-maxy-code 0.1.189 → 0.1.191

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 (50) hide show
  1. package/package.json +1 -1
  2. package/payload/platform/neo4j/schema.cypher +16 -1
  3. package/payload/platform/plugins/admin/.claude-plugin/plugin.json +1 -1
  4. package/payload/platform/plugins/admin/PLUGIN.md +3 -4
  5. package/payload/platform/plugins/admin/mcp/dist/__tests__/admin-authoring-skill-gate.test.d.ts +2 -0
  6. package/payload/platform/plugins/admin/mcp/dist/__tests__/admin-authoring-skill-gate.test.d.ts.map +1 -0
  7. package/payload/platform/plugins/admin/mcp/dist/__tests__/admin-authoring-skill-gate.test.js +79 -0
  8. package/payload/platform/plugins/admin/mcp/dist/__tests__/admin-authoring-skill-gate.test.js.map +1 -0
  9. package/payload/platform/plugins/admin/mcp/dist/index.js +21 -95
  10. package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -1
  11. package/payload/platform/plugins/admin/mcp/dist/skill-resolution.d.ts +9 -0
  12. package/payload/platform/plugins/admin/mcp/dist/skill-resolution.d.ts.map +1 -1
  13. package/payload/platform/plugins/admin/mcp/dist/skill-resolution.js +47 -0
  14. package/payload/platform/plugins/admin/mcp/dist/skill-resolution.js.map +1 -1
  15. package/payload/platform/plugins/admin/skills/file-presentation/SKILL.md +6 -12
  16. package/payload/platform/plugins/admin/skills/professional-document/SKILL.md +1 -1
  17. package/payload/platform/plugins/docs/references/admin-ui.md +17 -2
  18. package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest-extract.d.ts +9 -0
  19. package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest-extract.d.ts.map +1 -1
  20. package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest-extract.js +1 -1
  21. package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest-extract.js.map +1 -1
  22. package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.js +9 -0
  23. package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.js.map +1 -1
  24. package/payload/platform/plugins/memory/skills/document-ingest/SKILL.md +2 -0
  25. package/payload/platform/plugins/scheduling/PLUGIN.md +1 -1
  26. package/payload/platform/plugins/scheduling/mcp/dist/index.js +1 -1
  27. package/payload/platform/plugins/scheduling/mcp/dist/index.js.map +1 -1
  28. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.d.ts.map +1 -1
  29. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js +0 -1
  30. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js.map +1 -1
  31. package/payload/platform/services/claude-session-manager/dist/rc-daemon.d.ts +21 -0
  32. package/payload/platform/services/claude-session-manager/dist/rc-daemon.d.ts.map +1 -1
  33. package/payload/platform/services/claude-session-manager/dist/rc-daemon.js +113 -2
  34. package/payload/platform/services/claude-session-manager/dist/rc-daemon.js.map +1 -1
  35. package/payload/platform/services/claude-session-manager/dist/system-prompt.js +1 -1
  36. package/payload/platform/services/claude-session-manager/dist/system-prompt.js.map +1 -1
  37. package/payload/platform/templates/specialists/agents/content-producer.md +2 -2
  38. package/payload/platform/templates/specialists/agents/personal-assistant.md +1 -1
  39. package/payload/server/public/assets/{admin-Cst6NkUE.js → admin-DFxXLXfg.js} +53 -53
  40. package/payload/server/public/assets/{data-COwlCdrl.js → data-DtYcqL1G.js} +1 -1
  41. package/payload/server/public/assets/{graph-CkUZEqdW.js → graph-C-XPyGsO.js} +1 -1
  42. package/payload/server/public/assets/graph-labels-CZin3myX.js +1 -0
  43. package/payload/server/public/assets/page-CKlvbjKn.js +1 -0
  44. package/payload/server/public/assets/{page-DC2hsMPj.js → page-DSj21hpG.js} +3 -3
  45. package/payload/server/public/data.html +3 -3
  46. package/payload/server/public/graph.html +3 -3
  47. package/payload/server/public/index.html +4 -4
  48. package/payload/server/server.js +750 -312
  49. package/payload/server/public/assets/graph-labels-Dt1bmq6W.js +0 -1
  50. package/payload/server/public/assets/page-CGXxYYRR.js +0 -1
@@ -756,8 +756,8 @@ var serveStatic = (options = { root: "" }) => {
756
756
  );
757
757
  let stats = getStats(path2);
758
758
  if (stats && stats.isDirectory()) {
759
- const indexFile = options.index ?? "index.html";
760
- path2 = join(path2, indexFile);
759
+ const indexFile2 = options.index ?? "index.html";
760
+ path2 = join(path2, indexFile2);
761
761
  stats = getStats(path2);
762
762
  }
763
763
  if (!stats) {
@@ -816,7 +816,7 @@ var serveStatic = (options = { root: "" }) => {
816
816
 
817
817
  // server/index.ts
818
818
  import { readFileSync as readFileSync20, existsSync as existsSync23, watchFile } from "fs";
819
- import { resolve as resolve22, join as join14, basename as basename5 } from "path";
819
+ import { resolve as resolve24, join as join15, basename as basename5 } from "path";
820
820
  import { homedir as homedir2 } from "os";
821
821
  import { monitorEventLoopDelay } from "perf_hooks";
822
822
 
@@ -1274,7 +1274,7 @@ var credsSaveQueue = Promise.resolve();
1274
1274
  async function drainCredsSaveQueue(timeoutMs = 5e3) {
1275
1275
  console.error(`${TAG2} draining credential save queue\u2026`);
1276
1276
  const timer2 = new Promise(
1277
- (resolve23) => setTimeout(() => resolve23("timeout"), timeoutMs)
1277
+ (resolve25) => setTimeout(() => resolve25("timeout"), timeoutMs)
1278
1278
  );
1279
1279
  const result = await Promise.race([
1280
1280
  credsSaveQueue.then(() => "drained"),
@@ -1402,11 +1402,11 @@ async function createWaSocket(opts) {
1402
1402
  return sock;
1403
1403
  }
1404
1404
  async function waitForConnection(sock) {
1405
- return new Promise((resolve23, reject) => {
1405
+ return new Promise((resolve25, reject) => {
1406
1406
  const handler = (update) => {
1407
1407
  if (update.connection === "open") {
1408
1408
  sock.ev.off("connection.update", handler);
1409
- resolve23();
1409
+ resolve25();
1410
1410
  }
1411
1411
  if (update.connection === "close") {
1412
1412
  sock.ev.off("connection.update", handler);
@@ -1520,14 +1520,14 @@ ${inspected}`;
1520
1520
  return inspect2(err, INSPECT_OPTS2);
1521
1521
  }
1522
1522
  function withTimeout(label, promise, timeoutMs) {
1523
- return new Promise((resolve23, reject) => {
1523
+ return new Promise((resolve25, reject) => {
1524
1524
  const timer2 = setTimeout(() => {
1525
1525
  reject(new Error(`${label} timed out after ${timeoutMs}ms`));
1526
1526
  }, timeoutMs);
1527
1527
  promise.then(
1528
1528
  (value) => {
1529
1529
  clearTimeout(timer2);
1530
- resolve23(value);
1530
+ resolve25(value);
1531
1531
  },
1532
1532
  (err) => {
1533
1533
  clearTimeout(timer2);
@@ -2062,8 +2062,8 @@ async function persistWhatsAppMessage(input) {
2062
2062
  const { givenName, familyName } = splitName(input.pushName);
2063
2063
  const prev = sessionWriteLocks.get(input.cacheKey);
2064
2064
  let release;
2065
- const mine = new Promise((resolve23) => {
2066
- release = resolve23;
2065
+ const mine = new Promise((resolve25) => {
2066
+ release = resolve25;
2067
2067
  });
2068
2068
  const chained = (prev ?? Promise.resolve()).then(() => mine);
2069
2069
  sessionWriteLocks.set(input.cacheKey, chained);
@@ -3099,11 +3099,11 @@ async function connectWithReconnect(conn) {
3099
3099
  console.error(
3100
3100
  `${TAG12} reconnecting account=${conn.accountId} in ${delay}ms (attempt ${decision.nextAttempts}/${maxAttempts})`
3101
3101
  );
3102
- await new Promise((resolve23) => {
3103
- const timer2 = setTimeout(resolve23, delay);
3102
+ await new Promise((resolve25) => {
3103
+ const timer2 = setTimeout(resolve25, delay);
3104
3104
  conn.abortController.signal.addEventListener("abort", () => {
3105
3105
  clearTimeout(timer2);
3106
- resolve23();
3106
+ resolve25();
3107
3107
  }, { once: true });
3108
3108
  });
3109
3109
  }
@@ -3111,16 +3111,16 @@ async function connectWithReconnect(conn) {
3111
3111
  }
3112
3112
  }
3113
3113
  function waitForDisconnectEvent(conn) {
3114
- return new Promise((resolve23) => {
3114
+ return new Promise((resolve25) => {
3115
3115
  if (!conn.sock) {
3116
- resolve23();
3116
+ resolve25();
3117
3117
  return;
3118
3118
  }
3119
3119
  const sock = conn.sock;
3120
3120
  const handler = (update) => {
3121
3121
  if (update.connection === "close") {
3122
3122
  sock.ev.off("connection.update", handler);
3123
- resolve23();
3123
+ resolve25();
3124
3124
  }
3125
3125
  };
3126
3126
  sock.ev.on("connection.update", handler);
@@ -3382,8 +3382,8 @@ async function handleInboundMessage(conn, msg) {
3382
3382
  const conversationKey = isGroup ? remoteJid : senderPhone;
3383
3383
  const debounceKey = `${conn.accountId}:${conversationKey}:${senderPhone}`;
3384
3384
  let resolvePending;
3385
- const sttPending = new Promise((resolve23) => {
3386
- resolvePending = resolve23;
3385
+ const sttPending = new Promise((resolve25) => {
3386
+ resolvePending = resolve25;
3387
3387
  });
3388
3388
  if (conn.debouncer) conn.debouncer.registerPending(debounceKey, sttPending);
3389
3389
  try {
@@ -3787,20 +3787,20 @@ function buildX11Env(chromiumWrapperPath, transport = "vnc") {
3787
3787
 
3788
3788
  // server/routes/health.ts
3789
3789
  function checkPort(port2, timeoutMs = 500) {
3790
- return new Promise((resolve23) => {
3790
+ return new Promise((resolve25) => {
3791
3791
  const socket = createConnection2(port2, "127.0.0.1");
3792
3792
  socket.setTimeout(timeoutMs);
3793
3793
  socket.once("connect", () => {
3794
3794
  socket.destroy();
3795
- resolve23(true);
3795
+ resolve25(true);
3796
3796
  });
3797
3797
  socket.once("error", () => {
3798
3798
  socket.destroy();
3799
- resolve23(false);
3799
+ resolve25(false);
3800
3800
  });
3801
3801
  socket.once("timeout", () => {
3802
3802
  socket.destroy();
3803
- resolve23(false);
3803
+ resolve25(false);
3804
3804
  });
3805
3805
  });
3806
3806
  }
@@ -3865,9 +3865,9 @@ var health_default = app;
3865
3865
 
3866
3866
  // app/lib/attachments.ts
3867
3867
  import { randomUUID as randomUUID3 } from "crypto";
3868
- import { mkdir as mkdir2, readFile, stat as stat2, writeFile as writeFile2 } from "fs/promises";
3868
+ import { mkdir as mkdir2, writeFile as writeFile2 } from "fs/promises";
3869
3869
  import { realpathSync } from "fs";
3870
- import { resolve as resolve3, extname, basename } from "path";
3870
+ import { resolve as resolve3, extname } from "path";
3871
3871
  var PLATFORM_ROOT3 = process.env.MAXY_PLATFORM_ROOT ?? resolve3(process.cwd(), "../platform");
3872
3872
  var ATTACHMENTS_ROOT = resolve3(PLATFORM_ROOT3, "..", "data/uploads");
3873
3873
  var SUPPORTED_MIME_TYPES = /* @__PURE__ */ new Set([
@@ -3957,18 +3957,6 @@ function validateFilePathInAccount(filePath, accountDir) {
3957
3957
  }
3958
3958
  return resolved;
3959
3959
  }
3960
- async function storeGeneratedFile(accountId, filePath) {
3961
- const fileStat = await stat2(filePath);
3962
- if (fileStat.size > MAX_FILE_SIZE_BYTES) {
3963
- throw new Error(
3964
- `File exceeds the 50 MB limit (${(fileStat.size / 1024 / 1024).toFixed(1)} MB).`
3965
- );
3966
- }
3967
- const filename = basename(filePath);
3968
- const mimeType = detectMimeType(filePath);
3969
- const buffer = await readFile(filePath);
3970
- return writeAttachment(accountId, filename, mimeType, fileStat.size, buffer);
3971
- }
3972
3960
  async function storeComponentArtefact(accountId, attachmentId, mimeType, content, filename) {
3973
3961
  const buffer = Buffer.from(content, "utf-8");
3974
3962
  if (buffer.byteLength > MAX_FILE_SIZE_BYTES) {
@@ -4548,12 +4536,12 @@ async function dispatchOnce(input) {
4548
4536
  });
4549
4537
  if (!entry) return { error: "spawn-failed" };
4550
4538
  entry.lastInboundAt = Date.now();
4551
- let resolve23;
4539
+ let resolve25;
4552
4540
  const turnPromise = new Promise((r) => {
4553
- resolve23 = r;
4541
+ resolve25 = r;
4554
4542
  });
4555
4543
  const listener = (text) => {
4556
- resolve23(text);
4544
+ resolve25(text);
4557
4545
  };
4558
4546
  entry.subscribers.add(listener);
4559
4547
  const writeOk = await writeInput(entry, input.text);
@@ -4887,8 +4875,8 @@ ${lines}]`;
4887
4875
  var chat_default = app2;
4888
4876
 
4889
4877
  // server/routes/whatsapp.ts
4890
- import { join as join5, resolve as resolve6, basename as basename2 } from "path";
4891
- import { readFile as readFile2, stat as stat3 } from "fs/promises";
4878
+ import { join as join5, resolve as resolve6, basename } from "path";
4879
+ import { readFile, stat as stat2 } from "fs/promises";
4892
4880
  import { realpathSync as realpathSync2, readdirSync as readdirSync2, readFileSync as readFileSync6, existsSync as existsSync4 } from "fs";
4893
4881
 
4894
4882
  // app/lib/whatsapp/login.ts
@@ -4995,8 +4983,8 @@ async function startLogin(opts) {
4995
4983
  resetActiveLogin(accountId);
4996
4984
  let resolveQr = null;
4997
4985
  let rejectQr = null;
4998
- const qrPromise = new Promise((resolve23, reject) => {
4999
- resolveQr = resolve23;
4986
+ const qrPromise = new Promise((resolve25, reject) => {
4987
+ resolveQr = resolve25;
5000
4988
  rejectQr = reject;
5001
4989
  });
5002
4990
  const qrTimer = setTimeout(
@@ -5820,12 +5808,12 @@ app3.post("/send-document", async (c) => {
5820
5808
  console.error(`${TAG17} send-document path error: ${String(err)}`);
5821
5809
  return c.json({ error: String(err) }, 500);
5822
5810
  }
5823
- const fileStat = await stat3(resolvedPath);
5811
+ const fileStat = await stat2(resolvedPath);
5824
5812
  if (fileStat.size > MAX_FILE_SIZE_BYTES) {
5825
5813
  return c.json({ error: `File exceeds 50 MB limit (${(fileStat.size / 1024 / 1024).toFixed(1)} MB)` }, 400);
5826
5814
  }
5827
- const buffer = Buffer.from(await readFile2(resolvedPath));
5828
- const filename = basename2(resolvedPath);
5815
+ const buffer = Buffer.from(await readFile(resolvedPath));
5816
+ const filename = basename(resolvedPath);
5829
5817
  const mimetype = detectMimeType(resolvedPath);
5830
5818
  const sock = getSocket(accountId);
5831
5819
  if (!sock) {
@@ -6781,7 +6769,7 @@ var session_default = app6;
6781
6769
 
6782
6770
  // server/routes/admin/logs.ts
6783
6771
  import { existsSync as existsSync10, readdirSync as readdirSync5, readFileSync as readFileSync10, statSync as statSync5 } from "fs";
6784
- import { resolve as resolve7, basename as basename3 } from "path";
6772
+ import { resolve as resolve7, basename as basename2 } from "path";
6785
6773
 
6786
6774
  // app/lib/logs-read-resolve.ts
6787
6775
  import { existsSync as existsSync9 } from "fs";
@@ -6813,7 +6801,7 @@ app7.get("/", async (c) => {
6813
6801
  if (accountLogDir) logDirs.push(accountLogDir);
6814
6802
  logDirs.push(LOG_DIR);
6815
6803
  if (fileParam) {
6816
- const safe = basename3(fileParam);
6804
+ const safe = basename2(fileParam);
6817
6805
  const searched = [];
6818
6806
  for (const dir of logDirs) {
6819
6807
  const filePath = resolve7(dir, safe);
@@ -6883,7 +6871,7 @@ app7.get("/", async (c) => {
6883
6871
  if (hit) {
6884
6872
  console.info(`[admin/logs] resolved cacheKey=${cacheKeySlice} sessionId=${sessionIdSlice} via=${primaryId === cacheKey ? "cacheKey" : primaryId === sessionKeyFromConv ? "reverse-lookup" : "sessionId-fallback"}`);
6885
6873
  try {
6886
- const filename = basename3(hit.path);
6874
+ const filename = basename2(hit.path);
6887
6875
  const buffer = readFileSync10(hit.path);
6888
6876
  const onDiskBytes = statSync5(hit.path).size;
6889
6877
  const headers = {
@@ -6969,7 +6957,7 @@ app8.get("/", (c) => {
6969
6957
  var claude_info_default = app8;
6970
6958
 
6971
6959
  // server/routes/admin/attachment.ts
6972
- import { readFile as readFile3, readdir } from "fs/promises";
6960
+ import { readFile as readFile2, readdir } from "fs/promises";
6973
6961
  import { existsSync as existsSync11 } from "fs";
6974
6962
  import { resolve as resolve8 } from "path";
6975
6963
  var app9 = new Hono();
@@ -6993,7 +6981,7 @@ app9.get("/:attachmentId", requireAdminSession, async (c) => {
6993
6981
  }
6994
6982
  let meta;
6995
6983
  try {
6996
- meta = JSON.parse(await readFile3(metaPath, "utf-8"));
6984
+ meta = JSON.parse(await readFile2(metaPath, "utf-8"));
6997
6985
  } catch {
6998
6986
  return new Response("Not found", { status: 404 });
6999
6987
  }
@@ -7003,7 +6991,7 @@ app9.get("/:attachmentId", requireAdminSession, async (c) => {
7003
6991
  return new Response("Not found", { status: 404 });
7004
6992
  }
7005
6993
  const filePath = resolve8(dir, dataFile);
7006
- const buffer = await readFile3(filePath);
6994
+ const buffer = await readFile2(filePath);
7007
6995
  return new Response(new Uint8Array(buffer), {
7008
6996
  headers: {
7009
6997
  "Content-Type": meta.mimeType,
@@ -8016,10 +8004,10 @@ function computeSpecialistDomains(accountId) {
8016
8004
  const resolveDesc = (qualified) => {
8017
8005
  if (!qualified.startsWith("mcp__")) return void 0;
8018
8006
  const rest = qualified.slice(5);
8019
- const sep4 = rest.indexOf("__");
8020
- if (sep4 < 0) return void 0;
8021
- const plugin = rest.slice(0, sep4);
8022
- const tool = rest.slice(sep4 + 2);
8007
+ const sep7 = rest.indexOf("__");
8008
+ if (sep7 < 0) return void 0;
8009
+ const plugin = rest.slice(0, sep7);
8010
+ const tool = rest.slice(sep7 + 2);
8023
8011
  let map = descByPlugin.get(plugin);
8024
8012
  if (!map) {
8025
8013
  const dir = pluginDirs.get(plugin);
@@ -8398,9 +8386,9 @@ var events_default = app14;
8398
8386
 
8399
8387
  // server/routes/admin/files.ts
8400
8388
  import { createReadStream as createReadStream2 } from "fs";
8401
- import { readdir as readdir2, readFile as readFile4, stat as stat4, mkdir as mkdir3, writeFile as writeFile4, unlink as unlink2 } from "fs/promises";
8389
+ import { readdir as readdir3, readFile as readFile4, stat as stat4, mkdir as mkdir3, writeFile as writeFile4, unlink as unlink2 } from "fs/promises";
8402
8390
  import { realpathSync as realpathSync4 } from "fs";
8403
- import { basename as basename4, dirname as dirname3, join as join11, resolve as resolve13, sep as sep3 } from "path";
8391
+ import { basename as basename4, dirname as dirname3, join as join12, resolve as resolve14, sep as sep4 } from "path";
8404
8392
  import { Readable as Readable2 } from "stream";
8405
8393
 
8406
8394
  // app/lib/data-path.ts
@@ -8408,17 +8396,28 @@ import { realpathSync as realpathSync3 } from "fs";
8408
8396
  import { resolve as resolve12, normalize, sep as sep2, relative } from "path";
8409
8397
  var PLATFORM_ROOT5 = process.env.MAXY_PLATFORM_ROOT ?? resolve12(process.cwd(), "../platform");
8410
8398
  var DATA_ROOT = resolve12(PLATFORM_ROOT5, "..", "data");
8411
- function resolveDataPath(raw) {
8399
+ var CLAUDE_UPLOADS_ROOT = process.env.CLAUDE_CONFIG_DIR ? resolve12(process.env.CLAUDE_CONFIG_DIR, "uploads") : null;
8400
+ var ACCOUNT_PARTITION_DIRS = /* @__PURE__ */ new Set(["uploads", "accounts"]);
8401
+ var ACCOUNT_UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
8402
+ function crossesForeignAccountPartition(relPath2, accountId) {
8403
+ const segs = relPath2.split("/").filter(Boolean);
8404
+ if (segs.length < 2) return false;
8405
+ if (!ACCOUNT_PARTITION_DIRS.has(segs[0])) return false;
8406
+ const owner = segs[1];
8407
+ if (!ACCOUNT_UUID_RE.test(owner)) return false;
8408
+ return owner !== accountId;
8409
+ }
8410
+ function resolveUnderRoot(raw, rootAbs, rootLabel) {
8412
8411
  const cleaned = normalize("/" + (raw ?? "").replace(/\\/g, "/")).replace(/^\/+/, "");
8413
- const absolute = resolve12(DATA_ROOT, cleaned);
8414
- let dataRootReal;
8412
+ const absolute = resolve12(rootAbs, cleaned);
8413
+ let rootReal;
8415
8414
  try {
8416
- dataRootReal = realpathSync3(DATA_ROOT);
8415
+ rootReal = realpathSync3(rootAbs);
8417
8416
  } catch (err) {
8418
8417
  return {
8419
8418
  ok: false,
8420
8419
  status: 500,
8421
- error: `DATA_ROOT not accessible: ${err instanceof Error ? err.message : String(err)}`
8420
+ error: `${rootLabel} not accessible: ${err instanceof Error ? err.message : String(err)}`
8422
8421
  };
8423
8422
  }
8424
8423
  let resolvedReal;
@@ -8427,8 +8426,8 @@ function resolveDataPath(raw) {
8427
8426
  } catch (err) {
8428
8427
  const code = err.code;
8429
8428
  if (code === "ENOENT") {
8430
- if (absolute !== dataRootReal && !absolute.startsWith(dataRootReal + sep2)) {
8431
- return { ok: false, status: 403, error: "Path escapes DATA_ROOT", resolved: absolute };
8429
+ if (absolute !== rootReal && !absolute.startsWith(rootReal + sep2)) {
8430
+ return { ok: false, status: 403, error: `Path escapes ${rootLabel}`, resolved: absolute };
8432
8431
  }
8433
8432
  return { ok: false, status: 404, error: "Not found" };
8434
8433
  }
@@ -8438,11 +8437,20 @@ function resolveDataPath(raw) {
8438
8437
  error: err instanceof Error ? err.message : String(err)
8439
8438
  };
8440
8439
  }
8441
- if (resolvedReal !== dataRootReal && !resolvedReal.startsWith(dataRootReal + sep2)) {
8442
- return { ok: false, status: 403, error: "Path escapes DATA_ROOT", resolved: resolvedReal };
8440
+ if (resolvedReal !== rootReal && !resolvedReal.startsWith(rootReal + sep2)) {
8441
+ return { ok: false, status: 403, error: `Path escapes ${rootLabel}`, resolved: resolvedReal };
8442
+ }
8443
+ const relPath2 = relative(rootReal, resolvedReal) || ".";
8444
+ return { ok: true, absolute: resolvedReal, dataRootReal: rootReal, relative: relPath2 };
8445
+ }
8446
+ function resolveDataPath(raw) {
8447
+ return resolveUnderRoot(raw, DATA_ROOT, "DATA_ROOT");
8448
+ }
8449
+ function resolveClaudeUploadsPath(raw) {
8450
+ if (!CLAUDE_UPLOADS_ROOT) {
8451
+ return { ok: false, status: 404, error: "Not found" };
8443
8452
  }
8444
- const relPath2 = relative(dataRootReal, resolvedReal) || ".";
8445
- return { ok: true, absolute: resolvedReal, dataRootReal, relative: relPath2 };
8453
+ return resolveUnderRoot(raw, CLAUDE_UPLOADS_ROOT, "CLAUDE_UPLOADS_ROOT");
8446
8454
  }
8447
8455
 
8448
8456
  // ../lib/graph-trash/src/index.ts
@@ -8758,11 +8766,291 @@ async function cascadeDeleteDocument(params) {
8758
8766
  }
8759
8767
  }
8760
8768
 
8769
+ // app/lib/file-index.ts
8770
+ import * as fsp from "fs/promises";
8771
+ import { resolve as resolve13, relative as relative2, join as join11, basename as basename3, extname as extname2, sep as sep3 } from "path";
8772
+ import { execFile as execFile2 } from "child_process";
8773
+ import { promisify as promisify2 } from "util";
8774
+ var execFileAsync2 = promisify2(execFile2);
8775
+ var CONTENT_CAP = 1e5;
8776
+ var RECONCILE_DEBOUNCE_MS = 1e4;
8777
+ var lastReconcileAt = /* @__PURE__ */ new Map();
8778
+ var TEXT_EXTENSIONS = /* @__PURE__ */ new Set([
8779
+ ".txt",
8780
+ ".md",
8781
+ ".markdown",
8782
+ ".csv",
8783
+ ".tsv",
8784
+ ".json",
8785
+ ".jsonl",
8786
+ ".log",
8787
+ ".yaml",
8788
+ ".yml",
8789
+ ".xml",
8790
+ ".toml",
8791
+ ".ini",
8792
+ ".env",
8793
+ ".sql",
8794
+ ".sh",
8795
+ ".py",
8796
+ ".js",
8797
+ ".ts",
8798
+ ".tsx",
8799
+ ".jsx",
8800
+ ".css",
8801
+ ".svg",
8802
+ ".rtf"
8803
+ ]);
8804
+ var HTML_EXTENSIONS = /* @__PURE__ */ new Set([".html", ".htm"]);
8805
+ var MIME_BY_EXT2 = {
8806
+ ".pdf": "application/pdf",
8807
+ ".txt": "text/plain",
8808
+ ".md": "text/markdown",
8809
+ ".markdown": "text/markdown",
8810
+ ".csv": "text/csv",
8811
+ ".json": "application/json",
8812
+ ".html": "text/html",
8813
+ ".htm": "text/html",
8814
+ ".png": "image/png",
8815
+ ".jpg": "image/jpeg",
8816
+ ".jpeg": "image/jpeg",
8817
+ ".gif": "image/gif",
8818
+ ".webp": "image/webp"
8819
+ };
8820
+ function mimeForExt(ext) {
8821
+ return MIME_BY_EXT2[ext.toLowerCase()] ?? "application/octet-stream";
8822
+ }
8823
+ async function extractPdf(absolute) {
8824
+ const { stdout } = await execFileAsync2("pdftotext", [absolute, "-"], {
8825
+ maxBuffer: 10 * 1024 * 1024
8826
+ });
8827
+ return stdout.trim();
8828
+ }
8829
+ function stripHtml(html) {
8830
+ return html.replace(/<script[\s\S]*?<\/script>/gi, " ").replace(/<style[\s\S]*?<\/style>/gi, " ").replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
8831
+ }
8832
+ async function extractFileContent(absolute) {
8833
+ const ext = extname2(absolute).toLowerCase();
8834
+ try {
8835
+ if (ext === ".pdf") return (await extractPdf(absolute)).slice(0, CONTENT_CAP);
8836
+ if (HTML_EXTENSIONS.has(ext)) {
8837
+ const raw = await fsp.readFile(absolute, "utf-8");
8838
+ return stripHtml(raw).slice(0, CONTENT_CAP);
8839
+ }
8840
+ if (TEXT_EXTENSIONS.has(ext)) {
8841
+ const raw = await fsp.readFile(absolute, "utf-8");
8842
+ return raw.slice(0, CONTENT_CAP);
8843
+ }
8844
+ return "";
8845
+ } catch (err) {
8846
+ const msg = err instanceof Error ? err.message : String(err);
8847
+ console.error(`[file-index] extract-failed path="${relative2(DATA_ROOT, absolute)}" err="${msg}"`);
8848
+ return "";
8849
+ }
8850
+ }
8851
+ async function readDisplayName(absolute) {
8852
+ const dir = absolute.slice(0, absolute.length - basename3(absolute).length);
8853
+ const base = basename3(absolute);
8854
+ const dot = base.lastIndexOf(".");
8855
+ const stem = dot === -1 ? base : base.slice(0, dot);
8856
+ if (base === `${stem}.meta.json`) return null;
8857
+ try {
8858
+ const raw = await fsp.readFile(join11(dir, `${stem}.meta.json`), "utf-8");
8859
+ const meta = JSON.parse(raw);
8860
+ const dn = meta.displayName ?? meta.filename;
8861
+ return typeof dn === "string" && dn.length > 0 ? dn : null;
8862
+ } catch {
8863
+ return null;
8864
+ }
8865
+ }
8866
+ async function walkSubtree(root, dataRoot, out) {
8867
+ let entries;
8868
+ try {
8869
+ entries = await fsp.readdir(root, { withFileTypes: true });
8870
+ } catch (err) {
8871
+ if (err.code === "ENOENT") return { clean: true };
8872
+ console.error(`[file-index] walk-error dir="${relative2(dataRoot, root)}" err="${err.message}"`);
8873
+ return { clean: false };
8874
+ }
8875
+ let clean = true;
8876
+ for (const entry of entries) {
8877
+ if (entry.isSymbolicLink()) continue;
8878
+ const abs = join11(root, entry.name);
8879
+ if (entry.isDirectory()) {
8880
+ const sub = await walkSubtree(abs, dataRoot, out);
8881
+ if (!sub.clean) clean = false;
8882
+ } else if (entry.isFile()) {
8883
+ try {
8884
+ const st = await fsp.stat(abs);
8885
+ out.push({
8886
+ absolute: abs,
8887
+ relativePath: relative2(dataRoot, abs).split(sep3).join("/"),
8888
+ sizeBytes: st.size,
8889
+ modifiedAt: st.mtime.toISOString()
8890
+ });
8891
+ } catch (err) {
8892
+ console.error(`[file-index] stat-error path="${relative2(dataRoot, abs)}" err="${err.message}"`);
8893
+ clean = false;
8894
+ }
8895
+ }
8896
+ }
8897
+ return { clean };
8898
+ }
8899
+ async function upsertFileArtifact(session, f) {
8900
+ await session.run(
8901
+ `MERGE (f:FileArtifact {accountId: $accountId, relativePath: $relativePath})
8902
+ SET f.name = $name,
8903
+ f.displayName = $displayName,
8904
+ f.mimeType = $mimeType,
8905
+ f.sizeBytes = $sizeBytes,
8906
+ f.modifiedAt = $modifiedAt,
8907
+ f.content = $content,
8908
+ f.embedding = $embedding`,
8909
+ f
8910
+ );
8911
+ }
8912
+ async function buildArtifact(accountId, wf, embed2) {
8913
+ const name = basename3(wf.absolute);
8914
+ const content = await extractFileContent(wf.absolute);
8915
+ const displayName = await readDisplayName(wf.absolute);
8916
+ const embedInput = `${name}
8917
+ ${content}`.slice(0, CONTENT_CAP);
8918
+ let embedding = null;
8919
+ const embedStart = Date.now();
8920
+ try {
8921
+ embedding = await embed2(embedInput);
8922
+ } catch (err) {
8923
+ console.error(`[file-index] embed-failed path="${wf.relativePath}" err="${err.message}"`);
8924
+ }
8925
+ console.error(
8926
+ `[file-index] index-file account=${accountId} path="${wf.relativePath}" chars=${content.length} embed-ms=${Date.now() - embedStart}`
8927
+ );
8928
+ return {
8929
+ accountId,
8930
+ relativePath: wf.relativePath,
8931
+ name,
8932
+ displayName,
8933
+ mimeType: mimeForExt(extname2(wf.absolute)),
8934
+ sizeBytes: wf.sizeBytes,
8935
+ modifiedAt: wf.modifiedAt,
8936
+ content,
8937
+ embedding
8938
+ };
8939
+ }
8940
+ async function withSession(opts, fn) {
8941
+ if (opts?.session) return fn(opts.session);
8942
+ const session = getSession();
8943
+ try {
8944
+ return await fn(session);
8945
+ } finally {
8946
+ await session.close();
8947
+ }
8948
+ }
8949
+ async function reconcileFileIndex(accountId, opts) {
8950
+ const dataRoot = opts?.dataRoot ?? DATA_ROOT;
8951
+ const embed2 = opts?.embed ?? embed;
8952
+ const t0 = Date.now();
8953
+ return withSession(opts, async (session) => {
8954
+ const walked = [];
8955
+ const subtreeRoots = [
8956
+ resolve13(dataRoot, "uploads", accountId),
8957
+ resolve13(dataRoot, "accounts", accountId)
8958
+ ];
8959
+ let clean = true;
8960
+ for (const root of subtreeRoots) {
8961
+ const r = await walkSubtree(root, dataRoot, walked);
8962
+ if (!r.clean) clean = false;
8963
+ }
8964
+ const existing = /* @__PURE__ */ new Map();
8965
+ const existingRes = await session.run(
8966
+ `MATCH (f:FileArtifact {accountId: $accountId})
8967
+ RETURN f.relativePath AS relativePath, f.sizeBytes AS sizeBytes,
8968
+ f.modifiedAt AS modifiedAt, f.embedding IS NOT NULL AS hasEmbedding`,
8969
+ { accountId }
8970
+ );
8971
+ for (const rec of existingRes.records) {
8972
+ existing.set(rec.get("relativePath"), {
8973
+ sizeBytes: typeof rec.get("sizeBytes")?.toNumber === "function" ? rec.get("sizeBytes").toNumber() : Number(rec.get("sizeBytes")),
8974
+ modifiedAt: rec.get("modifiedAt"),
8975
+ hasEmbedding: rec.get("hasEmbedding") === true
8976
+ });
8977
+ }
8978
+ let upserted = 0;
8979
+ let skipped = 0;
8980
+ for (const wf of walked) {
8981
+ const prior = existing.get(wf.relativePath);
8982
+ const unchanged = prior && prior.sizeBytes === wf.sizeBytes && prior.modifiedAt === wf.modifiedAt && prior.hasEmbedding;
8983
+ if (unchanged) {
8984
+ skipped++;
8985
+ continue;
8986
+ }
8987
+ await upsertFileArtifact(session, await buildArtifact(accountId, wf, embed2));
8988
+ upserted++;
8989
+ }
8990
+ let pruned = 0;
8991
+ if (clean) {
8992
+ const foundPaths = walked.map((w) => w.relativePath);
8993
+ const pruneRes = await session.run(
8994
+ `MATCH (f:FileArtifact {accountId: $accountId})
8995
+ WHERE NOT f.relativePath IN $foundPaths
8996
+ DETACH DELETE f
8997
+ RETURN count(f) AS pruned`,
8998
+ { accountId, foundPaths }
8999
+ );
9000
+ const c = pruneRes.records[0]?.get("pruned");
9001
+ pruned = typeof c?.toNumber === "function" ? c.toNumber() : Number(c ?? 0);
9002
+ }
9003
+ console.error(
9004
+ `[file-index] reconcile account=${accountId} walked=${walked.length} upserted=${upserted} skipped=${skipped} pruned=${pruned} clean=${clean} ms=${Date.now() - t0}`
9005
+ );
9006
+ return { walked: walked.length, upserted, skipped, pruned };
9007
+ });
9008
+ }
9009
+ async function reconcileFileIndexDebounced(accountId, opts) {
9010
+ const now = Date.now();
9011
+ const last = lastReconcileAt.get(accountId) ?? 0;
9012
+ if (now - last < RECONCILE_DEBOUNCE_MS) return;
9013
+ lastReconcileAt.set(accountId, now);
9014
+ try {
9015
+ await reconcileFileIndex(accountId, opts);
9016
+ } catch (err) {
9017
+ console.error(`[file-index] reconcile-failed account=${accountId} err="${err.message}"`);
9018
+ }
9019
+ }
9020
+ async function indexFile(accountId, relativePath, opts) {
9021
+ const dataRoot = opts?.dataRoot ?? DATA_ROOT;
9022
+ const embed2 = opts?.embed ?? embed;
9023
+ const absolute = resolve13(dataRoot, relativePath);
9024
+ await withSession(opts, async (session) => {
9025
+ const st = await fsp.stat(absolute);
9026
+ const wf = {
9027
+ absolute,
9028
+ relativePath,
9029
+ sizeBytes: st.size,
9030
+ modifiedAt: st.mtime.toISOString()
9031
+ };
9032
+ await upsertFileArtifact(session, await buildArtifact(accountId, wf, embed2));
9033
+ });
9034
+ }
9035
+ async function dropFileIndex(accountId, relativePath, opts) {
9036
+ await withSession(opts, async (session) => {
9037
+ const res = await session.run(
9038
+ `MATCH (f:FileArtifact {accountId: $accountId, relativePath: $relativePath})
9039
+ DETACH DELETE f
9040
+ RETURN count(f) AS dropped`,
9041
+ { accountId, relativePath }
9042
+ );
9043
+ const c = res.records[0]?.get("dropped");
9044
+ const dropped = typeof c?.toNumber === "function" ? c.toNumber() : Number(c ?? 0);
9045
+ console.error(`[file-index] drop account=${accountId} path="${relativePath}" dropped=${dropped}`);
9046
+ });
9047
+ }
9048
+
8761
9049
  // server/routes/admin/files.ts
8762
9050
  var UUID_RE3 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
8763
9051
  async function readMeta(absDir, baseName) {
8764
9052
  try {
8765
- const raw = await readFile4(join11(absDir, `${baseName}.meta.json`), "utf8");
9053
+ const raw = await readFile4(join12(absDir, `${baseName}.meta.json`), "utf8");
8766
9054
  const parsed = JSON.parse(raw);
8767
9055
  if (typeof parsed?.filename === "string") {
8768
9056
  return { filename: parsed.filename, mimeType: typeof parsed.mimeType === "string" ? parsed.mimeType : void 0 };
@@ -8773,16 +9061,16 @@ async function readMeta(absDir, baseName) {
8773
9061
  }
8774
9062
  async function readAccountNames() {
8775
9063
  const map = /* @__PURE__ */ new Map();
8776
- const accountsDir = resolve13(DATA_ROOT, "accounts");
9064
+ const accountsDir = resolve14(DATA_ROOT, "accounts");
8777
9065
  let names;
8778
9066
  try {
8779
- names = await readdir2(accountsDir);
9067
+ names = await readdir3(accountsDir);
8780
9068
  } catch {
8781
9069
  return map;
8782
9070
  }
8783
9071
  for (const name of names) {
8784
9072
  if (!UUID_RE3.test(name)) continue;
8785
- const configPath2 = resolve13(accountsDir, name, "account.json");
9073
+ const configPath2 = resolve14(accountsDir, name, "account.json");
8786
9074
  try {
8787
9075
  const raw = await readFile4(configPath2, "utf8");
8788
9076
  const parsed = JSON.parse(raw);
@@ -8800,7 +9088,7 @@ async function readAccountNames() {
8800
9088
  }
8801
9089
  async function enrich(absolute, entry, accountNames) {
8802
9090
  if (entry.kind === "directory" && UUID_RE3.test(entry.name)) {
8803
- const meta = await readMeta(join11(absolute, entry.name), entry.name);
9091
+ const meta = await readMeta(join12(absolute, entry.name), entry.name);
8804
9092
  if (meta?.filename) {
8805
9093
  entry.displayName = meta.filename;
8806
9094
  entry.mimeType = meta.mimeType;
@@ -8831,6 +9119,23 @@ function buildDisplayPath(relPath2, accountNames) {
8831
9119
  return dn ? { name: seg, displayName: dn } : { name: seg };
8832
9120
  });
8833
9121
  }
9122
+ async function knowledgeDocOwnedByAccount(accountId, attachmentId) {
9123
+ if (!attachmentId) return false;
9124
+ const session = getSession();
9125
+ try {
9126
+ const result = await session.run(
9127
+ `MATCH (d:KnowledgeDocument { accountId: $accountId, attachmentId: $attachmentId })
9128
+ RETURN count(d) > 0 AS owned`,
9129
+ { accountId, attachmentId }
9130
+ );
9131
+ return result.records[0]?.get("owned") === true;
9132
+ } catch (err) {
9133
+ console.error(`[data] kd-ownership-check-failed attachmentId="${attachmentId.slice(0, 8)}\u2026" err="${err instanceof Error ? err.message : String(err)}"`);
9134
+ return false;
9135
+ } finally {
9136
+ await session.close();
9137
+ }
9138
+ }
8834
9139
  var app15 = new Hono();
8835
9140
  app15.get("/", requireAdminSession, async (c) => {
8836
9141
  const cacheKey = c.var.cacheKey;
@@ -8852,14 +9157,14 @@ app15.get("/", requireAdminSession, async (c) => {
8852
9157
  if (!info.isDirectory()) {
8853
9158
  return c.json({ error: "Path is not a directory \u2014 use /api/admin/files/download for files" }, 400);
8854
9159
  }
8855
- const names = await readdir2(absolute);
9160
+ const names = await readdir3(absolute);
8856
9161
  const entries = [];
8857
9162
  for (const name of names) {
8858
9163
  if (UUID_RE3.test(name.replace(/\.meta\.json$/, "")) && name.endsWith(".meta.json")) {
8859
9164
  continue;
8860
9165
  }
8861
9166
  try {
8862
- const entryPath = join11(absolute, name);
9167
+ const entryPath = join12(absolute, name);
8863
9168
  const s = await stat4(entryPath);
8864
9169
  entries.push({
8865
9170
  name,
@@ -8895,20 +9200,36 @@ app15.get("/", requireAdminSession, async (c) => {
8895
9200
  });
8896
9201
  app15.get("/download", requireAdminSession, async (c) => {
8897
9202
  const cacheKey = c.var.cacheKey;
8898
- if (!getAccountIdForSession(cacheKey)) {
9203
+ const accountId = getAccountIdForSession(cacheKey);
9204
+ if (!accountId) {
8899
9205
  console.error(`[data] auth-rejected endpoint="/api/admin/files/download" reason="no account for session"`);
8900
9206
  return c.json({ error: "Account not found for session" }, 401);
8901
9207
  }
8902
9208
  const rawPath = c.req.query("path") ?? "";
8903
9209
  if (!rawPath) return c.json({ error: "path required" }, 400);
8904
- const resolution = resolveDataPath(rawPath);
9210
+ const root = c.req.query("root") ?? "data";
9211
+ if (root !== "data" && root !== "claude-uploads") {
9212
+ return c.json({ error: "invalid root" }, 400);
9213
+ }
9214
+ const resolution = root === "claude-uploads" ? resolveClaudeUploadsPath(rawPath) : resolveDataPath(rawPath);
8905
9215
  if (!resolution.ok) {
8906
9216
  if (resolution.status === 403) {
8907
- console.error(`[data] path-traversal-blocked requested="${rawPath}" resolved="${resolution.resolved ?? "n/a"}"`);
9217
+ console.error(`[data] path-traversal-blocked root="${root}" requested="${rawPath}" resolved="${resolution.resolved ?? "n/a"}"`);
8908
9218
  }
8909
9219
  return c.json({ error: resolution.error }, resolution.status);
8910
9220
  }
8911
9221
  const { absolute, relative: relPath2 } = resolution;
9222
+ if (root === "claude-uploads") {
9223
+ const attachmentId = relPath2.split("/").filter(Boolean)[0] ?? "";
9224
+ const owned = await knowledgeDocOwnedByAccount(accountId, attachmentId);
9225
+ if (!owned) {
9226
+ console.error(`[data] account-scope-blocked root="claude-uploads" attachmentId="${attachmentId.slice(0, 8)}\u2026" account=${accountId.slice(0, 8)}\u2026`);
9227
+ return c.json({ error: "Not found" }, 404);
9228
+ }
9229
+ } else if (crossesForeignAccountPartition(relPath2, accountId)) {
9230
+ console.error(`[data] account-scope-blocked endpoint="/api/admin/files/download" path="${relPath2}" account=${accountId.slice(0, 8)}\u2026`);
9231
+ return c.json({ error: "Not found" }, 404);
9232
+ }
8912
9233
  try {
8913
9234
  const info = await stat4(absolute);
8914
9235
  if (!info.isFile()) {
@@ -8918,7 +9239,7 @@ app15.get("/download", requireAdminSession, async (c) => {
8918
9239
  const mimeType = detectMimeType(absolute);
8919
9240
  const nodeStream = createReadStream2(absolute);
8920
9241
  const webStream = Readable2.toWeb(nodeStream);
8921
- console.error(`[data] file-download path="${relPath2}" size=${info.size}`);
9242
+ console.error(`[data] file-download root="${root}" path="${relPath2}" size=${info.size}`);
8922
9243
  const safeQuotedName = filename.replace(/[\r\n"\\]/g, "_");
8923
9244
  const encodedName = encodeURIComponent(filename);
8924
9245
  return new Response(webStream, {
@@ -8973,13 +9294,13 @@ app15.post("/upload", requireAdminSession, async (c) => {
8973
9294
  }
8974
9295
  const safeName = basename4(file.name).replace(/[\0/\\]/g, "_");
8975
9296
  const finalName = `${Date.now()}-${safeName}`;
8976
- const destDir = resolve13(DATA_ROOT, "uploads", accountId);
8977
- const destPath = resolve13(destDir, finalName);
9297
+ const destDir = resolve14(DATA_ROOT, "uploads", accountId);
9298
+ const destPath = resolve14(destDir, finalName);
8978
9299
  try {
8979
9300
  await mkdir3(destDir, { recursive: true });
8980
9301
  const dataRootReal = realpathSync4(DATA_ROOT);
8981
9302
  const destDirReal = realpathSync4(destDir);
8982
- if (destDirReal !== dataRootReal && !destDirReal.startsWith(dataRootReal + sep3)) {
9303
+ if (destDirReal !== dataRootReal && !destDirReal.startsWith(dataRootReal + sep4)) {
8983
9304
  console.error(`[data] path-traversal-blocked requested="uploads/${accountId}" resolved="${destDirReal}"`);
8984
9305
  return c.json({ error: "Upload destination escapes DATA_ROOT" }, 403);
8985
9306
  }
@@ -8992,6 +9313,9 @@ app15.post("/upload", requireAdminSession, async (c) => {
8992
9313
  }
8993
9314
  const relativeDest = `uploads/${accountId}/${finalName}`;
8994
9315
  console.error(`[data] file-upload filename="${safeName}" size=${file.size} dest="${relativeDest}"`);
9316
+ void indexFile(accountId, relativeDest).catch((err) => {
9317
+ console.error(`[data] file-upload index-failed dest="${relativeDest}" err="${err instanceof Error ? err.message : String(err)}"`);
9318
+ });
8995
9319
  return c.json({
8996
9320
  path: relativeDest,
8997
9321
  filename: finalName,
@@ -9032,7 +9356,7 @@ app15.delete("/", requireAdminSession, async (c) => {
9032
9356
  }
9033
9357
  const dot = base.lastIndexOf(".");
9034
9358
  const stem = dot === -1 ? base : base.slice(0, dot);
9035
- const sidecarPath = UUID_RE3.test(stem) && base !== `${stem}.meta.json` ? join11(dirname3(absolute), `${stem}.meta.json`) : null;
9359
+ const sidecarPath = UUID_RE3.test(stem) && base !== `${stem}.meta.json` ? join12(dirname3(absolute), `${stem}.meta.json`) : null;
9036
9360
  await unlink2(absolute);
9037
9361
  if (sidecarPath) {
9038
9362
  try {
@@ -9041,6 +9365,11 @@ app15.delete("/", requireAdminSession, async (c) => {
9041
9365
  }
9042
9366
  }
9043
9367
  console.error(`[data] file-delete path="${relPath2}" bytes=${info.size}`);
9368
+ try {
9369
+ await dropFileIndex(accountId, relPath2);
9370
+ } catch (err) {
9371
+ console.error(`[data] file-delete index-drop-failed path="${relPath2}" err="${err instanceof Error ? err.message : String(err)}"`);
9372
+ }
9044
9373
  const parsed = parseAttachmentPath(relPath2);
9045
9374
  if (parsed) {
9046
9375
  try {
@@ -9860,6 +10189,9 @@ app16.get("/", requireAdminSession, async (c) => {
9860
10189
  }
9861
10190
  const wantsBodyFulltext = !wildcard && labels.some((l) => CONVERSATION_PARENT_LABELS.has(l));
9862
10191
  const forwardedLabels = wildcard ? void 0 : wantsBodyFulltext ? Array.from(/* @__PURE__ */ new Set([...labels, ...MESSAGE_FAMILY_LABELS])) : labels;
10192
+ if (labels.includes("FileArtifact")) {
10193
+ await reconcileFileIndexDebounced(accountId);
10194
+ }
9863
10195
  const started = Date.now();
9864
10196
  const session = getSession();
9865
10197
  try {
@@ -11064,43 +11396,130 @@ var WRITE_CYPHER = `
11064
11396
  `;
11065
11397
  var graph_default_view_default = app21;
11066
11398
 
11067
- // server/routes/admin/file-attach.ts
11068
- var app22 = new Hono();
11069
- app22.post("/", async (c) => {
11399
+ // server/routes/admin/sidebar-artefacts.ts
11400
+ import neo4j2 from "neo4j-driver";
11401
+ import { readFile as readFile5, readdir as readdir4, stat as stat5 } from "fs/promises";
11402
+ import { resolve as resolve16, relative as relative4, isAbsolute, sep as sep6 } from "path";
11403
+ import { existsSync as existsSync16 } from "fs";
11404
+
11405
+ // app/lib/doc-source-resolve.ts
11406
+ import { readdirSync as readdirSync8, realpathSync as realpathSync5, statSync as statSync7 } from "fs";
11407
+ import { relative as relative3, resolve as resolve15, sep as sep5 } from "path";
11408
+ function containedFile(absPath, root) {
11070
11409
  try {
11071
- const body = await c.req.json();
11072
- const { filePath, accountId } = body;
11073
- if (!filePath || !accountId) {
11074
- return c.json({ error: "Missing required fields: filePath, accountId" }, 400);
11410
+ const real = realpathSync5(absPath);
11411
+ const rootReal = realpathSync5(root);
11412
+ if (real !== rootReal && !real.startsWith(rootReal + sep5)) return null;
11413
+ return statSync7(real).isFile() ? real : null;
11414
+ } catch {
11415
+ return null;
11416
+ }
11417
+ }
11418
+ function dataFileInDir(dir) {
11419
+ let entries;
11420
+ try {
11421
+ entries = readdirSync8(dir, { withFileTypes: true }).flatMap((e) => e.isFile() ? [e.name] : []);
11422
+ } catch {
11423
+ return null;
11424
+ }
11425
+ const file = entries.find((e) => !e.endsWith(".meta.json"));
11426
+ return file ? resolve15(dir, file) : null;
11427
+ }
11428
+ function mapToDownloadRoot(absPath, roots) {
11429
+ const underData = containedFile(absPath, roots.dataRoot);
11430
+ if (underData) {
11431
+ return { root: "data", relPath: relative3(realpathSync5(roots.dataRoot), underData), absReal: underData };
11432
+ }
11433
+ if (roots.claudeUploadsRoot) {
11434
+ const underClaude = containedFile(absPath, roots.claudeUploadsRoot);
11435
+ if (underClaude) {
11436
+ return {
11437
+ root: "claude-uploads",
11438
+ relPath: relative3(realpathSync5(roots.claudeUploadsRoot), underClaude),
11439
+ absReal: underClaude
11440
+ };
11075
11441
  }
11076
- const attachment = await storeGeneratedFile(accountId, filePath);
11077
- console.error(
11078
- `[admin:file-attach] stored path=${filePath} id=${attachment.attachmentId} filename=${JSON.stringify(attachment.filename)} size=${attachment.sizeBytes} mime=${attachment.mimeType}`
11079
- );
11080
- return c.json({
11081
- attachmentId: attachment.attachmentId,
11082
- filename: attachment.filename,
11083
- sizeBytes: attachment.sizeBytes,
11084
- mimeType: attachment.mimeType
11085
- });
11086
- } catch (err) {
11087
- const message = err instanceof Error ? err.message : String(err);
11088
- console.error(`[admin:file-attach] error: ${message}`);
11089
- return c.json({ error: message }, 500);
11090
11442
  }
11091
- });
11092
- var file_attach_default = app22;
11443
+ return null;
11444
+ }
11445
+ function resolveDocDownload(input, roots) {
11446
+ const { accountId, attachmentId, name, sourcePath, sourceType } = input;
11447
+ if (sourcePath) {
11448
+ const mapped = mapToDownloadRoot(sourcePath, roots);
11449
+ if (mapped) {
11450
+ return { ok: true, absPath: mapped.absReal, root: mapped.root, relPath: mapped.relPath, via: "stored" };
11451
+ }
11452
+ }
11453
+ if (attachmentId) {
11454
+ const uploadsDir = resolve15(roots.attachmentsRoot, accountId, attachmentId);
11455
+ const file = dataFileInDir(uploadsDir);
11456
+ if (file) {
11457
+ const real = containedFile(file, roots.dataRoot);
11458
+ if (real) {
11459
+ return { ok: true, absPath: real, root: "data", relPath: relative3(realpathSync5(roots.dataRoot), real), via: "uploads" };
11460
+ }
11461
+ }
11462
+ }
11463
+ if (attachmentId && roots.claudeUploadsRoot) {
11464
+ const claudeDir = resolve15(roots.claudeUploadsRoot, attachmentId);
11465
+ const file = dataFileInDir(claudeDir);
11466
+ if (file) {
11467
+ const real = containedFile(file, roots.claudeUploadsRoot);
11468
+ if (real) {
11469
+ return {
11470
+ ok: true,
11471
+ absPath: real,
11472
+ root: "claude-uploads",
11473
+ relPath: relative3(realpathSync5(roots.claudeUploadsRoot), real),
11474
+ via: "claude-uploads"
11475
+ };
11476
+ }
11477
+ }
11478
+ }
11479
+ if (name) {
11480
+ const outputFile = resolve15(roots.accountsDir, accountId, "output", name);
11481
+ const real = containedFile(outputFile, roots.dataRoot);
11482
+ if (real) {
11483
+ return { ok: true, absPath: real, root: "data", relPath: relative3(realpathSync5(roots.dataRoot), real), via: "output" };
11484
+ }
11485
+ }
11486
+ return { ok: false, reason: sourceType === "web" ? "no-persisted-file" : "unresolved" };
11487
+ }
11093
11488
 
11094
11489
  // server/routes/admin/sidebar-artefacts.ts
11095
- import neo4j2 from "neo4j-driver";
11096
- import { readFile as readFile5, readdir as readdir3, stat as stat5 } from "fs/promises";
11097
- import { resolve as resolve14, relative as relative2, isAbsolute } from "path";
11098
- import { existsSync as existsSync16 } from "fs";
11099
11490
  var LIMIT = 50;
11100
11491
  var TEXT_MIME_PREFIXES = ["text/", "application/json", "application/markdown"];
11101
11492
  var ADMIN_AGENT_FILES = ["IDENTITY.md", "SOUL.md", "KNOWLEDGE.md"];
11102
- var app23 = new Hono();
11103
- app23.get("/", requireAdminSession, async (c) => {
11493
+ function toDataRootRelPath(absPath) {
11494
+ if (absPath !== DATA_ROOT && !absPath.startsWith(DATA_ROOT + sep6)) return null;
11495
+ return relative4(DATA_ROOT, absPath);
11496
+ }
11497
+ function resolveDocDownloadRow(meta) {
11498
+ const result = resolveDocDownload(
11499
+ {
11500
+ accountId: meta.accountId,
11501
+ attachmentId: meta.attachmentId,
11502
+ name: meta.name,
11503
+ sourcePath: meta.sourcePath,
11504
+ sourceType: meta.sourceType
11505
+ },
11506
+ {
11507
+ dataRoot: DATA_ROOT,
11508
+ attachmentsRoot: ATTACHMENTS_ROOT,
11509
+ accountsDir: ACCOUNTS_DIR,
11510
+ claudeUploadsRoot: CLAUDE_UPLOADS_ROOT
11511
+ }
11512
+ );
11513
+ const aid = meta.attachmentId ? meta.attachmentId.slice(0, 8) : "none";
11514
+ if (result.ok) {
11515
+ console.log(`[admin/sidebar-artefacts] download-resolved id=${aid} via=${result.via} root=${result.root}`);
11516
+ return { downloadPath: result.relPath, downloadRoot: result.root };
11517
+ }
11518
+ console.log(`[admin/sidebar-artefacts] not-downloadable id=${aid} reason=${result.reason}`);
11519
+ return { downloadPath: null, downloadRoot: null };
11520
+ }
11521
+ var app22 = new Hono();
11522
+ app22.get("/", requireAdminSession, async (c) => {
11104
11523
  const cacheKey = c.var.cacheKey;
11105
11524
  const accountId = getAccountIdForSession(cacheKey);
11106
11525
  if (!accountId) {
@@ -11111,7 +11530,7 @@ app23.get("/", requireAdminSession, async (c) => {
11111
11530
  if (docs === null) {
11112
11531
  return c.json({ error: "Failed to load artefacts" }, 500);
11113
11532
  }
11114
- const accountDir = resolve14(ACCOUNTS_DIR, accountId);
11533
+ const accountDir = resolve16(ACCOUNTS_DIR, accountId);
11115
11534
  const agents = await fetchAgentTemplateRows(accountDir);
11116
11535
  const artefacts = [...docs, ...agents].sort(
11117
11536
  (a, b) => (b.updatedAt ?? "").localeCompare(a.updatedAt ?? "")
@@ -11130,7 +11549,8 @@ async function fetchKnowledgeDocs(accountId) {
11130
11549
  `MATCH (d:KnowledgeDocument { accountId: $accountId })
11131
11550
  WHERE d.deletedAt IS NULL AND NOT d:Trashed
11132
11551
  RETURN d.attachmentId AS id, d.name AS name, d.updatedAt AS updatedAt,
11133
- d.attachmentId AS attachmentId, d.encodingFormat AS mimeType
11552
+ d.attachmentId AS attachmentId, d.encodingFormat AS mimeType,
11553
+ d.sourcePath AS sourcePath, d.sourceType AS sourceType
11134
11554
  ORDER BY d.updatedAt DESC
11135
11555
  LIMIT $limit`,
11136
11556
  { accountId, limit: neo4j2.int(LIMIT) }
@@ -11140,7 +11560,9 @@ async function fetchKnowledgeDocs(accountId) {
11140
11560
  name: r.get("name") ?? "",
11141
11561
  updatedAt: r.get("updatedAt") ?? "",
11142
11562
  attachmentId: r.get("attachmentId"),
11143
- mimeType: r.get("mimeType") ?? ""
11563
+ mimeType: r.get("mimeType") ?? "",
11564
+ sourcePath: r.get("sourcePath") ?? null,
11565
+ sourceType: r.get("sourceType") ?? null
11144
11566
  }));
11145
11567
  } catch (err) {
11146
11568
  const message = err instanceof Error ? err.message : String(err);
@@ -11152,6 +11574,13 @@ async function fetchKnowledgeDocs(accountId) {
11152
11574
  }
11153
11575
  return Promise.all(metas.map(async (m) => {
11154
11576
  const { content, skipReason } = await readArtefactContent(accountId, m.attachmentId, m.mimeType, m.name);
11577
+ const { downloadPath, downloadRoot } = resolveDocDownloadRow({
11578
+ accountId,
11579
+ attachmentId: m.attachmentId,
11580
+ name: m.name,
11581
+ sourcePath: m.sourcePath,
11582
+ sourceType: m.sourceType
11583
+ });
11155
11584
  return {
11156
11585
  id: m.id,
11157
11586
  name: m.name,
@@ -11160,7 +11589,9 @@ async function fetchKnowledgeDocs(accountId) {
11160
11589
  content,
11161
11590
  editable: skipReason === null,
11162
11591
  mimeType: m.mimeType,
11163
- skipReason
11592
+ skipReason,
11593
+ downloadPath,
11594
+ downloadRoot
11164
11595
  };
11165
11596
  }));
11166
11597
  }
@@ -11174,8 +11605,8 @@ async function readArtefactContent(accountId, attachmentId, mimeType, displayNam
11174
11605
  logSkip(displayName, "non-text-mime", mimeType);
11175
11606
  return { content: "", skipReason: "non-text-mime" };
11176
11607
  }
11177
- const accountDir = resolve14(ATTACHMENTS_ROOT, accountId);
11178
- const dir = resolve14(accountDir, attachmentId);
11608
+ const accountDir = resolve16(ATTACHMENTS_ROOT, accountId);
11609
+ const dir = resolve16(accountDir, attachmentId);
11179
11610
  try {
11180
11611
  validateFilePathInAccount(dir, accountDir);
11181
11612
  } catch {
@@ -11183,13 +11614,13 @@ async function readArtefactContent(accountId, attachmentId, mimeType, displayNam
11183
11614
  return { content: "", skipReason: "containment-rejected" };
11184
11615
  }
11185
11616
  try {
11186
- const entries = await readdir3(dir);
11617
+ const entries = await readdir4(dir);
11187
11618
  const dataFile = entries.find((f) => !f.endsWith(".meta.json"));
11188
11619
  if (!dataFile) {
11189
11620
  logSkip(displayName, "missing-on-disk", mimeType);
11190
11621
  return { content: "", skipReason: "missing-on-disk" };
11191
11622
  }
11192
- return { content: await readFile5(resolve14(dir, dataFile), "utf-8"), skipReason: null };
11623
+ return { content: await readFile5(resolve16(dir, dataFile), "utf-8"), skipReason: null };
11193
11624
  } catch (err) {
11194
11625
  const message = err instanceof Error ? err.message : String(err);
11195
11626
  console.error(`[admin/sidebar-artefacts] read-failed attachmentId=${attachmentId.slice(0, 8)} error="${message}"`);
@@ -11205,8 +11636,8 @@ function logSkip(name, reason, mimeType) {
11205
11636
  async function fetchAgentTemplateRows(accountDir) {
11206
11637
  const rows = [];
11207
11638
  for (const filename of ADMIN_AGENT_FILES) {
11208
- const overridePath = resolve14(accountDir, "agents", "admin", filename);
11209
- const bundledPath = resolve14(PLATFORM_ROOT, "templates", "agents", "admin", filename);
11639
+ const overridePath = resolve16(accountDir, "agents", "admin", filename);
11640
+ const bundledPath = resolve16(PLATFORM_ROOT, "templates", "agents", "admin", filename);
11210
11641
  const labelStem = filename.replace(/\.md$/, "");
11211
11642
  const row = await readAgentTemplateRow({
11212
11643
  id: `agent-template:admin:${filename}`,
@@ -11220,12 +11651,12 @@ async function fetchAgentTemplateRows(accountDir) {
11220
11651
  });
11221
11652
  if (row) rows.push(row);
11222
11653
  }
11223
- const overrideDir = resolve14(accountDir, "specialists", "agents");
11224
- const bundledDir = resolve14(PLATFORM_ROOT, "templates", "specialists", "agents");
11654
+ const overrideDir = resolve16(accountDir, "specialists", "agents");
11655
+ const bundledDir = resolve16(PLATFORM_ROOT, "templates", "specialists", "agents");
11225
11656
  const specialistNames = await unionSpecialistFilenames(overrideDir, bundledDir);
11226
11657
  for (const filename of specialistNames) {
11227
- const overridePath = resolve14(overrideDir, filename);
11228
- const bundledPath = resolve14(bundledDir, filename);
11658
+ const overridePath = resolve16(overrideDir, filename);
11659
+ const bundledPath = resolve16(bundledDir, filename);
11229
11660
  const row = await readAgentTemplateRow({
11230
11661
  id: `agent-template:specialist:${filename}`,
11231
11662
  displayName: filename.replace(/\.md$/, ""),
@@ -11245,7 +11676,7 @@ async function unionSpecialistFilenames(overrideDir, bundledDir) {
11245
11676
  for (const dir of [overrideDir, bundledDir]) {
11246
11677
  if (!existsSync16(dir)) continue;
11247
11678
  try {
11248
- const entries = await readdir3(dir);
11679
+ const entries = await readdir4(dir);
11249
11680
  for (const entry of entries) {
11250
11681
  if (entry.endsWith(".md")) names.add(entry);
11251
11682
  }
@@ -11284,6 +11715,7 @@ async function readAgentTemplateRow(inp) {
11284
11715
  readFile5(chosenPath, "utf-8"),
11285
11716
  stat5(chosenPath)
11286
11717
  ]);
11718
+ const dlPath = toDataRootRelPath(chosenPath);
11287
11719
  return {
11288
11720
  id: inp.id,
11289
11721
  name: inp.displayName,
@@ -11292,7 +11724,14 @@ async function readAgentTemplateRow(inp) {
11292
11724
  content,
11293
11725
  editable: inp.editable,
11294
11726
  mimeType: "text/markdown",
11295
- skipReason: null
11727
+ skipReason: null,
11728
+ // Override paths live under <accountDir> (inside DATA_ROOT) and are
11729
+ // downloadable; bundled-fallback templates live under PLATFORM_ROOT
11730
+ // (outside DATA_ROOT) → null, so the client shows "not downloadable".
11731
+ // Agent templates only ever resolve under DATA_ROOT, so the download
11732
+ // root is `data` whenever the path is non-null.
11733
+ downloadPath: dlPath,
11734
+ downloadRoot: dlPath ? "data" : null
11296
11735
  };
11297
11736
  } catch (err) {
11298
11737
  const message = err instanceof Error ? err.message : String(err);
@@ -11303,19 +11742,19 @@ async function readAgentTemplateRow(inp) {
11303
11742
  }
11304
11743
  }
11305
11744
  function isWithin(target, root) {
11306
- const rel = relative2(root, target);
11745
+ const rel = relative4(root, target);
11307
11746
  return !rel.startsWith("..") && !isAbsolute(rel);
11308
11747
  }
11309
- var sidebar_artefacts_default = app23;
11748
+ var sidebar_artefacts_default = app22;
11310
11749
 
11311
11750
  // server/routes/admin/sidebar-artefact-save.ts
11312
- import { mkdir as mkdir4, readdir as readdir4, stat as stat6, writeFile as writeFile5 } from "fs/promises";
11313
- import { resolve as resolve15 } from "path";
11751
+ import { mkdir as mkdir4, readdir as readdir5, stat as stat6, writeFile as writeFile5 } from "fs/promises";
11752
+ import { resolve as resolve17 } from "path";
11314
11753
  import { existsSync as existsSync17 } from "fs";
11315
11754
  var ADMIN_AGENT_FILES2 = /* @__PURE__ */ new Set(["IDENTITY.md", "SOUL.md", "KNOWLEDGE.md"]);
11316
11755
  var UUID_RE4 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
11317
- var app24 = new Hono();
11318
- app24.post("/", requireAdminSession, async (c) => {
11756
+ var app23 = new Hono();
11757
+ app23.post("/", requireAdminSession, async (c) => {
11319
11758
  const cacheKey = c.var.cacheKey;
11320
11759
  const accountId = getAccountIdForSession(cacheKey);
11321
11760
  if (!accountId) return c.json({ error: "Account not found for session" }, 401);
@@ -11323,7 +11762,7 @@ app24.post("/", requireAdminSession, async (c) => {
11323
11762
  if (!body || typeof body.id !== "string" || typeof body.content !== "string") {
11324
11763
  return c.json({ error: "id and content required" }, 400);
11325
11764
  }
11326
- const accountDir = resolve15(ACCOUNTS_DIR, accountId);
11765
+ const accountDir = resolve17(ACCOUNTS_DIR, accountId);
11327
11766
  const resolved = await resolveSavePath(body.id, accountId, accountDir);
11328
11767
  if (resolved.kind === "heal-race") {
11329
11768
  c.header("Retry-After", "2");
@@ -11368,17 +11807,17 @@ async function resolveSavePath(id, accountId, accountDir) {
11368
11807
  if (role !== "admin" || !ADMIN_AGENT_FILES2.has(filename)) {
11369
11808
  return { kind: "reject", status: 400, reason: "invalid-id" };
11370
11809
  }
11371
- const parent = resolve15(accountDir, "agents", "admin");
11810
+ const parent = resolve17(accountDir, "agents", "admin");
11372
11811
  await mkdir4(parent, { recursive: true });
11373
11812
  try {
11374
11813
  validateFilePathInAccount(parent, accountDir);
11375
11814
  } catch {
11376
11815
  return { kind: "reject", status: 400, reason: "containment-rejected" };
11377
11816
  }
11378
- return { kind: "admin-template", path: resolve15(parent, filename) };
11817
+ return { kind: "admin-template", path: resolve17(parent, filename) };
11379
11818
  }
11380
11819
  if (UUID_RE4.test(id)) {
11381
- const dir = resolve15(ATTACHMENTS_ROOT, accountId, id);
11820
+ const dir = resolve17(ATTACHMENTS_ROOT, accountId, id);
11382
11821
  if (!existsSync17(dir)) {
11383
11822
  const attShort = id.slice(0, 8);
11384
11823
  if (isHealPending(accountId, id)) {
@@ -11411,31 +11850,31 @@ async function resolveSavePath(id, accountId, accountDir) {
11411
11850
  }
11412
11851
  }
11413
11852
  try {
11414
- validateFilePathInAccount(dir, resolve15(ATTACHMENTS_ROOT, accountId));
11853
+ validateFilePathInAccount(dir, resolve17(ATTACHMENTS_ROOT, accountId));
11415
11854
  } catch {
11416
11855
  return { kind: "reject", status: 400, reason: "containment-rejected" };
11417
11856
  }
11418
- const entries = await readdir4(dir);
11857
+ const entries = await readdir5(dir);
11419
11858
  const dataFile = entries.find((f) => !f.endsWith(".meta.json"));
11420
11859
  if (!dataFile) {
11421
11860
  return { kind: "reject", status: 400, reason: "not-found" };
11422
11861
  }
11423
- return { kind: "knowledge-doc", path: resolve15(dir, dataFile) };
11862
+ return { kind: "knowledge-doc", path: resolve17(dir, dataFile) };
11424
11863
  }
11425
11864
  return { kind: "reject", status: 400, reason: "invalid-id" };
11426
11865
  }
11427
11866
  function relPath(absPath, root) {
11428
11867
  return absPath.startsWith(root) ? absPath.slice(root.length + 1) : absPath;
11429
11868
  }
11430
- var sidebar_artefact_save_default = app24;
11869
+ var sidebar_artefact_save_default = app23;
11431
11870
 
11432
11871
  // server/routes/admin/sidebar-artefact-content.ts
11433
- import { readFile as readFile6, readdir as readdir5 } from "fs/promises";
11872
+ import { readFile as readFile6, readdir as readdir6 } from "fs/promises";
11434
11873
  import { existsSync as existsSync18 } from "fs";
11435
- import { resolve as resolve16 } from "path";
11874
+ import { resolve as resolve18 } from "path";
11436
11875
  var UUID_RE5 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
11437
- var app25 = new Hono();
11438
- app25.get("/", requireAdminSession, async (c) => {
11876
+ var app24 = new Hono();
11877
+ app24.get("/", requireAdminSession, async (c) => {
11439
11878
  const cacheKey = c.var.cacheKey;
11440
11879
  const accountId = getAccountIdForSession(cacheKey);
11441
11880
  if (!accountId) return new Response("Unauthorized", { status: 401 });
@@ -11444,26 +11883,26 @@ app25.get("/", requireAdminSession, async (c) => {
11444
11883
  console.error(`[admin/sidebar-artefact-content] not-found id=${id.slice(0, 8)}`);
11445
11884
  return new Response("Not found", { status: 404 });
11446
11885
  }
11447
- const dir = resolve16(ATTACHMENTS_ROOT, accountId, id);
11886
+ const dir = resolve18(ATTACHMENTS_ROOT, accountId, id);
11448
11887
  if (!existsSync18(dir)) {
11449
11888
  console.error(`[admin/sidebar-artefact-content] not-found id=${id.slice(0, 8)}`);
11450
11889
  return new Response("Not found", { status: 404 });
11451
11890
  }
11452
11891
  let meta;
11453
11892
  try {
11454
- meta = JSON.parse(await readFile6(resolve16(dir, `${id}.meta.json`), "utf-8"));
11893
+ meta = JSON.parse(await readFile6(resolve18(dir, `${id}.meta.json`), "utf-8"));
11455
11894
  } catch {
11456
11895
  console.error(`[admin/sidebar-artefact-content] not-found id=${id.slice(0, 8)}`);
11457
11896
  return new Response("Not found", { status: 404 });
11458
11897
  }
11459
- const entries = await readdir5(dir);
11898
+ const entries = await readdir6(dir);
11460
11899
  const dataFile = entries.find((f) => !f.endsWith(".meta.json"));
11461
11900
  if (!dataFile) {
11462
11901
  console.error(`[admin/sidebar-artefact-content] not-found id=${id.slice(0, 8)}`);
11463
11902
  return new Response("Not found", { status: 404 });
11464
11903
  }
11465
11904
  const start = Date.now();
11466
- const buffer = await readFile6(resolve16(dir, dataFile));
11905
+ const buffer = await readFile6(resolve18(dir, dataFile));
11467
11906
  const ms = Date.now() - start;
11468
11907
  console.log(
11469
11908
  `[admin/sidebar-artefact-content] account=${accountId} id=${id.slice(0, 8)} mime=${meta.mimeType} bytes=${buffer.length} ms=${ms}`
@@ -11476,7 +11915,7 @@ app25.get("/", requireAdminSession, async (c) => {
11476
11915
  }
11477
11916
  });
11478
11917
  });
11479
- var sidebar_artefact_content_default = app25;
11918
+ var sidebar_artefact_content_default = app24;
11480
11919
 
11481
11920
  // server/routes/admin/system-stats.ts
11482
11921
  import { readFile as readFile7 } from "fs/promises";
@@ -11573,8 +12012,8 @@ async function sample() {
11573
12012
  if (process.platform === "linux") return sampleLinux();
11574
12013
  return sampleOsFallback();
11575
12014
  }
11576
- var app26 = new Hono();
11577
- app26.get("/", async (c) => {
12015
+ var app25 = new Hono();
12016
+ app25.get("/", async (c) => {
11578
12017
  const started = Date.now();
11579
12018
  if (!inflight) {
11580
12019
  inflight = sample().finally(() => {
@@ -11597,14 +12036,14 @@ app26.get("/", async (c) => {
11597
12036
  return c.json({ degraded: true, reason, sampledAtMs: Date.now() });
11598
12037
  }
11599
12038
  });
11600
- var system_stats_default = app26;
12039
+ var system_stats_default = app25;
11601
12040
 
11602
12041
  // server/routes/admin/health.ts
11603
12042
  import { existsSync as existsSync19, readFileSync as readFileSync14 } from "fs";
11604
- import { resolve as resolve17, join as join12 } from "path";
11605
- var PLATFORM_ROOT6 = process.env.MAXY_PLATFORM_ROOT ?? resolve17(process.cwd(), "..");
12043
+ import { resolve as resolve19, join as join13 } from "path";
12044
+ var PLATFORM_ROOT6 = process.env.MAXY_PLATFORM_ROOT ?? resolve19(process.cwd(), "..");
11606
12045
  var brandHostname = "maxy";
11607
- var brandJsonPath = join12(PLATFORM_ROOT6, "config", "brand.json");
12046
+ var brandJsonPath = join13(PLATFORM_ROOT6, "config", "brand.json");
11608
12047
  if (existsSync19(brandJsonPath)) {
11609
12048
  try {
11610
12049
  const brand = JSON.parse(readFileSync14(brandJsonPath, "utf-8"));
@@ -11612,7 +12051,7 @@ if (existsSync19(brandJsonPath)) {
11612
12051
  } catch {
11613
12052
  }
11614
12053
  }
11615
- var VERSION_FILE = resolve17(PLATFORM_ROOT6, `config/.${brandHostname}-version`);
12054
+ var VERSION_FILE = resolve19(PLATFORM_ROOT6, `config/.${brandHostname}-version`);
11616
12055
  var PROCESS_STARTED_AT = (/* @__PURE__ */ new Date()).toISOString();
11617
12056
  var PROBE_TIMEOUT_MS = 1e3;
11618
12057
  function readVersion() {
@@ -11642,8 +12081,8 @@ async function probeConversationDb() {
11642
12081
  });
11643
12082
  }
11644
12083
  }
11645
- var app27 = new Hono();
11646
- app27.get("/", async (c) => {
12084
+ var app26 = new Hono();
12085
+ app26.get("/", async (c) => {
11647
12086
  const version = readVersion();
11648
12087
  const probe = await probeConversationDb();
11649
12088
  const uptimeMs = Date.now() - new Date(PROCESS_STARTED_AT).getTime();
@@ -11665,7 +12104,7 @@ app27.get("/", async (c) => {
11665
12104
  uptimeMs
11666
12105
  });
11667
12106
  });
11668
- var health_default2 = app27;
12107
+ var health_default2 = app26;
11669
12108
 
11670
12109
  // server/routes/admin/linkedin-ingest.ts
11671
12110
  var TAG20 = "[linkedin-ingest-route]";
@@ -11761,8 +12200,8 @@ function buildInitialMessage(env) {
11761
12200
  }
11762
12201
  return header;
11763
12202
  }
11764
- var app28 = new Hono();
11765
- app28.post("/", requireAdminSession, async (c) => {
12203
+ var app27 = new Hono();
12204
+ app27.post("/", requireAdminSession, async (c) => {
11766
12205
  let body;
11767
12206
  try {
11768
12207
  body = await c.req.json();
@@ -11816,7 +12255,7 @@ app28.post("/", requireAdminSession, async (c) => {
11816
12255
  );
11817
12256
  return c.json({ ok: true, dispatchId: envelope.dispatchId, taskId: claudeSessionId }, 202);
11818
12257
  });
11819
- var linkedin_ingest_default = app28;
12258
+ var linkedin_ingest_default = app27;
11820
12259
 
11821
12260
  // server/routes/admin/post-turn-context.ts
11822
12261
  import neo4j3 from "neo4j-driver";
@@ -11858,8 +12297,8 @@ function pruneProperties(raw) {
11858
12297
  }
11859
12298
  return out;
11860
12299
  }
11861
- var app29 = new Hono();
11862
- app29.get("/", async (c) => {
12300
+ var app28 = new Hono();
12301
+ app28.get("/", async (c) => {
11863
12302
  const remoteAddr = c.env?.incoming?.socket?.remoteAddress ?? "";
11864
12303
  if (!isLoopbackAddr2(remoteAddr)) {
11865
12304
  console.error(`${TAG21} reject reason=non-loopback remoteAddr=${remoteAddr}`);
@@ -11919,7 +12358,7 @@ app29.get("/", async (c) => {
11919
12358
  await session.close();
11920
12359
  }
11921
12360
  });
11922
- var post_turn_context_default = app29;
12361
+ var post_turn_context_default = app28;
11923
12362
 
11924
12363
  // server/routes/admin/public-session-context.ts
11925
12364
  import neo4j4 from "neo4j-driver";
@@ -11961,8 +12400,8 @@ function pruneProperties2(raw) {
11961
12400
  }
11962
12401
  return out;
11963
12402
  }
11964
- var app30 = new Hono();
11965
- app30.get("/", async (c) => {
12403
+ var app29 = new Hono();
12404
+ app29.get("/", async (c) => {
11966
12405
  const remoteAddr = c.env?.incoming?.socket?.remoteAddress ?? "";
11967
12406
  if (!isLoopbackAddr3(remoteAddr)) {
11968
12407
  console.error(`${TAG22} reject reason=non-loopback remoteAddr=${remoteAddr}`);
@@ -12021,15 +12460,15 @@ app30.get("/", async (c) => {
12021
12460
  await session.close();
12022
12461
  }
12023
12462
  });
12024
- var public_session_context_default = app30;
12463
+ var public_session_context_default = app29;
12025
12464
 
12026
12465
  // server/routes/admin/public-session-exit.ts
12027
12466
  var TAG23 = "[public-session-exit-route]";
12028
12467
  function isLoopbackAddr4(addr) {
12029
12468
  return addr === "127.0.0.1" || addr === "::1" || addr === "::ffff:127.0.0.1";
12030
12469
  }
12031
- var app31 = new Hono();
12032
- app31.post("/", async (c) => {
12470
+ var app30 = new Hono();
12471
+ app30.post("/", async (c) => {
12033
12472
  const remoteAddr = c.env?.incoming?.socket?.remoteAddress ?? "";
12034
12473
  if (!isLoopbackAddr4(remoteAddr)) {
12035
12474
  console.error(`${TAG23} reject reason=non-loopback remoteAddr=${remoteAddr}`);
@@ -12056,15 +12495,15 @@ app31.post("/", async (c) => {
12056
12495
  handlePublicSessionExit(sessionId);
12057
12496
  return c.json({ ok: true });
12058
12497
  });
12059
- var public_session_exit_default = app31;
12498
+ var public_session_exit_default = app30;
12060
12499
 
12061
12500
  // server/routes/admin/access-session-evict.ts
12062
12501
  var TAG24 = "[access-session-evict]";
12063
12502
  function isLoopbackAddr5(addr) {
12064
12503
  return addr === "127.0.0.1" || addr === "::1" || addr === "::ffff:127.0.0.1";
12065
12504
  }
12066
- var app32 = new Hono();
12067
- app32.post("/", async (c) => {
12505
+ var app31 = new Hono();
12506
+ app31.post("/", async (c) => {
12068
12507
  const remoteAddr = c.env?.incoming?.socket?.remoteAddress ?? "";
12069
12508
  if (!isLoopbackAddr5(remoteAddr)) {
12070
12509
  console.error(`${TAG24} reject reason=non-loopback remoteAddr=${remoteAddr}`);
@@ -12092,49 +12531,48 @@ app32.post("/", async (c) => {
12092
12531
  console.log(`${TAG24} grantId=${grantId} dropped=${dropped}`);
12093
12532
  return c.json({ ok: true, dropped });
12094
12533
  });
12095
- var access_session_evict_default = app32;
12534
+ var access_session_evict_default = app31;
12096
12535
 
12097
12536
  // server/routes/admin/index.ts
12098
- var app33 = new Hono();
12099
- app33.route("/session", session_default);
12100
- app33.route("/logs", logs_default);
12101
- app33.route("/claude-info", claude_info_default);
12102
- app33.route("/attachment", attachment_default);
12103
- app33.route("/agents", agents_default);
12104
- app33.route("/sessions", sessions_default);
12105
- app33.route("/claude-sessions", claude_sessions_default);
12106
- app33.route("/log-ingest", log_ingest_default);
12107
- app33.route("/events", events_default);
12108
- app33.route("/files", files_default);
12109
- app33.route("/graph-search", graph_search_default);
12110
- app33.route("/graph-subgraph", graph_subgraph_default);
12111
- app33.route("/graph-delete", graph_delete_default);
12112
- app33.route("/graph-restore", graph_restore_default);
12113
- app33.route("/graph-labels-in-graph", graph_labels_in_graph_default);
12114
- app33.route("/graph-default-view", graph_default_view_default);
12115
- app33.route("/file-attach", file_attach_default);
12116
- app33.route("/sidebar-artefacts", sidebar_artefacts_default);
12117
- app33.route("/sidebar-artefact-save", sidebar_artefact_save_default);
12118
- app33.route("/sidebar-artefact-content", sidebar_artefact_content_default);
12119
- app33.route("/system-stats", system_stats_default);
12120
- app33.route("/health-brand", health_default2);
12121
- app33.route("/linkedin-ingest", linkedin_ingest_default);
12122
- app33.route("/post-turn-context", post_turn_context_default);
12123
- app33.route("/public-session-context", public_session_context_default);
12124
- app33.route("/public-session-exit", public_session_exit_default);
12125
- app33.route("/access-session-evict", access_session_evict_default);
12126
- var admin_default = app33;
12537
+ var app32 = new Hono();
12538
+ app32.route("/session", session_default);
12539
+ app32.route("/logs", logs_default);
12540
+ app32.route("/claude-info", claude_info_default);
12541
+ app32.route("/attachment", attachment_default);
12542
+ app32.route("/agents", agents_default);
12543
+ app32.route("/sessions", sessions_default);
12544
+ app32.route("/claude-sessions", claude_sessions_default);
12545
+ app32.route("/log-ingest", log_ingest_default);
12546
+ app32.route("/events", events_default);
12547
+ app32.route("/files", files_default);
12548
+ app32.route("/graph-search", graph_search_default);
12549
+ app32.route("/graph-subgraph", graph_subgraph_default);
12550
+ app32.route("/graph-delete", graph_delete_default);
12551
+ app32.route("/graph-restore", graph_restore_default);
12552
+ app32.route("/graph-labels-in-graph", graph_labels_in_graph_default);
12553
+ app32.route("/graph-default-view", graph_default_view_default);
12554
+ app32.route("/sidebar-artefacts", sidebar_artefacts_default);
12555
+ app32.route("/sidebar-artefact-save", sidebar_artefact_save_default);
12556
+ app32.route("/sidebar-artefact-content", sidebar_artefact_content_default);
12557
+ app32.route("/system-stats", system_stats_default);
12558
+ app32.route("/health-brand", health_default2);
12559
+ app32.route("/linkedin-ingest", linkedin_ingest_default);
12560
+ app32.route("/post-turn-context", post_turn_context_default);
12561
+ app32.route("/public-session-context", public_session_context_default);
12562
+ app32.route("/public-session-exit", public_session_exit_default);
12563
+ app32.route("/access-session-evict", access_session_evict_default);
12564
+ var admin_default = app32;
12127
12565
 
12128
12566
  // app/lib/access-gate.ts
12129
12567
  import neo4j5 from "neo4j-driver";
12130
12568
  import { readFileSync as readFileSync15 } from "fs";
12131
- import { resolve as resolve18 } from "path";
12569
+ import { resolve as resolve20 } from "path";
12132
12570
  import { randomUUID as randomUUID8 } from "crypto";
12133
- var PLATFORM_ROOT7 = process.env.MAXY_PLATFORM_ROOT ?? resolve18(process.cwd(), "..");
12571
+ var PLATFORM_ROOT7 = process.env.MAXY_PLATFORM_ROOT ?? resolve20(process.cwd(), "..");
12134
12572
  var driver = null;
12135
12573
  function readPassword() {
12136
12574
  if (process.env.NEO4J_PASSWORD) return process.env.NEO4J_PASSWORD;
12137
- const passwordFile = resolve18(PLATFORM_ROOT7, "config/.neo4j-password");
12575
+ const passwordFile = resolve20(PLATFORM_ROOT7, "config/.neo4j-password");
12138
12576
  try {
12139
12577
  return readFileSync15(passwordFile, "utf-8").trim();
12140
12578
  } catch {
@@ -12337,8 +12775,8 @@ async function generateNewMagicToken(grantId) {
12337
12775
  var TAG25 = "[access-verify]";
12338
12776
  var MINT_TAG = "[access-session-mint]";
12339
12777
  var COOKIE_NAME = "__access_session";
12340
- var app34 = new Hono();
12341
- app34.post("/", async (c) => {
12778
+ var app33 = new Hono();
12779
+ app33.post("/", async (c) => {
12342
12780
  const ip = c.var.clientIp || "unknown";
12343
12781
  let body;
12344
12782
  try {
@@ -12420,13 +12858,13 @@ app34.post("/", async (c) => {
12420
12858
  displayName: grant.displayName
12421
12859
  });
12422
12860
  });
12423
- var verify_token_default = app34;
12861
+ var verify_token_default = app33;
12424
12862
 
12425
12863
  // app/lib/access-email.ts
12426
12864
  import { spawn as spawn2 } from "child_process";
12427
- import { resolve as resolve19 } from "path";
12428
- var PLATFORM_ROOT8 = process.env.MAXY_PLATFORM_ROOT ?? resolve19(process.cwd(), "..");
12429
- var SEND_SCRIPT = resolve19(
12865
+ import { resolve as resolve21 } from "path";
12866
+ var PLATFORM_ROOT8 = process.env.MAXY_PLATFORM_ROOT ?? resolve21(process.cwd(), "..");
12867
+ var SEND_SCRIPT = resolve21(
12430
12868
  PLATFORM_ROOT8,
12431
12869
  "plugins",
12432
12870
  "email",
@@ -12483,9 +12921,9 @@ async function sendMagicLinkEmail(payload) {
12483
12921
 
12484
12922
  // server/routes/access/request-magic-link.ts
12485
12923
  var TAG26 = "[access-request-link]";
12486
- var app35 = new Hono();
12924
+ var app34 = new Hono();
12487
12925
  var VISITOR_MESSAGE = "If that email is on the invite list, a fresh link is on the way.";
12488
- app35.post("/", async (c) => {
12926
+ app34.post("/", async (c) => {
12489
12927
  let body;
12490
12928
  try {
12491
12929
  body = await c.req.json();
@@ -12559,17 +12997,17 @@ app35.post("/", async (c) => {
12559
12997
  );
12560
12998
  return c.json({ message: VISITOR_MESSAGE }, 200);
12561
12999
  });
12562
- var request_magic_link_default = app35;
13000
+ var request_magic_link_default = app34;
12563
13001
 
12564
13002
  // server/routes/access/index.ts
12565
- var app36 = new Hono();
12566
- app36.route("/verify-token", verify_token_default);
12567
- app36.route("/request-magic-link", request_magic_link_default);
12568
- var access_default = app36;
13003
+ var app35 = new Hono();
13004
+ app35.route("/verify-token", verify_token_default);
13005
+ app35.route("/request-magic-link", request_magic_link_default);
13006
+ var access_default = app35;
12569
13007
 
12570
13008
  // server/routes/sites.ts
12571
- import { existsSync as existsSync20, readFileSync as readFileSync16, realpathSync as realpathSync5, statSync as statSync7 } from "fs";
12572
- import { resolve as resolve20 } from "path";
13009
+ import { existsSync as existsSync20, readFileSync as readFileSync16, realpathSync as realpathSync6, statSync as statSync8 } from "fs";
13010
+ import { resolve as resolve22 } from "path";
12573
13011
  var SAFE_SEG_RE = /^[a-z0-9_][a-z0-9_.-]{0,99}$/i;
12574
13012
  var MIME = {
12575
13013
  ".html": "text/html; charset=utf-8",
@@ -12600,8 +13038,8 @@ function getExt(p) {
12600
13038
  if (idx < p.lastIndexOf("/")) return "";
12601
13039
  return p.slice(idx).toLowerCase();
12602
13040
  }
12603
- var app37 = new Hono();
12604
- app37.get("/:rel{.*}", (c) => {
13041
+ var app36 = new Hono();
13042
+ app36.get("/:rel{.*}", (c) => {
12605
13043
  const reqPath = c.req.path;
12606
13044
  const rawRel = c.req.param("rel") ?? "";
12607
13045
  const trimmed = rawRel.replace(/^\/+/, "").replace(/\/+$/, "");
@@ -12626,15 +13064,15 @@ app37.get("/:rel{.*}", (c) => {
12626
13064
  }
12627
13065
  segments.push(seg);
12628
13066
  }
12629
- const rootDir = resolve20(account.accountDir, "sites");
12630
- let filePath = segments.length === 0 ? rootDir : resolve20(rootDir, ...segments);
13067
+ const rootDir = resolve22(account.accountDir, "sites");
13068
+ let filePath = segments.length === 0 ? rootDir : resolve22(rootDir, ...segments);
12631
13069
  if (filePath !== rootDir && !filePath.startsWith(rootDir + "/")) {
12632
13070
  console.error(`[sites] path-traversal-rejected path=${reqPath} reason=escape status=403`);
12633
13071
  return c.text("Forbidden", 403);
12634
13072
  }
12635
13073
  let stat7;
12636
13074
  try {
12637
- stat7 = existsSync20(filePath) ? statSync7(filePath) : null;
13075
+ stat7 = existsSync20(filePath) ? statSync8(filePath) : null;
12638
13076
  } catch {
12639
13077
  stat7 = null;
12640
13078
  }
@@ -12647,7 +13085,7 @@ app37.get("/:rel{.*}", (c) => {
12647
13085
  return c.redirect(target, 301);
12648
13086
  }
12649
13087
  if (stat7?.isDirectory()) {
12650
- filePath = resolve20(filePath, "index.html");
13088
+ filePath = resolve22(filePath, "index.html");
12651
13089
  }
12652
13090
  if (!filePath.startsWith(rootDir + "/")) {
12653
13091
  console.error(`[sites] path-traversal-rejected path=${reqPath} reason=escape status=403`);
@@ -12660,8 +13098,8 @@ app37.get("/:rel{.*}", (c) => {
12660
13098
  let realPath;
12661
13099
  let realRoot;
12662
13100
  try {
12663
- realPath = realpathSync5(filePath);
12664
- realRoot = realpathSync5(rootDir);
13101
+ realPath = realpathSync6(filePath);
13102
+ realRoot = realpathSync6(rootDir);
12665
13103
  } catch {
12666
13104
  console.error(`[sites] not-found path=${reqPath} status=404`);
12667
13105
  return c.text("Not found", 404);
@@ -12704,7 +13142,7 @@ app37.get("/:rel{.*}", (c) => {
12704
13142
  "X-Content-Type-Options": "nosniff"
12705
13143
  });
12706
13144
  });
12707
- var sites_default = app37;
13145
+ var sites_default = app36;
12708
13146
 
12709
13147
  // app/lib/visitor-token.ts
12710
13148
  import { createHmac, randomBytes, timingSafeEqual } from "crypto";
@@ -12785,7 +13223,7 @@ var VISITOR_COOKIE_MAX_AGE_SECONDS = Math.floor(DEFAULT_TTL_MS / 1e3);
12785
13223
 
12786
13224
  // app/lib/brand-config.ts
12787
13225
  import { existsSync as existsSync21, readFileSync as readFileSync18 } from "fs";
12788
- import { join as join13 } from "path";
13226
+ import { join as join14 } from "path";
12789
13227
  var cached2 = null;
12790
13228
  var cachedAttempted = false;
12791
13229
  function readBrandConfig() {
@@ -12793,7 +13231,7 @@ function readBrandConfig() {
12793
13231
  cachedAttempted = true;
12794
13232
  const platformRoot = process.env.MAXY_PLATFORM_ROOT;
12795
13233
  if (!platformRoot) return null;
12796
- const brandPath = join13(platformRoot, "config", "brand.json");
13234
+ const brandPath = join14(platformRoot, "config", "brand.json");
12797
13235
  if (!existsSync21(brandPath)) return null;
12798
13236
  try {
12799
13237
  cached2 = JSON.parse(readFileSync18(brandPath, "utf-8"));
@@ -12804,7 +13242,7 @@ function readBrandConfig() {
12804
13242
  }
12805
13243
 
12806
13244
  // server/routes/visitor-consent.ts
12807
- var app38 = new Hono();
13245
+ var app37 = new Hono();
12808
13246
  var CONSENT_COOKIE_NAME = "mxy_consent";
12809
13247
  var CONSENT_COOKIE_MAX_AGE_SECONDS = 60 * 60 * 24 * 365;
12810
13248
  var DEFAULT_CONSENT_COPY = {
@@ -12849,17 +13287,17 @@ function siteSlugFromReferer(referer) {
12849
13287
  return "";
12850
13288
  }
12851
13289
  }
12852
- app38.options("/consent", (c) => {
13290
+ app37.options("/consent", (c) => {
12853
13291
  const origin = getOrigin(c);
12854
13292
  setCorsHeaders(c, origin);
12855
13293
  return c.body(null, 204);
12856
13294
  });
12857
- app38.options("/brand-config", (c) => {
13295
+ app37.options("/brand-config", (c) => {
12858
13296
  const origin = getOrigin(c);
12859
13297
  setCorsHeaders(c, origin);
12860
13298
  return c.body(null, 204);
12861
13299
  });
12862
- app38.post("/consent", async (c) => {
13300
+ app37.post("/consent", async (c) => {
12863
13301
  const origin = getOrigin(c);
12864
13302
  setCorsHeaders(c, origin);
12865
13303
  let raw;
@@ -12899,7 +13337,7 @@ app38.post("/consent", async (c) => {
12899
13337
  console.log(`[consent] ${parsed.decision} site=${site} brand=${brandName} tokenBound=${tokenBound}`);
12900
13338
  return c.body(null, 204);
12901
13339
  });
12902
- app38.get("/brand-config", (c) => {
13340
+ app37.get("/brand-config", (c) => {
12903
13341
  const origin = getOrigin(c);
12904
13342
  setCorsHeaders(c, origin);
12905
13343
  const brand = readBrandConfig();
@@ -12909,7 +13347,7 @@ app38.get("/brand-config", (c) => {
12909
13347
  c.header("Cache-Control", "public, max-age=300");
12910
13348
  return c.json({ consent: { copy, palette } });
12911
13349
  });
12912
- var visitor_consent_default = app38;
13350
+ var visitor_consent_default = app37;
12913
13351
 
12914
13352
  // server/routes/listings.ts
12915
13353
  function getCookie(headerValue, name) {
@@ -12930,14 +13368,14 @@ function appendConsentParams(pageUrl, token) {
12930
13368
  const hashIdx = pageUrl.indexOf("#");
12931
13369
  const hash = hashIdx >= 0 ? pageUrl.slice(hashIdx) : "";
12932
13370
  const base = hashIdx >= 0 ? pageUrl.slice(0, hashIdx) : pageUrl;
12933
- const sep4 = base.indexOf("?") >= 0 ? "&" : "?";
12934
- return base + sep4 + `consent=needed&v=${encodeURIComponent(token)}` + hash;
13371
+ const sep7 = base.indexOf("?") >= 0 ? "&" : "?";
13372
+ return base + sep7 + `consent=needed&v=${encodeURIComponent(token)}` + hash;
12935
13373
  }
12936
13374
  }
12937
13375
  var SAFE_SLUG_RE = /^[a-z0-9](?:[a-z0-9-]{0,118}[a-z0-9])?$/;
12938
13376
  var CHAT_SESSION_KEY_RE = /^[A-Za-z0-9][A-Za-z0-9_-]{7,127}$/;
12939
- var app39 = new Hono();
12940
- app39.get("/:slug/click", async (c) => {
13377
+ var app38 = new Hono();
13378
+ app38.get("/:slug/click", async (c) => {
12941
13379
  const slug = c.req.param("slug") ?? "";
12942
13380
  const rawSession = c.req.query("session") ?? "";
12943
13381
  const sessionKey = CHAT_SESSION_KEY_RE.test(rawSession) ? rawSession : "invalid";
@@ -13001,10 +13439,10 @@ app39.get("/:slug/click", async (c) => {
13001
13439
  console.log(`[property-card-click] sessionKey=${sessionKey} listingSlug=${slug} consent=${consentCookie ?? "absent"} ts=${(/* @__PURE__ */ new Date()).toISOString()}`);
13002
13440
  return c.redirect(redirectUrl, 302);
13003
13441
  });
13004
- var listings_default = app39;
13442
+ var listings_default = app38;
13005
13443
 
13006
13444
  // server/routes/visitor-event.ts
13007
- var app40 = new Hono();
13445
+ var app39 = new Hono();
13008
13446
  var BOT_UA_RE = /\b(bot|crawl|spider|slurp|headlesschrome|phantomjs|googlebot|bingbot|yandex|baiduspider|ahrefsbot|semrushbot|mj12bot|dotbot|petalbot)\b/i;
13009
13447
  var buckets = /* @__PURE__ */ new Map();
13010
13448
  var RATE_LIMIT = 60;
@@ -13071,12 +13509,12 @@ function originAllowed(origin, allowlist) {
13071
13509
  return false;
13072
13510
  }
13073
13511
  }
13074
- app40.options("/event", (c) => {
13512
+ app39.options("/event", (c) => {
13075
13513
  const origin = getOrigin2(c);
13076
13514
  setCorsHeaders2(c, origin);
13077
13515
  return c.body(null, 204);
13078
13516
  });
13079
- app40.post("/event", async (c) => {
13517
+ app39.post("/event", async (c) => {
13080
13518
  const origin = getOrigin2(c);
13081
13519
  setCorsHeaders2(c, origin);
13082
13520
  const ua = c.req.header("user-agent") ?? "";
@@ -13281,7 +13719,7 @@ async function writeEvent(opts) {
13281
13719
  );
13282
13720
  }
13283
13721
  }
13284
- var visitor_event_default = app40;
13722
+ var visitor_event_default = app39;
13285
13723
 
13286
13724
  // app/lib/graph-health.ts
13287
13725
  var HOUR_MS = 60 * 60 * 1e3;
@@ -13425,8 +13863,8 @@ function broadcastAdminShutdown(reason) {
13425
13863
 
13426
13864
  // ../lib/entitlement/src/index.ts
13427
13865
  import { createPublicKey, createHash as createHash4, verify as cryptoVerify } from "crypto";
13428
- import { existsSync as existsSync22, readFileSync as readFileSync19, statSync as statSync8 } from "fs";
13429
- import { resolve as resolve21 } from "path";
13866
+ import { existsSync as existsSync22, readFileSync as readFileSync19, statSync as statSync9 } from "fs";
13867
+ import { resolve as resolve23 } from "path";
13430
13868
 
13431
13869
  // ../lib/entitlement/src/canonicalize.ts
13432
13870
  function canonicalize(value) {
@@ -13461,7 +13899,7 @@ var PUBKEY_SHA256 = "8eee6bcb33545fd13b16d3199a5735ca5db5062834c7b49dfe4f23801d9
13461
13899
  var GRACE_DAYS = 7;
13462
13900
  var GRACE_MS = GRACE_DAYS * 24 * 60 * 60 * 1e3;
13463
13901
  function pubkeyPath(brand) {
13464
- return resolve21(brand.platformRoot, "lib", "entitlement", "rubytech-pubkey.pem");
13902
+ return resolve23(brand.platformRoot, "lib", "entitlement", "rubytech-pubkey.pem");
13465
13903
  }
13466
13904
  var memo = null;
13467
13905
  function memoKey(mtimeMs, account) {
@@ -13473,11 +13911,11 @@ function resolveEntitlement(brand, account) {
13473
13911
  if (brand.commercialMode !== true) {
13474
13912
  return logResolved(implicitTrust(account), null);
13475
13913
  }
13476
- const entitlementPath = resolve21(brand.configDir, "entitlement.json");
13914
+ const entitlementPath = resolve23(brand.configDir, "entitlement.json");
13477
13915
  if (!existsSync22(entitlementPath)) {
13478
13916
  return logResolved(anonymousFallback("missing"), { reason: "missing" });
13479
13917
  }
13480
- const stat7 = statSync8(entitlementPath);
13918
+ const stat7 = statSync9(entitlementPath);
13481
13919
  const key = memoKey(stat7.mtimeMs, account);
13482
13920
  if (memo && memo.key === key) {
13483
13921
  return memo.result;
@@ -13664,7 +14102,7 @@ function clientFrom(c) {
13664
14102
  );
13665
14103
  }
13666
14104
  var PLATFORM_ROOT9 = process.env.MAXY_PLATFORM_ROOT || "";
13667
- var BRAND_JSON_PATH = PLATFORM_ROOT9 ? join14(PLATFORM_ROOT9, "config", "brand.json") : "";
14105
+ var BRAND_JSON_PATH = PLATFORM_ROOT9 ? join15(PLATFORM_ROOT9, "config", "brand.json") : "";
13668
14106
  var BRAND = { productName: "Maxy", hostname: "maxy", configDir: ".maxy", marketingUrl: "https://getmaxy.com" };
13669
14107
  if (BRAND_JSON_PATH && !existsSync23(BRAND_JSON_PATH)) {
13670
14108
  console.error(`[brand] WARNING: brand.json not found at ${BRAND_JSON_PATH} \u2014 using Maxy defaults`);
@@ -13690,7 +14128,7 @@ var brandLoginOpts = {
13690
14128
  bodyFont: BRAND.defaultFonts?.body,
13691
14129
  logoContainsName: !!BRAND.logoContainsName
13692
14130
  };
13693
- var ALIAS_DOMAINS_PATH = join14(homedir2(), BRAND.configDir, "alias-domains.json");
14131
+ var ALIAS_DOMAINS_PATH = join15(homedir2(), BRAND.configDir, "alias-domains.json");
13694
14132
  function loadAliasDomains() {
13695
14133
  try {
13696
14134
  if (!existsSync23(ALIAS_DOMAINS_PATH)) return null;
@@ -13718,9 +14156,9 @@ watchFile(ALIAS_DOMAINS_PATH, { interval: 2e3 }, () => {
13718
14156
  function isPublicHost(host) {
13719
14157
  return host.startsWith("public.") || aliasDomains.has(host);
13720
14158
  }
13721
- var app41 = new Hono();
13722
- app41.use("*", clientIpMiddleware);
13723
- app41.use("*", async (c, next) => {
14159
+ var app40 = new Hono();
14160
+ app40.use("*", clientIpMiddleware);
14161
+ app40.use("*", async (c, next) => {
13724
14162
  await next();
13725
14163
  c.header("X-Content-Type-Options", "nosniff");
13726
14164
  c.header("Referrer-Policy", "strict-origin-when-cross-origin");
@@ -13730,7 +14168,7 @@ app41.use("*", async (c, next) => {
13730
14168
  );
13731
14169
  });
13732
14170
  var HTTP_LOG_PATHS = /* @__PURE__ */ new Set(["/vnc-viewer.html", "/vnc-popout.html"]);
13733
- app41.use("*", async (c, next) => {
14171
+ app40.use("*", async (c, next) => {
13734
14172
  if (!HTTP_LOG_PATHS.has(c.req.path)) {
13735
14173
  await next();
13736
14174
  return;
@@ -13763,7 +14201,7 @@ var PUBLIC_ALLOWED_PREFIXES = [
13763
14201
  "/sites/"
13764
14202
  ];
13765
14203
  var PUBLIC_ALLOWED_EXACT = ["/favicon.ico"];
13766
- app41.use("*", async (c, next) => {
14204
+ app40.use("*", async (c, next) => {
13767
14205
  const host = (c.req.header("host") ?? "").split(":")[0];
13768
14206
  if (!isPublicHost(host)) {
13769
14207
  await next();
@@ -13803,7 +14241,7 @@ function resolveRemoteAuthOpts() {
13803
14241
  return brandLoginOpts;
13804
14242
  }
13805
14243
  var MAX_LOGIN_BODY = 8 * 1024;
13806
- app41.post("/__remote-auth/login", async (c) => {
14244
+ app40.post("/__remote-auth/login", async (c) => {
13807
14245
  const client = clientFrom(c);
13808
14246
  const clientIp = client.ip || "unknown";
13809
14247
  if (!requestIsTlsTerminated(c)) {
@@ -13848,7 +14286,7 @@ app41.post("/__remote-auth/login", async (c) => {
13848
14286
  }
13849
14287
  });
13850
14288
  });
13851
- app41.get("/__remote-auth/logout", (c) => {
14289
+ app40.get("/__remote-auth/logout", (c) => {
13852
14290
  const client = clientFrom(c);
13853
14291
  const clientIp = client.ip || "unknown";
13854
14292
  console.error(`[remote-auth] logout ip=${clientIp}`);
@@ -13861,7 +14299,7 @@ app41.get("/__remote-auth/logout", (c) => {
13861
14299
  }
13862
14300
  });
13863
14301
  });
13864
- app41.post("/__remote-auth/change-password", async (c) => {
14302
+ app40.post("/__remote-auth/change-password", async (c) => {
13865
14303
  const client = clientFrom(c);
13866
14304
  const clientIp = client.ip || "unknown";
13867
14305
  const rateLimited = checkRateLimit(client);
@@ -13912,13 +14350,13 @@ app41.post("/__remote-auth/change-password", async (c) => {
13912
14350
  return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(), mode: "change", changeError: "Failed to save password", redirect }), 200);
13913
14351
  }
13914
14352
  });
13915
- app41.get("/__remote-auth/setup", (c) => {
14353
+ app40.get("/__remote-auth/setup", (c) => {
13916
14354
  if (isRemoteAuthConfigured()) {
13917
14355
  return c.redirect("/");
13918
14356
  }
13919
14357
  return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(), mode: "setup" }), 200);
13920
14358
  });
13921
- app41.post("/__remote-auth/set-initial-password", async (c) => {
14359
+ app40.post("/__remote-auth/set-initial-password", async (c) => {
13922
14360
  if (isRemoteAuthConfigured()) {
13923
14361
  return c.redirect("/");
13924
14362
  }
@@ -13956,10 +14394,10 @@ app41.post("/__remote-auth/set-initial-password", async (c) => {
13956
14394
  return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(), mode: "setup", setupError: "Failed to save password. Please try again." }), 200);
13957
14395
  }
13958
14396
  });
13959
- app41.get("/api/remote-auth/status", (c) => {
14397
+ app40.get("/api/remote-auth/status", (c) => {
13960
14398
  return c.json({ configured: isRemoteAuthConfigured() });
13961
14399
  });
13962
- app41.post("/api/remote-auth/set-password", async (c) => {
14400
+ app40.post("/api/remote-auth/set-password", async (c) => {
13963
14401
  let body;
13964
14402
  try {
13965
14403
  body = await c.req.json();
@@ -13990,9 +14428,9 @@ app41.post("/api/remote-auth/set-password", async (c) => {
13990
14428
  return c.json({ error: "Failed to save password" }, 500);
13991
14429
  }
13992
14430
  });
13993
- app41.route("/api/_client-error", client_error_default);
14431
+ app40.route("/api/_client-error", client_error_default);
13994
14432
  console.log("[client-error-route] mounted");
13995
- app41.use("*", async (c, next) => {
14433
+ app40.use("*", async (c, next) => {
13996
14434
  const host = (c.req.header("host") ?? "").split(":")[0];
13997
14435
  const path2 = c.req.path;
13998
14436
  if (path2 === "/favicon.ico" || path2.startsWith("/assets/") || path2.startsWith("/brand/")) {
@@ -14025,12 +14463,12 @@ app41.use("*", async (c, next) => {
14025
14463
  console.error(`[remote-auth] login required ip=${clientIp} path=${path2} ${disambig}`);
14026
14464
  return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(), redirect: path2 }), 200);
14027
14465
  });
14028
- app41.route("/api/health", health_default);
14029
- app41.route("/api/chat", chat_default);
14030
- app41.route("/api/whatsapp", whatsapp_default);
14031
- app41.route("/api/onboarding", onboarding_default);
14032
- app41.route("/api/admin", admin_default);
14033
- app41.route("/api/access", access_default);
14466
+ app40.route("/api/health", health_default);
14467
+ app40.route("/api/chat", chat_default);
14468
+ app40.route("/api/whatsapp", whatsapp_default);
14469
+ app40.route("/api/onboarding", onboarding_default);
14470
+ app40.route("/api/admin", admin_default);
14471
+ app40.route("/api/access", access_default);
14034
14472
  var SAFE_SLUG_RE2 = /^[a-z][a-z0-9-]{2,49}$/;
14035
14473
  var SAFE_FILENAME_RE = /^[a-z0-9_][a-z0-9_.-]{0,99}$/i;
14036
14474
  var IMAGE_MIME = {
@@ -14042,7 +14480,7 @@ var IMAGE_MIME = {
14042
14480
  ".svg": "image/svg+xml",
14043
14481
  ".ico": "image/x-icon"
14044
14482
  };
14045
- app41.get("/agent-assets/:slug/:filename", (c) => {
14483
+ app40.get("/agent-assets/:slug/:filename", (c) => {
14046
14484
  const slug = c.req.param("slug");
14047
14485
  const filename = c.req.param("filename");
14048
14486
  if (!SAFE_SLUG_RE2.test(slug)) {
@@ -14058,8 +14496,8 @@ app41.get("/agent-assets/:slug/:filename", (c) => {
14058
14496
  console.error(`[agent-assets] no-account slug=${slug} file=${filename}`);
14059
14497
  return c.text("Not found", 404);
14060
14498
  }
14061
- const filePath = resolve22(account.accountDir, "agents", slug, "assets", filename);
14062
- const expectedDir = resolve22(account.accountDir, "agents", slug, "assets");
14499
+ const filePath = resolve24(account.accountDir, "agents", slug, "assets", filename);
14500
+ const expectedDir = resolve24(account.accountDir, "agents", slug, "assets");
14063
14501
  if (!filePath.startsWith(expectedDir + "/")) {
14064
14502
  console.error(`[agent-assets] path-traversal-rejected slug=${slug} file=${filename}`);
14065
14503
  return c.text("Forbidden", 403);
@@ -14077,7 +14515,7 @@ app41.get("/agent-assets/:slug/:filename", (c) => {
14077
14515
  "Cache-Control": "public, max-age=3600"
14078
14516
  });
14079
14517
  });
14080
- app41.get("/generated/:filename", (c) => {
14518
+ app40.get("/generated/:filename", (c) => {
14081
14519
  const filename = c.req.param("filename");
14082
14520
  if (!SAFE_FILENAME_RE.test(filename) || filename.includes("..")) {
14083
14521
  console.error(`[generated] serve file=${filename} status=403`);
@@ -14088,8 +14526,8 @@ app41.get("/generated/:filename", (c) => {
14088
14526
  console.error(`[generated] serve file=${filename} status=404`);
14089
14527
  return c.text("Not found", 404);
14090
14528
  }
14091
- const filePath = resolve22(account.accountDir, "generated", filename);
14092
- const expectedDir = resolve22(account.accountDir, "generated");
14529
+ const filePath = resolve24(account.accountDir, "generated", filename);
14530
+ const expectedDir = resolve24(account.accountDir, "generated");
14093
14531
  if (!filePath.startsWith(expectedDir + "/")) {
14094
14532
  console.error(`[generated] serve file=${filename} status=403`);
14095
14533
  return c.text("Forbidden", 403);
@@ -14107,10 +14545,10 @@ app41.get("/generated/:filename", (c) => {
14107
14545
  "Cache-Control": "public, max-age=86400"
14108
14546
  });
14109
14547
  });
14110
- app41.route("/sites", sites_default);
14111
- app41.route("/listings", listings_default);
14112
- app41.route("/v", visitor_event_default);
14113
- app41.route("/v", visitor_consent_default);
14548
+ app40.route("/sites", sites_default);
14549
+ app40.route("/listings", listings_default);
14550
+ app40.route("/v", visitor_event_default);
14551
+ app40.route("/v", visitor_consent_default);
14114
14552
  var htmlCache = /* @__PURE__ */ new Map();
14115
14553
  var brandLogoPath = "/brand/maxy-monochrome.png";
14116
14554
  var brandIconPath = "/brand/maxy-monochrome.png";
@@ -14133,7 +14571,7 @@ var brandScript = `<script>window.__BRAND__=${JSON.stringify({
14133
14571
  function readInstalledVersion() {
14134
14572
  try {
14135
14573
  if (!PLATFORM_ROOT9) return "unknown";
14136
- const versionFile = join14(PLATFORM_ROOT9, "config", `.${BRAND.hostname}-version`);
14574
+ const versionFile = join15(PLATFORM_ROOT9, "config", `.${BRAND.hostname}-version`);
14137
14575
  if (!existsSync23(versionFile)) return "unknown";
14138
14576
  const content = readFileSync20(versionFile, "utf-8").trim();
14139
14577
  return content || "unknown";
@@ -14176,7 +14614,7 @@ var clientErrorReporterScript = `<script>
14176
14614
  function cachedHtml(file) {
14177
14615
  let html = htmlCache.get(file);
14178
14616
  if (!html) {
14179
- html = readFileSync20(resolve22(process.cwd(), "public", file), "utf-8");
14617
+ html = readFileSync20(resolve24(process.cwd(), "public", file), "utf-8");
14180
14618
  const productNameEsc = escapeHtml(BRAND.productName);
14181
14619
  html = html.replace(/<title>([^<]*)<\/title>/, (_match, inner) => `<title>${escapeHtml(inner).replace(/Maxy/g, productNameEsc)}</title>`);
14182
14620
  html = html.replace('href="/favicon.ico"', `href="${escapeHtml(brandFaviconPath)}"`);
@@ -14192,14 +14630,14 @@ ${clientErrorReporterScript}
14192
14630
  }
14193
14631
  var brandedHtmlCache = /* @__PURE__ */ new Map();
14194
14632
  function loadBrandingCache(agentSlug) {
14195
- const configDir2 = join14(homedir2(), BRAND.configDir);
14633
+ const configDir2 = join15(homedir2(), BRAND.configDir);
14196
14634
  try {
14197
- const accountJsonPath = join14(configDir2, "account.json");
14635
+ const accountJsonPath = join15(configDir2, "account.json");
14198
14636
  if (!existsSync23(accountJsonPath)) return null;
14199
14637
  const account = JSON.parse(readFileSync20(accountJsonPath, "utf-8"));
14200
14638
  const accountId = account.accountId;
14201
14639
  if (!accountId) return null;
14202
- const cachePath = join14(configDir2, "branding-cache", accountId, `${agentSlug}.json`);
14640
+ const cachePath = join15(configDir2, "branding-cache", accountId, `${agentSlug}.json`);
14203
14641
  if (!existsSync23(cachePath)) return null;
14204
14642
  return JSON.parse(readFileSync20(cachePath, "utf-8"));
14205
14643
  } catch {
@@ -14208,8 +14646,8 @@ function loadBrandingCache(agentSlug) {
14208
14646
  }
14209
14647
  function resolveDefaultSlug() {
14210
14648
  try {
14211
- const configDir2 = join14(homedir2(), BRAND.configDir);
14212
- const accountJsonPath = join14(configDir2, "account.json");
14649
+ const configDir2 = join15(homedir2(), BRAND.configDir);
14650
+ const accountJsonPath = join15(configDir2, "account.json");
14213
14651
  if (!existsSync23(accountJsonPath)) return null;
14214
14652
  const account = JSON.parse(readFileSync20(accountJsonPath, "utf-8"));
14215
14653
  return account.defaultAgent || null;
@@ -14247,7 +14685,7 @@ function brandedPublicHtml(agentSlug) {
14247
14685
  function escapeHtml(s) {
14248
14686
  return s.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
14249
14687
  }
14250
- app41.get("/", (c) => {
14688
+ app40.get("/", (c) => {
14251
14689
  const host = (c.req.header("host") ?? "").split(":")[0];
14252
14690
  if (isPublicHost(host)) {
14253
14691
  const defaultSlug = resolveDefaultSlug();
@@ -14255,12 +14693,12 @@ app41.get("/", (c) => {
14255
14693
  }
14256
14694
  return c.html(cachedHtml("index.html"));
14257
14695
  });
14258
- app41.get("/public", (c) => {
14696
+ app40.get("/public", (c) => {
14259
14697
  const host = (c.req.header("host") ?? "").split(":")[0];
14260
14698
  if (isPublicHost(host)) return c.text("Not found", 404);
14261
14699
  return c.html(cachedHtml("public.html"));
14262
14700
  });
14263
- app41.get("/chat", (c) => {
14701
+ app40.get("/chat", (c) => {
14264
14702
  const host = (c.req.header("host") ?? "").split(":")[0];
14265
14703
  if (isPublicHost(host)) return c.text("Not found", 404);
14266
14704
  return c.html(cachedHtml("public.html"));
@@ -14279,12 +14717,12 @@ async function logViewerFetch(c, next) {
14279
14717
  duration_ms: Date.now() - start
14280
14718
  });
14281
14719
  }
14282
- app41.use("/vnc-viewer.html", logViewerFetch);
14283
- app41.use("/vnc-popout.html", logViewerFetch);
14284
- app41.get("/vnc-popout.html", (c) => {
14720
+ app40.use("/vnc-viewer.html", logViewerFetch);
14721
+ app40.use("/vnc-popout.html", logViewerFetch);
14722
+ app40.get("/vnc-popout.html", (c) => {
14285
14723
  let html = htmlCache.get("vnc-popout.html");
14286
14724
  if (!html) {
14287
- html = readFileSync20(resolve22(process.cwd(), "public", "vnc-popout.html"), "utf-8");
14725
+ html = readFileSync20(resolve24(process.cwd(), "public", "vnc-popout.html"), "utf-8");
14288
14726
  const name = escapeHtml(BRAND.productName);
14289
14727
  html = html.replace("<title>Browser \u2014 Maxy</title>", `<title>${name}</title>`);
14290
14728
  html = html.replace("</head>", ` ${brandScript}
@@ -14294,7 +14732,7 @@ app41.get("/vnc-popout.html", (c) => {
14294
14732
  }
14295
14733
  return c.html(html);
14296
14734
  });
14297
- app41.post("/api/vnc/client-event", async (c) => {
14735
+ app40.post("/api/vnc/client-event", async (c) => {
14298
14736
  let body;
14299
14737
  try {
14300
14738
  body = await c.req.json();
@@ -14315,25 +14753,25 @@ app41.post("/api/vnc/client-event", async (c) => {
14315
14753
  });
14316
14754
  return c.json({ ok: true });
14317
14755
  });
14318
- app41.get("/g/:slug", (c) => {
14756
+ app40.get("/g/:slug", (c) => {
14319
14757
  return c.html(brandedPublicHtml());
14320
14758
  });
14321
- app41.get("/graph", (c) => {
14759
+ app40.get("/graph", (c) => {
14322
14760
  const host = (c.req.header("host") ?? "").split(":")[0];
14323
14761
  if (isPublicHost(host)) return c.text("Not found", 404);
14324
14762
  return c.html(cachedHtml("graph.html"));
14325
14763
  });
14326
- app41.get("/sessions", (c) => {
14764
+ app40.get("/sessions", (c) => {
14327
14765
  const host = (c.req.header("host") ?? "").split(":")[0];
14328
14766
  if (isPublicHost(host)) return c.text("Not found", 404);
14329
14767
  return c.html(cachedHtml("sessions.html"));
14330
14768
  });
14331
- app41.get("/data", (c) => {
14769
+ app40.get("/data", (c) => {
14332
14770
  const host = (c.req.header("host") ?? "").split(":")[0];
14333
14771
  if (isPublicHost(host)) return c.text("Not found", 404);
14334
14772
  return c.html(cachedHtml("data.html"));
14335
14773
  });
14336
- app41.get("/:slug", async (c, next) => {
14774
+ app40.get("/:slug", async (c, next) => {
14337
14775
  const slug = c.req.param("slug");
14338
14776
  if (AGENT_SLUG_PATTERN.test(`/${slug}`)) {
14339
14777
  const branding = loadBrandingCache(slug);
@@ -14343,15 +14781,15 @@ app41.get("/:slug", async (c, next) => {
14343
14781
  await next();
14344
14782
  });
14345
14783
  if (brandFaviconPath !== "/favicon.ico") {
14346
- app41.get("/favicon.ico", (c) => {
14784
+ app40.get("/favicon.ico", (c) => {
14347
14785
  c.header("Cache-Control", "public, max-age=300");
14348
14786
  return c.redirect(brandFaviconPath, 302);
14349
14787
  });
14350
14788
  }
14351
- app41.use("/*", serveStatic({ root: "./public" }));
14789
+ app40.use("/*", serveStatic({ root: "./public" }));
14352
14790
  var port = requirePortEnv("MAXY_UI_INTERNAL_PORT", { tag: "ui-server" });
14353
14791
  var hostname = process.env.HOSTNAME ?? "127.0.0.1";
14354
- var httpServer = serve({ fetch: app41.fetch, port, hostname });
14792
+ var httpServer = serve({ fetch: app40.fetch, port, hostname });
14355
14793
  console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
14356
14794
  {
14357
14795
  const loopHist = monitorEventLoopDelay({ resolution: 50 });
@@ -14387,7 +14825,7 @@ for (const m of SUBAPP_MANIFEST) {
14387
14825
  }
14388
14826
  try {
14389
14827
  const registered = [];
14390
- for (const r of app41.routes ?? []) {
14828
+ for (const r of app40.routes ?? []) {
14391
14829
  if (typeof r.path !== "string" || r.path.includes(":") || r.path.includes("*")) continue;
14392
14830
  if (AGENT_SLUG_PATTERN.test(r.path)) {
14393
14831
  registered.push({ method: (r.method ?? "ALL").toUpperCase(), path: r.path });
@@ -14502,7 +14940,7 @@ if (bootAccountConfig?.whatsapp) {
14502
14940
  }
14503
14941
  init({
14504
14942
  configDir: configDirForWhatsApp,
14505
- platformRoot: resolve22(process.env.MAXY_PLATFORM_ROOT ?? join14(__dirname, "..")),
14943
+ platformRoot: resolve24(process.env.MAXY_PLATFORM_ROOT ?? join15(__dirname, "..")),
14506
14944
  accountConfig: bootAccountConfig,
14507
14945
  onMessage: async (msg) => {
14508
14946
  if (process.env.WHATSAPP_PTY_BRIDGE_ENABLED === "true" && msg.text && !msg.isOwnerMirror) {