@rubytech/create-maxy-code 0.1.191 → 0.1.193

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 (172) hide show
  1. package/dist/__tests__/brew-resolve.test.js +1 -1
  2. package/dist/__tests__/macos-darwin-branch.test.js +1 -1
  3. package/dist/index.js +2 -2
  4. package/package.json +1 -1
  5. package/payload/platform/lib/mcp-eager/dist/index.d.ts +55 -0
  6. package/payload/platform/lib/mcp-eager/dist/index.d.ts.map +1 -1
  7. package/payload/platform/lib/mcp-eager/dist/index.js +76 -0
  8. package/payload/platform/lib/mcp-eager/dist/index.js.map +1 -1
  9. package/payload/platform/lib/mcp-eager/package.json +7 -0
  10. package/payload/platform/lib/mcp-eager/src/index.ts +101 -0
  11. package/payload/platform/plugins/admin/hooks/__tests__/pre-tool-use-admin-tool-gate.test.sh +102 -0
  12. package/payload/platform/plugins/admin/hooks/__tests__/pre-tool-use-base64-guard.test.sh +4 -4
  13. package/payload/platform/plugins/admin/hooks/pre-tool-use.sh +56 -0
  14. package/payload/platform/plugins/browser/mcp/dist/index.js +19 -19
  15. package/payload/platform/plugins/browser/mcp/dist/index.js.map +1 -1
  16. package/payload/platform/plugins/docs/references/projects-guide.md +21 -0
  17. package/payload/platform/plugins/email/mcp/dist/index.js +12 -12
  18. package/payload/platform/plugins/email/mcp/dist/index.js.map +1 -1
  19. package/payload/platform/plugins/memory/mcp/dist/index.js +8 -8
  20. package/payload/platform/plugins/memory/mcp/dist/index.js.map +1 -1
  21. package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.d.ts.map +1 -1
  22. package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.js +35 -0
  23. package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.js.map +1 -1
  24. package/payload/platform/plugins/prompt-optimiser/skills/prompt-optimiser/SKILL.md +1 -0
  25. package/payload/platform/plugins/url-get/mcp/dist/index.js +2 -2
  26. package/payload/platform/plugins/url-get/mcp/dist/index.js.map +1 -1
  27. package/payload/platform/plugins/work/PLUGIN.md +2 -0
  28. package/payload/platform/plugins/work/skills/execute-task/SKILL.md +101 -0
  29. package/payload/platform/services/claude-session-manager/dist/rc-daemon.d.ts.map +1 -1
  30. package/payload/platform/services/claude-session-manager/dist/rc-daemon.js +46 -2
  31. package/payload/platform/services/claude-session-manager/dist/rc-daemon.js.map +1 -1
  32. package/payload/premium-plugins/writer-craft/lib/mcp-eager/dist/index.d.ts +116 -0
  33. package/payload/premium-plugins/writer-craft/lib/mcp-eager/dist/index.d.ts.map +1 -0
  34. package/payload/premium-plugins/writer-craft/lib/mcp-eager/dist/index.js +125 -0
  35. package/payload/premium-plugins/writer-craft/lib/mcp-eager/dist/index.js.map +1 -0
  36. package/payload/premium-plugins/writer-craft/lib/mcp-eager/package.json +7 -0
  37. package/payload/premium-plugins/writer-craft/mcp/dist/index.d.ts.map +1 -1
  38. package/payload/premium-plugins/writer-craft/mcp/dist/index.js +5 -4
  39. package/payload/premium-plugins/writer-craft/mcp/dist/index.js.map +1 -1
  40. package/payload/premium-plugins/writer-craft/mcp/src/index.ts +5 -4
  41. package/payload/server/public/assets/AdminShell-BoJbnn6d.js +1 -0
  42. package/payload/server/public/assets/{Checkbox-g2WZRfCt.js → Checkbox-G-kyM1P1.js} +1 -1
  43. package/payload/server/public/assets/admin-B-GDNkoN.js +1 -0
  44. package/payload/server/public/assets/{arc-DMDAZHAN.js → arc-aUiRP9AS.js} +1 -1
  45. package/payload/server/public/assets/architecture-YZFGNWBL--v-pJPNp.js +1 -0
  46. package/payload/server/public/assets/{architectureDiagram-Q4EWVU46-BVKxPUIS.js → architectureDiagram-Q4EWVU46-BGgdXdD2.js} +1 -1
  47. package/payload/server/public/assets/{blockDiagram-DXYQGD6D-BpD-1G1V.js → blockDiagram-DXYQGD6D-DKWoL4Rp.js} +1 -1
  48. package/payload/server/public/assets/{brand-BGvv6AYI.js → brand-CYcGHhlS.js} +1 -1
  49. package/payload/server/public/assets/{brand-BQW2DYy_.css → brand-MbpD2Bhr.css} +1 -1
  50. package/payload/server/public/assets/{c4Diagram-AHTNJAMY-CFl9Dr6U.js → c4Diagram-AHTNJAMY-Ca6rs6F4.js} +1 -1
  51. package/payload/server/public/assets/channel-BwsV7WTV.js +1 -0
  52. package/payload/server/public/assets/{chunk-2KRD3SAO-Di4bO8ir.js → chunk-2KRD3SAO-BK3470lx.js} +1 -1
  53. package/payload/server/public/assets/{chunk-336JU56O-B2Uc6pEC.js → chunk-336JU56O-DcdMZImV.js} +2 -2
  54. package/payload/server/public/assets/chunk-426QAEUC-9FSP-c1z.js +1 -0
  55. package/payload/server/public/assets/{chunk-4BX2VUAB-DyEhFk-Z.js → chunk-4BX2VUAB-BOvVdJLf.js} +1 -1
  56. package/payload/server/public/assets/{chunk-4TB4RGXK-Dvf5uiFR.js → chunk-4TB4RGXK-CC1QpEvA.js} +1 -1
  57. package/payload/server/public/assets/{chunk-55IACEB6-BRJOZLpU.js → chunk-55IACEB6-BwZyF7vR.js} +1 -1
  58. package/payload/server/public/assets/{chunk-5FUZZQ4R-DUQR1rF9.js → chunk-5FUZZQ4R-o1b70dEE.js} +1 -1
  59. package/payload/server/public/assets/{chunk-5PVQY5BW-OTvNug7K.js → chunk-5PVQY5BW-C7VCYRz9.js} +1 -1
  60. package/payload/server/public/assets/{chunk-67CJDMHE-BG6-9r6c.js → chunk-67CJDMHE-D5bhMrtY.js} +1 -1
  61. package/payload/server/public/assets/{chunk-7N4EOEYR-BvMbitX7.js → chunk-7N4EOEYR-Si7Lgrwc.js} +1 -1
  62. package/payload/server/public/assets/{chunk-AA7GKIK3-CE8mGBD5.js → chunk-AA7GKIK3-DMuHtDqO.js} +1 -1
  63. package/payload/server/public/assets/{chunk-BSJP7CBP-DP7LTBll.js → chunk-BSJP7CBP-L79XKVcb.js} +1 -1
  64. package/payload/server/public/assets/{chunk-CIAEETIT-VfnIdN-h.js → chunk-CIAEETIT-C0O7Upmg.js} +1 -1
  65. package/payload/server/public/assets/{chunk-EDXVE4YY-CRAsAWbD.js → chunk-EDXVE4YY-B8d5xj_4.js} +1 -1
  66. package/payload/server/public/assets/{chunk-ENJZ2VHE-BNllMoi_.js → chunk-ENJZ2VHE-SSbr4n2X.js} +1 -1
  67. package/payload/server/public/assets/{chunk-FMBD7UC4-w-yBZsN2.js → chunk-FMBD7UC4-C_E43NFJ.js} +1 -1
  68. package/payload/server/public/assets/{chunk-FOC6F5B3-f9cFywhd.js → chunk-FOC6F5B3-D9lWWHAu.js} +1 -1
  69. package/payload/server/public/assets/{chunk-ICPOFSXX-vTeVS4qd.js → chunk-ICPOFSXX-Cgj9YSpX.js} +2 -2
  70. package/payload/server/public/assets/{chunk-K5T4RW27-B_ZUrFUq.js → chunk-K5T4RW27-DuhsNH4c.js} +1 -1
  71. package/payload/server/public/assets/{chunk-KGLVRYIC-CcWTvRlI.js → chunk-KGLVRYIC-B4-A1Abi.js} +1 -1
  72. package/payload/server/public/assets/{chunk-LIHQZDEY-D-5-peQw.js → chunk-LIHQZDEY-BxqgHRgT.js} +1 -1
  73. package/payload/server/public/assets/{chunk-ORNJ4GCN-B4Z5L25I.js → chunk-ORNJ4GCN-DEYQ5WaJ.js} +1 -1
  74. package/payload/server/public/assets/{chunk-OYMX7WX6-BdSFcvNa.js → chunk-OYMX7WX6-DNEEcCqI.js} +1 -1
  75. package/payload/server/public/assets/chunk-QZHKN3VN-Bd-GrQM6.js +1 -0
  76. package/payload/server/public/assets/{chunk-U2HBQHQK-BpFXup4D.js → chunk-U2HBQHQK-DuaoK83q.js} +1 -1
  77. package/payload/server/public/assets/{chunk-X2U36JSP-DF-6QHx4.js → chunk-X2U36JSP-CzMp0dpq.js} +1 -1
  78. package/payload/server/public/assets/{chunk-XPW4576I-m1Y_r88I.js → chunk-XPW4576I-CBfZXZDB.js} +1 -1
  79. package/payload/server/public/assets/{chunk-YZCP3GAM-CKgDoLRT.js → chunk-YZCP3GAM-B2wBDwSa.js} +1 -1
  80. package/payload/server/public/assets/{chunk-ZZ45TVLE-B0ACvZCZ.js → chunk-ZZ45TVLE-DkyF7XKW.js} +1 -1
  81. package/payload/server/public/assets/classDiagram-6PBFFD2Q-CEWNfHVo.js +1 -0
  82. package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-BBjzRxux.js +1 -0
  83. package/payload/server/public/assets/clone-CXtSonnn.js +1 -0
  84. package/payload/server/public/assets/{cose-bilkent-S5V4N54A-BhtgY3T7.js → cose-bilkent-S5V4N54A-Br2gjtEO.js} +1 -1
  85. package/payload/server/public/assets/{dagre-3yl9rfxR.js → dagre-AGj1P0p4.js} +1 -1
  86. package/payload/server/public/assets/{dagre-KV5264BT-D-Ux4uwL.js → dagre-KV5264BT-Cnrxcu6A.js} +1 -1
  87. package/payload/server/public/assets/data-Bg0Frxt3.js +1 -0
  88. package/payload/server/public/assets/{diagram-5BDNPKRD-D-MOMikl.js → diagram-5BDNPKRD-gyFYSx73.js} +1 -1
  89. package/payload/server/public/assets/{diagram-G4DWMVQ6-DKdFcnhf.js → diagram-G4DWMVQ6-DFmZEv5C.js} +1 -1
  90. package/payload/server/public/assets/{diagram-MMDJMWI5-ajamR766.js → diagram-MMDJMWI5-DA4qbuEW.js} +1 -1
  91. package/payload/server/public/assets/{diagram-TYMM5635-D2Pbk2Kk.js → diagram-TYMM5635-D4n-8gig.js} +1 -1
  92. package/payload/server/public/assets/{erDiagram-SMLLAGMA-v3o948pk.js → erDiagram-SMLLAGMA-CigRMjF1.js} +1 -1
  93. package/payload/server/public/assets/{flatten-BsWEYbBB.js → flatten-Bo6YRmWl.js} +1 -1
  94. package/payload/server/public/assets/{flowDiagram-DWJPFMVM-byC1a6GE.js → flowDiagram-DWJPFMVM-ClS27acA.js} +1 -1
  95. package/payload/server/public/assets/{ganttDiagram-T4ZO3ILL-Dybzs-Sw.js → ganttDiagram-T4ZO3ILL-Dr1IulXe.js} +1 -1
  96. package/payload/server/public/assets/gitGraph-7Q5UKJZL-CI0s_tqn.js +1 -0
  97. package/payload/server/public/assets/{gitGraphDiagram-UUTBAWPF-DvzXYLuW.js → gitGraphDiagram-UUTBAWPF-lwLibdPs.js} +1 -1
  98. package/payload/server/public/assets/graph-CpozZ74I.js +51 -0
  99. package/payload/server/public/assets/graph-labels-C2y666R0.js +1 -0
  100. package/payload/server/public/assets/{graphlib-DvH_spaA.js → graphlib-DBvoJMrc.js} +1 -1
  101. package/payload/server/public/assets/info-OMHHGYJF-g3gYW7Qm.js +1 -0
  102. package/payload/server/public/assets/infoDiagram-42DDH7IO-D4kReEIV.js +2 -0
  103. package/payload/server/public/assets/{isEmpty-BWl67LAZ.js → isEmpty-D6Kr-M1M.js} +1 -1
  104. package/payload/server/public/assets/{ishikawaDiagram-UXIWVN3A-CpY5W64P.js → ishikawaDiagram-UXIWVN3A-BnteQRQ0.js} +1 -1
  105. package/payload/server/public/assets/{journeyDiagram-VCZTEJTY-ymMAQVdm.js → journeyDiagram-VCZTEJTY-CXu6hLyJ.js} +1 -1
  106. package/payload/server/public/assets/{kanban-definition-6JOO6SKY-_NX9hNK3.js → kanban-definition-6JOO6SKY-CGDW2jle.js} +1 -1
  107. package/payload/server/public/assets/{line-Dz7zKWX-.js → line-pdg5hxC3.js} +1 -1
  108. package/payload/server/public/assets/{linear-0O14Y6uf.js → linear-DOh_6k2k.js} +1 -1
  109. package/payload/server/public/assets/{mermaid-parser.core-BCWd1en6.js → mermaid-parser.core-o3lIIK3Z.js} +2 -2
  110. package/payload/server/public/assets/{mermaid.core-gSoFkVoN.js → mermaid.core-DLR6Q3rV.js} +3 -3
  111. package/payload/server/public/assets/{mindmap-definition-QFDTVHPH-QuIyK2bd.js → mindmap-definition-QFDTVHPH-CH4cB0Dn.js} +1 -1
  112. package/payload/server/public/assets/{ordinal-krseTxxN.js → ordinal-BDi6f4xk.js} +1 -1
  113. package/payload/server/public/assets/packet-4T2RLAQJ-CT0TB9HI.js +1 -0
  114. package/payload/server/public/assets/pie-ZZUOXDRM-CXLe7TFF.js +1 -0
  115. package/payload/server/public/assets/{pieDiagram-DEJITSTG-DHK0yy7U.js → pieDiagram-DEJITSTG-Ba4voL_Y.js} +1 -1
  116. package/payload/server/public/assets/public-C8mil9h0.js +35 -0
  117. package/payload/server/public/assets/{quadrantDiagram-34T5L4WZ-wK1jwWo5.js → quadrantDiagram-34T5L4WZ-Cqn2xehe.js} +1 -1
  118. package/payload/server/public/assets/radar-PYXPWWZC-DnPLBl-D.js +1 -0
  119. package/payload/server/public/assets/{reduce-tk-xY6Fv.js → reduce-CGi9ik8i.js} +1 -1
  120. package/payload/server/public/assets/{requirementDiagram-MS252O5E-W_mH85_d.js → requirementDiagram-MS252O5E-DLfNx99l.js} +1 -1
  121. package/payload/server/public/assets/{sankeyDiagram-XADWPNL6-nGAM-YLQ.js → sankeyDiagram-XADWPNL6-uck_V-cb.js} +1 -1
  122. package/payload/server/public/assets/{sequenceDiagram-FGHM5R23-SMfUCwBH.js → sequenceDiagram-FGHM5R23-DmQalqud.js} +1 -1
  123. package/payload/server/public/assets/{stateDiagram-FHFEXIEX-CYKXFirH.js → stateDiagram-FHFEXIEX-DUdWeVRa.js} +1 -1
  124. package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-DNTpFwdy.js +1 -0
  125. package/payload/server/public/assets/{timeline-definition-GMOUNBTQ-DethbNFa.js → timeline-definition-GMOUNBTQ-BgL1wM5n.js} +1 -1
  126. package/payload/server/public/assets/treeView-SZITEDCU-C3cb7Xwe.js +1 -0
  127. package/payload/server/public/assets/treemap-W4RFUUIX-Dc7G3Bgm.js +1 -0
  128. package/payload/server/public/assets/useSelectionMode-BLpVlOgm.js +5 -0
  129. package/payload/server/public/assets/{vennDiagram-DHZGUBPP-BUlJTe9Z.js → vennDiagram-DHZGUBPP-7FHjIqPi.js} +1 -1
  130. package/payload/server/public/assets/wardley-RL74JXVD-DtgibWAt.js +1 -0
  131. package/payload/server/public/assets/{wardleyDiagram-NUSXRM2D-CL1TvZ7j.js → wardleyDiagram-NUSXRM2D-BHzo4V1J.js} +1 -1
  132. package/payload/server/public/assets/{xychartDiagram-5P7HB3ND-Cav10l-Z.js → xychartDiagram-5P7HB3ND-DtuWhqwh.js} +1 -1
  133. package/payload/server/public/data.html +5 -5
  134. package/payload/server/public/graph.html +6 -6
  135. package/payload/server/public/index.html +6 -8
  136. package/payload/server/public/public.html +5 -5
  137. package/payload/server/server.js +478 -456
  138. package/payload/server/public/assets/admin-DFxXLXfg.js +0 -216
  139. package/payload/server/public/assets/architecture-YZFGNWBL-COhEvUpo.js +0 -1
  140. package/payload/server/public/assets/channel-BTEFjudQ.js +0 -1
  141. package/payload/server/public/assets/chunk-426QAEUC-CcIGcm29.js +0 -1
  142. package/payload/server/public/assets/chunk-QZHKN3VN-CWh_0JsP.js +0 -1
  143. package/payload/server/public/assets/classDiagram-6PBFFD2Q-ClkdniKF.js +0 -1
  144. package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-kjJetfh2.js +0 -1
  145. package/payload/server/public/assets/clone-BllVm2iY.js +0 -1
  146. package/payload/server/public/assets/data-DtYcqL1G.js +0 -1
  147. package/payload/server/public/assets/gitGraph-7Q5UKJZL-DeTNsAO0.js +0 -1
  148. package/payload/server/public/assets/graph-C-XPyGsO.js +0 -1
  149. package/payload/server/public/assets/graph-labels-CZin3myX.js +0 -1
  150. package/payload/server/public/assets/info-OMHHGYJF-DJJ9GlS6.js +0 -1
  151. package/payload/server/public/assets/infoDiagram-42DDH7IO-C2FeU8nE.js +0 -2
  152. package/payload/server/public/assets/lib-BEvO-mE0.js +0 -33
  153. package/payload/server/public/assets/packet-4T2RLAQJ-CGbvGkvF.js +0 -1
  154. package/payload/server/public/assets/page-CKlvbjKn.js +0 -1
  155. package/payload/server/public/assets/page-DSj21hpG.js +0 -51
  156. package/payload/server/public/assets/pie-ZZUOXDRM-BZy8rjFn.js +0 -1
  157. package/payload/server/public/assets/public-BnS9zREj.js +0 -7
  158. package/payload/server/public/assets/radar-PYXPWWZC-DcfWIVXr.js +0 -1
  159. package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-B9ZwM8kx.js +0 -1
  160. package/payload/server/public/assets/treeView-SZITEDCU-3WugwVdj.js +0 -1
  161. package/payload/server/public/assets/treemap-W4RFUUIX-Cf5mDLlu.js +0 -1
  162. package/payload/server/public/assets/wardley-RL74JXVD-Bv4md4b3.js +0 -1
  163. /package/payload/server/public/assets/{_baseFor-BHtDrjIo.js → _baseFor-Cam2PbSt.js} +0 -0
  164. /package/payload/server/public/assets/{array-DetWRiSa.js → array-DYRGGQae.js} +0 -0
  165. /package/payload/server/public/assets/{cytoscape.esm-C9yNhe1u.js → cytoscape.esm-nWsJMTNI.js} +0 -0
  166. /package/payload/server/public/assets/{defaultLocale-_WRwicXn.js → defaultLocale-Du1XY3Dp.js} +0 -0
  167. /package/payload/server/public/assets/{dist-Bd4S37oi.js → dist-BzAsli7o.js} +0 -0
  168. /package/payload/server/public/assets/{init-sTEcj9YX.js → init-B5BXBRcm.js} +0 -0
  169. /package/payload/server/public/assets/{katex-s61Rgv6l.js → katex-HOUACuRw.js} +0 -0
  170. /package/payload/server/public/assets/{path-B0Ik7Tu9.js → path-CNO468J-.js} +0 -0
  171. /package/payload/server/public/assets/{rough.esm-DKRO8IF-.js → rough.esm-DRO6hWPh.js} +0 -0
  172. /package/payload/server/public/assets/{src-B6XdH6xq.js → src-CWiyyVfn.js} +0 -0
@@ -815,8 +815,8 @@ var serveStatic = (options = { root: "" }) => {
815
815
  };
816
816
 
817
817
  // server/index.ts
818
- import { readFileSync as readFileSync20, existsSync as existsSync23, watchFile } from "fs";
819
- import { resolve as resolve24, join as join15, basename as basename5 } from "path";
818
+ import { readFileSync as readFileSync20, existsSync as existsSync21, watchFile } from "fs";
819
+ import { resolve as resolve23, join as join15, basename as basename6 } 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
- (resolve25) => setTimeout(() => resolve25("timeout"), timeoutMs)
1277
+ (resolve24) => setTimeout(() => resolve24("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((resolve25, reject) => {
1405
+ return new Promise((resolve24, reject) => {
1406
1406
  const handler = (update) => {
1407
1407
  if (update.connection === "open") {
1408
1408
  sock.ev.off("connection.update", handler);
1409
- resolve25();
1409
+ resolve24();
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((resolve25, reject) => {
1523
+ return new Promise((resolve24, 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
- resolve25(value);
1530
+ resolve24(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((resolve25) => {
2066
- release = resolve25;
2065
+ const mine = new Promise((resolve24) => {
2066
+ release = resolve24;
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((resolve25) => {
3103
- const timer2 = setTimeout(resolve25, delay);
3102
+ await new Promise((resolve24) => {
3103
+ const timer2 = setTimeout(resolve24, delay);
3104
3104
  conn.abortController.signal.addEventListener("abort", () => {
3105
3105
  clearTimeout(timer2);
3106
- resolve25();
3106
+ resolve24();
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((resolve25) => {
3114
+ return new Promise((resolve24) => {
3115
3115
  if (!conn.sock) {
3116
- resolve25();
3116
+ resolve24();
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
- resolve25();
3123
+ resolve24();
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((resolve25) => {
3386
- resolvePending = resolve25;
3385
+ const sttPending = new Promise((resolve24) => {
3386
+ resolvePending = resolve24;
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((resolve25) => {
3790
+ return new Promise((resolve24) => {
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
- resolve25(true);
3795
+ resolve24(true);
3796
3796
  });
3797
3797
  socket.once("error", () => {
3798
3798
  socket.destroy();
3799
- resolve25(false);
3799
+ resolve24(false);
3800
3800
  });
3801
3801
  socket.once("timeout", () => {
3802
3802
  socket.destroy();
3803
- resolve25(false);
3803
+ resolve24(false);
3804
3804
  });
3805
3805
  });
3806
3806
  }
@@ -4536,12 +4536,12 @@ async function dispatchOnce(input) {
4536
4536
  });
4537
4537
  if (!entry) return { error: "spawn-failed" };
4538
4538
  entry.lastInboundAt = Date.now();
4539
- let resolve25;
4539
+ let resolve24;
4540
4540
  const turnPromise = new Promise((r) => {
4541
- resolve25 = r;
4541
+ resolve24 = r;
4542
4542
  });
4543
4543
  const listener = (text) => {
4544
- resolve25(text);
4544
+ resolve24(text);
4545
4545
  };
4546
4546
  entry.subscribers.add(listener);
4547
4547
  const writeOk = await writeInput(entry, input.text);
@@ -4983,8 +4983,8 @@ async function startLogin(opts) {
4983
4983
  resetActiveLogin(accountId);
4984
4984
  let resolveQr = null;
4985
4985
  let rejectQr = null;
4986
- const qrPromise = new Promise((resolve25, reject) => {
4987
- resolveQr = resolve25;
4986
+ const qrPromise = new Promise((resolve24, reject) => {
4987
+ resolveQr = resolve24;
4988
4988
  rejectQr = reject;
4989
4989
  });
4990
4990
  const qrTimer = setTimeout(
@@ -7245,24 +7245,6 @@ function openResumeStreamLog(accountId, sessionId) {
7245
7245
  }
7246
7246
  return createWriteStream(resolvePath(dir, `${sessionId}.log`), { flags: "a" });
7247
7247
  }
7248
- var healPending = /* @__PURE__ */ new Map();
7249
- function markHealPending(accountId, attachmentId) {
7250
- let set = healPending.get(accountId);
7251
- if (!set) {
7252
- set = /* @__PURE__ */ new Set();
7253
- healPending.set(accountId, set);
7254
- }
7255
- set.add(attachmentId);
7256
- }
7257
- function clearHealPending(accountId, attachmentId) {
7258
- const set = healPending.get(accountId);
7259
- if (!set) return;
7260
- set.delete(attachmentId);
7261
- if (set.size === 0) healPending.delete(accountId);
7262
- }
7263
- function isHealPending(accountId, attachmentId) {
7264
- return healPending.get(accountId)?.has(attachmentId) ?? false;
7265
- }
7266
7248
  function validateAndShapeAttachments(raws, conversationAccountId, sessionId, messageId, streamLogPath) {
7267
7249
  const chips = [];
7268
7250
  let valid = 0;
@@ -7640,17 +7622,14 @@ app11.post("/:id/resume", async (c) => {
7640
7622
  textOffset: c2.textOffset
7641
7623
  };
7642
7624
  if (!c2.artefactAttachmentId || !c2.artefactMimeType || !c2.artefactTitle) return base;
7643
- markHealPending(accountId, c2.artefactAttachmentId);
7644
7625
  try {
7645
7626
  const dataObj = JSON.parse(c2.data);
7646
7627
  const bytesPick = pickComponentBytes(dataObj);
7647
7628
  if (!bytesPick) {
7648
- clearHealPending(accountId, c2.artefactAttachmentId);
7649
7629
  return base;
7650
7630
  }
7651
7631
  const filename = bytesPick.mimeType === "text/html" ? `${c2.artefactTitle}.html` : `${c2.artefactTitle}.md`;
7652
7632
  await storeComponentArtefact(accountId, c2.artefactAttachmentId, bytesPick.mimeType, bytesPick.content, filename);
7653
- clearHealPending(accountId, c2.artefactAttachmentId);
7654
7633
  streamLogPath.write(
7655
7634
  `[${(/* @__PURE__ */ new Date()).toISOString()}] [component-persist] heal componentName=${c2.name} attachmentId=${c2.artefactAttachmentId.slice(0, 8)} mimeType=${bytesPick.mimeType} bytes=${bytesPick.content.length} outcome=ok
7656
7635
  `
@@ -7662,7 +7641,6 @@ app11.post("/:id/resume", async (c) => {
7662
7641
  artefactTitle: c2.artefactTitle
7663
7642
  };
7664
7643
  } catch (writeErr) {
7665
- clearHealPending(accountId, c2.artefactAttachmentId);
7666
7644
  const reason2 = writeErr instanceof Error ? writeErr.message : String(writeErr);
7667
7645
  streamLogPath.write(
7668
7646
  `[${(/* @__PURE__ */ new Date()).toISOString()}] [component-persist] heal componentName=${c2.name} attachmentId=${c2.artefactAttachmentId.slice(0, 8)} outcome=disk-fail reason=${JSON.stringify(reason2.slice(0, 200))}
@@ -8769,11 +8747,23 @@ async function cascadeDeleteDocument(params) {
8769
8747
  // app/lib/file-index.ts
8770
8748
  import * as fsp from "fs/promises";
8771
8749
  import { resolve as resolve13, relative as relative2, join as join11, basename as basename3, extname as extname2, sep as sep3 } from "path";
8750
+ import { tmpdir as tmpdir2 } from "os";
8772
8751
  import { execFile as execFile2 } from "child_process";
8773
8752
  import { promisify as promisify2 } from "util";
8774
8753
  var execFileAsync2 = promisify2(execFile2);
8775
8754
  var CONTENT_CAP = 1e5;
8776
- var RECONCILE_DEBOUNCE_MS = 1e4;
8755
+ var OCR_TIMEOUT_MS = 3e4;
8756
+ var OCR_IMAGE_EXTENSIONS = /* @__PURE__ */ new Set([
8757
+ ".png",
8758
+ ".jpg",
8759
+ ".jpeg",
8760
+ ".gif",
8761
+ ".webp",
8762
+ ".tiff",
8763
+ ".tif",
8764
+ ".bmp"
8765
+ ]);
8766
+ var RECONCILE_DEBOUNCE_MS = 5 * 60 * 1e3;
8777
8767
  var lastReconcileAt = /* @__PURE__ */ new Map();
8778
8768
  var TEXT_EXTENSIONS = /* @__PURE__ */ new Set([
8779
8769
  ".txt",
@@ -8826,26 +8816,60 @@ async function extractPdf(absolute) {
8826
8816
  });
8827
8817
  return stdout.trim();
8828
8818
  }
8819
+ async function ocrPdf(absolute) {
8820
+ const outPdf = join11(tmpdir2(), `file-index-ocr-${process.pid}-${Date.now()}.pdf`);
8821
+ try {
8822
+ await execFileAsync2(
8823
+ "ocrmypdf",
8824
+ ["--skip-text", "--quiet", "--output-type", "pdf", absolute, outPdf],
8825
+ { maxBuffer: 10 * 1024 * 1024, timeout: OCR_TIMEOUT_MS }
8826
+ );
8827
+ const { stdout } = await execFileAsync2("pdftotext", [outPdf, "-"], {
8828
+ maxBuffer: 10 * 1024 * 1024,
8829
+ timeout: OCR_TIMEOUT_MS
8830
+ });
8831
+ return stdout.trim();
8832
+ } finally {
8833
+ await fsp.rm(outPdf, { force: true });
8834
+ }
8835
+ }
8836
+ async function ocrImage(absolute) {
8837
+ const { stdout } = await execFileAsync2(
8838
+ "tesseract",
8839
+ [absolute, "-", "-l", "eng", "--psm", "3"],
8840
+ { maxBuffer: 10 * 1024 * 1024, timeout: OCR_TIMEOUT_MS }
8841
+ );
8842
+ return stdout.trim();
8843
+ }
8829
8844
  function stripHtml(html) {
8830
8845
  return html.replace(/<script[\s\S]*?<\/script>/gi, " ").replace(/<style[\s\S]*?<\/style>/gi, " ").replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
8831
8846
  }
8832
8847
  async function extractFileContent(absolute) {
8833
8848
  const ext = extname2(absolute).toLowerCase();
8834
8849
  try {
8835
- if (ext === ".pdf") return (await extractPdf(absolute)).slice(0, CONTENT_CAP);
8850
+ if (ext === ".pdf") {
8851
+ const text = (await extractPdf(absolute)).slice(0, CONTENT_CAP);
8852
+ if (text.length > 0) return { content: text, route: "pdftotext" };
8853
+ const ocred = (await ocrPdf(absolute)).slice(0, CONTENT_CAP);
8854
+ return ocred.length > 0 ? { content: ocred, route: "ocr-pdf" } : { content: "", route: "name-only" };
8855
+ }
8836
8856
  if (HTML_EXTENSIONS.has(ext)) {
8837
8857
  const raw = await fsp.readFile(absolute, "utf-8");
8838
- return stripHtml(raw).slice(0, CONTENT_CAP);
8858
+ return { content: stripHtml(raw).slice(0, CONTENT_CAP), route: "html" };
8839
8859
  }
8840
8860
  if (TEXT_EXTENSIONS.has(ext)) {
8841
8861
  const raw = await fsp.readFile(absolute, "utf-8");
8842
- return raw.slice(0, CONTENT_CAP);
8862
+ return { content: raw.slice(0, CONTENT_CAP), route: "text" };
8843
8863
  }
8844
- return "";
8864
+ if (OCR_IMAGE_EXTENSIONS.has(ext)) {
8865
+ const ocred = (await ocrImage(absolute)).slice(0, CONTENT_CAP);
8866
+ return ocred.length > 0 ? { content: ocred, route: "ocr-image" } : { content: "", route: "name-only" };
8867
+ }
8868
+ return { content: "", route: "name-only" };
8845
8869
  } catch (err) {
8846
8870
  const msg = err instanceof Error ? err.message : String(err);
8847
8871
  console.error(`[file-index] extract-failed path="${relative2(DATA_ROOT, absolute)}" err="${msg}"`);
8848
- return "";
8872
+ return { content: "", route: "name-only" };
8849
8873
  }
8850
8874
  }
8851
8875
  async function readDisplayName(absolute) {
@@ -8909,9 +8933,86 @@ async function upsertFileArtifact(session, f) {
8909
8933
  f
8910
8934
  );
8911
8935
  }
8936
+ async function linkFileArtifactToKnowledgeDocument(session, accountId, relativePath, absolutePath) {
8937
+ const res = await session.run(
8938
+ `MATCH (f:FileArtifact {accountId: $accountId, relativePath: $relativePath})
8939
+ OPTIONAL MATCH (d:KnowledgeDocument {accountId: $accountId, sourcePath: $absolutePath})
8940
+ WHERE NOT d:Trashed
8941
+ FOREACH (_ IN CASE WHEN d IS NULL THEN [] ELSE [1] END |
8942
+ MERGE (f)-[:INGESTED_AS]->(d))
8943
+ RETURN elementId(d) AS docId`,
8944
+ { accountId, relativePath, absolutePath }
8945
+ );
8946
+ const docId = res.records[0]?.get("docId");
8947
+ if (docId) {
8948
+ console.error(
8949
+ `[file-index] linked-kd account=${accountId} relativePath="${relativePath}" docId=${docId.slice(0, 12)}`
8950
+ );
8951
+ }
8952
+ }
8953
+ async function findLinkedKnowledgeDocs(session, accountId, relativePaths) {
8954
+ if (relativePaths.length === 0) return [];
8955
+ const res = await session.run(
8956
+ `MATCH (f:FileArtifact {accountId: $accountId})-[:INGESTED_AS]->(d:KnowledgeDocument)
8957
+ WHERE f.relativePath IN $relativePaths AND NOT d:Trashed
8958
+ OPTIONAL MATCH (d)-[:HAS_SECTION]->(s:Section)
8959
+ OPTIONAL MATCH (s)-[:HAS_CHUNK]->(c:Chunk)
8960
+ WITH d, collect(DISTINCT s) AS ss, collect(DISTINCT c) AS cc
8961
+ RETURN elementId(d) AS docId,
8962
+ [x IN ss WHERE x IS NOT NULL | elementId(x)] AS sectionIds,
8963
+ [x IN cc WHERE x IS NOT NULL | elementId(x)] AS chunkIds`,
8964
+ { accountId, relativePaths }
8965
+ );
8966
+ return res.records.map((rec) => ({
8967
+ docId: rec.get("docId"),
8968
+ sectionIds: rec.get("sectionIds") ?? [],
8969
+ chunkIds: rec.get("chunkIds") ?? []
8970
+ }));
8971
+ }
8972
+ async function cascadeTrashLinkedKnowledgeDocs(session, accountId, relativePaths) {
8973
+ const linked = await findLinkedKnowledgeDocs(session, accountId, relativePaths);
8974
+ let kds = 0, sections = 0, chunks = 0;
8975
+ for (const { docId, sectionIds, chunkIds } of linked) {
8976
+ try {
8977
+ await trashNode({
8978
+ session,
8979
+ accountId,
8980
+ elementId: docId,
8981
+ by: "file-index:cascade",
8982
+ reason: "physical file removed from FileArtifact index"
8983
+ });
8984
+ kds++;
8985
+ for (const sid of sectionIds) {
8986
+ await trashNode({
8987
+ session,
8988
+ accountId,
8989
+ elementId: sid,
8990
+ by: `file-index:cascade:from-${docId}`,
8991
+ reason: `cascade from KnowledgeDocument ${docId}`
8992
+ });
8993
+ sections++;
8994
+ }
8995
+ for (const cid of chunkIds) {
8996
+ await trashNode({
8997
+ session,
8998
+ accountId,
8999
+ elementId: cid,
9000
+ by: `file-index:cascade:from-${docId}`,
9001
+ reason: `cascade from KnowledgeDocument ${docId}`
9002
+ });
9003
+ chunks++;
9004
+ }
9005
+ } catch (err) {
9006
+ console.error(
9007
+ `[file-index] cascade-trash-failed account=${accountId} docId=${docId.slice(0, 12)} err="${err.message}"`
9008
+ );
9009
+ }
9010
+ }
9011
+ return { kds, sections, chunks };
9012
+ }
8912
9013
  async function buildArtifact(accountId, wf, embed2) {
8913
9014
  const name = basename3(wf.absolute);
8914
- const content = await extractFileContent(wf.absolute);
9015
+ const { content, route } = await extractFileContent(wf.absolute);
8915
9016
  const displayName = await readDisplayName(wf.absolute);
8916
9017
  const embedInput = `${name}
8917
9018
  ${content}`.slice(0, CONTENT_CAP);
@@ -8923,7 +9024,7 @@ ${content}`.slice(0, CONTENT_CAP);
8923
9024
  console.error(`[file-index] embed-failed path="${wf.relativePath}" err="${err.message}"`);
8924
9025
  }
8925
9026
  console.error(
8926
- `[file-index] index-file account=${accountId} path="${wf.relativePath}" chars=${content.length} embed-ms=${Date.now() - embedStart}`
9027
+ `[file-index] index-file account=${accountId} path="${wf.relativePath}" extractor=${route} chars=${content.length} embed-ms=${Date.now() - embedStart}`
8927
9028
  );
8928
9029
  return {
8929
9030
  accountId,
@@ -8982,14 +9083,29 @@ async function reconcileFileIndex(accountId, opts) {
8982
9083
  const unchanged = prior && prior.sizeBytes === wf.sizeBytes && prior.modifiedAt === wf.modifiedAt && prior.hasEmbedding;
8983
9084
  if (unchanged) {
8984
9085
  skipped++;
8985
- continue;
9086
+ } else {
9087
+ await upsertFileArtifact(session, await buildArtifact(accountId, wf, embed2));
9088
+ upserted++;
8986
9089
  }
8987
- await upsertFileArtifact(session, await buildArtifact(accountId, wf, embed2));
8988
- upserted++;
9090
+ await linkFileArtifactToKnowledgeDocument(session, accountId, wf.relativePath, wf.absolute);
8989
9091
  }
8990
9092
  let pruned = 0;
9093
+ let cascadeKds = 0, cascadeSections = 0, cascadeChunks = 0;
8991
9094
  if (clean) {
8992
9095
  const foundPaths = walked.map((w) => w.relativePath);
9096
+ const goneRes = await session.run(
9097
+ `MATCH (f:FileArtifact {accountId: $accountId})
9098
+ WHERE NOT f.relativePath IN $foundPaths
9099
+ RETURN f.relativePath AS rp`,
9100
+ { accountId, foundPaths }
9101
+ );
9102
+ const gonePaths = goneRes.records.map((r) => r.get("rp"));
9103
+ if (gonePaths.length > 0) {
9104
+ const cascade = await cascadeTrashLinkedKnowledgeDocs(session, accountId, gonePaths);
9105
+ cascadeKds = cascade.kds;
9106
+ cascadeSections = cascade.sections;
9107
+ cascadeChunks = cascade.chunks;
9108
+ }
8993
9109
  const pruneRes = await session.run(
8994
9110
  `MATCH (f:FileArtifact {accountId: $accountId})
8995
9111
  WHERE NOT f.relativePath IN $foundPaths
@@ -9001,7 +9117,7 @@ async function reconcileFileIndex(accountId, opts) {
9001
9117
  pruned = typeof c?.toNumber === "function" ? c.toNumber() : Number(c ?? 0);
9002
9118
  }
9003
9119
  console.error(
9004
- `[file-index] reconcile account=${accountId} walked=${walked.length} upserted=${upserted} skipped=${skipped} pruned=${pruned} clean=${clean} ms=${Date.now() - t0}`
9120
+ `[file-index] reconcile account=${accountId} walked=${walked.length} upserted=${upserted} skipped=${skipped} pruned=${pruned} cascade-kds=${cascadeKds} cascade-sections=${cascadeSections} cascade-chunks=${cascadeChunks} clean=${clean} ms=${Date.now() - t0}`
9005
9121
  );
9006
9122
  return { walked: walked.length, upserted, skipped, pruned };
9007
9123
  });
@@ -9030,10 +9146,12 @@ async function indexFile(accountId, relativePath, opts) {
9030
9146
  modifiedAt: st.mtime.toISOString()
9031
9147
  };
9032
9148
  await upsertFileArtifact(session, await buildArtifact(accountId, wf, embed2));
9149
+ await linkFileArtifactToKnowledgeDocument(session, accountId, relativePath, absolute);
9033
9150
  });
9034
9151
  }
9035
9152
  async function dropFileIndex(accountId, relativePath, opts) {
9036
9153
  await withSession(opts, async (session) => {
9154
+ const cascade = await cascadeTrashLinkedKnowledgeDocs(session, accountId, [relativePath]);
9037
9155
  const res = await session.run(
9038
9156
  `MATCH (f:FileArtifact {accountId: $accountId, relativePath: $relativePath})
9039
9157
  DETACH DELETE f
@@ -9042,7 +9160,9 @@ async function dropFileIndex(accountId, relativePath, opts) {
9042
9160
  );
9043
9161
  const c = res.records[0]?.get("dropped");
9044
9162
  const dropped = typeof c?.toNumber === "function" ? c.toNumber() : Number(c ?? 0);
9045
- console.error(`[file-index] drop account=${accountId} path="${relativePath}" dropped=${dropped}`);
9163
+ console.error(
9164
+ `[file-index] drop account=${accountId} path="${relativePath}" dropped=${dropped} cascade-kds=${cascade.kds} cascade-sections=${cascade.sections} cascade-chunks=${cascade.chunks}`
9165
+ );
9046
9166
  });
9047
9167
  }
9048
9168
 
@@ -9139,7 +9259,8 @@ async function knowledgeDocOwnedByAccount(accountId, attachmentId) {
9139
9259
  var app15 = new Hono();
9140
9260
  app15.get("/", requireAdminSession, async (c) => {
9141
9261
  const cacheKey = c.var.cacheKey;
9142
- if (!getAccountIdForSession(cacheKey)) {
9262
+ const accountId = getAccountIdForSession(cacheKey);
9263
+ if (!accountId) {
9143
9264
  console.error(`[data] auth-rejected endpoint="/api/admin/files" reason="no account for session"`);
9144
9265
  return c.json({ error: "Account not found for session" }, 401);
9145
9266
  }
@@ -9152,17 +9273,26 @@ app15.get("/", requireAdminSession, async (c) => {
9152
9273
  return c.json({ error: resolution.error }, resolution.status);
9153
9274
  }
9154
9275
  const { absolute, relative: relPath2 } = resolution;
9276
+ if (crossesForeignAccountPartition(relPath2, accountId)) {
9277
+ console.error(`[data] account-scope-blocked endpoint="/api/admin/files" path="${relPath2}" account=${accountId.slice(0, 8)}\u2026`);
9278
+ return c.json({ error: "Not found" }, 404);
9279
+ }
9155
9280
  try {
9156
9281
  const info = await stat4(absolute);
9157
9282
  if (!info.isDirectory()) {
9158
9283
  return c.json({ error: "Path is not a directory \u2014 use /api/admin/files/download for files" }, 400);
9159
9284
  }
9160
9285
  const names = await readdir3(absolute);
9286
+ const segs = relPath2.split("/").filter(Boolean);
9287
+ const isAccountParent = segs.length === 1 && (segs[0] === "accounts" || segs[0] === "uploads");
9161
9288
  const entries = [];
9162
9289
  for (const name of names) {
9163
9290
  if (UUID_RE3.test(name.replace(/\.meta\.json$/, "")) && name.endsWith(".meta.json")) {
9164
9291
  continue;
9165
9292
  }
9293
+ if (isAccountParent && UUID_RE3.test(name) && name !== accountId) {
9294
+ continue;
9295
+ }
9166
9296
  try {
9167
9297
  const entryPath = join12(absolute, name);
9168
9298
  const s = await stat4(entryPath);
@@ -9340,6 +9470,10 @@ app15.delete("/", requireAdminSession, async (c) => {
9340
9470
  return c.json({ error: resolution.error }, resolution.status);
9341
9471
  }
9342
9472
  const { absolute, relative: relPath2 } = resolution;
9473
+ if (crossesForeignAccountPartition(relPath2, accountId)) {
9474
+ console.error(`[data] account-scope-blocked endpoint="DELETE /api/admin/files" path="${relPath2}" account=${accountId.slice(0, 8)}\u2026`);
9475
+ return c.json({ error: "Not found" }, 404);
9476
+ }
9343
9477
  const base = basename4(absolute);
9344
9478
  const segments = relPath2.split("/").filter(Boolean);
9345
9479
  if (base === "account.json" || segments.includes(".git")) {
@@ -11397,126 +11531,14 @@ var WRITE_CYPHER = `
11397
11531
  var graph_default_view_default = app21;
11398
11532
 
11399
11533
  // 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";
11534
+ import { readdir as readdir4, stat as stat5 } from "fs/promises";
11535
+ import { resolve as resolve15, relative as relative3, isAbsolute, sep as sep5, basename as basename5 } from "path";
11403
11536
  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) {
11409
- try {
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
- };
11441
- }
11442
- }
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
- }
11488
-
11489
- // server/routes/admin/sidebar-artefacts.ts
11490
11537
  var LIMIT = 50;
11491
- var TEXT_MIME_PREFIXES = ["text/", "application/json", "application/markdown"];
11492
11538
  var ADMIN_AGENT_FILES = ["IDENTITY.md", "SOUL.md", "KNOWLEDGE.md"];
11493
11539
  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 };
11540
+ if (absPath !== DATA_ROOT && !absPath.startsWith(DATA_ROOT + sep5)) return null;
11541
+ return relative3(DATA_ROOT, absPath);
11520
11542
  }
11521
11543
  var app22 = new Hono();
11522
11544
  app22.get("/", requireAdminSession, async (c) => {
@@ -11526,118 +11548,64 @@ app22.get("/", requireAdminSession, async (c) => {
11526
11548
  return c.json({ error: "Account not found for session" }, 401);
11527
11549
  }
11528
11550
  const start = Date.now();
11529
- const docs = await fetchKnowledgeDocs(accountId);
11530
- if (docs === null) {
11551
+ const outputs = await fetchOutputArtefacts(accountId);
11552
+ if (outputs === null) {
11531
11553
  return c.json({ error: "Failed to load artefacts" }, 500);
11532
11554
  }
11533
- const accountDir = resolve16(ACCOUNTS_DIR, accountId);
11555
+ const accountDir = resolve15(ACCOUNTS_DIR, accountId);
11534
11556
  const agents = await fetchAgentTemplateRows(accountDir);
11535
- const artefacts = [...docs, ...agents].sort(
11557
+ const artefacts = [...outputs, ...agents].sort(
11536
11558
  (a, b) => (b.updatedAt ?? "").localeCompare(a.updatedAt ?? "")
11537
11559
  ).slice(0, LIMIT);
11538
11560
  const ms = Date.now() - start;
11539
11561
  console.log(
11540
- `[admin/sidebar-artefacts] account=${accountId} count=${artefacts.length} docs=${docs.length} agents=${agents.length} ms=${ms}`
11562
+ `[admin/sidebar-artefacts] account=${accountId} count=${artefacts.length} outputs=${outputs.length} agents=${agents.length} ms=${ms}`
11541
11563
  );
11542
11564
  return c.json({ artefacts });
11543
11565
  });
11544
- async function fetchKnowledgeDocs(accountId) {
11566
+ async function fetchOutputArtefacts(accountId) {
11545
11567
  const session = getSession();
11546
- let metas = [];
11547
11568
  try {
11548
11569
  const result = await session.run(
11549
- `MATCH (d:KnowledgeDocument { accountId: $accountId })
11550
- WHERE d.deletedAt IS NULL AND NOT d:Trashed
11551
- RETURN d.attachmentId AS id, d.name AS name, d.updatedAt AS updatedAt,
11552
- d.attachmentId AS attachmentId, d.encodingFormat AS mimeType,
11553
- d.sourcePath AS sourcePath, d.sourceType AS sourceType
11554
- ORDER BY d.updatedAt DESC
11555
- LIMIT $limit`,
11556
- { accountId, limit: neo4j2.int(LIMIT) }
11557
- );
11558
- metas = result.records.map((r) => ({
11559
- id: r.get("id"),
11560
- name: r.get("name") ?? "",
11561
- updatedAt: r.get("updatedAt") ?? "",
11562
- attachmentId: r.get("attachmentId"),
11563
- mimeType: r.get("mimeType") ?? "",
11564
- sourcePath: r.get("sourcePath") ?? null,
11565
- sourceType: r.get("sourceType") ?? null
11566
- }));
11570
+ `MATCH (f:FileArtifact { accountId: $accountId })
11571
+ WHERE f.relativePath CONTAINS '/output/'
11572
+ RETURN f.relativePath AS relativePath,
11573
+ f.displayName AS displayName,
11574
+ f.mimeType AS mimeType,
11575
+ f.modifiedAt AS modifiedAt
11576
+ ORDER BY f.modifiedAt DESC
11577
+ LIMIT 50`,
11578
+ { accountId }
11579
+ );
11580
+ return result.records.map((r) => {
11581
+ const relativePath = r.get("relativePath");
11582
+ const displayName = r.get("displayName") ?? null;
11583
+ const mimeType = r.get("mimeType") ?? "";
11584
+ const modifiedAt = r.get("modifiedAt") ?? "";
11585
+ return {
11586
+ id: `output-file:${relativePath}`,
11587
+ name: displayName ?? basename5(relativePath),
11588
+ kind: "output-file",
11589
+ updatedAt: modifiedAt,
11590
+ editable: false,
11591
+ mimeType,
11592
+ downloadPath: relativePath,
11593
+ downloadRoot: "data"
11594
+ };
11595
+ });
11567
11596
  } catch (err) {
11568
11597
  const message = err instanceof Error ? err.message : String(err);
11569
11598
  console.error(`[admin/sidebar-artefacts] account=${accountId} error="${message}"`);
11570
- await session.close();
11571
11599
  return null;
11572
11600
  } finally {
11573
11601
  await session.close();
11574
11602
  }
11575
- return Promise.all(metas.map(async (m) => {
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
- });
11584
- return {
11585
- id: m.id,
11586
- name: m.name,
11587
- kind: "knowledge-doc",
11588
- updatedAt: m.updatedAt,
11589
- content,
11590
- editable: skipReason === null,
11591
- mimeType: m.mimeType,
11592
- skipReason,
11593
- downloadPath,
11594
- downloadRoot
11595
- };
11596
- }));
11597
- }
11598
- async function readArtefactContent(accountId, attachmentId, mimeType, displayName) {
11599
- if (!attachmentId) {
11600
- logSkip(displayName, "null-attachmentId", mimeType);
11601
- return { content: "", skipReason: "null-attachmentId" };
11602
- }
11603
- const isText = TEXT_MIME_PREFIXES.some((p) => mimeType.startsWith(p));
11604
- if (!isText) {
11605
- logSkip(displayName, "non-text-mime", mimeType);
11606
- return { content: "", skipReason: "non-text-mime" };
11607
- }
11608
- const accountDir = resolve16(ATTACHMENTS_ROOT, accountId);
11609
- const dir = resolve16(accountDir, attachmentId);
11610
- try {
11611
- validateFilePathInAccount(dir, accountDir);
11612
- } catch {
11613
- logSkip(displayName, "containment-rejected", mimeType);
11614
- return { content: "", skipReason: "containment-rejected" };
11615
- }
11616
- try {
11617
- const entries = await readdir4(dir);
11618
- const dataFile = entries.find((f) => !f.endsWith(".meta.json"));
11619
- if (!dataFile) {
11620
- logSkip(displayName, "missing-on-disk", mimeType);
11621
- return { content: "", skipReason: "missing-on-disk" };
11622
- }
11623
- return { content: await readFile5(resolve16(dir, dataFile), "utf-8"), skipReason: null };
11624
- } catch (err) {
11625
- const message = err instanceof Error ? err.message : String(err);
11626
- console.error(`[admin/sidebar-artefacts] read-failed attachmentId=${attachmentId.slice(0, 8)} error="${message}"`);
11627
- logSkip(displayName, "missing-on-disk", mimeType);
11628
- return { content: "", skipReason: "missing-on-disk" };
11629
- }
11630
- }
11631
- function logSkip(name, reason, mimeType) {
11632
- console.log(
11633
- `[admin/sidebar-artefacts] content-skipped name="${name}" reason=${reason} mimeType=${mimeType || "none"}`
11634
- );
11635
11603
  }
11636
11604
  async function fetchAgentTemplateRows(accountDir) {
11637
11605
  const rows = [];
11638
11606
  for (const filename of ADMIN_AGENT_FILES) {
11639
- const overridePath = resolve16(accountDir, "agents", "admin", filename);
11640
- const bundledPath = resolve16(PLATFORM_ROOT, "templates", "agents", "admin", filename);
11607
+ const overridePath = resolve15(accountDir, "agents", "admin", filename);
11608
+ const bundledPath = resolve15(PLATFORM_ROOT, "templates", "agents", "admin", filename);
11641
11609
  const labelStem = filename.replace(/\.md$/, "");
11642
11610
  const row = await readAgentTemplateRow({
11643
11611
  id: `agent-template:admin:${filename}`,
@@ -11651,12 +11619,12 @@ async function fetchAgentTemplateRows(accountDir) {
11651
11619
  });
11652
11620
  if (row) rows.push(row);
11653
11621
  }
11654
- const overrideDir = resolve16(accountDir, "specialists", "agents");
11655
- const bundledDir = resolve16(PLATFORM_ROOT, "templates", "specialists", "agents");
11622
+ const overrideDir = resolve15(accountDir, "specialists", "agents");
11623
+ const bundledDir = resolve15(PLATFORM_ROOT, "templates", "specialists", "agents");
11656
11624
  const specialistNames = await unionSpecialistFilenames(overrideDir, bundledDir);
11657
11625
  for (const filename of specialistNames) {
11658
- const overridePath = resolve16(overrideDir, filename);
11659
- const bundledPath = resolve16(bundledDir, filename);
11626
+ const overridePath = resolve15(overrideDir, filename);
11627
+ const bundledPath = resolve15(bundledDir, filename);
11660
11628
  const row = await readAgentTemplateRow({
11661
11629
  id: `agent-template:specialist:${filename}`,
11662
11630
  displayName: filename.replace(/\.md$/, ""),
@@ -11711,20 +11679,15 @@ async function readAgentTemplateRow(inp) {
11711
11679
  }
11712
11680
  if (!chosenPath) return null;
11713
11681
  try {
11714
- const [content, st] = await Promise.all([
11715
- readFile5(chosenPath, "utf-8"),
11716
- stat5(chosenPath)
11717
- ]);
11682
+ const st = await stat5(chosenPath);
11718
11683
  const dlPath = toDataRootRelPath(chosenPath);
11719
11684
  return {
11720
11685
  id: inp.id,
11721
11686
  name: inp.displayName,
11722
11687
  kind: "agent-template",
11723
11688
  updatedAt: st.mtime.toISOString(),
11724
- content,
11725
11689
  editable: inp.editable,
11726
11690
  mimeType: "text/markdown",
11727
- skipReason: null,
11728
11691
  // Override paths live under <accountDir> (inside DATA_ROOT) and are
11729
11692
  // downloadable; bundled-fallback templates live under PLATFORM_ROOT
11730
11693
  // (outside DATA_ROOT) → null, so the client shows "not downloadable".
@@ -11742,17 +11705,15 @@ async function readAgentTemplateRow(inp) {
11742
11705
  }
11743
11706
  }
11744
11707
  function isWithin(target, root) {
11745
- const rel = relative4(root, target);
11708
+ const rel = relative3(root, target);
11746
11709
  return !rel.startsWith("..") && !isAbsolute(rel);
11747
11710
  }
11748
11711
  var sidebar_artefacts_default = app22;
11749
11712
 
11750
11713
  // server/routes/admin/sidebar-artefact-save.ts
11751
- import { mkdir as mkdir4, readdir as readdir5, stat as stat6, writeFile as writeFile5 } from "fs/promises";
11752
- import { resolve as resolve17 } from "path";
11753
- import { existsSync as existsSync17 } from "fs";
11714
+ import { mkdir as mkdir4, stat as stat6, writeFile as writeFile5 } from "fs/promises";
11715
+ import { resolve as resolve16 } from "path";
11754
11716
  var ADMIN_AGENT_FILES2 = /* @__PURE__ */ new Set(["IDENTITY.md", "SOUL.md", "KNOWLEDGE.md"]);
11755
- var UUID_RE4 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
11756
11717
  var app23 = new Hono();
11757
11718
  app23.post("/", requireAdminSession, async (c) => {
11758
11719
  const cacheKey = c.var.cacheKey;
@@ -11762,12 +11723,8 @@ app23.post("/", requireAdminSession, async (c) => {
11762
11723
  if (!body || typeof body.id !== "string" || typeof body.content !== "string") {
11763
11724
  return c.json({ error: "id and content required" }, 400);
11764
11725
  }
11765
- const accountDir = resolve17(ACCOUNTS_DIR, accountId);
11766
- const resolved = await resolveSavePath(body.id, accountId, accountDir);
11767
- if (resolved.kind === "heal-race") {
11768
- c.header("Retry-After", "2");
11769
- return c.json({ error: "heal-race", reason: resolved.reason }, 503);
11770
- }
11726
+ const accountDir = resolve16(ACCOUNTS_DIR, accountId);
11727
+ const resolved = await resolveSavePath(body.id, accountDir);
11771
11728
  if (resolved.kind === "reject") {
11772
11729
  console.error(
11773
11730
  `[admin/sidebar-artefact-save] auth-rejected reason=${resolved.reason} path=${body.id}`
@@ -11796,129 +11753,88 @@ app23.post("/", requireAdminSession, async (c) => {
11796
11753
  );
11797
11754
  return c.json({ updatedAt });
11798
11755
  });
11799
- async function resolveSavePath(id, accountId, accountDir) {
11800
- if (id.startsWith("agent-template:")) {
11801
- const parts = id.split(":");
11802
- if (parts.length !== 3) return { kind: "reject", status: 400, reason: "invalid-id" };
11803
- const [, role, filename] = parts;
11804
- if (role === "specialist") {
11805
- return { kind: "reject", status: 403, reason: "specialist-write-blocked" };
11806
- }
11807
- if (role !== "admin" || !ADMIN_AGENT_FILES2.has(filename)) {
11808
- return { kind: "reject", status: 400, reason: "invalid-id" };
11809
- }
11810
- const parent = resolve17(accountDir, "agents", "admin");
11811
- await mkdir4(parent, { recursive: true });
11812
- try {
11813
- validateFilePathInAccount(parent, accountDir);
11814
- } catch {
11815
- return { kind: "reject", status: 400, reason: "containment-rejected" };
11816
- }
11817
- return { kind: "admin-template", path: resolve17(parent, filename) };
11756
+ async function resolveSavePath(id, accountDir) {
11757
+ if (!id.startsWith("agent-template:")) {
11758
+ return { kind: "reject", status: 400, reason: "invalid-id" };
11818
11759
  }
11819
- if (UUID_RE4.test(id)) {
11820
- const dir = resolve17(ATTACHMENTS_ROOT, accountId, id);
11821
- if (!existsSync17(dir)) {
11822
- const attShort = id.slice(0, 8);
11823
- if (isHealPending(accountId, id)) {
11824
- console.error(`[admin/sidebar-artefact-save] heal-race attachmentId=${attShort} outcome=503-retry source=heal-pending`);
11825
- return { kind: "heal-race", reason: "dir-pending" };
11826
- }
11827
- const session = getSession();
11828
- try {
11829
- const result = await session.run(
11830
- `MATCH (k:KnowledgeDocument {accountId: $accountId, attachmentId: $attachmentId})
11831
- RETURN count(k) AS cnt`,
11832
- { accountId, attachmentId: id }
11833
- );
11834
- const cnt = result.records[0]?.get("cnt")?.toNumber?.() ?? 0;
11835
- if (cnt > 0) {
11836
- console.error(`[admin/sidebar-artefact-save] heal-race attachmentId=${attShort} outcome=503-retry source=neo4j-row`);
11837
- return { kind: "heal-race", reason: "dir-pending" };
11838
- }
11839
- console.error(`[admin/sidebar-artefact-save] heal-race attachmentId=${attShort} outcome=400-terminal`);
11840
- return { kind: "reject", status: 400, reason: "not-found" };
11841
- } catch (neoErr) {
11842
- const reason = neoErr instanceof Error ? neoErr.message.slice(0, 120) : String(neoErr).slice(0, 120);
11843
- console.error(`[admin/sidebar-artefact-save] heal-race attachmentId=${attShort} outcome=neo4j-fail reason="${reason}"`);
11844
- return { kind: "heal-race", reason: "neo4j-fail" };
11845
- } finally {
11846
- try {
11847
- await session.close();
11848
- } catch {
11849
- }
11850
- }
11851
- }
11852
- try {
11853
- validateFilePathInAccount(dir, resolve17(ATTACHMENTS_ROOT, accountId));
11854
- } catch {
11855
- return { kind: "reject", status: 400, reason: "containment-rejected" };
11856
- }
11857
- const entries = await readdir5(dir);
11858
- const dataFile = entries.find((f) => !f.endsWith(".meta.json"));
11859
- if (!dataFile) {
11860
- return { kind: "reject", status: 400, reason: "not-found" };
11861
- }
11862
- return { kind: "knowledge-doc", path: resolve17(dir, dataFile) };
11760
+ const parts = id.split(":");
11761
+ if (parts.length !== 3) return { kind: "reject", status: 400, reason: "invalid-id" };
11762
+ const [, role, filename] = parts;
11763
+ if (role === "specialist") {
11764
+ return { kind: "reject", status: 403, reason: "specialist-write-blocked" };
11765
+ }
11766
+ if (role !== "admin" || !ADMIN_AGENT_FILES2.has(filename)) {
11767
+ return { kind: "reject", status: 400, reason: "invalid-id" };
11768
+ }
11769
+ const parent = resolve16(accountDir, "agents", "admin");
11770
+ await mkdir4(parent, { recursive: true });
11771
+ try {
11772
+ validateFilePathInAccount(parent, accountDir);
11773
+ } catch {
11774
+ return { kind: "reject", status: 400, reason: "containment-rejected" };
11863
11775
  }
11864
- return { kind: "reject", status: 400, reason: "invalid-id" };
11776
+ return { kind: "admin-template", path: resolve16(parent, filename) };
11865
11777
  }
11866
11778
  function relPath(absPath, root) {
11867
11779
  return absPath.startsWith(root) ? absPath.slice(root.length + 1) : absPath;
11868
11780
  }
11869
11781
  var sidebar_artefact_save_default = app23;
11870
11782
 
11871
- // server/routes/admin/sidebar-artefact-content.ts
11872
- import { readFile as readFile6, readdir as readdir6 } from "fs/promises";
11873
- import { existsSync as existsSync18 } from "fs";
11874
- import { resolve as resolve18 } from "path";
11875
- var UUID_RE5 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
11783
+ // server/routes/admin/sidebar-sessions.ts
11876
11784
  var app24 = new Hono();
11877
11785
  app24.get("/", requireAdminSession, async (c) => {
11878
11786
  const cacheKey = c.var.cacheKey;
11879
11787
  const accountId = getAccountIdForSession(cacheKey);
11880
- if (!accountId) return new Response("Unauthorized", { status: 401 });
11881
- const id = c.req.query("id") ?? "";
11882
- if (!UUID_RE5.test(id)) {
11883
- console.error(`[admin/sidebar-artefact-content] not-found id=${id.slice(0, 8)}`);
11884
- return new Response("Not found", { status: 404 });
11788
+ if (!accountId) {
11789
+ return c.json({ error: "Account not found for session" }, 401);
11885
11790
  }
11886
- const dir = resolve18(ATTACHMENTS_ROOT, accountId, id);
11887
- if (!existsSync18(dir)) {
11888
- console.error(`[admin/sidebar-artefact-content] not-found id=${id.slice(0, 8)}`);
11889
- return new Response("Not found", { status: 404 });
11791
+ const port2 = requirePortEnv("CLAUDE_SESSION_MANAGER_PORT", { tag: "admin-sessions-list" });
11792
+ const url = `http://127.0.0.1:${port2}/list?senderId=${encodeURIComponent(accountId)}`;
11793
+ let upstream = null;
11794
+ try {
11795
+ upstream = await fetch(url);
11796
+ } catch (err) {
11797
+ const message = err instanceof Error ? err.message : String(err);
11798
+ console.error(`[admin-sessions-list] account=${accountId} fetch-failed error="${message}"`);
11799
+ return c.json({ error: "session-manager-unreachable" }, 503);
11890
11800
  }
11891
- let meta;
11801
+ if (!upstream.ok) {
11802
+ console.error(`[admin-sessions-list] account=${accountId} upstream-status=${upstream.status}`);
11803
+ return c.json({ error: "session-manager-error", status: upstream.status }, 502);
11804
+ }
11805
+ let payloads;
11892
11806
  try {
11893
- meta = JSON.parse(await readFile6(resolve18(dir, `${id}.meta.json`), "utf-8"));
11894
- } catch {
11895
- console.error(`[admin/sidebar-artefact-content] not-found id=${id.slice(0, 8)}`);
11896
- return new Response("Not found", { status: 404 });
11807
+ payloads = await upstream.json();
11808
+ } catch (err) {
11809
+ const message = err instanceof Error ? err.message : String(err);
11810
+ console.error(`[admin-sessions-list] account=${accountId} parse-failed error="${message}"`);
11811
+ return c.json({ error: "session-manager-bad-body" }, 502);
11897
11812
  }
11898
- const entries = await readdir6(dir);
11899
- const dataFile = entries.find((f) => !f.endsWith(".meta.json"));
11900
- if (!dataFile) {
11901
- console.error(`[admin/sidebar-artefact-content] not-found id=${id.slice(0, 8)}`);
11902
- return new Response("Not found", { status: 404 });
11813
+ if (!Array.isArray(payloads)) {
11814
+ console.error(`[admin-sessions-list] account=${accountId} bad-shape=not-array`);
11815
+ return c.json({ error: "session-manager-bad-body" }, 502);
11903
11816
  }
11904
- const start = Date.now();
11905
- const buffer = await readFile6(resolve18(dir, dataFile));
11906
- const ms = Date.now() - start;
11907
- console.log(
11908
- `[admin/sidebar-artefact-content] account=${accountId} id=${id.slice(0, 8)} mime=${meta.mimeType} bytes=${buffer.length} ms=${ms}`
11909
- );
11910
- return new Response(new Uint8Array(buffer), {
11911
- headers: {
11912
- "Content-Type": meta.mimeType,
11913
- "Content-Disposition": `inline; filename="${meta.filename}"`,
11914
- "Cache-Control": "private, max-age=3600"
11915
- }
11916
- });
11817
+ const rows = [];
11818
+ for (const p of payloads) {
11819
+ const sessionId = typeof p.sessionId === "string" ? p.sessionId : null;
11820
+ const capturedUrl = typeof p.url === "string" ? p.url : null;
11821
+ const startedAt = typeof p.startedAt === "string" ? p.startedAt : null;
11822
+ if (!sessionId || !capturedUrl || !startedAt) continue;
11823
+ const displayName = typeof p.displayName === "string" && p.displayName.length > 0 ? p.displayName : sessionId.slice(0, 8);
11824
+ rows.push({
11825
+ sessionId,
11826
+ title: displayName,
11827
+ url: capturedUrl,
11828
+ capturedAt: startedAt
11829
+ });
11830
+ }
11831
+ console.log(`[admin-sessions-list] account=${accountId} rows=${rows.length}`);
11832
+ return c.json({ sessions: rows });
11917
11833
  });
11918
- var sidebar_artefact_content_default = app24;
11834
+ var sidebar_sessions_default = app24;
11919
11835
 
11920
11836
  // server/routes/admin/system-stats.ts
11921
- import { readFile as readFile7 } from "fs/promises";
11837
+ import { readFile as readFile5 } from "fs/promises";
11922
11838
  import { cpus, loadavg, totalmem, freemem } from "os";
11923
11839
  function parseCpuStat(blob) {
11924
11840
  const line = blob.split("\n", 1)[0] ?? "";
@@ -11962,11 +11878,11 @@ function parseMeminfo(blob) {
11962
11878
  }
11963
11879
  var SAMPLE_DELAY_MS = 100;
11964
11880
  async function sampleLinux() {
11965
- const a = parseCpuStat(await readFile7("/proc/stat", "utf-8"));
11881
+ const a = parseCpuStat(await readFile5("/proc/stat", "utf-8"));
11966
11882
  await new Promise((r) => setTimeout(r, SAMPLE_DELAY_MS));
11967
- const b = parseCpuStat(await readFile7("/proc/stat", "utf-8"));
11968
- const meminfo = parseMeminfo(await readFile7("/proc/meminfo", "utf-8"));
11969
- const loadavgRaw = (await readFile7("/proc/loadavg", "utf-8")).trim();
11883
+ const b = parseCpuStat(await readFile5("/proc/stat", "utf-8"));
11884
+ const meminfo = parseMeminfo(await readFile5("/proc/meminfo", "utf-8"));
11885
+ const loadavgRaw = (await readFile5("/proc/loadavg", "utf-8")).trim();
11970
11886
  const [l1, l5, l15] = loadavgRaw.split(/\s+/, 3).map(Number);
11971
11887
  if (![l1, l5, l15].every(Number.isFinite)) {
11972
11888
  throw new Error(`malformed /proc/loadavg: ${loadavgRaw}`);
@@ -12039,23 +11955,23 @@ app25.get("/", async (c) => {
12039
11955
  var system_stats_default = app25;
12040
11956
 
12041
11957
  // server/routes/admin/health.ts
12042
- import { existsSync as existsSync19, readFileSync as readFileSync14 } from "fs";
12043
- import { resolve as resolve19, join as join13 } from "path";
12044
- var PLATFORM_ROOT6 = process.env.MAXY_PLATFORM_ROOT ?? resolve19(process.cwd(), "..");
11958
+ import { existsSync as existsSync17, readFileSync as readFileSync14 } from "fs";
11959
+ import { resolve as resolve17, join as join13 } from "path";
11960
+ var PLATFORM_ROOT6 = process.env.MAXY_PLATFORM_ROOT ?? resolve17(process.cwd(), "..");
12045
11961
  var brandHostname = "maxy";
12046
11962
  var brandJsonPath = join13(PLATFORM_ROOT6, "config", "brand.json");
12047
- if (existsSync19(brandJsonPath)) {
11963
+ if (existsSync17(brandJsonPath)) {
12048
11964
  try {
12049
11965
  const brand = JSON.parse(readFileSync14(brandJsonPath, "utf-8"));
12050
11966
  if (brand.hostname) brandHostname = brand.hostname;
12051
11967
  } catch {
12052
11968
  }
12053
11969
  }
12054
- var VERSION_FILE = resolve19(PLATFORM_ROOT6, `config/.${brandHostname}-version`);
11970
+ var VERSION_FILE = resolve17(PLATFORM_ROOT6, `config/.${brandHostname}-version`);
12055
11971
  var PROCESS_STARTED_AT = (/* @__PURE__ */ new Date()).toISOString();
12056
11972
  var PROBE_TIMEOUT_MS = 1e3;
12057
11973
  function readVersion() {
12058
- if (!existsSync19(VERSION_FILE)) return "unknown";
11974
+ if (!existsSync17(VERSION_FILE)) return "unknown";
12059
11975
  return readFileSync14(VERSION_FILE, "utf-8").trim() || "unknown";
12060
11976
  }
12061
11977
  async function probeConversationDb() {
@@ -12258,7 +12174,7 @@ app27.post("/", requireAdminSession, async (c) => {
12258
12174
  var linkedin_ingest_default = app27;
12259
12175
 
12260
12176
  // server/routes/admin/post-turn-context.ts
12261
- import neo4j3 from "neo4j-driver";
12177
+ import neo4j2 from "neo4j-driver";
12262
12178
  var TAG21 = "[post-turn-context]";
12263
12179
  var STRIPPED_PROPERTIES2 = /* @__PURE__ */ new Set([
12264
12180
  "embedding",
@@ -12271,10 +12187,10 @@ function isLoopbackAddr2(addr) {
12271
12187
  return addr === "127.0.0.1" || addr === "::1" || addr === "::ffff:127.0.0.1";
12272
12188
  }
12273
12189
  function convertValue(value) {
12274
- if (neo4j3.isDateTime(value) || neo4j3.isDate(value) || neo4j3.isLocalDateTime(value) || neo4j3.isLocalTime(value) || neo4j3.isTime(value) || neo4j3.isDuration(value)) {
12190
+ if (neo4j2.isDateTime(value) || neo4j2.isDate(value) || neo4j2.isLocalDateTime(value) || neo4j2.isLocalTime(value) || neo4j2.isTime(value) || neo4j2.isDuration(value)) {
12275
12191
  return value.toString();
12276
12192
  }
12277
- if (neo4j3.isInt(value)) {
12193
+ if (neo4j2.isInt(value)) {
12278
12194
  return value.inSafeRange() ? value.toNumber() : value.toString();
12279
12195
  }
12280
12196
  if (Array.isArray(value)) {
@@ -12361,7 +12277,7 @@ app28.get("/", async (c) => {
12361
12277
  var post_turn_context_default = app28;
12362
12278
 
12363
12279
  // server/routes/admin/public-session-context.ts
12364
- import neo4j4 from "neo4j-driver";
12280
+ import neo4j3 from "neo4j-driver";
12365
12281
  var TAG22 = "[public-session-context]";
12366
12282
  var STRIPPED_PROPERTIES3 = /* @__PURE__ */ new Set([
12367
12283
  "embedding",
@@ -12374,10 +12290,10 @@ function isLoopbackAddr3(addr) {
12374
12290
  return addr === "127.0.0.1" || addr === "::1" || addr === "::ffff:127.0.0.1";
12375
12291
  }
12376
12292
  function convertValue2(value) {
12377
- if (neo4j4.isDateTime(value) || neo4j4.isDate(value) || neo4j4.isLocalDateTime(value) || neo4j4.isLocalTime(value) || neo4j4.isTime(value) || neo4j4.isDuration(value)) {
12293
+ if (neo4j3.isDateTime(value) || neo4j3.isDate(value) || neo4j3.isLocalDateTime(value) || neo4j3.isLocalTime(value) || neo4j3.isTime(value) || neo4j3.isDuration(value)) {
12378
12294
  return value.toString();
12379
12295
  }
12380
- if (neo4j4.isInt(value)) {
12296
+ if (neo4j3.isInt(value)) {
12381
12297
  return value.inSafeRange() ? value.toNumber() : value.toString();
12382
12298
  }
12383
12299
  if (Array.isArray(value)) {
@@ -12553,7 +12469,7 @@ app32.route("/graph-labels-in-graph", graph_labels_in_graph_default);
12553
12469
  app32.route("/graph-default-view", graph_default_view_default);
12554
12470
  app32.route("/sidebar-artefacts", sidebar_artefacts_default);
12555
12471
  app32.route("/sidebar-artefact-save", sidebar_artefact_save_default);
12556
- app32.route("/sidebar-artefact-content", sidebar_artefact_content_default);
12472
+ app32.route("/sidebar-sessions", sidebar_sessions_default);
12557
12473
  app32.route("/system-stats", system_stats_default);
12558
12474
  app32.route("/health-brand", health_default2);
12559
12475
  app32.route("/linkedin-ingest", linkedin_ingest_default);
@@ -12564,15 +12480,15 @@ app32.route("/access-session-evict", access_session_evict_default);
12564
12480
  var admin_default = app32;
12565
12481
 
12566
12482
  // app/lib/access-gate.ts
12567
- import neo4j5 from "neo4j-driver";
12483
+ import neo4j4 from "neo4j-driver";
12568
12484
  import { readFileSync as readFileSync15 } from "fs";
12569
- import { resolve as resolve20 } from "path";
12485
+ import { resolve as resolve18 } from "path";
12570
12486
  import { randomUUID as randomUUID8 } from "crypto";
12571
- var PLATFORM_ROOT7 = process.env.MAXY_PLATFORM_ROOT ?? resolve20(process.cwd(), "..");
12487
+ var PLATFORM_ROOT7 = process.env.MAXY_PLATFORM_ROOT ?? resolve18(process.cwd(), "..");
12572
12488
  var driver = null;
12573
12489
  function readPassword() {
12574
12490
  if (process.env.NEO4J_PASSWORD) return process.env.NEO4J_PASSWORD;
12575
- const passwordFile = resolve20(PLATFORM_ROOT7, "config/.neo4j-password");
12491
+ const passwordFile = resolve18(PLATFORM_ROOT7, "config/.neo4j-password");
12576
12492
  try {
12577
12493
  return readFileSync15(passwordFile, "utf-8").trim();
12578
12494
  } catch {
@@ -12592,7 +12508,7 @@ function getDriver() {
12592
12508
  const user = process.env.NEO4J_USER ?? "neo4j";
12593
12509
  const password = readPassword();
12594
12510
  console.error(`[ui/access-gate] resolved neo4j_uri=${uri}`);
12595
- driver = neo4j5.driver(uri, neo4j5.auth.basic(user, password), {
12511
+ driver = neo4j4.driver(uri, neo4j4.auth.basic(user, password), {
12596
12512
  maxConnectionPoolSize: 5
12597
12513
  });
12598
12514
  }
@@ -12862,9 +12778,9 @@ var verify_token_default = app33;
12862
12778
 
12863
12779
  // app/lib/access-email.ts
12864
12780
  import { spawn as spawn2 } from "child_process";
12865
- import { resolve as resolve21 } from "path";
12866
- var PLATFORM_ROOT8 = process.env.MAXY_PLATFORM_ROOT ?? resolve21(process.cwd(), "..");
12867
- var SEND_SCRIPT = resolve21(
12781
+ import { resolve as resolve19 } from "path";
12782
+ var PLATFORM_ROOT8 = process.env.MAXY_PLATFORM_ROOT ?? resolve19(process.cwd(), "..");
12783
+ var SEND_SCRIPT = resolve19(
12868
12784
  PLATFORM_ROOT8,
12869
12785
  "plugins",
12870
12786
  "email",
@@ -13006,8 +12922,8 @@ app35.route("/request-magic-link", request_magic_link_default);
13006
12922
  var access_default = app35;
13007
12923
 
13008
12924
  // server/routes/sites.ts
13009
- import { existsSync as existsSync20, readFileSync as readFileSync16, realpathSync as realpathSync6, statSync as statSync8 } from "fs";
13010
- import { resolve as resolve22 } from "path";
12925
+ import { existsSync as existsSync18, readFileSync as readFileSync16, realpathSync as realpathSync5, statSync as statSync7 } from "fs";
12926
+ import { resolve as resolve20 } from "path";
13011
12927
  var SAFE_SEG_RE = /^[a-z0-9_][a-z0-9_.-]{0,99}$/i;
13012
12928
  var MIME = {
13013
12929
  ".html": "text/html; charset=utf-8",
@@ -13064,19 +12980,19 @@ app36.get("/:rel{.*}", (c) => {
13064
12980
  }
13065
12981
  segments.push(seg);
13066
12982
  }
13067
- const rootDir = resolve22(account.accountDir, "sites");
13068
- let filePath = segments.length === 0 ? rootDir : resolve22(rootDir, ...segments);
12983
+ const rootDir = resolve20(account.accountDir, "sites");
12984
+ let filePath = segments.length === 0 ? rootDir : resolve20(rootDir, ...segments);
13069
12985
  if (filePath !== rootDir && !filePath.startsWith(rootDir + "/")) {
13070
12986
  console.error(`[sites] path-traversal-rejected path=${reqPath} reason=escape status=403`);
13071
12987
  return c.text("Forbidden", 403);
13072
12988
  }
13073
- let stat7;
12989
+ let stat8;
13074
12990
  try {
13075
- stat7 = existsSync20(filePath) ? statSync8(filePath) : null;
12991
+ stat8 = existsSync18(filePath) ? statSync7(filePath) : null;
13076
12992
  } catch {
13077
- stat7 = null;
12993
+ stat8 = null;
13078
12994
  }
13079
- if (stat7?.isDirectory() && !reqPath.endsWith("/")) {
12995
+ if (stat8?.isDirectory() && !reqPath.endsWith("/")) {
13080
12996
  const search = new URL(c.req.url).search;
13081
12997
  const target = `${reqPath}/${search}`;
13082
12998
  console.log(
@@ -13084,22 +13000,22 @@ app36.get("/:rel{.*}", (c) => {
13084
13000
  );
13085
13001
  return c.redirect(target, 301);
13086
13002
  }
13087
- if (stat7?.isDirectory()) {
13088
- filePath = resolve22(filePath, "index.html");
13003
+ if (stat8?.isDirectory()) {
13004
+ filePath = resolve20(filePath, "index.html");
13089
13005
  }
13090
13006
  if (!filePath.startsWith(rootDir + "/")) {
13091
13007
  console.error(`[sites] path-traversal-rejected path=${reqPath} reason=escape status=403`);
13092
13008
  return c.text("Forbidden", 403);
13093
13009
  }
13094
- if (!existsSync20(filePath)) {
13010
+ if (!existsSync18(filePath)) {
13095
13011
  console.error(`[sites] not-found path=${reqPath} status=404`);
13096
13012
  return c.text("Not found", 404);
13097
13013
  }
13098
13014
  let realPath;
13099
13015
  let realRoot;
13100
13016
  try {
13101
- realPath = realpathSync6(filePath);
13102
- realRoot = realpathSync6(rootDir);
13017
+ realPath = realpathSync5(filePath);
13018
+ realRoot = realpathSync5(rootDir);
13103
13019
  } catch {
13104
13020
  console.error(`[sites] not-found path=${reqPath} status=404`);
13105
13021
  return c.text("Not found", 404);
@@ -13222,7 +13138,7 @@ var VISITOR_COOKIE_NAME = "mxy_v";
13222
13138
  var VISITOR_COOKIE_MAX_AGE_SECONDS = Math.floor(DEFAULT_TTL_MS / 1e3);
13223
13139
 
13224
13140
  // app/lib/brand-config.ts
13225
- import { existsSync as existsSync21, readFileSync as readFileSync18 } from "fs";
13141
+ import { existsSync as existsSync19, readFileSync as readFileSync18 } from "fs";
13226
13142
  import { join as join14 } from "path";
13227
13143
  var cached2 = null;
13228
13144
  var cachedAttempted = false;
@@ -13232,7 +13148,7 @@ function readBrandConfig() {
13232
13148
  const platformRoot = process.env.MAXY_PLATFORM_ROOT;
13233
13149
  if (!platformRoot) return null;
13234
13150
  const brandPath = join14(platformRoot, "config", "brand.json");
13235
- if (!existsSync21(brandPath)) return null;
13151
+ if (!existsSync19(brandPath)) return null;
13236
13152
  try {
13237
13153
  cached2 = JSON.parse(readFileSync18(brandPath, "utf-8"));
13238
13154
  return cached2;
@@ -13802,6 +13718,109 @@ function startGraphHealthTimer() {
13802
13718
  if (typeof timer.unref === "function") timer.unref();
13803
13719
  }
13804
13720
 
13721
+ // app/lib/file-watcher.ts
13722
+ import * as fsp2 from "fs/promises";
13723
+ import { resolve as resolve21, sep as sep6 } from "path";
13724
+ var ACCOUNT_UUID_RE2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
13725
+ var DEFAULT_COALESCE_MS = 500;
13726
+ var ROOTS = ["uploads", "accounts"];
13727
+ function _routeEvent(rootName, filename) {
13728
+ if (!filename) return null;
13729
+ const segs = filename.split(sep6).filter(Boolean);
13730
+ if (segs.length < 2) return null;
13731
+ const accountId = segs[0];
13732
+ if (!ACCOUNT_UUID_RE2.test(accountId)) return null;
13733
+ return { accountId, relativePath: `${rootName}/${segs.join("/")}` };
13734
+ }
13735
+ async function startFileWatcher(opts = {}) {
13736
+ const dataRoot = opts.dataRoot ?? DATA_ROOT;
13737
+ const coalesceMs = opts.coalesceMs ?? DEFAULT_COALESCE_MS;
13738
+ const indexFn = opts.index ?? indexFile;
13739
+ const dropFn = opts.drop ?? dropFileIndex;
13740
+ for (const r of ROOTS) {
13741
+ try {
13742
+ await fsp2.mkdir(resolve21(dataRoot, r), { recursive: true });
13743
+ } catch (err) {
13744
+ console.error(
13745
+ `[file-watcher] start-failed root="${r}" err="${err.message}" \u2014 index will be maintained by the 5-min reconcile backstop only`
13746
+ );
13747
+ return { stop: async () => {
13748
+ } };
13749
+ }
13750
+ }
13751
+ const controller = new AbortController();
13752
+ const timers = /* @__PURE__ */ new Map();
13753
+ function schedule(relativePath, accountId) {
13754
+ const existing = timers.get(relativePath);
13755
+ if (existing) clearTimeout(existing);
13756
+ const t = setTimeout(() => {
13757
+ timers.delete(relativePath);
13758
+ void runHook(relativePath, accountId);
13759
+ }, coalesceMs);
13760
+ timers.set(relativePath, t);
13761
+ }
13762
+ async function runHook(relativePath, accountId) {
13763
+ const absolute = resolve21(dataRoot, relativePath);
13764
+ let exists = false;
13765
+ try {
13766
+ const st = await fsp2.stat(absolute);
13767
+ exists = st.isFile();
13768
+ } catch (err) {
13769
+ const code = err.code;
13770
+ if (code !== "ENOENT") {
13771
+ console.error(`[file-watcher] stat-error path="${relativePath}" err="${err.message}"`);
13772
+ }
13773
+ }
13774
+ try {
13775
+ if (exists) {
13776
+ await indexFn(accountId, relativePath);
13777
+ } else {
13778
+ await dropFn(accountId, relativePath);
13779
+ }
13780
+ } catch (err) {
13781
+ console.error(`[file-watcher] hook-failed path="${relativePath}" err="${err.message}"`);
13782
+ } finally {
13783
+ opts.onSettled?.(relativePath);
13784
+ }
13785
+ }
13786
+ async function watchRoot(rootName) {
13787
+ const absRoot = resolve21(dataRoot, rootName);
13788
+ try {
13789
+ const iter = fsp2.watch(absRoot, { recursive: true, signal: controller.signal });
13790
+ for await (const event of iter) {
13791
+ const routed = _routeEvent(rootName, event.filename);
13792
+ if (!routed) continue;
13793
+ schedule(routed.relativePath, routed.accountId);
13794
+ }
13795
+ } catch (err) {
13796
+ const code = err.code;
13797
+ const name = err.name;
13798
+ if (code === "ABORT_ERR" || name === "AbortError") return;
13799
+ console.error(`[file-watcher] loop-error root="${rootName}" err="${err.message}"`);
13800
+ }
13801
+ }
13802
+ for (const r of ROOTS) void watchRoot(r);
13803
+ const sigHandler = () => {
13804
+ void stop();
13805
+ };
13806
+ process.once("SIGTERM", sigHandler);
13807
+ process.once("SIGINT", sigHandler);
13808
+ let stopped = false;
13809
+ async function stop() {
13810
+ if (stopped) return;
13811
+ stopped = true;
13812
+ process.off("SIGTERM", sigHandler);
13813
+ process.off("SIGINT", sigHandler);
13814
+ controller.abort();
13815
+ for (const t of timers.values()) clearTimeout(t);
13816
+ timers.clear();
13817
+ }
13818
+ console.error(
13819
+ `[file-watcher] started roots=${ROOTS.join(",")} dataRoot="${dataRoot}" coalesce-ms=${coalesceMs}`
13820
+ );
13821
+ return { stop };
13822
+ }
13823
+
13805
13824
  // app/lib/whatsapp/inbound/claude-bridge.ts
13806
13825
  var TAG27 = "[whatsapp-adaptor]";
13807
13826
  function whatsappTurnTimeoutMs() {
@@ -13863,8 +13882,8 @@ function broadcastAdminShutdown(reason) {
13863
13882
 
13864
13883
  // ../lib/entitlement/src/index.ts
13865
13884
  import { createPublicKey, createHash as createHash4, verify as cryptoVerify } from "crypto";
13866
- import { existsSync as existsSync22, readFileSync as readFileSync19, statSync as statSync9 } from "fs";
13867
- import { resolve as resolve23 } from "path";
13885
+ import { existsSync as existsSync20, readFileSync as readFileSync19, statSync as statSync8 } from "fs";
13886
+ import { resolve as resolve22 } from "path";
13868
13887
 
13869
13888
  // ../lib/entitlement/src/canonicalize.ts
13870
13889
  function canonicalize(value) {
@@ -13899,7 +13918,7 @@ var PUBKEY_SHA256 = "8eee6bcb33545fd13b16d3199a5735ca5db5062834c7b49dfe4f23801d9
13899
13918
  var GRACE_DAYS = 7;
13900
13919
  var GRACE_MS = GRACE_DAYS * 24 * 60 * 60 * 1e3;
13901
13920
  function pubkeyPath(brand) {
13902
- return resolve23(brand.platformRoot, "lib", "entitlement", "rubytech-pubkey.pem");
13921
+ return resolve22(brand.platformRoot, "lib", "entitlement", "rubytech-pubkey.pem");
13903
13922
  }
13904
13923
  var memo = null;
13905
13924
  function memoKey(mtimeMs, account) {
@@ -13911,12 +13930,12 @@ function resolveEntitlement(brand, account) {
13911
13930
  if (brand.commercialMode !== true) {
13912
13931
  return logResolved(implicitTrust(account), null);
13913
13932
  }
13914
- const entitlementPath = resolve23(brand.configDir, "entitlement.json");
13915
- if (!existsSync22(entitlementPath)) {
13933
+ const entitlementPath = resolve22(brand.configDir, "entitlement.json");
13934
+ if (!existsSync20(entitlementPath)) {
13916
13935
  return logResolved(anonymousFallback("missing"), { reason: "missing" });
13917
13936
  }
13918
- const stat7 = statSync9(entitlementPath);
13919
- const key = memoKey(stat7.mtimeMs, account);
13937
+ const stat8 = statSync8(entitlementPath);
13938
+ const key = memoKey(stat8.mtimeMs, account);
13920
13939
  if (memo && memo.key === key) {
13921
13940
  return memo.result;
13922
13941
  }
@@ -14104,10 +14123,10 @@ function clientFrom(c) {
14104
14123
  var PLATFORM_ROOT9 = process.env.MAXY_PLATFORM_ROOT || "";
14105
14124
  var BRAND_JSON_PATH = PLATFORM_ROOT9 ? join15(PLATFORM_ROOT9, "config", "brand.json") : "";
14106
14125
  var BRAND = { productName: "Maxy", hostname: "maxy", configDir: ".maxy", marketingUrl: "https://getmaxy.com" };
14107
- if (BRAND_JSON_PATH && !existsSync23(BRAND_JSON_PATH)) {
14126
+ if (BRAND_JSON_PATH && !existsSync21(BRAND_JSON_PATH)) {
14108
14127
  console.error(`[brand] WARNING: brand.json not found at ${BRAND_JSON_PATH} \u2014 using Maxy defaults`);
14109
14128
  }
14110
- if (BRAND_JSON_PATH && existsSync23(BRAND_JSON_PATH)) {
14129
+ if (BRAND_JSON_PATH && existsSync21(BRAND_JSON_PATH)) {
14111
14130
  try {
14112
14131
  const parsed = JSON.parse(readFileSync20(BRAND_JSON_PATH, "utf-8"));
14113
14132
  BRAND = { ...BRAND, ...parsed };
@@ -14131,7 +14150,7 @@ var brandLoginOpts = {
14131
14150
  var ALIAS_DOMAINS_PATH = join15(homedir2(), BRAND.configDir, "alias-domains.json");
14132
14151
  function loadAliasDomains() {
14133
14152
  try {
14134
- if (!existsSync23(ALIAS_DOMAINS_PATH)) return null;
14153
+ if (!existsSync21(ALIAS_DOMAINS_PATH)) return null;
14135
14154
  const parsed = JSON.parse(readFileSync20(ALIAS_DOMAINS_PATH, "utf-8"));
14136
14155
  if (!Array.isArray(parsed)) {
14137
14156
  console.error("[alias-domains] malformed alias-domains.json \u2014 expected array");
@@ -14496,13 +14515,13 @@ app40.get("/agent-assets/:slug/:filename", (c) => {
14496
14515
  console.error(`[agent-assets] no-account slug=${slug} file=${filename}`);
14497
14516
  return c.text("Not found", 404);
14498
14517
  }
14499
- const filePath = resolve24(account.accountDir, "agents", slug, "assets", filename);
14500
- const expectedDir = resolve24(account.accountDir, "agents", slug, "assets");
14518
+ const filePath = resolve23(account.accountDir, "agents", slug, "assets", filename);
14519
+ const expectedDir = resolve23(account.accountDir, "agents", slug, "assets");
14501
14520
  if (!filePath.startsWith(expectedDir + "/")) {
14502
14521
  console.error(`[agent-assets] path-traversal-rejected slug=${slug} file=${filename}`);
14503
14522
  return c.text("Forbidden", 403);
14504
14523
  }
14505
- if (!existsSync23(filePath)) {
14524
+ if (!existsSync21(filePath)) {
14506
14525
  console.error(`[agent-assets] serve slug=${slug} file=${filename} status=404`);
14507
14526
  return c.text("Not found", 404);
14508
14527
  }
@@ -14526,13 +14545,13 @@ app40.get("/generated/:filename", (c) => {
14526
14545
  console.error(`[generated] serve file=${filename} status=404`);
14527
14546
  return c.text("Not found", 404);
14528
14547
  }
14529
- const filePath = resolve24(account.accountDir, "generated", filename);
14530
- const expectedDir = resolve24(account.accountDir, "generated");
14548
+ const filePath = resolve23(account.accountDir, "generated", filename);
14549
+ const expectedDir = resolve23(account.accountDir, "generated");
14531
14550
  if (!filePath.startsWith(expectedDir + "/")) {
14532
14551
  console.error(`[generated] serve file=${filename} status=403`);
14533
14552
  return c.text("Forbidden", 403);
14534
14553
  }
14535
- if (!existsSync23(filePath)) {
14554
+ if (!existsSync21(filePath)) {
14536
14555
  console.error(`[generated] serve file=${filename} status=404`);
14537
14556
  return c.text("Not found", 404);
14538
14557
  }
@@ -14552,7 +14571,7 @@ app40.route("/v", visitor_consent_default);
14552
14571
  var htmlCache = /* @__PURE__ */ new Map();
14553
14572
  var brandLogoPath = "/brand/maxy-monochrome.png";
14554
14573
  var brandIconPath = "/brand/maxy-monochrome.png";
14555
- if (BRAND_JSON_PATH && existsSync23(BRAND_JSON_PATH)) {
14574
+ if (BRAND_JSON_PATH && existsSync21(BRAND_JSON_PATH)) {
14556
14575
  try {
14557
14576
  const fullBrand = JSON.parse(readFileSync20(BRAND_JSON_PATH, "utf-8"));
14558
14577
  if (fullBrand.assets?.logo) brandLogoPath = `/brand/${fullBrand.assets.logo}`;
@@ -14572,7 +14591,7 @@ function readInstalledVersion() {
14572
14591
  try {
14573
14592
  if (!PLATFORM_ROOT9) return "unknown";
14574
14593
  const versionFile = join15(PLATFORM_ROOT9, "config", `.${BRAND.hostname}-version`);
14575
- if (!existsSync23(versionFile)) return "unknown";
14594
+ if (!existsSync21(versionFile)) return "unknown";
14576
14595
  const content = readFileSync20(versionFile, "utf-8").trim();
14577
14596
  return content || "unknown";
14578
14597
  } catch {
@@ -14614,7 +14633,7 @@ var clientErrorReporterScript = `<script>
14614
14633
  function cachedHtml(file) {
14615
14634
  let html = htmlCache.get(file);
14616
14635
  if (!html) {
14617
- html = readFileSync20(resolve24(process.cwd(), "public", file), "utf-8");
14636
+ html = readFileSync20(resolve23(process.cwd(), "public", file), "utf-8");
14618
14637
  const productNameEsc = escapeHtml(BRAND.productName);
14619
14638
  html = html.replace(/<title>([^<]*)<\/title>/, (_match, inner) => `<title>${escapeHtml(inner).replace(/Maxy/g, productNameEsc)}</title>`);
14620
14639
  html = html.replace('href="/favicon.ico"', `href="${escapeHtml(brandFaviconPath)}"`);
@@ -14633,12 +14652,12 @@ function loadBrandingCache(agentSlug) {
14633
14652
  const configDir2 = join15(homedir2(), BRAND.configDir);
14634
14653
  try {
14635
14654
  const accountJsonPath = join15(configDir2, "account.json");
14636
- if (!existsSync23(accountJsonPath)) return null;
14655
+ if (!existsSync21(accountJsonPath)) return null;
14637
14656
  const account = JSON.parse(readFileSync20(accountJsonPath, "utf-8"));
14638
14657
  const accountId = account.accountId;
14639
14658
  if (!accountId) return null;
14640
14659
  const cachePath = join15(configDir2, "branding-cache", accountId, `${agentSlug}.json`);
14641
- if (!existsSync23(cachePath)) return null;
14660
+ if (!existsSync21(cachePath)) return null;
14642
14661
  return JSON.parse(readFileSync20(cachePath, "utf-8"));
14643
14662
  } catch {
14644
14663
  return null;
@@ -14648,7 +14667,7 @@ function resolveDefaultSlug() {
14648
14667
  try {
14649
14668
  const configDir2 = join15(homedir2(), BRAND.configDir);
14650
14669
  const accountJsonPath = join15(configDir2, "account.json");
14651
- if (!existsSync23(accountJsonPath)) return null;
14670
+ if (!existsSync21(accountJsonPath)) return null;
14652
14671
  const account = JSON.parse(readFileSync20(accountJsonPath, "utf-8"));
14653
14672
  return account.defaultAgent || null;
14654
14673
  } catch {
@@ -14722,7 +14741,7 @@ app40.use("/vnc-popout.html", logViewerFetch);
14722
14741
  app40.get("/vnc-popout.html", (c) => {
14723
14742
  let html = htmlCache.get("vnc-popout.html");
14724
14743
  if (!html) {
14725
- html = readFileSync20(resolve24(process.cwd(), "public", "vnc-popout.html"), "utf-8");
14744
+ html = readFileSync20(resolve23(process.cwd(), "public", "vnc-popout.html"), "utf-8");
14726
14745
  const name = escapeHtml(BRAND.productName);
14727
14746
  html = html.replace("<title>Browser \u2014 Maxy</title>", `<title>${name}</title>`);
14728
14747
  html = html.replace("</head>", ` ${brandScript}
@@ -14847,7 +14866,7 @@ try {
14847
14866
  }
14848
14867
  (async () => {
14849
14868
  try {
14850
- if (!existsSync23(USERS_FILE)) return;
14869
+ if (!existsSync21(USERS_FILE)) return;
14851
14870
  const usersRaw = readFileSync20(USERS_FILE, "utf-8").trim();
14852
14871
  if (!usersRaw) return;
14853
14872
  const users = JSON.parse(usersRaw);
@@ -14864,6 +14883,9 @@ try {
14864
14883
  }
14865
14884
  })();
14866
14885
  startGraphHealthTimer();
14886
+ void startFileWatcher().catch((err) => {
14887
+ console.error(`[file-watcher] start-failed err="${err instanceof Error ? err.message : String(err)}"`);
14888
+ });
14867
14889
  try {
14868
14890
  const accounts = enumerateValidAccountIds(getAccountsDirFromEnv());
14869
14891
  console.error(`[graph-health] account-enumeration accounts=${accounts.length}`);
@@ -14882,7 +14904,7 @@ try {
14882
14904
  } catch (err) {
14883
14905
  console.error(`[graph-health] account-enumeration unavailable reason=${err instanceof Error ? err.message : String(err)}`);
14884
14906
  }
14885
- var configDirForWhatsApp = basename5(MAXY_DIR) || ".maxy";
14907
+ var configDirForWhatsApp = basename6(MAXY_DIR) || ".maxy";
14886
14908
  var bootAccount = resolveAccount();
14887
14909
  var bootAccountConfig = bootAccount?.config;
14888
14910
  var bootPublicAgent = bootAccount ? resolvePublicAgent(bootAccount.accountDir, { accountId: bootAccount.accountId })?.slug ?? null : null;
@@ -14940,7 +14962,7 @@ if (bootAccountConfig?.whatsapp) {
14940
14962
  }
14941
14963
  init({
14942
14964
  configDir: configDirForWhatsApp,
14943
- platformRoot: resolve24(process.env.MAXY_PLATFORM_ROOT ?? join15(__dirname, "..")),
14965
+ platformRoot: resolve23(process.env.MAXY_PLATFORM_ROOT ?? join15(__dirname, "..")),
14944
14966
  accountConfig: bootAccountConfig,
14945
14967
  onMessage: async (msg) => {
14946
14968
  if (process.env.WHATSAPP_PTY_BRIDGE_ENABLED === "true" && msg.text && !msg.isOwnerMirror) {