@rubytech/create-maxy-code 0.1.285 → 0.1.287

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 (100) hide show
  1. package/dist/__tests__/samba-provision.test.js +84 -16
  2. package/dist/index.js +17 -8
  3. package/dist/samba-provision.js +81 -24
  4. package/dist/uninstall.js +5 -1
  5. package/package.json +1 -1
  6. package/payload/platform/plugins/admin/PLUGIN.md +1 -1
  7. package/payload/platform/plugins/admin/hooks/__tests__/prompt-optimiser-compliance.test.sh +52 -3
  8. package/payload/platform/plugins/admin/hooks/__tests__/prompt-optimiser-directive.test.sh +55 -0
  9. package/payload/platform/plugins/admin/hooks/prompt-optimiser-compliance.sh +29 -13
  10. package/payload/platform/plugins/admin/hooks/prompt-optimiser-directive.sh +60 -11
  11. package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +30 -17
  12. package/payload/platform/plugins/docs/references/admin-ui.md +2 -2
  13. package/payload/platform/plugins/docs/references/deployment.md +1 -1
  14. package/payload/platform/plugins/docs/references/plugins-guide.md +1 -1
  15. package/payload/platform/plugins/docs/references/samba.md +25 -12
  16. package/payload/platform/plugins/whatsapp/PLUGIN.md +4 -0
  17. package/payload/platform/plugins/whatsapp/references/channels-whatsapp.md +1 -1
  18. package/payload/platform/scripts/__tests__/prompt-optimiser-audit.test.sh +59 -0
  19. package/payload/platform/scripts/installer-device-verify.sh +74 -3
  20. package/payload/platform/scripts/prompt-optimiser-audit.sh +82 -0
  21. package/payload/platform/services/whatsapp-channel/dist/notification.d.ts +46 -7
  22. package/payload/platform/services/whatsapp-channel/dist/notification.d.ts.map +1 -1
  23. package/payload/platform/services/whatsapp-channel/dist/notification.js +49 -9
  24. package/payload/platform/services/whatsapp-channel/dist/notification.js.map +1 -1
  25. package/payload/platform/services/whatsapp-channel/dist/server.js +70 -17
  26. package/payload/platform/services/whatsapp-channel/dist/server.js.map +1 -1
  27. package/payload/server/{chunk-QHD5TKLQ.js → chunk-EAJMRICW.js} +21 -0
  28. package/payload/server/maxy-edge.js +1 -1
  29. package/payload/server/public/assets/AdminShell-DkP2rJdF.js +1 -0
  30. package/payload/server/public/assets/{Checkbox-BdDmGlvK.js → Checkbox-CsQq7YPC.js} +1 -1
  31. package/payload/server/public/assets/{admin-BYtGQJ0d.js → admin-C_bkVvXh.js} +1 -1
  32. package/payload/server/public/assets/{architectureDiagram-Q4EWVU46-CBx_GoUY.js → architectureDiagram-Q4EWVU46-PGePlVpb.js} +1 -1
  33. package/payload/server/public/assets/{blockDiagram-DXYQGD6D-DvWR4fpv.js → blockDiagram-DXYQGD6D-BJCbJRRs.js} +1 -1
  34. package/payload/server/public/assets/{browser-2PXsTraZ.js → browser-DynPFG_A.js} +1 -1
  35. package/payload/server/public/assets/{c4Diagram-AHTNJAMY-BbSOxeeU.js → c4Diagram-AHTNJAMY-DcMhi4gF.js} +1 -1
  36. package/payload/server/public/assets/channel-idaGQqlP.js +1 -0
  37. package/payload/server/public/assets/{chunk-336JU56O-DJzmXTk5.js → chunk-336JU56O-0vhS0MI-.js} +2 -2
  38. package/payload/server/public/assets/{chunk-426QAEUC-BSwzskM5.js → chunk-426QAEUC-DbVU-p-q.js} +1 -1
  39. package/payload/server/public/assets/{chunk-4TB4RGXK-C-fH8BTZ.js → chunk-4TB4RGXK-dgx3qIz_.js} +1 -1
  40. package/payload/server/public/assets/{chunk-5FUZZQ4R-DW-MaiGX.js → chunk-5FUZZQ4R-BglTosdT.js} +1 -1
  41. package/payload/server/public/assets/{chunk-5PVQY5BW-DlKbwC1X.js → chunk-5PVQY5BW-hZB3AxUF.js} +1 -1
  42. package/payload/server/public/assets/{chunk-EDXVE4YY-DnkdxwAt.js → chunk-EDXVE4YY-DXqLJyYa.js} +1 -1
  43. package/payload/server/public/assets/{chunk-ENJZ2VHE-BTfezWsj.js → chunk-ENJZ2VHE-BD4I_a54.js} +1 -1
  44. package/payload/server/public/assets/{chunk-ICPOFSXX-BGg8uTtu.js → chunk-ICPOFSXX-BdQx0Ahc.js} +1 -1
  45. package/payload/server/public/assets/{chunk-OYMX7WX6-CYJInav8.js → chunk-OYMX7WX6-BDjtbZvS.js} +1 -1
  46. package/payload/server/public/assets/{chunk-U2HBQHQK-B6kD6ZHU.js → chunk-U2HBQHQK-hYN7A3g_.js} +1 -1
  47. package/payload/server/public/assets/{chunk-X2U36JSP-7tvWkjym.js → chunk-X2U36JSP-D3njJ7WG.js} +1 -1
  48. package/payload/server/public/assets/{chunk-YZCP3GAM-D6NjOb5l.js → chunk-YZCP3GAM-BfZKEcvU.js} +1 -1
  49. package/payload/server/public/assets/{chunk-ZZ45TVLE-Dqz69eJy.js → chunk-ZZ45TVLE-DLJMNqLn.js} +1 -1
  50. package/payload/server/public/assets/classDiagram-6PBFFD2Q-CmRFpirU.js +1 -0
  51. package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-DFy8xZjP.js +1 -0
  52. package/payload/server/public/assets/clone-kFPktUPQ.js +1 -0
  53. package/payload/server/public/assets/{dagre-di-KHpsq.js → dagre-DVLE4-m6.js} +1 -1
  54. package/payload/server/public/assets/{dagre-KV5264BT-DswETLwm.js → dagre-KV5264BT-DGjcX5uz.js} +1 -1
  55. package/payload/server/public/assets/{data-B6vpe0Zj.js → data-D1FFhz2k.js} +1 -1
  56. package/payload/server/public/assets/{diagram-5BDNPKRD-DWmq_Zkl.js → diagram-5BDNPKRD-CQI2o8Cv.js} +1 -1
  57. package/payload/server/public/assets/{diagram-G4DWMVQ6-BVi6yjLL.js → diagram-G4DWMVQ6-C8kXsEdQ.js} +1 -1
  58. package/payload/server/public/assets/{diagram-MMDJMWI5-D6XKyUAT.js → diagram-MMDJMWI5-DaWrNg_c.js} +1 -1
  59. package/payload/server/public/assets/{diagram-TYMM5635-sU1d7P6W.js → diagram-TYMM5635-Ql9QPUxX.js} +1 -1
  60. package/payload/server/public/assets/{erDiagram-SMLLAGMA-CY6qPZvu.js → erDiagram-SMLLAGMA-B2fBZhPo.js} +1 -1
  61. package/payload/server/public/assets/{flowDiagram-DWJPFMVM-CE9hd869.js → flowDiagram-DWJPFMVM-BA0LiOm5.js} +1 -1
  62. package/payload/server/public/assets/{ganttDiagram-T4ZO3ILL-Cbb885gt.js → ganttDiagram-T4ZO3ILL-CBKdEgUk.js} +1 -1
  63. package/payload/server/public/assets/{gitGraphDiagram-UUTBAWPF-BNzO3jBu.js → gitGraphDiagram-UUTBAWPF-BIyEfuwB.js} +1 -1
  64. package/payload/server/public/assets/{graph-DfegJdnT.js → graph-BxWBNsFs.js} +1 -1
  65. package/payload/server/public/assets/{graph-labels-BG4aqzs6.js → graph-labels-DSLdDXoF.js} +1 -1
  66. package/payload/server/public/assets/{graphlib-CIzKPoW_.js → graphlib-Cv4SvsQN.js} +1 -1
  67. package/payload/server/public/assets/{infoDiagram-42DDH7IO-DWGtofAT.js → infoDiagram-42DDH7IO-DovHLDHz.js} +1 -1
  68. package/payload/server/public/assets/{ishikawaDiagram-UXIWVN3A-UG1rus6G.js → ishikawaDiagram-UXIWVN3A-haFza9s4.js} +1 -1
  69. package/payload/server/public/assets/{journeyDiagram-VCZTEJTY-CYhHYXmj.js → journeyDiagram-VCZTEJTY-l-8tJ21z.js} +1 -1
  70. package/payload/server/public/assets/{kanban-definition-6JOO6SKY-CWHj9DHX.js → kanban-definition-6JOO6SKY-DSZUvFLs.js} +1 -1
  71. package/payload/server/public/assets/{line-vMN9CmXx.js → line-mfTElknw.js} +1 -1
  72. package/payload/server/public/assets/{mermaid-parser.core-5X7WWCZe.js → mermaid-parser.core-4temHHPS.js} +1 -1
  73. package/payload/server/public/assets/{mermaid.core-B0zUZsT6.js → mermaid.core-DO2iS9my.js} +3 -3
  74. package/payload/server/public/assets/{mindmap-definition-QFDTVHPH-BGr68ynR.js → mindmap-definition-QFDTVHPH-Cymf2IGG.js} +1 -1
  75. package/payload/server/public/assets/{pieDiagram-DEJITSTG-Bo3zU_w2.js → pieDiagram-DEJITSTG-_2tmuvMb.js} +1 -1
  76. package/payload/server/public/assets/{public-DCZ92LR9.js → public-Rz_GzOrN.js} +3 -3
  77. package/payload/server/public/assets/{quadrantDiagram-34T5L4WZ-o-dLMRj-.js → quadrantDiagram-34T5L4WZ-CVCrGnBi.js} +1 -1
  78. package/payload/server/public/assets/{requirementDiagram-MS252O5E-D_O_ZclX.js → requirementDiagram-MS252O5E-Bc-o8i8Z.js} +1 -1
  79. package/payload/server/public/assets/{sankeyDiagram-XADWPNL6-Doz7uWIn.js → sankeyDiagram-XADWPNL6-DrTBxqE6.js} +1 -1
  80. package/payload/server/public/assets/{sequenceDiagram-FGHM5R23-r1VW_jgc.js → sequenceDiagram-FGHM5R23-BjVjSsUJ.js} +1 -1
  81. package/payload/server/public/assets/{stateDiagram-FHFEXIEX-B7O06aua.js → stateDiagram-FHFEXIEX-DWAJP6ly.js} +1 -1
  82. package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-BHDyrxpj.js +1 -0
  83. package/payload/server/public/assets/{timeline-definition-GMOUNBTQ-WS2AL0yL.js → timeline-definition-GMOUNBTQ-Br_nbteH.js} +1 -1
  84. package/payload/server/public/assets/{useSelectionMode-lFraIQzT.css → useSelectionMode-BLb-o9RX.css} +1 -1
  85. package/payload/server/public/assets/{vennDiagram-DHZGUBPP-_-FLcVAh.js → vennDiagram-DHZGUBPP-D_ozeKV6.js} +1 -1
  86. package/payload/server/public/assets/{wardleyDiagram-NUSXRM2D-nZ3oO0ih.js → wardleyDiagram-NUSXRM2D-B87ZKC2B.js} +1 -1
  87. package/payload/server/public/assets/{xychartDiagram-5P7HB3ND-CqTdRM37.js → xychartDiagram-5P7HB3ND-DosRlk2T.js} +1 -1
  88. package/payload/server/public/browser.html +4 -4
  89. package/payload/server/public/data.html +5 -5
  90. package/payload/server/public/graph.html +6 -6
  91. package/payload/server/public/index.html +5 -5
  92. package/payload/server/public/public.html +4 -4
  93. package/payload/server/server.js +269 -81
  94. package/payload/server/public/assets/AdminShell-DwPVr9K1.js +0 -1
  95. package/payload/server/public/assets/channel-C4NqYS8u.js +0 -1
  96. package/payload/server/public/assets/classDiagram-6PBFFD2Q-CI3_kz04.js +0 -1
  97. package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-UP3RcKH9.js +0 -1
  98. package/payload/server/public/assets/clone-CvAPweMB.js +0 -1
  99. package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-Bo1g66pA.js +0 -1
  100. /package/payload/server/public/assets/{useSelectionMode-mzxTOyBv.js → useSelectionMode-hZ5bdL8b.js} +0 -0
@@ -62,6 +62,7 @@ import {
62
62
  listValidAccounts,
63
63
  loadAdminUserName,
64
64
  loadUserProfile,
65
+ loadWhatsappSenderName,
65
66
  persistMessage,
66
67
  projectAgent,
67
68
  reconcileEnabledPlugins,
@@ -96,7 +97,7 @@ import {
96
97
  vncLog,
97
98
  walkPremiumBundles,
98
99
  writeAdminUserAndPerson
99
- } from "./chunk-QHD5TKLQ.js";
100
+ } from "./chunk-EAJMRICW.js";
100
101
  import {
101
102
  __commonJS,
102
103
  __toESM
@@ -1201,7 +1202,7 @@ var serveStatic = (options = { root: "" }) => {
1201
1202
  };
1202
1203
 
1203
1204
  // server/index.ts
1204
- import { readFileSync as readFileSync24, existsSync as existsSync24, watchFile } from "fs";
1205
+ import { readFileSync as readFileSync24, existsSync as existsSync25, watchFile } from "fs";
1205
1206
  import { resolve as resolve27, join as join19, basename as basename7 } from "path";
1206
1207
  import { homedir as homedir3 } from "os";
1207
1208
  import { monitorEventLoopDelay } from "perf_hooks";
@@ -6861,7 +6862,7 @@ app3.get("/group-info", async (c) => {
6861
6862
  var whatsapp_default = app3;
6862
6863
 
6863
6864
  // server/routes/whatsapp-reader.ts
6864
- import { readFileSync as readFileSync10, watch, statSync as statSync4, openSync, readSync, closeSync } from "fs";
6865
+ import { readFileSync as readFileSync10, watch, statSync as statSync4, openSync, readSync, closeSync, existsSync as existsSync7, readdirSync as readdirSync6 } from "fs";
6865
6866
  import { basename as basename2, dirname, join as join9, resolve as resolve9, sep as sep2 } from "path";
6866
6867
 
6867
6868
  // server/routes/admin/sidebar-sessions.ts
@@ -7126,7 +7127,9 @@ function selectAdminChannelSessions(rows) {
7126
7127
  senderId: r.senderId,
7127
7128
  startedAt: r.startedAt,
7128
7129
  channel: r.channel,
7129
- operatorName: null
7130
+ operatorName: null,
7131
+ whatsappName: null,
7132
+ lastMessageAt: r.lastMessageAt
7130
7133
  }));
7131
7134
  }
7132
7135
 
@@ -7264,6 +7267,12 @@ function toolResultTurns(content, ts) {
7264
7267
  }
7265
7268
  return out;
7266
7269
  }
7270
+ function lastTurnTs(turns) {
7271
+ for (let i = turns.length - 1; i >= 0; i--) {
7272
+ if (turns[i].ts) return turns[i].ts;
7273
+ }
7274
+ return null;
7275
+ }
7267
7276
  function parseTranscript(lines) {
7268
7277
  const out = [];
7269
7278
  for (const line of lines) {
@@ -7340,6 +7349,7 @@ app5.get("/conversations", requireAdminSession, async (c) => {
7340
7349
  } catch {
7341
7350
  }
7342
7351
  const { title } = resolveTitle(body, sessionId, userTitles);
7352
+ const lastMessageAt = lastTurnTs(parseTranscript(body.split("\n")));
7343
7353
  rows.push({
7344
7354
  sessionId,
7345
7355
  projectDir,
@@ -7347,7 +7357,8 @@ app5.get("/conversations", requireAdminSession, async (c) => {
7347
7357
  startedAt: meta.startedAt ?? "",
7348
7358
  role: meta.role,
7349
7359
  channel: meta.channel,
7350
- senderId: meta.senderId
7360
+ senderId: meta.senderId,
7361
+ lastMessageAt
7351
7362
  });
7352
7363
  }
7353
7364
  const selected = selectAdminChannelSessions(rows);
@@ -7363,11 +7374,13 @@ app5.get("/conversations", requireAdminSession, async (c) => {
7363
7374
  }
7364
7375
  const admins = account?.config.admins ?? [];
7365
7376
  const nameBySender = /* @__PURE__ */ new Map();
7377
+ const whatsappNameBySender = /* @__PURE__ */ new Map();
7366
7378
  const distinct = [...new Set(selected.map((r) => r.senderId).filter((s) => !!s))];
7367
7379
  await Promise.all(
7368
7380
  distinct.map(async (senderId) => {
7369
7381
  if (!account) {
7370
7382
  nameBySender.set(senderId, null);
7383
+ whatsappNameBySender.set(senderId, null);
7371
7384
  return;
7372
7385
  }
7373
7386
  const idRes = classifyAdminUserId(users, admins, senderId);
@@ -7375,12 +7388,22 @@ app5.get("/conversations", requireAdminSession, async (c) => {
7375
7388
  const { name, reason } = resolveOperatorDisplay(idRes, nameRes);
7376
7389
  if (reason) console.error(`[wa-reader] op=operator-unresolved senderId=${senderId} reason=${reason}`);
7377
7390
  nameBySender.set(senderId, name);
7391
+ if (!name) {
7392
+ const waName = await loadWhatsappSenderName(account.accountId, senderId);
7393
+ if (waName) console.error(`[wa-reader] op=operator-name-fallback senderId=${senderId} source=whatsapp-pushname`);
7394
+ whatsappNameBySender.set(senderId, waName);
7395
+ } else {
7396
+ whatsappNameBySender.set(senderId, null);
7397
+ }
7378
7398
  })
7379
7399
  );
7380
7400
  const conversations = selected.map((r) => ({
7381
7401
  ...r,
7382
- operatorName: r.senderId ? nameBySender.get(r.senderId) ?? null : null
7402
+ operatorName: r.senderId ? nameBySender.get(r.senderId) ?? null : null,
7403
+ whatsappName: r.senderId ? whatsappNameBySender.get(r.senderId) ?? null : null
7383
7404
  }));
7405
+ const withTimestamp = conversations.filter((conv) => conv.lastMessageAt).length;
7406
+ console.log(`[wa-reader] op=conversations count=${conversations.length} withTimestamp=${withTimestamp}`);
7384
7407
  return c.json({ conversations });
7385
7408
  });
7386
7409
  function readFrom(path2, from) {
@@ -7492,12 +7515,60 @@ data: ${JSON.stringify(turn)}
7492
7515
  headers: { "Content-Type": "text/event-stream", "Cache-Control": "no-cache", Connection: "keep-alive" }
7493
7516
  });
7494
7517
  });
7518
+ var DIRECTIVE_NAME_RE = /^\d+-\d+\.txt$/;
7519
+ function directiveStoreDir(sessionId) {
7520
+ const account = resolveAccount();
7521
+ if (!account) return null;
7522
+ return resolve9(account.accountDir, "logs", "prompt-optimiser-directives", sessionId);
7523
+ }
7524
+ app5.get("/directives", requireAdminSession, (c) => {
7525
+ const sessionId = c.req.query("sessionId") ?? "";
7526
+ if (!SESSION_ID_RE2.test(sessionId)) return c.json({ error: "bad session reference" }, 400);
7527
+ const dir = directiveStoreDir(sessionId);
7528
+ if (!dir || !existsSync7(dir)) return c.json({ entries: [] });
7529
+ let names;
7530
+ try {
7531
+ names = readdirSync6(dir).filter((n) => DIRECTIVE_NAME_RE.test(n));
7532
+ } catch {
7533
+ return c.json({ entries: [] });
7534
+ }
7535
+ const entries = names.map((name) => {
7536
+ const [secsPart, pidPart] = name.replace(/\.txt$/, "").split("-");
7537
+ const secs = Number(secsPart);
7538
+ const pid = Number(pidPart);
7539
+ let len = 0;
7540
+ try {
7541
+ len = statSync4(join9(dir, name)).size;
7542
+ } catch {
7543
+ }
7544
+ return { name, secs, pid, ts: Number.isFinite(secs) ? new Date(secs * 1e3).toISOString() : null, len };
7545
+ }).sort((a, b) => a.secs - b.secs || a.pid - b.pid).map(({ name, ts, len }) => ({ name, ts, len }));
7546
+ return c.json({ entries });
7547
+ });
7548
+ app5.get("/directive", requireAdminSession, (c) => {
7549
+ const sessionId = c.req.query("sessionId") ?? "";
7550
+ const name = c.req.query("name") ?? "";
7551
+ if (!SESSION_ID_RE2.test(sessionId) || !DIRECTIVE_NAME_RE.test(name)) {
7552
+ return c.json({ error: "bad reference" }, 400);
7553
+ }
7554
+ const dir = directiveStoreDir(sessionId);
7555
+ if (!dir) return c.json({ error: "no account" }, 404);
7556
+ const path2 = resolve9(join9(dir, name));
7557
+ if (!path2.startsWith(dir + sep2)) return c.json({ error: "bad reference" }, 400);
7558
+ let body;
7559
+ try {
7560
+ body = readFileSync10(path2, "utf8");
7561
+ } catch {
7562
+ return c.json({ error: "not found" }, 404);
7563
+ }
7564
+ return c.text(body);
7565
+ });
7495
7566
  var whatsapp_reader_default = app5;
7496
7567
 
7497
7568
  // server/routes/telegram.ts
7498
7569
  import { homedir } from "os";
7499
7570
  import { resolve as resolve10, join as join10 } from "path";
7500
- import { existsSync as existsSync7, readFileSync as readFileSync11 } from "fs";
7571
+ import { existsSync as existsSync8, readFileSync as readFileSync11 } from "fs";
7501
7572
 
7502
7573
  // app/lib/telegram/access-control.ts
7503
7574
  function checkTelegramAccess(params) {
@@ -7549,7 +7620,7 @@ var DISPATCH_TIMEOUT_MS = 12e4;
7549
7620
  function configDirName() {
7550
7621
  const platformRoot2 = process.env.MAXY_PLATFORM_ROOT ?? resolve10(process.cwd(), "..");
7551
7622
  const brandPath = join10(platformRoot2, "config", "brand.json");
7552
- if (existsSync7(brandPath)) {
7623
+ if (existsSync8(brandPath)) {
7553
7624
  try {
7554
7625
  return JSON.parse(readFileSync11(brandPath, "utf-8")).configDir ?? ".maxy";
7555
7626
  } catch {
@@ -7583,7 +7654,7 @@ app6.post("/", async (c) => {
7583
7654
  return c.json({ ok: false }, 400);
7584
7655
  }
7585
7656
  const sp = secretPath(botType);
7586
- if (!existsSync7(sp)) {
7657
+ if (!existsSync8(sp)) {
7587
7658
  console.error(`${TAG21} op=reject reason=no-secret-file botType=${botType}`);
7588
7659
  return c.json({ ok: false }, 401);
7589
7660
  }
@@ -7650,11 +7721,11 @@ var telegram_default = app6;
7650
7721
 
7651
7722
  // server/routes/onboarding.ts
7652
7723
  import { spawn, spawnSync as spawnSync2, execFileSync as execFileSync2 } from "child_process";
7653
- import { openSync as openSync2, closeSync as closeSync2, writeFileSync as writeFileSync6, writeSync, existsSync as existsSync9, readFileSync as readFileSync13, unlinkSync } from "fs";
7724
+ import { openSync as openSync2, closeSync as closeSync2, writeFileSync as writeFileSync6, writeSync, existsSync as existsSync10, readFileSync as readFileSync13, unlinkSync } from "fs";
7654
7725
  import { createHash as createHash3, randomUUID as randomUUID7 } from "crypto";
7655
7726
 
7656
7727
  // ../lib/admins-write/src/index.ts
7657
- import { existsSync as existsSync8, readFileSync as readFileSync12, writeFileSync as writeFileSync5, renameSync, mkdirSync as mkdirSync2, readdirSync as readdirSync6, statSync as statSync5 } from "fs";
7728
+ import { existsSync as existsSync9, readFileSync as readFileSync12, writeFileSync as writeFileSync5, renameSync, mkdirSync as mkdirSync2, readdirSync as readdirSync7, statSync as statSync5 } from "fs";
7658
7729
  import { dirname as dirname2, join as join11 } from "path";
7659
7730
  function logLine(input, result) {
7660
7731
  const userIdShort = input.userId.slice(0, 8);
@@ -7672,7 +7743,7 @@ function writeAdminEntry(input) {
7672
7743
  const result = { usersJsonResult: "noop", accountJsonResult: "noop" };
7673
7744
  try {
7674
7745
  let users = [];
7675
- if (existsSync8(input.usersFile)) {
7746
+ if (existsSync9(input.usersFile)) {
7676
7747
  const raw = readFileSync12(input.usersFile, "utf-8").trim();
7677
7748
  if (raw) {
7678
7749
  const parsed = JSON.parse(raw);
@@ -7698,7 +7769,7 @@ function writeAdminEntry(input) {
7698
7769
  }
7699
7770
  try {
7700
7771
  const accountJsonPath = join11(input.accountDir, "account.json");
7701
- if (!existsSync8(accountJsonPath)) {
7772
+ if (!existsSync9(accountJsonPath)) {
7702
7773
  throw new Error(`account.json not found at ${accountJsonPath}`);
7703
7774
  }
7704
7775
  const config = JSON.parse(readFileSync12(accountJsonPath, "utf-8"));
@@ -7725,7 +7796,7 @@ function checkAdminAuthInvariant(input) {
7725
7796
  usersWithoutAccount: []
7726
7797
  };
7727
7798
  const usersUserIds = /* @__PURE__ */ new Set();
7728
- if (existsSync8(input.usersFile)) {
7799
+ if (existsSync9(input.usersFile)) {
7729
7800
  try {
7730
7801
  const raw = readFileSync12(input.usersFile, "utf-8").trim();
7731
7802
  if (raw) {
@@ -7740,10 +7811,10 @@ function checkAdminAuthInvariant(input) {
7740
7811
  }
7741
7812
  }
7742
7813
  const accountUserIds = /* @__PURE__ */ new Set();
7743
- if (existsSync8(input.accountsDir)) {
7814
+ if (existsSync9(input.accountsDir)) {
7744
7815
  let entries;
7745
7816
  try {
7746
- entries = readdirSync6(input.accountsDir);
7817
+ entries = readdirSync7(input.accountsDir);
7747
7818
  } catch (err) {
7748
7819
  const msg = err instanceof Error ? err.message : String(err);
7749
7820
  console.error(`[${input.tag}] accounts-dir unreadable accountsDir=${input.accountsDir} error=${msg}`);
@@ -7759,7 +7830,7 @@ function checkAdminAuthInvariant(input) {
7759
7830
  continue;
7760
7831
  }
7761
7832
  const accountJsonPath = join11(accountDir, "account.json");
7762
- if (!existsSync8(accountJsonPath)) continue;
7833
+ if (!existsSync9(accountJsonPath)) continue;
7763
7834
  let admins = [];
7764
7835
  try {
7765
7836
  const config = JSON.parse(readFileSync12(accountJsonPath, "utf-8"));
@@ -7802,7 +7873,7 @@ function hashPin2(pin) {
7802
7873
  return createHash3("sha256").update(pin).digest("hex");
7803
7874
  }
7804
7875
  function readUsersFile2() {
7805
- if (!existsSync9(USERS_FILE)) return null;
7876
+ if (!existsSync10(USERS_FILE)) return null;
7806
7877
  const raw = readFileSync13(USERS_FILE, "utf-8").trim();
7807
7878
  if (!raw) return [];
7808
7879
  return JSON.parse(raw);
@@ -7957,7 +8028,7 @@ app7.post("/set-pin", async (c) => {
7957
8028
  console.log(`[set-pin] wrote users.json + account.json admins: userId=${userId.slice(0, 8)}\u2026 role=owner`);
7958
8029
  if (process.platform === "linux") {
7959
8030
  let installOwner = null;
7960
- if (existsSync9(INSTALL_OWNER_FILE)) {
8031
+ if (existsSync10(INSTALL_OWNER_FILE)) {
7961
8032
  try {
7962
8033
  const raw = readFileSync13(INSTALL_OWNER_FILE, "utf-8").trim();
7963
8034
  if (raw.length > 0) installOwner = raw;
@@ -8040,7 +8111,7 @@ app7.delete("/set-pin", async (c) => {
8040
8111
  var onboarding_default = app7;
8041
8112
 
8042
8113
  // server/routes/client-error.ts
8043
- import { appendFileSync, existsSync as existsSync10, renameSync as renameSync2, statSync as statSync6 } from "fs";
8114
+ import { appendFileSync, existsSync as existsSync11, renameSync as renameSync2, statSync as statSync6 } from "fs";
8044
8115
  import { join as join12 } from "path";
8045
8116
  var CLIENT_ERRORS_LOG = join12(LOG_DIR, "client-errors.log");
8046
8117
  var MAX_LOG_SIZE = 10 * 1024 * 1024;
@@ -8085,7 +8156,7 @@ function stackHead(stack) {
8085
8156
  }
8086
8157
  function rotateIfNeeded() {
8087
8158
  try {
8088
- if (!existsSync10(CLIENT_ERRORS_LOG)) return;
8159
+ if (!existsSync11(CLIENT_ERRORS_LOG)) return;
8089
8160
  const stats = statSync6(CLIENT_ERRORS_LOG);
8090
8161
  if (stats.size < MAX_LOG_SIZE) return;
8091
8162
  renameSync2(CLIENT_ERRORS_LOG, CLIENT_ERRORS_LOG + ".1");
@@ -8367,18 +8438,18 @@ app9.post("/", async (c) => {
8367
8438
  var session_default = app9;
8368
8439
 
8369
8440
  // server/routes/admin/logs.ts
8370
- import { existsSync as existsSync12, readdirSync as readdirSync7, readFileSync as readFileSync14, statSync as statSync7 } from "fs";
8441
+ import { existsSync as existsSync13, readdirSync as readdirSync8, readFileSync as readFileSync14, statSync as statSync7 } from "fs";
8371
8442
  import { resolve as resolve11, basename as basename3 } from "path";
8372
8443
 
8373
8444
  // app/lib/logs-read-resolve.ts
8374
- import { existsSync as existsSync11 } from "fs";
8445
+ import { existsSync as existsSync12 } from "fs";
8375
8446
  import { join as join13 } from "path";
8376
8447
  function resolveSessionLogPaths(filename, logDirs) {
8377
8448
  const tried = [filename];
8378
8449
  const hits = [];
8379
8450
  for (const dir of logDirs) {
8380
8451
  const fullPath = join13(dir, filename);
8381
- if (existsSync11(fullPath)) {
8452
+ if (existsSync12(fullPath)) {
8382
8453
  hits.push({ path: fullPath, dir });
8383
8454
  }
8384
8455
  }
@@ -8506,10 +8577,10 @@ app10.get("/", async (c) => {
8506
8577
  const seen = /* @__PURE__ */ new Set();
8507
8578
  const logs = {};
8508
8579
  for (const dir of logDirs) {
8509
- if (!existsSync12(dir)) continue;
8580
+ if (!existsSync13(dir)) continue;
8510
8581
  let files;
8511
8582
  try {
8512
- files = readdirSync7(dir).filter((f) => f.endsWith(".log"));
8583
+ files = readdirSync8(dir).filter((f) => f.endsWith(".log"));
8513
8584
  } catch (err) {
8514
8585
  const reason = err instanceof Error ? err.message : String(err);
8515
8586
  console.warn(`[admin/logs] readdir-fail dir=${dir} reason=${reason}`);
@@ -8557,7 +8628,7 @@ var claude_info_default = app11;
8557
8628
 
8558
8629
  // server/routes/admin/attachment.ts
8559
8630
  import { readFile as readFile2, readdir } from "fs/promises";
8560
- import { existsSync as existsSync13 } from "fs";
8631
+ import { existsSync as existsSync14 } from "fs";
8561
8632
  import { resolve as resolve12 } from "path";
8562
8633
  var app12 = new Hono();
8563
8634
  app12.get("/:attachmentId", requireAdminSession, async (c) => {
@@ -8571,11 +8642,11 @@ app12.get("/:attachmentId", requireAdminSession, async (c) => {
8571
8642
  return new Response("Not found", { status: 404 });
8572
8643
  }
8573
8644
  const dir = resolve12(ATTACHMENTS_ROOT, accountId, attachmentId);
8574
- if (!existsSync13(dir)) {
8645
+ if (!existsSync14(dir)) {
8575
8646
  return new Response("Not found", { status: 404 });
8576
8647
  }
8577
8648
  const metaPath = resolve12(dir, `${attachmentId}.meta.json`);
8578
- if (!existsSync13(metaPath)) {
8649
+ if (!existsSync14(metaPath)) {
8579
8650
  return new Response("Not found", { status: 404 });
8580
8651
  }
8581
8652
  let meta;
@@ -8603,21 +8674,21 @@ var attachment_default = app12;
8603
8674
 
8604
8675
  // server/routes/admin/agents.ts
8605
8676
  import { resolve as resolve13 } from "path";
8606
- import { readdirSync as readdirSync8, readFileSync as readFileSync15, existsSync as existsSync14, rmSync } from "fs";
8677
+ import { readdirSync as readdirSync9, readFileSync as readFileSync15, existsSync as existsSync15, rmSync } from "fs";
8607
8678
  var app13 = new Hono();
8608
8679
  app13.get("/", (c) => {
8609
8680
  const account = resolveAccount();
8610
8681
  if (!account) return c.json({ agents: [] });
8611
8682
  const agentsDir = resolve13(account.accountDir, "agents");
8612
- if (!existsSync14(agentsDir)) return c.json({ agents: [] });
8683
+ if (!existsSync15(agentsDir)) return c.json({ agents: [] });
8613
8684
  const agents = [];
8614
8685
  try {
8615
- const entries = readdirSync8(agentsDir, { withFileTypes: true });
8686
+ const entries = readdirSync9(agentsDir, { withFileTypes: true });
8616
8687
  for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
8617
8688
  if (!entry.isDirectory()) continue;
8618
8689
  if (entry.name === "admin") continue;
8619
8690
  const configPath2 = resolve13(agentsDir, entry.name, "config.json");
8620
- if (!existsSync14(configPath2)) continue;
8691
+ if (!existsSync15(configPath2)) continue;
8621
8692
  try {
8622
8693
  const config = JSON.parse(readFileSync15(configPath2, "utf-8"));
8623
8694
  agents.push({
@@ -8646,7 +8717,7 @@ app13.delete("/:slug", async (c) => {
8646
8717
  return c.json({ error: "Invalid agent slug" }, 400);
8647
8718
  }
8648
8719
  const agentDir = resolve13(account.accountDir, "agents", slug);
8649
- if (!existsSync14(agentDir)) {
8720
+ if (!existsSync15(agentDir)) {
8650
8721
  return c.json({ error: "Agent not found" }, 404);
8651
8722
  }
8652
8723
  try {
@@ -8676,7 +8747,7 @@ app13.post("/:slug/project", async (c) => {
8676
8747
  return c.json({ error: "Invalid agent slug" }, 400);
8677
8748
  }
8678
8749
  const agentDir = resolve13(account.accountDir, "agents", slug);
8679
- if (!existsSync14(agentDir)) {
8750
+ if (!existsSync15(agentDir)) {
8680
8751
  return c.json({ error: "Agent not found on disk" }, 404);
8681
8752
  }
8682
8753
  try {
@@ -8692,7 +8763,7 @@ var agents_default = app13;
8692
8763
  // server/routes/admin/sessions.ts
8693
8764
  import crypto2 from "crypto";
8694
8765
  import { resolve as resolvePath } from "path";
8695
- import { existsSync as existsSync15, mkdirSync as mkdirSync3, createWriteStream } from "fs";
8766
+ import { existsSync as existsSync16, mkdirSync as mkdirSync3, createWriteStream } from "fs";
8696
8767
 
8697
8768
  // ../lib/admin-conversation-purge/src/index.ts
8698
8769
  async function purgeAdminConversationJsonls(args) {
@@ -8852,7 +8923,7 @@ function validateAndShapeAttachments(raws, conversationAccountId, sessionId, mes
8852
8923
  let reason = null;
8853
8924
  if (!a.attachmentId || !a.filename || !a.mimeType || !a.storagePath) reason = "schema-fail";
8854
8925
  else if (a.accountId !== conversationAccountId) reason = "account-mismatch";
8855
- else if (!existsSync15(a.storagePath)) reason = "missing-file";
8926
+ else if (!existsSync16(a.storagePath)) reason = "missing-file";
8856
8927
  if (reason) {
8857
8928
  invalid++;
8858
8929
  try {
@@ -9378,7 +9449,7 @@ app14.put("/:id/label", requireAdminSession, async (c) => {
9378
9449
  var sessions_default = app14;
9379
9450
 
9380
9451
  // app/lib/claude-agent/spawn-context.ts
9381
- import { existsSync as existsSync16, readFileSync as readFileSync16, readdirSync as readdirSync9, statSync as statSync8 } from "fs";
9452
+ import { existsSync as existsSync17, readFileSync as readFileSync16, readdirSync as readdirSync10, statSync as statSync8 } from "fs";
9382
9453
  import { dirname as dirname3, resolve as resolve14, join as join14 } from "path";
9383
9454
  async function resolveOwnerProfileBlock(accountId, userId) {
9384
9455
  if (!userId) return { ok: false, reason: "missing-user-id" };
@@ -9413,7 +9484,7 @@ function extractPluginToolDescriptions(pluginDir) {
9413
9484
  ];
9414
9485
  let raw = null;
9415
9486
  for (const path2 of candidates) {
9416
- if (!existsSync16(path2)) continue;
9487
+ if (!existsSync17(path2)) continue;
9417
9488
  try {
9418
9489
  raw = readFileSync16(path2, "utf-8");
9419
9490
  break;
@@ -9465,8 +9536,8 @@ function parseSkillPathsFromFrontmatter(fm) {
9465
9536
  function listPluginDirs() {
9466
9537
  const out = /* @__PURE__ */ new Map();
9467
9538
  const platformPlugins = resolve14(PLATFORM_ROOT, "plugins");
9468
- if (existsSync16(platformPlugins)) {
9469
- for (const name of readdirSync9(platformPlugins)) {
9539
+ if (existsSync17(platformPlugins)) {
9540
+ for (const name of readdirSync10(platformPlugins)) {
9470
9541
  const dir = join14(platformPlugins, name);
9471
9542
  try {
9472
9543
  if (statSync8(dir).isDirectory()) out.set(name, dir);
@@ -9475,12 +9546,12 @@ function listPluginDirs() {
9475
9546
  }
9476
9547
  }
9477
9548
  const premiumRoot = resolve14(dirname3(PLATFORM_ROOT), "premium-plugins");
9478
- if (existsSync16(premiumRoot)) {
9479
- for (const bundle of readdirSync9(premiumRoot)) {
9549
+ if (existsSync17(premiumRoot)) {
9550
+ for (const bundle of readdirSync10(premiumRoot)) {
9480
9551
  const bundlePlugins = join14(premiumRoot, bundle, "plugins");
9481
- if (!existsSync16(bundlePlugins)) continue;
9552
+ if (!existsSync17(bundlePlugins)) continue;
9482
9553
  try {
9483
- for (const name of readdirSync9(bundlePlugins)) {
9554
+ for (const name of readdirSync10(bundlePlugins)) {
9484
9555
  const dir = join14(bundlePlugins, name);
9485
9556
  try {
9486
9557
  if (statSync8(dir).isDirectory()) out.set(name, dir);
@@ -9495,7 +9566,7 @@ function listPluginDirs() {
9495
9566
  }
9496
9567
  function readEnabledPlugins(accountId) {
9497
9568
  const accountFile = resolve14(PLATFORM_ROOT, "..", "data/accounts", accountId, "account.json");
9498
- if (!existsSync16(accountFile)) return /* @__PURE__ */ new Set();
9569
+ if (!existsSync17(accountFile)) return /* @__PURE__ */ new Set();
9499
9570
  try {
9500
9571
  const parsed = JSON.parse(readFileSync16(accountFile, "utf-8"));
9501
9572
  return new Set(
@@ -9506,7 +9577,7 @@ function readEnabledPlugins(accountId) {
9506
9577
  }
9507
9578
  }
9508
9579
  function readFrontmatter(path2) {
9509
- if (!existsSync16(path2)) return null;
9580
+ if (!existsSync17(path2)) return null;
9510
9581
  let raw;
9511
9582
  try {
9512
9583
  raw = readFileSync16(path2, "utf-8");
@@ -9541,7 +9612,7 @@ ${fm}
9541
9612
  `plugin-manifest-tool-coverage plugin=${name} tools=${tools.length} with-desc=${withDesc}`
9542
9613
  );
9543
9614
  if (toolNames.length > 0 && descMap.size === 0) {
9544
- const hasSource = existsSync16(join14(dir, "mcp/dist/index.js")) || existsSync16(join14(dir, "mcp/src/index.ts"));
9615
+ const hasSource = existsSync17(join14(dir, "mcp/dist/index.js")) || existsSync17(join14(dir, "mcp/src/index.ts"));
9545
9616
  if (hasSource) {
9546
9617
  console.warn(
9547
9618
  `[spawn-context] WARN plugin=${name} mcp source present but tool-description regex matched zero entries \u2014 SDK upgrade may have changed the registration shape`
@@ -9568,10 +9639,10 @@ ${skillFm}
9568
9639
  }
9569
9640
  function computeSpecialistDomains(accountId) {
9570
9641
  const specialistsDir = resolve14(PLATFORM_ROOT, "..", "data/accounts", accountId, "specialists", "agents");
9571
- if (!existsSync16(specialistsDir)) return [];
9642
+ if (!existsSync17(specialistsDir)) return [];
9572
9643
  let entries;
9573
9644
  try {
9574
- entries = readdirSync9(specialistsDir);
9645
+ entries = readdirSync10(specialistsDir);
9575
9646
  } catch {
9576
9647
  return [];
9577
9648
  }
@@ -9614,7 +9685,7 @@ ${fm}
9614
9685
  }
9615
9686
  function computeDormantPlugins(accountId) {
9616
9687
  const accountFile = resolve14(PLATFORM_ROOT, "..", "data/accounts", accountId, "account.json");
9617
- if (!existsSync16(accountFile)) return [];
9688
+ if (!existsSync17(accountFile)) return [];
9618
9689
  let enabled;
9619
9690
  try {
9620
9691
  const raw = readFileSync16(accountFile, "utf-8");
@@ -9629,10 +9700,10 @@ function computeDormantPlugins(accountId) {
9629
9700
  return [];
9630
9701
  }
9631
9702
  const pluginsDir = resolve14(PLATFORM_ROOT, "plugins");
9632
- if (!existsSync16(pluginsDir)) return [];
9703
+ if (!existsSync17(pluginsDir)) return [];
9633
9704
  let entries;
9634
9705
  try {
9635
- entries = readdirSync9(pluginsDir);
9706
+ entries = readdirSync10(pluginsDir);
9636
9707
  } catch {
9637
9708
  return [];
9638
9709
  }
@@ -9640,7 +9711,7 @@ function computeDormantPlugins(accountId) {
9640
9711
  for (const name of entries) {
9641
9712
  if (enabled.has(name)) continue;
9642
9713
  const manifestPath = resolve14(pluginsDir, name, "PLUGIN.md");
9643
- if (!existsSync16(manifestPath)) continue;
9714
+ if (!existsSync17(manifestPath)) continue;
9644
9715
  let manifest;
9645
9716
  try {
9646
9717
  manifest = readFileSync16(manifestPath, "utf-8");
@@ -9657,11 +9728,11 @@ function computeDormantPlugins(accountId) {
9657
9728
  }
9658
9729
 
9659
9730
  // server/routes/admin/claude-sessions.ts
9660
- import { existsSync as existsSync17, readFileSync as readFileSync17 } from "fs";
9731
+ import { existsSync as existsSync18, readFileSync as readFileSync17 } from "fs";
9661
9732
  import { resolve as resolve15 } from "path";
9662
9733
  function readTunnelState(brandConfigDir) {
9663
9734
  const statePath = `${process.env.HOME ?? ""}/${brandConfigDir}/cloudflared/tunnel.state`;
9664
- if (!existsSync17(statePath)) return null;
9735
+ if (!existsSync18(statePath)) return null;
9665
9736
  try {
9666
9737
  const parsed = JSON.parse(readFileSync17(statePath, "utf-8"));
9667
9738
  const tunnelId = typeof parsed.tunnelId === "string" ? parsed.tunnelId : null;
@@ -13097,7 +13168,7 @@ var graph_default_view_default = app24;
13097
13168
  // server/routes/admin/sidebar-artefacts.ts
13098
13169
  import { readdir as readdir4, stat as stat5 } from "fs/promises";
13099
13170
  import { resolve as resolve19, relative as relative3, isAbsolute, sep as sep6, basename as basename6 } from "path";
13100
- import { existsSync as existsSync18 } from "fs";
13171
+ import { existsSync as existsSync19 } from "fs";
13101
13172
  var LIMIT = 50;
13102
13173
  var ADMIN_AGENT_FILES = ["IDENTITY.md", "SOUL.md", "KNOWLEDGE.md"];
13103
13174
  function toDataRootRelPath(absPath) {
@@ -13203,7 +13274,7 @@ async function fetchAgentTemplateRows(accountDir) {
13203
13274
  async function unionSpecialistFilenames(overrideDir, bundledDir) {
13204
13275
  const names = /* @__PURE__ */ new Set();
13205
13276
  for (const dir of [overrideDir, bundledDir]) {
13206
- if (!existsSync18(dir)) continue;
13277
+ if (!existsSync19(dir)) continue;
13207
13278
  try {
13208
13279
  const entries = await readdir4(dir);
13209
13280
  for (const entry of entries) {
@@ -13218,7 +13289,7 @@ async function unionSpecialistFilenames(overrideDir, bundledDir) {
13218
13289
  }
13219
13290
  async function readAgentTemplateRow(inp) {
13220
13291
  let chosenPath = null;
13221
- if (existsSync18(inp.overridePath)) {
13292
+ if (existsSync19(inp.overridePath)) {
13222
13293
  try {
13223
13294
  validateFilePathInAccount(inp.overridePath, inp.overrideRoot);
13224
13295
  chosenPath = inp.overridePath;
@@ -13229,7 +13300,7 @@ async function readAgentTemplateRow(inp) {
13229
13300
  );
13230
13301
  return null;
13231
13302
  }
13232
- } else if (existsSync18(inp.bundledPath)) {
13303
+ } else if (existsSync19(inp.bundledPath)) {
13233
13304
  if (!isWithin(inp.bundledPath, inp.bundledRoot)) {
13234
13305
  console.error(
13235
13306
  `[admin/sidebar-artefacts] agent-template-read-failed agent=${inp.displayName} kind=${inp.logName} error="bundled path outside PLATFORM_ROOT"`
@@ -13553,12 +13624,12 @@ app30.get("/", async (c) => {
13553
13624
  var system_stats_default = app30;
13554
13625
 
13555
13626
  // server/routes/admin/health.ts
13556
- import { existsSync as existsSync19, readFileSync as readFileSync18 } from "fs";
13627
+ import { existsSync as existsSync20, readFileSync as readFileSync18 } from "fs";
13557
13628
  import { resolve as resolve20, join as join17 } from "path";
13558
13629
  var PLATFORM_ROOT6 = process.env.MAXY_PLATFORM_ROOT ?? resolve20(process.cwd(), "..");
13559
13630
  var brandHostname = "maxy";
13560
13631
  var brandJsonPath = join17(PLATFORM_ROOT6, "config", "brand.json");
13561
- if (existsSync19(brandJsonPath)) {
13632
+ if (existsSync20(brandJsonPath)) {
13562
13633
  try {
13563
13634
  const brand = JSON.parse(readFileSync18(brandJsonPath, "utf-8"));
13564
13635
  if (brand.hostname) brandHostname = brand.hostname;
@@ -13569,7 +13640,7 @@ var VERSION_FILE = resolve20(PLATFORM_ROOT6, `config/.${brandHostname}-version`)
13569
13640
  var PROCESS_STARTED_AT = (/* @__PURE__ */ new Date()).toISOString();
13570
13641
  var PROBE_TIMEOUT_MS = 1e3;
13571
13642
  function readVersion() {
13572
- if (!existsSync19(VERSION_FILE)) return "unknown";
13643
+ if (!existsSync20(VERSION_FILE)) return "unknown";
13573
13644
  return readFileSync18(VERSION_FILE, "utf-8").trim() || "unknown";
13574
13645
  }
13575
13646
  async function probeConversationDb() {
@@ -14553,7 +14624,7 @@ app41.route("/request-magic-link", request_magic_link_default);
14553
14624
  var access_default = app41;
14554
14625
 
14555
14626
  // server/routes/sites.ts
14556
- import { existsSync as existsSync20, readFileSync as readFileSync20, realpathSync as realpathSync5, statSync as statSync9 } from "fs";
14627
+ import { existsSync as existsSync21, readFileSync as readFileSync20, realpathSync as realpathSync5, statSync as statSync9 } from "fs";
14557
14628
  import { resolve as resolve23 } from "path";
14558
14629
  var SAFE_SEG_RE = /^[a-z0-9_][a-z0-9_.-]{0,99}$/i;
14559
14630
  var MIME = {
@@ -14619,7 +14690,7 @@ app42.get("/:rel{.*}", (c) => {
14619
14690
  }
14620
14691
  let stat7;
14621
14692
  try {
14622
- stat7 = existsSync20(filePath) ? statSync9(filePath) : null;
14693
+ stat7 = existsSync21(filePath) ? statSync9(filePath) : null;
14623
14694
  } catch {
14624
14695
  stat7 = null;
14625
14696
  }
@@ -14638,7 +14709,7 @@ app42.get("/:rel{.*}", (c) => {
14638
14709
  console.error(`[sites] path-traversal-rejected path=${reqPath} reason=escape status=403`);
14639
14710
  return c.text("Forbidden", 403);
14640
14711
  }
14641
- if (!existsSync20(filePath)) {
14712
+ if (!existsSync21(filePath)) {
14642
14713
  console.error(`[sites] not-found path=${reqPath} status=404`);
14643
14714
  return c.text("Not found", 404);
14644
14715
  }
@@ -14769,7 +14840,7 @@ var VISITOR_COOKIE_NAME = "mxy_v";
14769
14840
  var VISITOR_COOKIE_MAX_AGE_SECONDS = Math.floor(DEFAULT_TTL_MS / 1e3);
14770
14841
 
14771
14842
  // app/lib/brand-config.ts
14772
- import { existsSync as existsSync21, readFileSync as readFileSync22 } from "fs";
14843
+ import { existsSync as existsSync22, readFileSync as readFileSync22 } from "fs";
14773
14844
  import { join as join18 } from "path";
14774
14845
  var cached2 = null;
14775
14846
  var cachedAttempted = false;
@@ -14779,7 +14850,7 @@ function readBrandConfig() {
14779
14850
  const platformRoot2 = process.env.MAXY_PLATFORM_ROOT;
14780
14851
  if (!platformRoot2) return null;
14781
14852
  const brandPath = join18(platformRoot2, "config", "brand.json");
14782
- if (!existsSync21(brandPath)) return null;
14853
+ if (!existsSync22(brandPath)) return null;
14783
14854
  try {
14784
14855
  cached2 = JSON.parse(readFileSync22(brandPath, "utf-8"));
14785
14856
  return cached2;
@@ -15270,7 +15341,7 @@ var visitor_event_default = app45;
15270
15341
 
15271
15342
  // server/routes/session.ts
15272
15343
  import { resolve as resolve24 } from "path";
15273
- import { existsSync as existsSync22, writeFileSync as writeFileSync8, mkdirSync as mkdirSync5 } from "fs";
15344
+ import { existsSync as existsSync23, writeFileSync as writeFileSync8, mkdirSync as mkdirSync5 } from "fs";
15274
15345
  var UUID_RE4 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
15275
15346
  function writeBrandingCache(accountId, agentSlug, branding) {
15276
15347
  try {
@@ -15365,7 +15436,7 @@ app46.post("/", async (c) => {
15365
15436
  let agentConfig = null;
15366
15437
  if (account) {
15367
15438
  const agentDir = resolve24(account.accountDir, "agents", agentSlug);
15368
- if (!existsSync22(agentDir) || !existsSync22(resolve24(agentDir, "config.json"))) {
15439
+ if (!existsSync23(agentDir) || !existsSync23(resolve24(agentDir, "config.json"))) {
15369
15440
  return c.json({ error: "Agent not found" }, 404);
15370
15441
  }
15371
15442
  agentConfig = resolveAgentConfig(account.accountDir, agentSlug);
@@ -16054,6 +16125,45 @@ function createWaChannelRoutes(deps) {
16054
16125
  console.error(`[whatsapp-native] op=reply-dispatch senderId=${senderId} bytes=${bytes}`);
16055
16126
  return c.json({ ok: true });
16056
16127
  });
16128
+ app48.post("/wa-channel/reply-document", async (c) => {
16129
+ const body = await c.req.json().catch(() => null);
16130
+ const senderId = body?.senderId;
16131
+ const files = body?.files;
16132
+ const caption = typeof body?.caption === "string" ? body.caption : void 0;
16133
+ if (typeof senderId !== "string" || !Array.isArray(files) || files.length === 0 || !files.every((f) => typeof f === "string")) {
16134
+ return c.json({ error: "senderId and a non-empty files string array required" }, 400);
16135
+ }
16136
+ const results = [];
16137
+ for (let i = 0; i < files.length; i++) {
16138
+ const file = files[i];
16139
+ console.error(`[whatsapp-native] op=reply-document-request senderId=${senderId} file=${file}`);
16140
+ let outcome;
16141
+ try {
16142
+ outcome = await deps.sendDocument(senderId, file, i === 0 ? caption : void 0);
16143
+ } catch (err) {
16144
+ const message = err instanceof Error ? err.message : String(err);
16145
+ const terminal = err instanceof WaReplyError ? err.terminal : false;
16146
+ console.error(
16147
+ `[whatsapp-native] op=reply-document-failed senderId=${senderId} file=${file} terminal=${terminal} error=${message}`
16148
+ );
16149
+ return c.json({ error: message, terminal }, 403);
16150
+ }
16151
+ if (outcome.ok) {
16152
+ console.error(`[whatsapp-native] op=reply-document-dispatch senderId=${senderId} file=${file}`);
16153
+ console.error(`[whatsapp-native] op=reply-document-reconciled senderId=${senderId} file=${file}`);
16154
+ results.push({ file, ok: true, messageId: outcome.messageId });
16155
+ } else {
16156
+ console.error(
16157
+ `[whatsapp-adaptor] file-delivery-unreconciled senderId=${senderId} file=${file} reason=${outcome.error}`
16158
+ );
16159
+ console.error(
16160
+ `[whatsapp-native] op=reply-document-failed senderId=${senderId} file=${file} terminal=false error=${outcome.error}`
16161
+ );
16162
+ results.push({ file, ok: false, error: outcome.error });
16163
+ }
16164
+ }
16165
+ return c.json({ ok: true, results });
16166
+ });
16057
16167
  app48.post("/wa-channel/ready", async (c) => {
16058
16168
  const body = await c.req.json().catch(() => null);
16059
16169
  const senderId = body?.senderId;
@@ -16086,6 +16196,10 @@ var WaGateway = class {
16086
16196
  }
16087
16197
  hub = new InboundHub();
16088
16198
  replies = /* @__PURE__ */ new Map();
16199
+ // Per-sender document-delivery context (the Baileys accountId), set on every
16200
+ // inbound. Its presence is the reply-only guard: a sender with no inbound has
16201
+ // no context, so a file reply to a cold recipient is refused.
16202
+ docContexts = /* @__PURE__ */ new Map();
16089
16203
  spawning = /* @__PURE__ */ new Set();
16090
16204
  seq = 0;
16091
16205
  /** Hono sub-app exposing the loopback channel routes; mount on the UI app. */
@@ -16093,6 +16207,7 @@ var WaGateway = class {
16093
16207
  return createWaChannelRoutes({
16094
16208
  hub: this.hub,
16095
16209
  sendOutbound: (senderId, text) => this.sendOutbound(senderId, text),
16210
+ sendDocument: (senderId, filePath, caption) => this.sendDocument(senderId, filePath, caption),
16096
16211
  onReady: (senderId) => {
16097
16212
  const count = this.hub.markReady(senderId, Date.now());
16098
16213
  if (count > 0) console.error(`[whatsapp-native] op=drained senderId=${senderId} count=${count}`);
@@ -16137,6 +16252,7 @@ var WaGateway = class {
16137
16252
  const openable = input.media.filter((m) => m.type !== "audio");
16138
16253
  const mediaField = openable.length > 0 ? `media=${openable.map((m) => m.type).join(",")} mediaPaths=${openable.map((m) => m.path).join(",")}` : input.media.some((m) => m.type === "audio") ? "media=audio mediaPath=transcribed" : "media=none";
16139
16254
  this.replies.set(input.senderId, input.reply);
16255
+ this.docContexts.set(input.senderId, { accountId: input.accountId });
16140
16256
  this.hub.deliver(
16141
16257
  {
16142
16258
  senderId: input.senderId,
@@ -16169,8 +16285,47 @@ var WaGateway = class {
16169
16285
  if (!reply) throw new Error(`wa-gateway: no reply route for sender ${senderId}`);
16170
16286
  await reply(text);
16171
16287
  }
16288
+ /** Deliver a file to a sender as a WhatsApp document. Throws `WaReplyError`
16289
+ * (reply-only guard) when no inbound has registered a document context for
16290
+ * the sender — file delivery is only ever a reply on a live conversation. */
16291
+ async sendDocument(senderId, filePath, caption) {
16292
+ const ctx = this.docContexts.get(senderId);
16293
+ if (!ctx) throw new WaReplyError(`no conversation for sender ${senderId}`, false);
16294
+ return this.deps.sendDocument({ senderId, accountId: ctx.accountId, filePath, caption });
16295
+ }
16172
16296
  };
16173
16297
 
16298
+ // app/lib/whatsapp/gateway/native-file-follower.ts
16299
+ function startNativeFileFollower(input) {
16300
+ const entry = {
16301
+ sessionId: input.sessionId,
16302
+ role: "admin",
16303
+ channel: "whatsapp",
16304
+ accountId: input.accountId,
16305
+ senderId: input.senderId,
16306
+ agentSlug: "admin",
16307
+ lastInboundAt: Date.now(),
16308
+ writeChain: Promise.resolve(),
16309
+ subscribers: /* @__PURE__ */ new Set(),
16310
+ pendingTurnText: "",
16311
+ followerAbort: null,
16312
+ followerRunning: false,
16313
+ sliceToken: "",
16314
+ personId: null,
16315
+ name: null
16316
+ };
16317
+ return startFollower({
16318
+ entry,
16319
+ tag: "[whatsapp-adaptor]",
16320
+ fileDelivery: makeWhatsAppFileDelivery(entry),
16321
+ // A resumed session's JSONL already holds prior SendUserFile tool_uses;
16322
+ // suppress replay so historical files are not re-sent on attach.
16323
+ suppressResumeReplay: true,
16324
+ onClose: input.onClose ?? (() => {
16325
+ })
16326
+ });
16327
+ }
16328
+
16174
16329
  // app/lib/whatsapp/inbound/channel-admin-binding-drift.ts
16175
16330
  function findChannelAdminBindingDrift(adminPhones, admins, users) {
16176
16331
  const drift = [];
@@ -16236,7 +16391,7 @@ function broadcastAdminShutdown(reason) {
16236
16391
 
16237
16392
  // ../lib/entitlement/src/index.ts
16238
16393
  import { createPublicKey, createHash as createHash5, verify as cryptoVerify } from "crypto";
16239
- import { existsSync as existsSync23, readFileSync as readFileSync23, statSync as statSync10 } from "fs";
16394
+ import { existsSync as existsSync24, readFileSync as readFileSync23, statSync as statSync10 } from "fs";
16240
16395
  import { resolve as resolve26 } from "path";
16241
16396
 
16242
16397
  // ../lib/entitlement/src/canonicalize.ts
@@ -16285,7 +16440,7 @@ function resolveEntitlement(brand, account) {
16285
16440
  return logResolved(implicitTrust(account), null);
16286
16441
  }
16287
16442
  const entitlementPath = resolve26(brand.configDir, "entitlement.json");
16288
- if (!existsSync23(entitlementPath)) {
16443
+ if (!existsSync24(entitlementPath)) {
16289
16444
  return logResolved(anonymousFallback("missing"), { reason: "missing" });
16290
16445
  }
16291
16446
  const stat7 = statSync10(entitlementPath);
@@ -16477,10 +16632,10 @@ function clientFrom(c) {
16477
16632
  var PLATFORM_ROOT9 = process.env.MAXY_PLATFORM_ROOT || "";
16478
16633
  var BRAND_JSON_PATH = PLATFORM_ROOT9 ? join19(PLATFORM_ROOT9, "config", "brand.json") : "";
16479
16634
  var BRAND = { productName: "Maxy", hostname: "maxy", configDir: ".maxy", marketingUrl: "https://getmaxy.com" };
16480
- if (BRAND_JSON_PATH && !existsSync24(BRAND_JSON_PATH)) {
16635
+ if (BRAND_JSON_PATH && !existsSync25(BRAND_JSON_PATH)) {
16481
16636
  console.error(`[brand] WARNING: brand.json not found at ${BRAND_JSON_PATH} \u2014 using Maxy defaults`);
16482
16637
  }
16483
- if (BRAND_JSON_PATH && existsSync24(BRAND_JSON_PATH)) {
16638
+ if (BRAND_JSON_PATH && existsSync25(BRAND_JSON_PATH)) {
16484
16639
  try {
16485
16640
  const parsed = JSON.parse(readFileSync24(BRAND_JSON_PATH, "utf-8"));
16486
16641
  BRAND = { ...BRAND, ...parsed };
@@ -16504,7 +16659,7 @@ var brandLoginOpts = {
16504
16659
  var ALIAS_DOMAINS_PATH = join19(homedir3(), BRAND.configDir, "alias-domains.json");
16505
16660
  function loadAliasDomains() {
16506
16661
  try {
16507
- if (!existsSync24(ALIAS_DOMAINS_PATH)) return null;
16662
+ if (!existsSync25(ALIAS_DOMAINS_PATH)) return null;
16508
16663
  const parsed = JSON.parse(readFileSync24(ALIAS_DOMAINS_PATH, "utf-8"));
16509
16664
  if (!Array.isArray(parsed)) {
16510
16665
  console.error("[alias-domains] malformed alias-domains.json \u2014 expected array");
@@ -16530,9 +16685,29 @@ function isPublicHost(host) {
16530
16685
  return host.startsWith("public.") || aliasDomains.has(host);
16531
16686
  }
16532
16687
  var app47 = new Hono();
16688
+ var nativeFileFollowers = /* @__PURE__ */ new Map();
16533
16689
  var waGateway = new WaGateway({
16534
16690
  gatewayUrl: `http://127.0.0.1:${process.env.MAXY_UI_INTERNAL_PORT ?? ""}`,
16535
16691
  serverPath: process.env.MAXY_WA_CHANNEL_SERVER_PATH ?? resolve27(process.env.MAXY_PLATFORM_ROOT ?? join19(__dirname, ".."), "services/whatsapp-channel/dist/server.js"),
16692
+ // Task 751 — file delivery on the native channel: resolve the platform
16693
+ // account + path validation here and funnel through the shared send core.
16694
+ sendDocument: async ({ senderId, accountId, filePath, caption }) => {
16695
+ let maxyAccountId;
16696
+ try {
16697
+ maxyAccountId = resolvePlatformAccountId();
16698
+ } catch (err) {
16699
+ return { ok: false, error: `account-unresolved: ${err instanceof Error ? err.message : String(err)}` };
16700
+ }
16701
+ const result = await sendWhatsAppDocument({
16702
+ to: senderId,
16703
+ filePath,
16704
+ caption,
16705
+ accountId,
16706
+ maxyAccountId,
16707
+ platformRoot: resolve27(process.env.MAXY_PLATFORM_ROOT ?? join19(__dirname, ".."))
16708
+ });
16709
+ return result.ok ? { ok: true, messageId: result.messageId } : { ok: false, error: result.error };
16710
+ },
16536
16711
  ensureChannelSession: async ({ accountId, senderId, gatewayUrl, serverPath }) => {
16537
16712
  const userId = resolveAdminUserId({ accountId, senderPhone: senderId }) ?? void 0;
16538
16713
  console.error(`[whatsapp-native] op=admin-identity senderId=${senderId} userId=${userId ?? "owner-fallback"}`);
@@ -16549,7 +16724,20 @@ var waGateway = new WaGateway({
16549
16724
  });
16550
16725
  if ("error" in result) {
16551
16726
  console.error(`[whatsapp-native] spawn-failed senderId=${senderId} error=${result.error} status=${result.status}`);
16727
+ return;
16552
16728
  }
16729
+ const prior = nativeFileFollowers.get(senderId);
16730
+ if (prior) prior.abort();
16731
+ let ac;
16732
+ ac = startNativeFileFollower({
16733
+ sessionId: adminSessionIdFor(accountId, senderId),
16734
+ senderId,
16735
+ accountId,
16736
+ onClose: () => {
16737
+ if (nativeFileFollowers.get(senderId) === ac) nativeFileFollowers.delete(senderId);
16738
+ }
16739
+ });
16740
+ nativeFileFollowers.set(senderId, ac);
16553
16741
  }
16554
16742
  });
16555
16743
  app47.route("/", waGateway.routes());
@@ -16878,7 +17066,7 @@ app47.get("/agent-assets/:slug/:filename", (c) => {
16878
17066
  console.error(`[agent-assets] path-traversal-rejected slug=${slug} file=${filename}`);
16879
17067
  return c.text("Forbidden", 403);
16880
17068
  }
16881
- if (!existsSync24(filePath)) {
17069
+ if (!existsSync25(filePath)) {
16882
17070
  console.error(`[agent-assets] serve slug=${slug} file=${filename} status=404`);
16883
17071
  return c.text("Not found", 404);
16884
17072
  }
@@ -16908,7 +17096,7 @@ app47.get("/generated/:filename", (c) => {
16908
17096
  console.error(`[generated] serve file=${filename} status=403`);
16909
17097
  return c.text("Forbidden", 403);
16910
17098
  }
16911
- if (!existsSync24(filePath)) {
17099
+ if (!existsSync25(filePath)) {
16912
17100
  console.error(`[generated] serve file=${filename} status=404`);
16913
17101
  return c.text("Not found", 404);
16914
17102
  }
@@ -16928,7 +17116,7 @@ app47.route("/v", visitor_consent_default);
16928
17116
  var htmlCache = /* @__PURE__ */ new Map();
16929
17117
  var brandLogoPath = "/brand/maxy-monochrome.png";
16930
17118
  var brandIconPath = "/brand/maxy-monochrome.png";
16931
- if (BRAND_JSON_PATH && existsSync24(BRAND_JSON_PATH)) {
17119
+ if (BRAND_JSON_PATH && existsSync25(BRAND_JSON_PATH)) {
16932
17120
  try {
16933
17121
  const fullBrand = JSON.parse(readFileSync24(BRAND_JSON_PATH, "utf-8"));
16934
17122
  if (fullBrand.assets?.logo) brandLogoPath = `/brand/${fullBrand.assets.logo}`;
@@ -16948,7 +17136,7 @@ function readInstalledVersion() {
16948
17136
  try {
16949
17137
  if (!PLATFORM_ROOT9) return "unknown";
16950
17138
  const versionFile = join19(PLATFORM_ROOT9, "config", `.${BRAND.hostname}-version`);
16951
- if (!existsSync24(versionFile)) return "unknown";
17139
+ if (!existsSync25(versionFile)) return "unknown";
16952
17140
  const content = readFileSync24(versionFile, "utf-8").trim();
16953
17141
  return content || "unknown";
16954
17142
  } catch {
@@ -17011,7 +17199,7 @@ function loadBrandingCache(agentSlug) {
17011
17199
  const accountId = getDefaultAccountId();
17012
17200
  if (!accountId) return null;
17013
17201
  const cachePath = join19(configDir2, "branding-cache", accountId, `${agentSlug}.json`);
17014
- if (!existsSync24(cachePath)) return null;
17202
+ if (!existsSync25(cachePath)) return null;
17015
17203
  return JSON.parse(readFileSync24(cachePath, "utf-8"));
17016
17204
  } catch {
17017
17205
  return null;
@@ -17256,7 +17444,7 @@ try {
17256
17444
  var ADMINUSER_RECONCILE_INTERVAL_MS = 60 * 60 * 1e3;
17257
17445
  async function runAdminUserReconcileTick() {
17258
17446
  try {
17259
- if (!existsSync24(USERS_FILE)) {
17447
+ if (!existsSync25(USERS_FILE)) {
17260
17448
  console.error("[adminuser-self-heal] skip reason=no-users-file");
17261
17449
  return;
17262
17450
  }