@rubytech/create-maxy-code 0.1.190 → 0.1.192

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 (175) 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/admin-ui.md +16 -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-extract.d.ts +9 -0
  22. package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest-extract.d.ts.map +1 -1
  23. package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest-extract.js +1 -1
  24. package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest-extract.js.map +1 -1
  25. package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.d.ts.map +1 -1
  26. package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.js +44 -0
  27. package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.js.map +1 -1
  28. package/payload/platform/plugins/memory/skills/document-ingest/SKILL.md +2 -0
  29. package/payload/platform/plugins/prompt-optimiser/skills/prompt-optimiser/SKILL.md +1 -0
  30. package/payload/platform/plugins/url-get/mcp/dist/index.js +2 -2
  31. package/payload/platform/plugins/url-get/mcp/dist/index.js.map +1 -1
  32. package/payload/platform/services/claude-session-manager/dist/rc-daemon.d.ts +21 -0
  33. package/payload/platform/services/claude-session-manager/dist/rc-daemon.d.ts.map +1 -1
  34. package/payload/platform/services/claude-session-manager/dist/rc-daemon.js +140 -2
  35. package/payload/platform/services/claude-session-manager/dist/rc-daemon.js.map +1 -1
  36. package/payload/premium-plugins/writer-craft/lib/mcp-eager/dist/index.d.ts +116 -0
  37. package/payload/premium-plugins/writer-craft/lib/mcp-eager/dist/index.d.ts.map +1 -0
  38. package/payload/premium-plugins/writer-craft/lib/mcp-eager/dist/index.js +125 -0
  39. package/payload/premium-plugins/writer-craft/lib/mcp-eager/dist/index.js.map +1 -0
  40. package/payload/premium-plugins/writer-craft/lib/mcp-eager/package.json +7 -0
  41. package/payload/premium-plugins/writer-craft/mcp/dist/index.d.ts.map +1 -1
  42. package/payload/premium-plugins/writer-craft/mcp/dist/index.js +5 -4
  43. package/payload/premium-plugins/writer-craft/mcp/dist/index.js.map +1 -1
  44. package/payload/premium-plugins/writer-craft/mcp/src/index.ts +5 -4
  45. package/payload/server/public/assets/AdminShell-CdgotOLV.js +1 -0
  46. package/payload/server/public/assets/{Checkbox-g2WZRfCt.js → Checkbox-DsHqjzGj.js} +1 -1
  47. package/payload/server/public/assets/admin-CWOprOdW.js +1 -0
  48. package/payload/server/public/assets/{arc-DMDAZHAN.js → arc-aUiRP9AS.js} +1 -1
  49. package/payload/server/public/assets/architecture-YZFGNWBL--v-pJPNp.js +1 -0
  50. package/payload/server/public/assets/{architectureDiagram-Q4EWVU46-BVKxPUIS.js → architectureDiagram-Q4EWVU46-DROzw8rH.js} +1 -1
  51. package/payload/server/public/assets/{blockDiagram-DXYQGD6D-BpD-1G1V.js → blockDiagram-DXYQGD6D-Dw9sieJu.js} +1 -1
  52. package/payload/server/public/assets/{brand-BGvv6AYI.js → brand-DKUA91KU.js} +1 -1
  53. package/payload/server/public/assets/{c4Diagram-AHTNJAMY-CFl9Dr6U.js → c4Diagram-AHTNJAMY-fnwntjFK.js} +1 -1
  54. package/payload/server/public/assets/channel-DEl-UCEn.js +1 -0
  55. package/payload/server/public/assets/{chunk-2KRD3SAO-Di4bO8ir.js → chunk-2KRD3SAO-BK3470lx.js} +1 -1
  56. package/payload/server/public/assets/{chunk-336JU56O-B2Uc6pEC.js → chunk-336JU56O-DVpyamlA.js} +2 -2
  57. package/payload/server/public/assets/chunk-426QAEUC-yiGugjCx.js +1 -0
  58. package/payload/server/public/assets/{chunk-4BX2VUAB-DyEhFk-Z.js → chunk-4BX2VUAB-BOvVdJLf.js} +1 -1
  59. package/payload/server/public/assets/{chunk-4TB4RGXK-Dvf5uiFR.js → chunk-4TB4RGXK-BAPL_MqI.js} +1 -1
  60. package/payload/server/public/assets/{chunk-55IACEB6-BRJOZLpU.js → chunk-55IACEB6-BwZyF7vR.js} +1 -1
  61. package/payload/server/public/assets/{chunk-5FUZZQ4R-DUQR1rF9.js → chunk-5FUZZQ4R-Ba-5D35r.js} +1 -1
  62. package/payload/server/public/assets/{chunk-5PVQY5BW-OTvNug7K.js → chunk-5PVQY5BW-BtDS8-t9.js} +1 -1
  63. package/payload/server/public/assets/{chunk-67CJDMHE-BG6-9r6c.js → chunk-67CJDMHE-D5bhMrtY.js} +1 -1
  64. package/payload/server/public/assets/{chunk-7N4EOEYR-BvMbitX7.js → chunk-7N4EOEYR-Si7Lgrwc.js} +1 -1
  65. package/payload/server/public/assets/{chunk-AA7GKIK3-CE8mGBD5.js → chunk-AA7GKIK3-DMuHtDqO.js} +1 -1
  66. package/payload/server/public/assets/{chunk-BSJP7CBP-DP7LTBll.js → chunk-BSJP7CBP-L79XKVcb.js} +1 -1
  67. package/payload/server/public/assets/{chunk-CIAEETIT-VfnIdN-h.js → chunk-CIAEETIT-C0O7Upmg.js} +1 -1
  68. package/payload/server/public/assets/{chunk-EDXVE4YY-CRAsAWbD.js → chunk-EDXVE4YY-CqcdpDg_.js} +1 -1
  69. package/payload/server/public/assets/{chunk-ENJZ2VHE-BNllMoi_.js → chunk-ENJZ2VHE-CaBy9U1e.js} +1 -1
  70. package/payload/server/public/assets/{chunk-FMBD7UC4-w-yBZsN2.js → chunk-FMBD7UC4-C_E43NFJ.js} +1 -1
  71. package/payload/server/public/assets/{chunk-FOC6F5B3-f9cFywhd.js → chunk-FOC6F5B3-D9lWWHAu.js} +1 -1
  72. package/payload/server/public/assets/{chunk-ICPOFSXX-vTeVS4qd.js → chunk-ICPOFSXX-BdPjOce4.js} +2 -2
  73. package/payload/server/public/assets/{chunk-K5T4RW27-B_ZUrFUq.js → chunk-K5T4RW27-DuhsNH4c.js} +1 -1
  74. package/payload/server/public/assets/{chunk-KGLVRYIC-CcWTvRlI.js → chunk-KGLVRYIC-B4-A1Abi.js} +1 -1
  75. package/payload/server/public/assets/{chunk-LIHQZDEY-D-5-peQw.js → chunk-LIHQZDEY-BxqgHRgT.js} +1 -1
  76. package/payload/server/public/assets/{chunk-ORNJ4GCN-B4Z5L25I.js → chunk-ORNJ4GCN-DEYQ5WaJ.js} +1 -1
  77. package/payload/server/public/assets/{chunk-OYMX7WX6-BdSFcvNa.js → chunk-OYMX7WX6-CK6wx_FD.js} +1 -1
  78. package/payload/server/public/assets/chunk-QZHKN3VN-Bd-GrQM6.js +1 -0
  79. package/payload/server/public/assets/{chunk-U2HBQHQK-BpFXup4D.js → chunk-U2HBQHQK-DyZFy43o.js} +1 -1
  80. package/payload/server/public/assets/{chunk-X2U36JSP-DF-6QHx4.js → chunk-X2U36JSP-CMTofLbX.js} +1 -1
  81. package/payload/server/public/assets/{chunk-XPW4576I-m1Y_r88I.js → chunk-XPW4576I-CBfZXZDB.js} +1 -1
  82. package/payload/server/public/assets/{chunk-YZCP3GAM-CKgDoLRT.js → chunk-YZCP3GAM-YfFrkYbQ.js} +1 -1
  83. package/payload/server/public/assets/{chunk-ZZ45TVLE-B0ACvZCZ.js → chunk-ZZ45TVLE-BVfvS5n3.js} +1 -1
  84. package/payload/server/public/assets/classDiagram-6PBFFD2Q-BZ9e1dmR.js +1 -0
  85. package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-B1_4kXdN.js +1 -0
  86. package/payload/server/public/assets/clone-CNXfxCnl.js +1 -0
  87. package/payload/server/public/assets/{cose-bilkent-S5V4N54A-BhtgY3T7.js → cose-bilkent-S5V4N54A-Br2gjtEO.js} +1 -1
  88. package/payload/server/public/assets/{dagre-3yl9rfxR.js → dagre-ComHkUs6.js} +1 -1
  89. package/payload/server/public/assets/{dagre-KV5264BT-D-Ux4uwL.js → dagre-KV5264BT-BB-QqzpF.js} +1 -1
  90. package/payload/server/public/assets/data-DEsRsfI1.js +1 -0
  91. package/payload/server/public/assets/{diagram-5BDNPKRD-D-MOMikl.js → diagram-5BDNPKRD-CpQQszhw.js} +1 -1
  92. package/payload/server/public/assets/{diagram-G4DWMVQ6-DKdFcnhf.js → diagram-G4DWMVQ6-DLdElVzs.js} +1 -1
  93. package/payload/server/public/assets/{diagram-MMDJMWI5-ajamR766.js → diagram-MMDJMWI5-JQ3ceLSO.js} +1 -1
  94. package/payload/server/public/assets/{diagram-TYMM5635-D2Pbk2Kk.js → diagram-TYMM5635-D_-AtVPR.js} +1 -1
  95. package/payload/server/public/assets/{erDiagram-SMLLAGMA-v3o948pk.js → erDiagram-SMLLAGMA-Dq50MKD-.js} +1 -1
  96. package/payload/server/public/assets/{flatten-BsWEYbBB.js → flatten-Bo6YRmWl.js} +1 -1
  97. package/payload/server/public/assets/{flowDiagram-DWJPFMVM-byC1a6GE.js → flowDiagram-DWJPFMVM-CEdKm-zL.js} +1 -1
  98. package/payload/server/public/assets/{ganttDiagram-T4ZO3ILL-Dybzs-Sw.js → ganttDiagram-T4ZO3ILL-CpTomdNN.js} +1 -1
  99. package/payload/server/public/assets/gitGraph-7Q5UKJZL-CI0s_tqn.js +1 -0
  100. package/payload/server/public/assets/{gitGraphDiagram-UUTBAWPF-DvzXYLuW.js → gitGraphDiagram-UUTBAWPF-VU-gjbee.js} +1 -1
  101. package/payload/server/public/assets/graph-BXaY_xoS.js +51 -0
  102. package/payload/server/public/assets/graph-labels-EqZyoU25.js +1 -0
  103. package/payload/server/public/assets/{graphlib-DvH_spaA.js → graphlib-nLzyerxi.js} +1 -1
  104. package/payload/server/public/assets/info-OMHHGYJF-g3gYW7Qm.js +1 -0
  105. package/payload/server/public/assets/infoDiagram-42DDH7IO-ThwX5Xgr.js +2 -0
  106. package/payload/server/public/assets/{isEmpty-BWl67LAZ.js → isEmpty-D6Kr-M1M.js} +1 -1
  107. package/payload/server/public/assets/{ishikawaDiagram-UXIWVN3A-CpY5W64P.js → ishikawaDiagram-UXIWVN3A-DaKxxHLW.js} +1 -1
  108. package/payload/server/public/assets/{journeyDiagram-VCZTEJTY-ymMAQVdm.js → journeyDiagram-VCZTEJTY-DtMAHpWz.js} +1 -1
  109. package/payload/server/public/assets/{kanban-definition-6JOO6SKY-_NX9hNK3.js → kanban-definition-6JOO6SKY-s898dJRu.js} +1 -1
  110. package/payload/server/public/assets/{line-Dz7zKWX-.js → line-D96q2CLD.js} +1 -1
  111. package/payload/server/public/assets/{linear-0O14Y6uf.js → linear-DOh_6k2k.js} +1 -1
  112. package/payload/server/public/assets/{mermaid-parser.core-BCWd1en6.js → mermaid-parser.core-CqFLtzz8.js} +2 -2
  113. package/payload/server/public/assets/{mermaid.core-gSoFkVoN.js → mermaid.core-o9rmBTu-.js} +3 -3
  114. package/payload/server/public/assets/{mindmap-definition-QFDTVHPH-QuIyK2bd.js → mindmap-definition-QFDTVHPH-BM8KKsaM.js} +1 -1
  115. package/payload/server/public/assets/{ordinal-krseTxxN.js → ordinal-BDi6f4xk.js} +1 -1
  116. package/payload/server/public/assets/packet-4T2RLAQJ-CT0TB9HI.js +1 -0
  117. package/payload/server/public/assets/pie-ZZUOXDRM-CXLe7TFF.js +1 -0
  118. package/payload/server/public/assets/{pieDiagram-DEJITSTG-DHK0yy7U.js → pieDiagram-DEJITSTG-B66Y-dcU.js} +1 -1
  119. package/payload/server/public/assets/public-ClAaV52O.js +35 -0
  120. package/payload/server/public/assets/{quadrantDiagram-34T5L4WZ-wK1jwWo5.js → quadrantDiagram-34T5L4WZ-Bv4mojq8.js} +1 -1
  121. package/payload/server/public/assets/radar-PYXPWWZC-DnPLBl-D.js +1 -0
  122. package/payload/server/public/assets/{reduce-tk-xY6Fv.js → reduce-CGi9ik8i.js} +1 -1
  123. package/payload/server/public/assets/{requirementDiagram-MS252O5E-W_mH85_d.js → requirementDiagram-MS252O5E-BNYdfEKS.js} +1 -1
  124. package/payload/server/public/assets/{sankeyDiagram-XADWPNL6-nGAM-YLQ.js → sankeyDiagram-XADWPNL6-Do8BgX92.js} +1 -1
  125. package/payload/server/public/assets/{sequenceDiagram-FGHM5R23-SMfUCwBH.js → sequenceDiagram-FGHM5R23-VN57FKth.js} +1 -1
  126. package/payload/server/public/assets/{stateDiagram-FHFEXIEX-CYKXFirH.js → stateDiagram-FHFEXIEX-CCtRfCg0.js} +1 -1
  127. package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-Cz319KBI.js +1 -0
  128. package/payload/server/public/assets/{timeline-definition-GMOUNBTQ-DethbNFa.js → timeline-definition-GMOUNBTQ-ja01S9Wd.js} +1 -1
  129. package/payload/server/public/assets/treeView-SZITEDCU-C3cb7Xwe.js +1 -0
  130. package/payload/server/public/assets/treemap-W4RFUUIX-Dc7G3Bgm.js +1 -0
  131. package/payload/server/public/assets/useSelectionMode-ejMSaa52.js +5 -0
  132. package/payload/server/public/assets/{vennDiagram-DHZGUBPP-BUlJTe9Z.js → vennDiagram-DHZGUBPP-wWbiL1f6.js} +1 -1
  133. package/payload/server/public/assets/wardley-RL74JXVD-DtgibWAt.js +1 -0
  134. package/payload/server/public/assets/{wardleyDiagram-NUSXRM2D-CL1TvZ7j.js → wardleyDiagram-NUSXRM2D-BZgue-PK.js} +1 -1
  135. package/payload/server/public/assets/{xychartDiagram-5P7HB3ND-Cav10l-Z.js → xychartDiagram-5P7HB3ND-B_ynYRnW.js} +1 -1
  136. package/payload/server/public/data.html +4 -4
  137. package/payload/server/public/graph.html +5 -5
  138. package/payload/server/public/index.html +5 -7
  139. package/payload/server/public/public.html +4 -4
  140. package/payload/server/server.js +546 -361
  141. package/payload/server/public/assets/admin-Cv2A5vnQ.js +0 -216
  142. package/payload/server/public/assets/architecture-YZFGNWBL-COhEvUpo.js +0 -1
  143. package/payload/server/public/assets/channel-BTEFjudQ.js +0 -1
  144. package/payload/server/public/assets/chunk-426QAEUC-CcIGcm29.js +0 -1
  145. package/payload/server/public/assets/chunk-QZHKN3VN-CWh_0JsP.js +0 -1
  146. package/payload/server/public/assets/classDiagram-6PBFFD2Q-ClkdniKF.js +0 -1
  147. package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-kjJetfh2.js +0 -1
  148. package/payload/server/public/assets/clone-BllVm2iY.js +0 -1
  149. package/payload/server/public/assets/data-DIa-MKNj.js +0 -1
  150. package/payload/server/public/assets/gitGraph-7Q5UKJZL-DeTNsAO0.js +0 -1
  151. package/payload/server/public/assets/graph-Y5y3kw6J.js +0 -1
  152. package/payload/server/public/assets/graph-labels-BIRCh328.js +0 -1
  153. package/payload/server/public/assets/info-OMHHGYJF-DJJ9GlS6.js +0 -1
  154. package/payload/server/public/assets/infoDiagram-42DDH7IO-C2FeU8nE.js +0 -2
  155. package/payload/server/public/assets/lib-BEvO-mE0.js +0 -33
  156. package/payload/server/public/assets/packet-4T2RLAQJ-CGbvGkvF.js +0 -1
  157. package/payload/server/public/assets/page-BCCqlOmj.js +0 -1
  158. package/payload/server/public/assets/page-DwNvJOvr.js +0 -51
  159. package/payload/server/public/assets/pie-ZZUOXDRM-BZy8rjFn.js +0 -1
  160. package/payload/server/public/assets/public-BnS9zREj.js +0 -7
  161. package/payload/server/public/assets/radar-PYXPWWZC-DcfWIVXr.js +0 -1
  162. package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-B9ZwM8kx.js +0 -1
  163. package/payload/server/public/assets/treeView-SZITEDCU-3WugwVdj.js +0 -1
  164. package/payload/server/public/assets/treemap-W4RFUUIX-Cf5mDLlu.js +0 -1
  165. package/payload/server/public/assets/wardley-RL74JXVD-Bv4md4b3.js +0 -1
  166. /package/payload/server/public/assets/{_baseFor-BHtDrjIo.js → _baseFor-Cam2PbSt.js} +0 -0
  167. /package/payload/server/public/assets/{array-DetWRiSa.js → array-DYRGGQae.js} +0 -0
  168. /package/payload/server/public/assets/{cytoscape.esm-C9yNhe1u.js → cytoscape.esm-nWsJMTNI.js} +0 -0
  169. /package/payload/server/public/assets/{defaultLocale-_WRwicXn.js → defaultLocale-Du1XY3Dp.js} +0 -0
  170. /package/payload/server/public/assets/{dist-Bd4S37oi.js → dist-BzAsli7o.js} +0 -0
  171. /package/payload/server/public/assets/{init-sTEcj9YX.js → init-B5BXBRcm.js} +0 -0
  172. /package/payload/server/public/assets/{katex-s61Rgv6l.js → katex-HOUACuRw.js} +0 -0
  173. /package/payload/server/public/assets/{path-B0Ik7Tu9.js → path-CNO468J-.js} +0 -0
  174. /package/payload/server/public/assets/{rough.esm-DKRO8IF-.js → rough.esm-DRO6hWPh.js} +0 -0
  175. /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 resolve23, join as join15, basename as basename5 } from "path";
818
+ import { readFileSync as readFileSync20, existsSync as existsSync22, 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
 
@@ -8004,10 +8004,10 @@ function computeSpecialistDomains(accountId) {
8004
8004
  const resolveDesc = (qualified) => {
8005
8005
  if (!qualified.startsWith("mcp__")) return void 0;
8006
8006
  const rest = qualified.slice(5);
8007
- const sep6 = rest.indexOf("__");
8008
- if (sep6 < 0) return void 0;
8009
- const plugin = rest.slice(0, sep6);
8010
- const tool = rest.slice(sep6 + 2);
8007
+ const sep7 = rest.indexOf("__");
8008
+ if (sep7 < 0) return void 0;
8009
+ const plugin = rest.slice(0, sep7);
8010
+ const tool = rest.slice(sep7 + 2);
8011
8011
  let map = descByPlugin.get(plugin);
8012
8012
  if (!map) {
8013
8013
  const dir = pluginDirs.get(plugin);
@@ -8396,6 +8396,7 @@ import { realpathSync as realpathSync3 } from "fs";
8396
8396
  import { resolve as resolve12, normalize, sep as sep2, relative } from "path";
8397
8397
  var PLATFORM_ROOT5 = process.env.MAXY_PLATFORM_ROOT ?? resolve12(process.cwd(), "../platform");
8398
8398
  var DATA_ROOT = resolve12(PLATFORM_ROOT5, "..", "data");
8399
+ var CLAUDE_UPLOADS_ROOT = process.env.CLAUDE_CONFIG_DIR ? resolve12(process.env.CLAUDE_CONFIG_DIR, "uploads") : null;
8399
8400
  var ACCOUNT_PARTITION_DIRS = /* @__PURE__ */ new Set(["uploads", "accounts"]);
8400
8401
  var ACCOUNT_UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
8401
8402
  function crossesForeignAccountPartition(relPath2, accountId) {
@@ -8406,17 +8407,17 @@ function crossesForeignAccountPartition(relPath2, accountId) {
8406
8407
  if (!ACCOUNT_UUID_RE.test(owner)) return false;
8407
8408
  return owner !== accountId;
8408
8409
  }
8409
- function resolveDataPath(raw) {
8410
+ function resolveUnderRoot(raw, rootAbs, rootLabel) {
8410
8411
  const cleaned = normalize("/" + (raw ?? "").replace(/\\/g, "/")).replace(/^\/+/, "");
8411
- const absolute = resolve12(DATA_ROOT, cleaned);
8412
- let dataRootReal;
8412
+ const absolute = resolve12(rootAbs, cleaned);
8413
+ let rootReal;
8413
8414
  try {
8414
- dataRootReal = realpathSync3(DATA_ROOT);
8415
+ rootReal = realpathSync3(rootAbs);
8415
8416
  } catch (err) {
8416
8417
  return {
8417
8418
  ok: false,
8418
8419
  status: 500,
8419
- error: `DATA_ROOT not accessible: ${err instanceof Error ? err.message : String(err)}`
8420
+ error: `${rootLabel} not accessible: ${err instanceof Error ? err.message : String(err)}`
8420
8421
  };
8421
8422
  }
8422
8423
  let resolvedReal;
@@ -8425,8 +8426,8 @@ function resolveDataPath(raw) {
8425
8426
  } catch (err) {
8426
8427
  const code = err.code;
8427
8428
  if (code === "ENOENT") {
8428
- if (absolute !== dataRootReal && !absolute.startsWith(dataRootReal + sep2)) {
8429
- return { ok: false, status: 403, error: "Path escapes DATA_ROOT", resolved: absolute };
8429
+ if (absolute !== rootReal && !absolute.startsWith(rootReal + sep2)) {
8430
+ return { ok: false, status: 403, error: `Path escapes ${rootLabel}`, resolved: absolute };
8430
8431
  }
8431
8432
  return { ok: false, status: 404, error: "Not found" };
8432
8433
  }
@@ -8436,11 +8437,20 @@ function resolveDataPath(raw) {
8436
8437
  error: err instanceof Error ? err.message : String(err)
8437
8438
  };
8438
8439
  }
8439
- if (resolvedReal !== dataRootReal && !resolvedReal.startsWith(dataRootReal + sep2)) {
8440
- return { ok: false, status: 403, error: "Path escapes DATA_ROOT", resolved: resolvedReal };
8440
+ if (resolvedReal !== rootReal && !resolvedReal.startsWith(rootReal + sep2)) {
8441
+ return { ok: false, status: 403, error: `Path escapes ${rootLabel}`, resolved: resolvedReal };
8442
+ }
8443
+ const relPath2 = relative(rootReal, resolvedReal) || ".";
8444
+ return { ok: true, absolute: resolvedReal, dataRootReal: rootReal, relative: relPath2 };
8445
+ }
8446
+ function resolveDataPath(raw) {
8447
+ return resolveUnderRoot(raw, DATA_ROOT, "DATA_ROOT");
8448
+ }
8449
+ function resolveClaudeUploadsPath(raw) {
8450
+ if (!CLAUDE_UPLOADS_ROOT) {
8451
+ return { ok: false, status: 404, error: "Not found" };
8441
8452
  }
8442
- const relPath2 = relative(dataRootReal, resolvedReal) || ".";
8443
- return { ok: true, absolute: resolvedReal, dataRootReal, relative: relPath2 };
8453
+ return resolveUnderRoot(raw, CLAUDE_UPLOADS_ROOT, "CLAUDE_UPLOADS_ROOT");
8444
8454
  }
8445
8455
 
8446
8456
  // ../lib/graph-trash/src/index.ts
@@ -8759,11 +8769,23 @@ async function cascadeDeleteDocument(params) {
8759
8769
  // app/lib/file-index.ts
8760
8770
  import * as fsp from "fs/promises";
8761
8771
  import { resolve as resolve13, relative as relative2, join as join11, basename as basename3, extname as extname2, sep as sep3 } from "path";
8772
+ import { tmpdir as tmpdir2 } from "os";
8762
8773
  import { execFile as execFile2 } from "child_process";
8763
8774
  import { promisify as promisify2 } from "util";
8764
8775
  var execFileAsync2 = promisify2(execFile2);
8765
8776
  var CONTENT_CAP = 1e5;
8766
- var RECONCILE_DEBOUNCE_MS = 1e4;
8777
+ var OCR_TIMEOUT_MS = 3e4;
8778
+ var OCR_IMAGE_EXTENSIONS = /* @__PURE__ */ new Set([
8779
+ ".png",
8780
+ ".jpg",
8781
+ ".jpeg",
8782
+ ".gif",
8783
+ ".webp",
8784
+ ".tiff",
8785
+ ".tif",
8786
+ ".bmp"
8787
+ ]);
8788
+ var RECONCILE_DEBOUNCE_MS = 5 * 60 * 1e3;
8767
8789
  var lastReconcileAt = /* @__PURE__ */ new Map();
8768
8790
  var TEXT_EXTENSIONS = /* @__PURE__ */ new Set([
8769
8791
  ".txt",
@@ -8816,26 +8838,60 @@ async function extractPdf(absolute) {
8816
8838
  });
8817
8839
  return stdout.trim();
8818
8840
  }
8841
+ async function ocrPdf(absolute) {
8842
+ const outPdf = join11(tmpdir2(), `file-index-ocr-${process.pid}-${Date.now()}.pdf`);
8843
+ try {
8844
+ await execFileAsync2(
8845
+ "ocrmypdf",
8846
+ ["--skip-text", "--quiet", "--output-type", "pdf", absolute, outPdf],
8847
+ { maxBuffer: 10 * 1024 * 1024, timeout: OCR_TIMEOUT_MS }
8848
+ );
8849
+ const { stdout } = await execFileAsync2("pdftotext", [outPdf, "-"], {
8850
+ maxBuffer: 10 * 1024 * 1024,
8851
+ timeout: OCR_TIMEOUT_MS
8852
+ });
8853
+ return stdout.trim();
8854
+ } finally {
8855
+ await fsp.rm(outPdf, { force: true });
8856
+ }
8857
+ }
8858
+ async function ocrImage(absolute) {
8859
+ const { stdout } = await execFileAsync2(
8860
+ "tesseract",
8861
+ [absolute, "-", "-l", "eng", "--psm", "3"],
8862
+ { maxBuffer: 10 * 1024 * 1024, timeout: OCR_TIMEOUT_MS }
8863
+ );
8864
+ return stdout.trim();
8865
+ }
8819
8866
  function stripHtml(html) {
8820
8867
  return html.replace(/<script[\s\S]*?<\/script>/gi, " ").replace(/<style[\s\S]*?<\/style>/gi, " ").replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
8821
8868
  }
8822
8869
  async function extractFileContent(absolute) {
8823
8870
  const ext = extname2(absolute).toLowerCase();
8824
8871
  try {
8825
- if (ext === ".pdf") return (await extractPdf(absolute)).slice(0, CONTENT_CAP);
8872
+ if (ext === ".pdf") {
8873
+ const text = (await extractPdf(absolute)).slice(0, CONTENT_CAP);
8874
+ if (text.length > 0) return { content: text, route: "pdftotext" };
8875
+ const ocred = (await ocrPdf(absolute)).slice(0, CONTENT_CAP);
8876
+ return ocred.length > 0 ? { content: ocred, route: "ocr-pdf" } : { content: "", route: "name-only" };
8877
+ }
8826
8878
  if (HTML_EXTENSIONS.has(ext)) {
8827
8879
  const raw = await fsp.readFile(absolute, "utf-8");
8828
- return stripHtml(raw).slice(0, CONTENT_CAP);
8880
+ return { content: stripHtml(raw).slice(0, CONTENT_CAP), route: "html" };
8829
8881
  }
8830
8882
  if (TEXT_EXTENSIONS.has(ext)) {
8831
8883
  const raw = await fsp.readFile(absolute, "utf-8");
8832
- return raw.slice(0, CONTENT_CAP);
8884
+ return { content: raw.slice(0, CONTENT_CAP), route: "text" };
8833
8885
  }
8834
- return "";
8886
+ if (OCR_IMAGE_EXTENSIONS.has(ext)) {
8887
+ const ocred = (await ocrImage(absolute)).slice(0, CONTENT_CAP);
8888
+ return ocred.length > 0 ? { content: ocred, route: "ocr-image" } : { content: "", route: "name-only" };
8889
+ }
8890
+ return { content: "", route: "name-only" };
8835
8891
  } catch (err) {
8836
8892
  const msg = err instanceof Error ? err.message : String(err);
8837
8893
  console.error(`[file-index] extract-failed path="${relative2(DATA_ROOT, absolute)}" err="${msg}"`);
8838
- return "";
8894
+ return { content: "", route: "name-only" };
8839
8895
  }
8840
8896
  }
8841
8897
  async function readDisplayName(absolute) {
@@ -8899,9 +8955,86 @@ async function upsertFileArtifact(session, f) {
8899
8955
  f
8900
8956
  );
8901
8957
  }
8958
+ async function linkFileArtifactToKnowledgeDocument(session, accountId, relativePath, absolutePath) {
8959
+ const res = await session.run(
8960
+ `MATCH (f:FileArtifact {accountId: $accountId, relativePath: $relativePath})
8961
+ OPTIONAL MATCH (d:KnowledgeDocument {accountId: $accountId, sourcePath: $absolutePath})
8962
+ WHERE NOT d:Trashed
8963
+ FOREACH (_ IN CASE WHEN d IS NULL THEN [] ELSE [1] END |
8964
+ MERGE (f)-[:INGESTED_AS]->(d))
8965
+ RETURN elementId(d) AS docId`,
8966
+ { accountId, relativePath, absolutePath }
8967
+ );
8968
+ const docId = res.records[0]?.get("docId");
8969
+ if (docId) {
8970
+ console.error(
8971
+ `[file-index] linked-kd account=${accountId} relativePath="${relativePath}" docId=${docId.slice(0, 12)}`
8972
+ );
8973
+ }
8974
+ }
8975
+ async function findLinkedKnowledgeDocs(session, accountId, relativePaths) {
8976
+ if (relativePaths.length === 0) return [];
8977
+ const res = await session.run(
8978
+ `MATCH (f:FileArtifact {accountId: $accountId})-[:INGESTED_AS]->(d:KnowledgeDocument)
8979
+ WHERE f.relativePath IN $relativePaths AND NOT d:Trashed
8980
+ OPTIONAL MATCH (d)-[:HAS_SECTION]->(s:Section)
8981
+ OPTIONAL MATCH (s)-[:HAS_CHUNK]->(c:Chunk)
8982
+ WITH d, collect(DISTINCT s) AS ss, collect(DISTINCT c) AS cc
8983
+ RETURN elementId(d) AS docId,
8984
+ [x IN ss WHERE x IS NOT NULL | elementId(x)] AS sectionIds,
8985
+ [x IN cc WHERE x IS NOT NULL | elementId(x)] AS chunkIds`,
8986
+ { accountId, relativePaths }
8987
+ );
8988
+ return res.records.map((rec) => ({
8989
+ docId: rec.get("docId"),
8990
+ sectionIds: rec.get("sectionIds") ?? [],
8991
+ chunkIds: rec.get("chunkIds") ?? []
8992
+ }));
8993
+ }
8994
+ async function cascadeTrashLinkedKnowledgeDocs(session, accountId, relativePaths) {
8995
+ const linked = await findLinkedKnowledgeDocs(session, accountId, relativePaths);
8996
+ let kds = 0, sections = 0, chunks = 0;
8997
+ for (const { docId, sectionIds, chunkIds } of linked) {
8998
+ try {
8999
+ await trashNode({
9000
+ session,
9001
+ accountId,
9002
+ elementId: docId,
9003
+ by: "file-index:cascade",
9004
+ reason: "physical file removed from FileArtifact index"
9005
+ });
9006
+ kds++;
9007
+ for (const sid of sectionIds) {
9008
+ await trashNode({
9009
+ session,
9010
+ accountId,
9011
+ elementId: sid,
9012
+ by: `file-index:cascade:from-${docId}`,
9013
+ reason: `cascade from KnowledgeDocument ${docId}`
9014
+ });
9015
+ sections++;
9016
+ }
9017
+ for (const cid of chunkIds) {
9018
+ await trashNode({
9019
+ session,
9020
+ accountId,
9021
+ elementId: cid,
9022
+ by: `file-index:cascade:from-${docId}`,
9023
+ reason: `cascade from KnowledgeDocument ${docId}`
9024
+ });
9025
+ chunks++;
9026
+ }
9027
+ } catch (err) {
9028
+ console.error(
9029
+ `[file-index] cascade-trash-failed account=${accountId} docId=${docId.slice(0, 12)} err="${err.message}"`
9030
+ );
9031
+ }
9032
+ }
9033
+ return { kds, sections, chunks };
9034
+ }
8902
9035
  async function buildArtifact(accountId, wf, embed2) {
8903
9036
  const name = basename3(wf.absolute);
8904
- const content = await extractFileContent(wf.absolute);
9037
+ const { content, route } = await extractFileContent(wf.absolute);
8905
9038
  const displayName = await readDisplayName(wf.absolute);
8906
9039
  const embedInput = `${name}
8907
9040
  ${content}`.slice(0, CONTENT_CAP);
@@ -8913,7 +9046,7 @@ ${content}`.slice(0, CONTENT_CAP);
8913
9046
  console.error(`[file-index] embed-failed path="${wf.relativePath}" err="${err.message}"`);
8914
9047
  }
8915
9048
  console.error(
8916
- `[file-index] index-file account=${accountId} path="${wf.relativePath}" chars=${content.length} embed-ms=${Date.now() - embedStart}`
9049
+ `[file-index] index-file account=${accountId} path="${wf.relativePath}" extractor=${route} chars=${content.length} embed-ms=${Date.now() - embedStart}`
8917
9050
  );
8918
9051
  return {
8919
9052
  accountId,
@@ -8972,14 +9105,29 @@ async function reconcileFileIndex(accountId, opts) {
8972
9105
  const unchanged = prior && prior.sizeBytes === wf.sizeBytes && prior.modifiedAt === wf.modifiedAt && prior.hasEmbedding;
8973
9106
  if (unchanged) {
8974
9107
  skipped++;
8975
- continue;
9108
+ } else {
9109
+ await upsertFileArtifact(session, await buildArtifact(accountId, wf, embed2));
9110
+ upserted++;
8976
9111
  }
8977
- await upsertFileArtifact(session, await buildArtifact(accountId, wf, embed2));
8978
- upserted++;
9112
+ await linkFileArtifactToKnowledgeDocument(session, accountId, wf.relativePath, wf.absolute);
8979
9113
  }
8980
9114
  let pruned = 0;
9115
+ let cascadeKds = 0, cascadeSections = 0, cascadeChunks = 0;
8981
9116
  if (clean) {
8982
9117
  const foundPaths = walked.map((w) => w.relativePath);
9118
+ const goneRes = await session.run(
9119
+ `MATCH (f:FileArtifact {accountId: $accountId})
9120
+ WHERE NOT f.relativePath IN $foundPaths
9121
+ RETURN f.relativePath AS rp`,
9122
+ { accountId, foundPaths }
9123
+ );
9124
+ const gonePaths = goneRes.records.map((r) => r.get("rp"));
9125
+ if (gonePaths.length > 0) {
9126
+ const cascade = await cascadeTrashLinkedKnowledgeDocs(session, accountId, gonePaths);
9127
+ cascadeKds = cascade.kds;
9128
+ cascadeSections = cascade.sections;
9129
+ cascadeChunks = cascade.chunks;
9130
+ }
8983
9131
  const pruneRes = await session.run(
8984
9132
  `MATCH (f:FileArtifact {accountId: $accountId})
8985
9133
  WHERE NOT f.relativePath IN $foundPaths
@@ -8991,7 +9139,7 @@ async function reconcileFileIndex(accountId, opts) {
8991
9139
  pruned = typeof c?.toNumber === "function" ? c.toNumber() : Number(c ?? 0);
8992
9140
  }
8993
9141
  console.error(
8994
- `[file-index] reconcile account=${accountId} walked=${walked.length} upserted=${upserted} skipped=${skipped} pruned=${pruned} clean=${clean} ms=${Date.now() - t0}`
9142
+ `[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}`
8995
9143
  );
8996
9144
  return { walked: walked.length, upserted, skipped, pruned };
8997
9145
  });
@@ -9020,10 +9168,12 @@ async function indexFile(accountId, relativePath, opts) {
9020
9168
  modifiedAt: st.mtime.toISOString()
9021
9169
  };
9022
9170
  await upsertFileArtifact(session, await buildArtifact(accountId, wf, embed2));
9171
+ await linkFileArtifactToKnowledgeDocument(session, accountId, relativePath, absolute);
9023
9172
  });
9024
9173
  }
9025
9174
  async function dropFileIndex(accountId, relativePath, opts) {
9026
9175
  await withSession(opts, async (session) => {
9176
+ const cascade = await cascadeTrashLinkedKnowledgeDocs(session, accountId, [relativePath]);
9027
9177
  const res = await session.run(
9028
9178
  `MATCH (f:FileArtifact {accountId: $accountId, relativePath: $relativePath})
9029
9179
  DETACH DELETE f
@@ -9032,7 +9182,9 @@ async function dropFileIndex(accountId, relativePath, opts) {
9032
9182
  );
9033
9183
  const c = res.records[0]?.get("dropped");
9034
9184
  const dropped = typeof c?.toNumber === "function" ? c.toNumber() : Number(c ?? 0);
9035
- console.error(`[file-index] drop account=${accountId} path="${relativePath}" dropped=${dropped}`);
9185
+ console.error(
9186
+ `[file-index] drop account=${accountId} path="${relativePath}" dropped=${dropped} cascade-kds=${cascade.kds} cascade-sections=${cascade.sections} cascade-chunks=${cascade.chunks}`
9187
+ );
9036
9188
  });
9037
9189
  }
9038
9190
 
@@ -9109,10 +9261,28 @@ function buildDisplayPath(relPath2, accountNames) {
9109
9261
  return dn ? { name: seg, displayName: dn } : { name: seg };
9110
9262
  });
9111
9263
  }
9264
+ async function knowledgeDocOwnedByAccount(accountId, attachmentId) {
9265
+ if (!attachmentId) return false;
9266
+ const session = getSession();
9267
+ try {
9268
+ const result = await session.run(
9269
+ `MATCH (d:KnowledgeDocument { accountId: $accountId, attachmentId: $attachmentId })
9270
+ RETURN count(d) > 0 AS owned`,
9271
+ { accountId, attachmentId }
9272
+ );
9273
+ return result.records[0]?.get("owned") === true;
9274
+ } catch (err) {
9275
+ console.error(`[data] kd-ownership-check-failed attachmentId="${attachmentId.slice(0, 8)}\u2026" err="${err instanceof Error ? err.message : String(err)}"`);
9276
+ return false;
9277
+ } finally {
9278
+ await session.close();
9279
+ }
9280
+ }
9112
9281
  var app15 = new Hono();
9113
9282
  app15.get("/", requireAdminSession, async (c) => {
9114
9283
  const cacheKey = c.var.cacheKey;
9115
- if (!getAccountIdForSession(cacheKey)) {
9284
+ const accountId = getAccountIdForSession(cacheKey);
9285
+ if (!accountId) {
9116
9286
  console.error(`[data] auth-rejected endpoint="/api/admin/files" reason="no account for session"`);
9117
9287
  return c.json({ error: "Account not found for session" }, 401);
9118
9288
  }
@@ -9125,17 +9295,26 @@ app15.get("/", requireAdminSession, async (c) => {
9125
9295
  return c.json({ error: resolution.error }, resolution.status);
9126
9296
  }
9127
9297
  const { absolute, relative: relPath2 } = resolution;
9298
+ if (crossesForeignAccountPartition(relPath2, accountId)) {
9299
+ console.error(`[data] account-scope-blocked endpoint="/api/admin/files" path="${relPath2}" account=${accountId.slice(0, 8)}\u2026`);
9300
+ return c.json({ error: "Not found" }, 404);
9301
+ }
9128
9302
  try {
9129
9303
  const info = await stat4(absolute);
9130
9304
  if (!info.isDirectory()) {
9131
9305
  return c.json({ error: "Path is not a directory \u2014 use /api/admin/files/download for files" }, 400);
9132
9306
  }
9133
9307
  const names = await readdir3(absolute);
9308
+ const segs = relPath2.split("/").filter(Boolean);
9309
+ const isAccountParent = segs.length === 1 && (segs[0] === "accounts" || segs[0] === "uploads");
9134
9310
  const entries = [];
9135
9311
  for (const name of names) {
9136
9312
  if (UUID_RE3.test(name.replace(/\.meta\.json$/, "")) && name.endsWith(".meta.json")) {
9137
9313
  continue;
9138
9314
  }
9315
+ if (isAccountParent && UUID_RE3.test(name) && name !== accountId) {
9316
+ continue;
9317
+ }
9139
9318
  try {
9140
9319
  const entryPath = join12(absolute, name);
9141
9320
  const s = await stat4(entryPath);
@@ -9180,15 +9359,26 @@ app15.get("/download", requireAdminSession, async (c) => {
9180
9359
  }
9181
9360
  const rawPath = c.req.query("path") ?? "";
9182
9361
  if (!rawPath) return c.json({ error: "path required" }, 400);
9183
- const resolution = resolveDataPath(rawPath);
9362
+ const root = c.req.query("root") ?? "data";
9363
+ if (root !== "data" && root !== "claude-uploads") {
9364
+ return c.json({ error: "invalid root" }, 400);
9365
+ }
9366
+ const resolution = root === "claude-uploads" ? resolveClaudeUploadsPath(rawPath) : resolveDataPath(rawPath);
9184
9367
  if (!resolution.ok) {
9185
9368
  if (resolution.status === 403) {
9186
- console.error(`[data] path-traversal-blocked requested="${rawPath}" resolved="${resolution.resolved ?? "n/a"}"`);
9369
+ console.error(`[data] path-traversal-blocked root="${root}" requested="${rawPath}" resolved="${resolution.resolved ?? "n/a"}"`);
9187
9370
  }
9188
9371
  return c.json({ error: resolution.error }, resolution.status);
9189
9372
  }
9190
9373
  const { absolute, relative: relPath2 } = resolution;
9191
- if (crossesForeignAccountPartition(relPath2, accountId)) {
9374
+ if (root === "claude-uploads") {
9375
+ const attachmentId = relPath2.split("/").filter(Boolean)[0] ?? "";
9376
+ const owned = await knowledgeDocOwnedByAccount(accountId, attachmentId);
9377
+ if (!owned) {
9378
+ console.error(`[data] account-scope-blocked root="claude-uploads" attachmentId="${attachmentId.slice(0, 8)}\u2026" account=${accountId.slice(0, 8)}\u2026`);
9379
+ return c.json({ error: "Not found" }, 404);
9380
+ }
9381
+ } else if (crossesForeignAccountPartition(relPath2, accountId)) {
9192
9382
  console.error(`[data] account-scope-blocked endpoint="/api/admin/files/download" path="${relPath2}" account=${accountId.slice(0, 8)}\u2026`);
9193
9383
  return c.json({ error: "Not found" }, 404);
9194
9384
  }
@@ -9201,7 +9391,7 @@ app15.get("/download", requireAdminSession, async (c) => {
9201
9391
  const mimeType = detectMimeType(absolute);
9202
9392
  const nodeStream = createReadStream2(absolute);
9203
9393
  const webStream = Readable2.toWeb(nodeStream);
9204
- console.error(`[data] file-download path="${relPath2}" size=${info.size}`);
9394
+ console.error(`[data] file-download root="${root}" path="${relPath2}" size=${info.size}`);
9205
9395
  const safeQuotedName = filename.replace(/[\r\n"\\]/g, "_");
9206
9396
  const encodedName = encodeURIComponent(filename);
9207
9397
  return new Response(webStream, {
@@ -9302,6 +9492,10 @@ app15.delete("/", requireAdminSession, async (c) => {
9302
9492
  return c.json({ error: resolution.error }, resolution.status);
9303
9493
  }
9304
9494
  const { absolute, relative: relPath2 } = resolution;
9495
+ if (crossesForeignAccountPartition(relPath2, accountId)) {
9496
+ console.error(`[data] account-scope-blocked endpoint="DELETE /api/admin/files" path="${relPath2}" account=${accountId.slice(0, 8)}\u2026`);
9497
+ return c.json({ error: "Not found" }, 404);
9498
+ }
9305
9499
  const base = basename4(absolute);
9306
9500
  const segments = relPath2.split("/").filter(Boolean);
9307
9501
  if (base === "account.json" || segments.includes(".git")) {
@@ -11359,35 +11553,15 @@ var WRITE_CYPHER = `
11359
11553
  var graph_default_view_default = app21;
11360
11554
 
11361
11555
  // server/routes/admin/sidebar-artefacts.ts
11362
- import neo4j2 from "neo4j-driver";
11363
- import { readFile as readFile5, readdir as readdir4, stat as stat5 } from "fs/promises";
11364
- import { resolve as resolve15, relative as relative3, isAbsolute, sep as sep5 } from "path";
11556
+ import { readdir as readdir4, stat as stat5 } from "fs/promises";
11557
+ import { resolve as resolve15, relative as relative3, isAbsolute, sep as sep5, basename as basename5 } from "path";
11365
11558
  import { existsSync as existsSync16 } from "fs";
11366
11559
  var LIMIT = 50;
11367
- var TEXT_MIME_PREFIXES = ["text/", "application/json", "application/markdown"];
11368
11560
  var ADMIN_AGENT_FILES = ["IDENTITY.md", "SOUL.md", "KNOWLEDGE.md"];
11369
11561
  function toDataRootRelPath(absPath) {
11370
11562
  if (absPath !== DATA_ROOT && !absPath.startsWith(DATA_ROOT + sep5)) return null;
11371
11563
  return relative3(DATA_ROOT, absPath);
11372
11564
  }
11373
- async function resolveDocAbsPath(accountId, attachmentId) {
11374
- if (!attachmentId) return null;
11375
- const accountDir = resolve15(ATTACHMENTS_ROOT, accountId);
11376
- const dir = resolve15(accountDir, attachmentId);
11377
- try {
11378
- validateFilePathInAccount(dir, accountDir);
11379
- } catch {
11380
- return null;
11381
- }
11382
- try {
11383
- const entries = await readdir4(dir);
11384
- const dataFile = entries.find((f) => !f.endsWith(".meta.json"));
11385
- if (!dataFile) return null;
11386
- return resolve15(dir, dataFile);
11387
- } catch {
11388
- return null;
11389
- }
11390
- }
11391
11565
  var app22 = new Hono();
11392
11566
  app22.get("/", requireAdminSession, async (c) => {
11393
11567
  const cacheKey = c.var.cacheKey;
@@ -11396,102 +11570,58 @@ app22.get("/", requireAdminSession, async (c) => {
11396
11570
  return c.json({ error: "Account not found for session" }, 401);
11397
11571
  }
11398
11572
  const start = Date.now();
11399
- const docs = await fetchKnowledgeDocs(accountId);
11400
- if (docs === null) {
11573
+ const outputs = await fetchOutputArtefacts(accountId);
11574
+ if (outputs === null) {
11401
11575
  return c.json({ error: "Failed to load artefacts" }, 500);
11402
11576
  }
11403
11577
  const accountDir = resolve15(ACCOUNTS_DIR, accountId);
11404
11578
  const agents = await fetchAgentTemplateRows(accountDir);
11405
- const artefacts = [...docs, ...agents].sort(
11579
+ const artefacts = [...outputs, ...agents].sort(
11406
11580
  (a, b) => (b.updatedAt ?? "").localeCompare(a.updatedAt ?? "")
11407
11581
  ).slice(0, LIMIT);
11408
11582
  const ms = Date.now() - start;
11409
11583
  console.log(
11410
- `[admin/sidebar-artefacts] account=${accountId} count=${artefacts.length} docs=${docs.length} agents=${agents.length} ms=${ms}`
11584
+ `[admin/sidebar-artefacts] account=${accountId} count=${artefacts.length} outputs=${outputs.length} agents=${agents.length} ms=${ms}`
11411
11585
  );
11412
11586
  return c.json({ artefacts });
11413
11587
  });
11414
- async function fetchKnowledgeDocs(accountId) {
11588
+ async function fetchOutputArtefacts(accountId) {
11415
11589
  const session = getSession();
11416
- let metas = [];
11417
11590
  try {
11418
11591
  const result = await session.run(
11419
- `MATCH (d:KnowledgeDocument { accountId: $accountId })
11420
- WHERE d.deletedAt IS NULL AND NOT d:Trashed
11421
- RETURN d.attachmentId AS id, d.name AS name, d.updatedAt AS updatedAt,
11422
- d.attachmentId AS attachmentId, d.encodingFormat AS mimeType
11423
- ORDER BY d.updatedAt DESC
11424
- LIMIT $limit`,
11425
- { accountId, limit: neo4j2.int(LIMIT) }
11426
- );
11427
- metas = result.records.map((r) => ({
11428
- id: r.get("id"),
11429
- name: r.get("name") ?? "",
11430
- updatedAt: r.get("updatedAt") ?? "",
11431
- attachmentId: r.get("attachmentId"),
11432
- mimeType: r.get("mimeType") ?? ""
11433
- }));
11592
+ `MATCH (f:FileArtifact { accountId: $accountId })
11593
+ WHERE f.relativePath CONTAINS '/output/'
11594
+ RETURN f.relativePath AS relativePath,
11595
+ f.displayName AS displayName,
11596
+ f.mimeType AS mimeType,
11597
+ f.modifiedAt AS modifiedAt
11598
+ ORDER BY f.modifiedAt DESC
11599
+ LIMIT 50`,
11600
+ { accountId }
11601
+ );
11602
+ return result.records.map((r) => {
11603
+ const relativePath = r.get("relativePath");
11604
+ const displayName = r.get("displayName") ?? null;
11605
+ const mimeType = r.get("mimeType") ?? "";
11606
+ const modifiedAt = r.get("modifiedAt") ?? "";
11607
+ return {
11608
+ id: `output-file:${relativePath}`,
11609
+ name: displayName ?? basename5(relativePath),
11610
+ kind: "output-file",
11611
+ updatedAt: modifiedAt,
11612
+ editable: false,
11613
+ mimeType,
11614
+ downloadPath: relativePath,
11615
+ downloadRoot: "data"
11616
+ };
11617
+ });
11434
11618
  } catch (err) {
11435
11619
  const message = err instanceof Error ? err.message : String(err);
11436
11620
  console.error(`[admin/sidebar-artefacts] account=${accountId} error="${message}"`);
11437
- await session.close();
11438
11621
  return null;
11439
11622
  } finally {
11440
11623
  await session.close();
11441
11624
  }
11442
- return Promise.all(metas.map(async (m) => {
11443
- const { content, skipReason } = await readArtefactContent(accountId, m.attachmentId, m.mimeType, m.name);
11444
- const absPath = await resolveDocAbsPath(accountId, m.attachmentId);
11445
- return {
11446
- id: m.id,
11447
- name: m.name,
11448
- kind: "knowledge-doc",
11449
- updatedAt: m.updatedAt,
11450
- content,
11451
- editable: skipReason === null,
11452
- mimeType: m.mimeType,
11453
- skipReason,
11454
- downloadPath: absPath ? toDataRootRelPath(absPath) : null
11455
- };
11456
- }));
11457
- }
11458
- async function readArtefactContent(accountId, attachmentId, mimeType, displayName) {
11459
- if (!attachmentId) {
11460
- logSkip(displayName, "null-attachmentId", mimeType);
11461
- return { content: "", skipReason: "null-attachmentId" };
11462
- }
11463
- const isText = TEXT_MIME_PREFIXES.some((p) => mimeType.startsWith(p));
11464
- if (!isText) {
11465
- logSkip(displayName, "non-text-mime", mimeType);
11466
- return { content: "", skipReason: "non-text-mime" };
11467
- }
11468
- const accountDir = resolve15(ATTACHMENTS_ROOT, accountId);
11469
- const dir = resolve15(accountDir, attachmentId);
11470
- try {
11471
- validateFilePathInAccount(dir, accountDir);
11472
- } catch {
11473
- logSkip(displayName, "containment-rejected", mimeType);
11474
- return { content: "", skipReason: "containment-rejected" };
11475
- }
11476
- try {
11477
- const entries = await readdir4(dir);
11478
- const dataFile = entries.find((f) => !f.endsWith(".meta.json"));
11479
- if (!dataFile) {
11480
- logSkip(displayName, "missing-on-disk", mimeType);
11481
- return { content: "", skipReason: "missing-on-disk" };
11482
- }
11483
- return { content: await readFile5(resolve15(dir, dataFile), "utf-8"), skipReason: null };
11484
- } catch (err) {
11485
- const message = err instanceof Error ? err.message : String(err);
11486
- console.error(`[admin/sidebar-artefacts] read-failed attachmentId=${attachmentId.slice(0, 8)} error="${message}"`);
11487
- logSkip(displayName, "missing-on-disk", mimeType);
11488
- return { content: "", skipReason: "missing-on-disk" };
11489
- }
11490
- }
11491
- function logSkip(name, reason, mimeType) {
11492
- console.log(
11493
- `[admin/sidebar-artefacts] content-skipped name="${name}" reason=${reason} mimeType=${mimeType || "none"}`
11494
- );
11495
11625
  }
11496
11626
  async function fetchAgentTemplateRows(accountDir) {
11497
11627
  const rows = [];
@@ -11571,23 +11701,22 @@ async function readAgentTemplateRow(inp) {
11571
11701
  }
11572
11702
  if (!chosenPath) return null;
11573
11703
  try {
11574
- const [content, st] = await Promise.all([
11575
- readFile5(chosenPath, "utf-8"),
11576
- stat5(chosenPath)
11577
- ]);
11704
+ const st = await stat5(chosenPath);
11705
+ const dlPath = toDataRootRelPath(chosenPath);
11578
11706
  return {
11579
11707
  id: inp.id,
11580
11708
  name: inp.displayName,
11581
11709
  kind: "agent-template",
11582
11710
  updatedAt: st.mtime.toISOString(),
11583
- content,
11584
11711
  editable: inp.editable,
11585
11712
  mimeType: "text/markdown",
11586
- skipReason: null,
11587
11713
  // Override paths live under <accountDir> (inside DATA_ROOT) and are
11588
11714
  // downloadable; bundled-fallback templates live under PLATFORM_ROOT
11589
11715
  // (outside DATA_ROOT) → null, so the client shows "not downloadable".
11590
- downloadPath: toDataRootRelPath(chosenPath)
11716
+ // Agent templates only ever resolve under DATA_ROOT, so the download
11717
+ // root is `data` whenever the path is non-null.
11718
+ downloadPath: dlPath,
11719
+ downloadRoot: dlPath ? "data" : null
11591
11720
  };
11592
11721
  } catch (err) {
11593
11722
  const message = err instanceof Error ? err.message : String(err);
@@ -11724,57 +11853,8 @@ function relPath(absPath, root) {
11724
11853
  }
11725
11854
  var sidebar_artefact_save_default = app23;
11726
11855
 
11727
- // server/routes/admin/sidebar-artefact-content.ts
11728
- import { readFile as readFile6, readdir as readdir6 } from "fs/promises";
11729
- import { existsSync as existsSync18 } from "fs";
11730
- import { resolve as resolve17 } from "path";
11731
- var UUID_RE5 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
11732
- var app24 = new Hono();
11733
- app24.get("/", requireAdminSession, async (c) => {
11734
- const cacheKey = c.var.cacheKey;
11735
- const accountId = getAccountIdForSession(cacheKey);
11736
- if (!accountId) return new Response("Unauthorized", { status: 401 });
11737
- const id = c.req.query("id") ?? "";
11738
- if (!UUID_RE5.test(id)) {
11739
- console.error(`[admin/sidebar-artefact-content] not-found id=${id.slice(0, 8)}`);
11740
- return new Response("Not found", { status: 404 });
11741
- }
11742
- const dir = resolve17(ATTACHMENTS_ROOT, accountId, id);
11743
- if (!existsSync18(dir)) {
11744
- console.error(`[admin/sidebar-artefact-content] not-found id=${id.slice(0, 8)}`);
11745
- return new Response("Not found", { status: 404 });
11746
- }
11747
- let meta;
11748
- try {
11749
- meta = JSON.parse(await readFile6(resolve17(dir, `${id}.meta.json`), "utf-8"));
11750
- } catch {
11751
- console.error(`[admin/sidebar-artefact-content] not-found id=${id.slice(0, 8)}`);
11752
- return new Response("Not found", { status: 404 });
11753
- }
11754
- const entries = await readdir6(dir);
11755
- const dataFile = entries.find((f) => !f.endsWith(".meta.json"));
11756
- if (!dataFile) {
11757
- console.error(`[admin/sidebar-artefact-content] not-found id=${id.slice(0, 8)}`);
11758
- return new Response("Not found", { status: 404 });
11759
- }
11760
- const start = Date.now();
11761
- const buffer = await readFile6(resolve17(dir, dataFile));
11762
- const ms = Date.now() - start;
11763
- console.log(
11764
- `[admin/sidebar-artefact-content] account=${accountId} id=${id.slice(0, 8)} mime=${meta.mimeType} bytes=${buffer.length} ms=${ms}`
11765
- );
11766
- return new Response(new Uint8Array(buffer), {
11767
- headers: {
11768
- "Content-Type": meta.mimeType,
11769
- "Content-Disposition": `inline; filename="${meta.filename}"`,
11770
- "Cache-Control": "private, max-age=3600"
11771
- }
11772
- });
11773
- });
11774
- var sidebar_artefact_content_default = app24;
11775
-
11776
11856
  // server/routes/admin/system-stats.ts
11777
- import { readFile as readFile7 } from "fs/promises";
11857
+ import { readFile as readFile5 } from "fs/promises";
11778
11858
  import { cpus, loadavg, totalmem, freemem } from "os";
11779
11859
  function parseCpuStat(blob) {
11780
11860
  const line = blob.split("\n", 1)[0] ?? "";
@@ -11818,11 +11898,11 @@ function parseMeminfo(blob) {
11818
11898
  }
11819
11899
  var SAMPLE_DELAY_MS = 100;
11820
11900
  async function sampleLinux() {
11821
- const a = parseCpuStat(await readFile7("/proc/stat", "utf-8"));
11901
+ const a = parseCpuStat(await readFile5("/proc/stat", "utf-8"));
11822
11902
  await new Promise((r) => setTimeout(r, SAMPLE_DELAY_MS));
11823
- const b = parseCpuStat(await readFile7("/proc/stat", "utf-8"));
11824
- const meminfo = parseMeminfo(await readFile7("/proc/meminfo", "utf-8"));
11825
- const loadavgRaw = (await readFile7("/proc/loadavg", "utf-8")).trim();
11903
+ const b = parseCpuStat(await readFile5("/proc/stat", "utf-8"));
11904
+ const meminfo = parseMeminfo(await readFile5("/proc/meminfo", "utf-8"));
11905
+ const loadavgRaw = (await readFile5("/proc/loadavg", "utf-8")).trim();
11826
11906
  const [l1, l5, l15] = loadavgRaw.split(/\s+/, 3).map(Number);
11827
11907
  if (![l1, l5, l15].every(Number.isFinite)) {
11828
11908
  throw new Error(`malformed /proc/loadavg: ${loadavgRaw}`);
@@ -11868,8 +11948,8 @@ async function sample() {
11868
11948
  if (process.platform === "linux") return sampleLinux();
11869
11949
  return sampleOsFallback();
11870
11950
  }
11871
- var app25 = new Hono();
11872
- app25.get("/", async (c) => {
11951
+ var app24 = new Hono();
11952
+ app24.get("/", async (c) => {
11873
11953
  const started = Date.now();
11874
11954
  if (!inflight) {
11875
11955
  inflight = sample().finally(() => {
@@ -11892,26 +11972,26 @@ app25.get("/", async (c) => {
11892
11972
  return c.json({ degraded: true, reason, sampledAtMs: Date.now() });
11893
11973
  }
11894
11974
  });
11895
- var system_stats_default = app25;
11975
+ var system_stats_default = app24;
11896
11976
 
11897
11977
  // server/routes/admin/health.ts
11898
- import { existsSync as existsSync19, readFileSync as readFileSync14 } from "fs";
11899
- import { resolve as resolve18, join as join13 } from "path";
11900
- var PLATFORM_ROOT6 = process.env.MAXY_PLATFORM_ROOT ?? resolve18(process.cwd(), "..");
11978
+ import { existsSync as existsSync18, readFileSync as readFileSync14 } from "fs";
11979
+ import { resolve as resolve17, join as join13 } from "path";
11980
+ var PLATFORM_ROOT6 = process.env.MAXY_PLATFORM_ROOT ?? resolve17(process.cwd(), "..");
11901
11981
  var brandHostname = "maxy";
11902
11982
  var brandJsonPath = join13(PLATFORM_ROOT6, "config", "brand.json");
11903
- if (existsSync19(brandJsonPath)) {
11983
+ if (existsSync18(brandJsonPath)) {
11904
11984
  try {
11905
11985
  const brand = JSON.parse(readFileSync14(brandJsonPath, "utf-8"));
11906
11986
  if (brand.hostname) brandHostname = brand.hostname;
11907
11987
  } catch {
11908
11988
  }
11909
11989
  }
11910
- var VERSION_FILE = resolve18(PLATFORM_ROOT6, `config/.${brandHostname}-version`);
11990
+ var VERSION_FILE = resolve17(PLATFORM_ROOT6, `config/.${brandHostname}-version`);
11911
11991
  var PROCESS_STARTED_AT = (/* @__PURE__ */ new Date()).toISOString();
11912
11992
  var PROBE_TIMEOUT_MS = 1e3;
11913
11993
  function readVersion() {
11914
- if (!existsSync19(VERSION_FILE)) return "unknown";
11994
+ if (!existsSync18(VERSION_FILE)) return "unknown";
11915
11995
  return readFileSync14(VERSION_FILE, "utf-8").trim() || "unknown";
11916
11996
  }
11917
11997
  async function probeConversationDb() {
@@ -11937,8 +12017,8 @@ async function probeConversationDb() {
11937
12017
  });
11938
12018
  }
11939
12019
  }
11940
- var app26 = new Hono();
11941
- app26.get("/", async (c) => {
12020
+ var app25 = new Hono();
12021
+ app25.get("/", async (c) => {
11942
12022
  const version = readVersion();
11943
12023
  const probe = await probeConversationDb();
11944
12024
  const uptimeMs = Date.now() - new Date(PROCESS_STARTED_AT).getTime();
@@ -11960,7 +12040,7 @@ app26.get("/", async (c) => {
11960
12040
  uptimeMs
11961
12041
  });
11962
12042
  });
11963
- var health_default2 = app26;
12043
+ var health_default2 = app25;
11964
12044
 
11965
12045
  // server/routes/admin/linkedin-ingest.ts
11966
12046
  var TAG20 = "[linkedin-ingest-route]";
@@ -12056,8 +12136,8 @@ function buildInitialMessage(env) {
12056
12136
  }
12057
12137
  return header;
12058
12138
  }
12059
- var app27 = new Hono();
12060
- app27.post("/", requireAdminSession, async (c) => {
12139
+ var app26 = new Hono();
12140
+ app26.post("/", requireAdminSession, async (c) => {
12061
12141
  let body;
12062
12142
  try {
12063
12143
  body = await c.req.json();
@@ -12111,10 +12191,10 @@ app27.post("/", requireAdminSession, async (c) => {
12111
12191
  );
12112
12192
  return c.json({ ok: true, dispatchId: envelope.dispatchId, taskId: claudeSessionId }, 202);
12113
12193
  });
12114
- var linkedin_ingest_default = app27;
12194
+ var linkedin_ingest_default = app26;
12115
12195
 
12116
12196
  // server/routes/admin/post-turn-context.ts
12117
- import neo4j3 from "neo4j-driver";
12197
+ import neo4j2 from "neo4j-driver";
12118
12198
  var TAG21 = "[post-turn-context]";
12119
12199
  var STRIPPED_PROPERTIES2 = /* @__PURE__ */ new Set([
12120
12200
  "embedding",
@@ -12127,10 +12207,10 @@ function isLoopbackAddr2(addr) {
12127
12207
  return addr === "127.0.0.1" || addr === "::1" || addr === "::ffff:127.0.0.1";
12128
12208
  }
12129
12209
  function convertValue(value) {
12130
- if (neo4j3.isDateTime(value) || neo4j3.isDate(value) || neo4j3.isLocalDateTime(value) || neo4j3.isLocalTime(value) || neo4j3.isTime(value) || neo4j3.isDuration(value)) {
12210
+ if (neo4j2.isDateTime(value) || neo4j2.isDate(value) || neo4j2.isLocalDateTime(value) || neo4j2.isLocalTime(value) || neo4j2.isTime(value) || neo4j2.isDuration(value)) {
12131
12211
  return value.toString();
12132
12212
  }
12133
- if (neo4j3.isInt(value)) {
12213
+ if (neo4j2.isInt(value)) {
12134
12214
  return value.inSafeRange() ? value.toNumber() : value.toString();
12135
12215
  }
12136
12216
  if (Array.isArray(value)) {
@@ -12153,8 +12233,8 @@ function pruneProperties(raw) {
12153
12233
  }
12154
12234
  return out;
12155
12235
  }
12156
- var app28 = new Hono();
12157
- app28.get("/", async (c) => {
12236
+ var app27 = new Hono();
12237
+ app27.get("/", async (c) => {
12158
12238
  const remoteAddr = c.env?.incoming?.socket?.remoteAddress ?? "";
12159
12239
  if (!isLoopbackAddr2(remoteAddr)) {
12160
12240
  console.error(`${TAG21} reject reason=non-loopback remoteAddr=${remoteAddr}`);
@@ -12214,10 +12294,10 @@ app28.get("/", async (c) => {
12214
12294
  await session.close();
12215
12295
  }
12216
12296
  });
12217
- var post_turn_context_default = app28;
12297
+ var post_turn_context_default = app27;
12218
12298
 
12219
12299
  // server/routes/admin/public-session-context.ts
12220
- import neo4j4 from "neo4j-driver";
12300
+ import neo4j3 from "neo4j-driver";
12221
12301
  var TAG22 = "[public-session-context]";
12222
12302
  var STRIPPED_PROPERTIES3 = /* @__PURE__ */ new Set([
12223
12303
  "embedding",
@@ -12230,10 +12310,10 @@ function isLoopbackAddr3(addr) {
12230
12310
  return addr === "127.0.0.1" || addr === "::1" || addr === "::ffff:127.0.0.1";
12231
12311
  }
12232
12312
  function convertValue2(value) {
12233
- if (neo4j4.isDateTime(value) || neo4j4.isDate(value) || neo4j4.isLocalDateTime(value) || neo4j4.isLocalTime(value) || neo4j4.isTime(value) || neo4j4.isDuration(value)) {
12313
+ if (neo4j3.isDateTime(value) || neo4j3.isDate(value) || neo4j3.isLocalDateTime(value) || neo4j3.isLocalTime(value) || neo4j3.isTime(value) || neo4j3.isDuration(value)) {
12234
12314
  return value.toString();
12235
12315
  }
12236
- if (neo4j4.isInt(value)) {
12316
+ if (neo4j3.isInt(value)) {
12237
12317
  return value.inSafeRange() ? value.toNumber() : value.toString();
12238
12318
  }
12239
12319
  if (Array.isArray(value)) {
@@ -12256,8 +12336,8 @@ function pruneProperties2(raw) {
12256
12336
  }
12257
12337
  return out;
12258
12338
  }
12259
- var app29 = new Hono();
12260
- app29.get("/", async (c) => {
12339
+ var app28 = new Hono();
12340
+ app28.get("/", async (c) => {
12261
12341
  const remoteAddr = c.env?.incoming?.socket?.remoteAddress ?? "";
12262
12342
  if (!isLoopbackAddr3(remoteAddr)) {
12263
12343
  console.error(`${TAG22} reject reason=non-loopback remoteAddr=${remoteAddr}`);
@@ -12316,15 +12396,15 @@ app29.get("/", async (c) => {
12316
12396
  await session.close();
12317
12397
  }
12318
12398
  });
12319
- var public_session_context_default = app29;
12399
+ var public_session_context_default = app28;
12320
12400
 
12321
12401
  // server/routes/admin/public-session-exit.ts
12322
12402
  var TAG23 = "[public-session-exit-route]";
12323
12403
  function isLoopbackAddr4(addr) {
12324
12404
  return addr === "127.0.0.1" || addr === "::1" || addr === "::ffff:127.0.0.1";
12325
12405
  }
12326
- var app30 = new Hono();
12327
- app30.post("/", async (c) => {
12406
+ var app29 = new Hono();
12407
+ app29.post("/", async (c) => {
12328
12408
  const remoteAddr = c.env?.incoming?.socket?.remoteAddress ?? "";
12329
12409
  if (!isLoopbackAddr4(remoteAddr)) {
12330
12410
  console.error(`${TAG23} reject reason=non-loopback remoteAddr=${remoteAddr}`);
@@ -12351,15 +12431,15 @@ app30.post("/", async (c) => {
12351
12431
  handlePublicSessionExit(sessionId);
12352
12432
  return c.json({ ok: true });
12353
12433
  });
12354
- var public_session_exit_default = app30;
12434
+ var public_session_exit_default = app29;
12355
12435
 
12356
12436
  // server/routes/admin/access-session-evict.ts
12357
12437
  var TAG24 = "[access-session-evict]";
12358
12438
  function isLoopbackAddr5(addr) {
12359
12439
  return addr === "127.0.0.1" || addr === "::1" || addr === "::ffff:127.0.0.1";
12360
12440
  }
12361
- var app31 = new Hono();
12362
- app31.post("/", async (c) => {
12441
+ var app30 = new Hono();
12442
+ app30.post("/", async (c) => {
12363
12443
  const remoteAddr = c.env?.incoming?.socket?.remoteAddress ?? "";
12364
12444
  if (!isLoopbackAddr5(remoteAddr)) {
12365
12445
  console.error(`${TAG24} reject reason=non-loopback remoteAddr=${remoteAddr}`);
@@ -12387,48 +12467,47 @@ app31.post("/", async (c) => {
12387
12467
  console.log(`${TAG24} grantId=${grantId} dropped=${dropped}`);
12388
12468
  return c.json({ ok: true, dropped });
12389
12469
  });
12390
- var access_session_evict_default = app31;
12470
+ var access_session_evict_default = app30;
12391
12471
 
12392
12472
  // server/routes/admin/index.ts
12393
- var app32 = new Hono();
12394
- app32.route("/session", session_default);
12395
- app32.route("/logs", logs_default);
12396
- app32.route("/claude-info", claude_info_default);
12397
- app32.route("/attachment", attachment_default);
12398
- app32.route("/agents", agents_default);
12399
- app32.route("/sessions", sessions_default);
12400
- app32.route("/claude-sessions", claude_sessions_default);
12401
- app32.route("/log-ingest", log_ingest_default);
12402
- app32.route("/events", events_default);
12403
- app32.route("/files", files_default);
12404
- app32.route("/graph-search", graph_search_default);
12405
- app32.route("/graph-subgraph", graph_subgraph_default);
12406
- app32.route("/graph-delete", graph_delete_default);
12407
- app32.route("/graph-restore", graph_restore_default);
12408
- app32.route("/graph-labels-in-graph", graph_labels_in_graph_default);
12409
- app32.route("/graph-default-view", graph_default_view_default);
12410
- app32.route("/sidebar-artefacts", sidebar_artefacts_default);
12411
- app32.route("/sidebar-artefact-save", sidebar_artefact_save_default);
12412
- app32.route("/sidebar-artefact-content", sidebar_artefact_content_default);
12413
- app32.route("/system-stats", system_stats_default);
12414
- app32.route("/health-brand", health_default2);
12415
- app32.route("/linkedin-ingest", linkedin_ingest_default);
12416
- app32.route("/post-turn-context", post_turn_context_default);
12417
- app32.route("/public-session-context", public_session_context_default);
12418
- app32.route("/public-session-exit", public_session_exit_default);
12419
- app32.route("/access-session-evict", access_session_evict_default);
12420
- var admin_default = app32;
12473
+ var app31 = new Hono();
12474
+ app31.route("/session", session_default);
12475
+ app31.route("/logs", logs_default);
12476
+ app31.route("/claude-info", claude_info_default);
12477
+ app31.route("/attachment", attachment_default);
12478
+ app31.route("/agents", agents_default);
12479
+ app31.route("/sessions", sessions_default);
12480
+ app31.route("/claude-sessions", claude_sessions_default);
12481
+ app31.route("/log-ingest", log_ingest_default);
12482
+ app31.route("/events", events_default);
12483
+ app31.route("/files", files_default);
12484
+ app31.route("/graph-search", graph_search_default);
12485
+ app31.route("/graph-subgraph", graph_subgraph_default);
12486
+ app31.route("/graph-delete", graph_delete_default);
12487
+ app31.route("/graph-restore", graph_restore_default);
12488
+ app31.route("/graph-labels-in-graph", graph_labels_in_graph_default);
12489
+ app31.route("/graph-default-view", graph_default_view_default);
12490
+ app31.route("/sidebar-artefacts", sidebar_artefacts_default);
12491
+ app31.route("/sidebar-artefact-save", sidebar_artefact_save_default);
12492
+ app31.route("/system-stats", system_stats_default);
12493
+ app31.route("/health-brand", health_default2);
12494
+ app31.route("/linkedin-ingest", linkedin_ingest_default);
12495
+ app31.route("/post-turn-context", post_turn_context_default);
12496
+ app31.route("/public-session-context", public_session_context_default);
12497
+ app31.route("/public-session-exit", public_session_exit_default);
12498
+ app31.route("/access-session-evict", access_session_evict_default);
12499
+ var admin_default = app31;
12421
12500
 
12422
12501
  // app/lib/access-gate.ts
12423
- import neo4j5 from "neo4j-driver";
12502
+ import neo4j4 from "neo4j-driver";
12424
12503
  import { readFileSync as readFileSync15 } from "fs";
12425
- import { resolve as resolve19 } from "path";
12504
+ import { resolve as resolve18 } from "path";
12426
12505
  import { randomUUID as randomUUID8 } from "crypto";
12427
- var PLATFORM_ROOT7 = process.env.MAXY_PLATFORM_ROOT ?? resolve19(process.cwd(), "..");
12506
+ var PLATFORM_ROOT7 = process.env.MAXY_PLATFORM_ROOT ?? resolve18(process.cwd(), "..");
12428
12507
  var driver = null;
12429
12508
  function readPassword() {
12430
12509
  if (process.env.NEO4J_PASSWORD) return process.env.NEO4J_PASSWORD;
12431
- const passwordFile = resolve19(PLATFORM_ROOT7, "config/.neo4j-password");
12510
+ const passwordFile = resolve18(PLATFORM_ROOT7, "config/.neo4j-password");
12432
12511
  try {
12433
12512
  return readFileSync15(passwordFile, "utf-8").trim();
12434
12513
  } catch {
@@ -12448,7 +12527,7 @@ function getDriver() {
12448
12527
  const user = process.env.NEO4J_USER ?? "neo4j";
12449
12528
  const password = readPassword();
12450
12529
  console.error(`[ui/access-gate] resolved neo4j_uri=${uri}`);
12451
- driver = neo4j5.driver(uri, neo4j5.auth.basic(user, password), {
12530
+ driver = neo4j4.driver(uri, neo4j4.auth.basic(user, password), {
12452
12531
  maxConnectionPoolSize: 5
12453
12532
  });
12454
12533
  }
@@ -12631,8 +12710,8 @@ async function generateNewMagicToken(grantId) {
12631
12710
  var TAG25 = "[access-verify]";
12632
12711
  var MINT_TAG = "[access-session-mint]";
12633
12712
  var COOKIE_NAME = "__access_session";
12634
- var app33 = new Hono();
12635
- app33.post("/", async (c) => {
12713
+ var app32 = new Hono();
12714
+ app32.post("/", async (c) => {
12636
12715
  const ip = c.var.clientIp || "unknown";
12637
12716
  let body;
12638
12717
  try {
@@ -12714,13 +12793,13 @@ app33.post("/", async (c) => {
12714
12793
  displayName: grant.displayName
12715
12794
  });
12716
12795
  });
12717
- var verify_token_default = app33;
12796
+ var verify_token_default = app32;
12718
12797
 
12719
12798
  // app/lib/access-email.ts
12720
12799
  import { spawn as spawn2 } from "child_process";
12721
- import { resolve as resolve20 } from "path";
12722
- var PLATFORM_ROOT8 = process.env.MAXY_PLATFORM_ROOT ?? resolve20(process.cwd(), "..");
12723
- var SEND_SCRIPT = resolve20(
12800
+ import { resolve as resolve19 } from "path";
12801
+ var PLATFORM_ROOT8 = process.env.MAXY_PLATFORM_ROOT ?? resolve19(process.cwd(), "..");
12802
+ var SEND_SCRIPT = resolve19(
12724
12803
  PLATFORM_ROOT8,
12725
12804
  "plugins",
12726
12805
  "email",
@@ -12777,9 +12856,9 @@ async function sendMagicLinkEmail(payload) {
12777
12856
 
12778
12857
  // server/routes/access/request-magic-link.ts
12779
12858
  var TAG26 = "[access-request-link]";
12780
- var app34 = new Hono();
12859
+ var app33 = new Hono();
12781
12860
  var VISITOR_MESSAGE = "If that email is on the invite list, a fresh link is on the way.";
12782
- app34.post("/", async (c) => {
12861
+ app33.post("/", async (c) => {
12783
12862
  let body;
12784
12863
  try {
12785
12864
  body = await c.req.json();
@@ -12853,17 +12932,17 @@ app34.post("/", async (c) => {
12853
12932
  );
12854
12933
  return c.json({ message: VISITOR_MESSAGE }, 200);
12855
12934
  });
12856
- var request_magic_link_default = app34;
12935
+ var request_magic_link_default = app33;
12857
12936
 
12858
12937
  // server/routes/access/index.ts
12859
- var app35 = new Hono();
12860
- app35.route("/verify-token", verify_token_default);
12861
- app35.route("/request-magic-link", request_magic_link_default);
12862
- var access_default = app35;
12938
+ var app34 = new Hono();
12939
+ app34.route("/verify-token", verify_token_default);
12940
+ app34.route("/request-magic-link", request_magic_link_default);
12941
+ var access_default = app34;
12863
12942
 
12864
12943
  // server/routes/sites.ts
12865
- import { existsSync as existsSync20, readFileSync as readFileSync16, realpathSync as realpathSync5, statSync as statSync7 } from "fs";
12866
- import { resolve as resolve21 } from "path";
12944
+ import { existsSync as existsSync19, readFileSync as readFileSync16, realpathSync as realpathSync5, statSync as statSync7 } from "fs";
12945
+ import { resolve as resolve20 } from "path";
12867
12946
  var SAFE_SEG_RE = /^[a-z0-9_][a-z0-9_.-]{0,99}$/i;
12868
12947
  var MIME = {
12869
12948
  ".html": "text/html; charset=utf-8",
@@ -12894,8 +12973,8 @@ function getExt(p) {
12894
12973
  if (idx < p.lastIndexOf("/")) return "";
12895
12974
  return p.slice(idx).toLowerCase();
12896
12975
  }
12897
- var app36 = new Hono();
12898
- app36.get("/:rel{.*}", (c) => {
12976
+ var app35 = new Hono();
12977
+ app35.get("/:rel{.*}", (c) => {
12899
12978
  const reqPath = c.req.path;
12900
12979
  const rawRel = c.req.param("rel") ?? "";
12901
12980
  const trimmed = rawRel.replace(/^\/+/, "").replace(/\/+$/, "");
@@ -12920,19 +12999,19 @@ app36.get("/:rel{.*}", (c) => {
12920
12999
  }
12921
13000
  segments.push(seg);
12922
13001
  }
12923
- const rootDir = resolve21(account.accountDir, "sites");
12924
- let filePath = segments.length === 0 ? rootDir : resolve21(rootDir, ...segments);
13002
+ const rootDir = resolve20(account.accountDir, "sites");
13003
+ let filePath = segments.length === 0 ? rootDir : resolve20(rootDir, ...segments);
12925
13004
  if (filePath !== rootDir && !filePath.startsWith(rootDir + "/")) {
12926
13005
  console.error(`[sites] path-traversal-rejected path=${reqPath} reason=escape status=403`);
12927
13006
  return c.text("Forbidden", 403);
12928
13007
  }
12929
- let stat7;
13008
+ let stat8;
12930
13009
  try {
12931
- stat7 = existsSync20(filePath) ? statSync7(filePath) : null;
13010
+ stat8 = existsSync19(filePath) ? statSync7(filePath) : null;
12932
13011
  } catch {
12933
- stat7 = null;
13012
+ stat8 = null;
12934
13013
  }
12935
- if (stat7?.isDirectory() && !reqPath.endsWith("/")) {
13014
+ if (stat8?.isDirectory() && !reqPath.endsWith("/")) {
12936
13015
  const search = new URL(c.req.url).search;
12937
13016
  const target = `${reqPath}/${search}`;
12938
13017
  console.log(
@@ -12940,14 +13019,14 @@ app36.get("/:rel{.*}", (c) => {
12940
13019
  );
12941
13020
  return c.redirect(target, 301);
12942
13021
  }
12943
- if (stat7?.isDirectory()) {
12944
- filePath = resolve21(filePath, "index.html");
13022
+ if (stat8?.isDirectory()) {
13023
+ filePath = resolve20(filePath, "index.html");
12945
13024
  }
12946
13025
  if (!filePath.startsWith(rootDir + "/")) {
12947
13026
  console.error(`[sites] path-traversal-rejected path=${reqPath} reason=escape status=403`);
12948
13027
  return c.text("Forbidden", 403);
12949
13028
  }
12950
- if (!existsSync20(filePath)) {
13029
+ if (!existsSync19(filePath)) {
12951
13030
  console.error(`[sites] not-found path=${reqPath} status=404`);
12952
13031
  return c.text("Not found", 404);
12953
13032
  }
@@ -12998,7 +13077,7 @@ app36.get("/:rel{.*}", (c) => {
12998
13077
  "X-Content-Type-Options": "nosniff"
12999
13078
  });
13000
13079
  });
13001
- var sites_default = app36;
13080
+ var sites_default = app35;
13002
13081
 
13003
13082
  // app/lib/visitor-token.ts
13004
13083
  import { createHmac, randomBytes, timingSafeEqual } from "crypto";
@@ -13078,7 +13157,7 @@ var VISITOR_COOKIE_NAME = "mxy_v";
13078
13157
  var VISITOR_COOKIE_MAX_AGE_SECONDS = Math.floor(DEFAULT_TTL_MS / 1e3);
13079
13158
 
13080
13159
  // app/lib/brand-config.ts
13081
- import { existsSync as existsSync21, readFileSync as readFileSync18 } from "fs";
13160
+ import { existsSync as existsSync20, readFileSync as readFileSync18 } from "fs";
13082
13161
  import { join as join14 } from "path";
13083
13162
  var cached2 = null;
13084
13163
  var cachedAttempted = false;
@@ -13088,7 +13167,7 @@ function readBrandConfig() {
13088
13167
  const platformRoot = process.env.MAXY_PLATFORM_ROOT;
13089
13168
  if (!platformRoot) return null;
13090
13169
  const brandPath = join14(platformRoot, "config", "brand.json");
13091
- if (!existsSync21(brandPath)) return null;
13170
+ if (!existsSync20(brandPath)) return null;
13092
13171
  try {
13093
13172
  cached2 = JSON.parse(readFileSync18(brandPath, "utf-8"));
13094
13173
  return cached2;
@@ -13098,7 +13177,7 @@ function readBrandConfig() {
13098
13177
  }
13099
13178
 
13100
13179
  // server/routes/visitor-consent.ts
13101
- var app37 = new Hono();
13180
+ var app36 = new Hono();
13102
13181
  var CONSENT_COOKIE_NAME = "mxy_consent";
13103
13182
  var CONSENT_COOKIE_MAX_AGE_SECONDS = 60 * 60 * 24 * 365;
13104
13183
  var DEFAULT_CONSENT_COPY = {
@@ -13143,17 +13222,17 @@ function siteSlugFromReferer(referer) {
13143
13222
  return "";
13144
13223
  }
13145
13224
  }
13146
- app37.options("/consent", (c) => {
13225
+ app36.options("/consent", (c) => {
13147
13226
  const origin = getOrigin(c);
13148
13227
  setCorsHeaders(c, origin);
13149
13228
  return c.body(null, 204);
13150
13229
  });
13151
- app37.options("/brand-config", (c) => {
13230
+ app36.options("/brand-config", (c) => {
13152
13231
  const origin = getOrigin(c);
13153
13232
  setCorsHeaders(c, origin);
13154
13233
  return c.body(null, 204);
13155
13234
  });
13156
- app37.post("/consent", async (c) => {
13235
+ app36.post("/consent", async (c) => {
13157
13236
  const origin = getOrigin(c);
13158
13237
  setCorsHeaders(c, origin);
13159
13238
  let raw;
@@ -13193,7 +13272,7 @@ app37.post("/consent", async (c) => {
13193
13272
  console.log(`[consent] ${parsed.decision} site=${site} brand=${brandName} tokenBound=${tokenBound}`);
13194
13273
  return c.body(null, 204);
13195
13274
  });
13196
- app37.get("/brand-config", (c) => {
13275
+ app36.get("/brand-config", (c) => {
13197
13276
  const origin = getOrigin(c);
13198
13277
  setCorsHeaders(c, origin);
13199
13278
  const brand = readBrandConfig();
@@ -13203,7 +13282,7 @@ app37.get("/brand-config", (c) => {
13203
13282
  c.header("Cache-Control", "public, max-age=300");
13204
13283
  return c.json({ consent: { copy, palette } });
13205
13284
  });
13206
- var visitor_consent_default = app37;
13285
+ var visitor_consent_default = app36;
13207
13286
 
13208
13287
  // server/routes/listings.ts
13209
13288
  function getCookie(headerValue, name) {
@@ -13224,14 +13303,14 @@ function appendConsentParams(pageUrl, token) {
13224
13303
  const hashIdx = pageUrl.indexOf("#");
13225
13304
  const hash = hashIdx >= 0 ? pageUrl.slice(hashIdx) : "";
13226
13305
  const base = hashIdx >= 0 ? pageUrl.slice(0, hashIdx) : pageUrl;
13227
- const sep6 = base.indexOf("?") >= 0 ? "&" : "?";
13228
- return base + sep6 + `consent=needed&v=${encodeURIComponent(token)}` + hash;
13306
+ const sep7 = base.indexOf("?") >= 0 ? "&" : "?";
13307
+ return base + sep7 + `consent=needed&v=${encodeURIComponent(token)}` + hash;
13229
13308
  }
13230
13309
  }
13231
13310
  var SAFE_SLUG_RE = /^[a-z0-9](?:[a-z0-9-]{0,118}[a-z0-9])?$/;
13232
13311
  var CHAT_SESSION_KEY_RE = /^[A-Za-z0-9][A-Za-z0-9_-]{7,127}$/;
13233
- var app38 = new Hono();
13234
- app38.get("/:slug/click", async (c) => {
13312
+ var app37 = new Hono();
13313
+ app37.get("/:slug/click", async (c) => {
13235
13314
  const slug = c.req.param("slug") ?? "";
13236
13315
  const rawSession = c.req.query("session") ?? "";
13237
13316
  const sessionKey = CHAT_SESSION_KEY_RE.test(rawSession) ? rawSession : "invalid";
@@ -13295,10 +13374,10 @@ app38.get("/:slug/click", async (c) => {
13295
13374
  console.log(`[property-card-click] sessionKey=${sessionKey} listingSlug=${slug} consent=${consentCookie ?? "absent"} ts=${(/* @__PURE__ */ new Date()).toISOString()}`);
13296
13375
  return c.redirect(redirectUrl, 302);
13297
13376
  });
13298
- var listings_default = app38;
13377
+ var listings_default = app37;
13299
13378
 
13300
13379
  // server/routes/visitor-event.ts
13301
- var app39 = new Hono();
13380
+ var app38 = new Hono();
13302
13381
  var BOT_UA_RE = /\b(bot|crawl|spider|slurp|headlesschrome|phantomjs|googlebot|bingbot|yandex|baiduspider|ahrefsbot|semrushbot|mj12bot|dotbot|petalbot)\b/i;
13303
13382
  var buckets = /* @__PURE__ */ new Map();
13304
13383
  var RATE_LIMIT = 60;
@@ -13365,12 +13444,12 @@ function originAllowed(origin, allowlist) {
13365
13444
  return false;
13366
13445
  }
13367
13446
  }
13368
- app39.options("/event", (c) => {
13447
+ app38.options("/event", (c) => {
13369
13448
  const origin = getOrigin2(c);
13370
13449
  setCorsHeaders2(c, origin);
13371
13450
  return c.body(null, 204);
13372
13451
  });
13373
- app39.post("/event", async (c) => {
13452
+ app38.post("/event", async (c) => {
13374
13453
  const origin = getOrigin2(c);
13375
13454
  setCorsHeaders2(c, origin);
13376
13455
  const ua = c.req.header("user-agent") ?? "";
@@ -13575,7 +13654,7 @@ async function writeEvent(opts) {
13575
13654
  );
13576
13655
  }
13577
13656
  }
13578
- var visitor_event_default = app39;
13657
+ var visitor_event_default = app38;
13579
13658
 
13580
13659
  // app/lib/graph-health.ts
13581
13660
  var HOUR_MS = 60 * 60 * 1e3;
@@ -13658,6 +13737,109 @@ function startGraphHealthTimer() {
13658
13737
  if (typeof timer.unref === "function") timer.unref();
13659
13738
  }
13660
13739
 
13740
+ // app/lib/file-watcher.ts
13741
+ import * as fsp2 from "fs/promises";
13742
+ import { resolve as resolve21, sep as sep6 } from "path";
13743
+ 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;
13744
+ var DEFAULT_COALESCE_MS = 500;
13745
+ var ROOTS = ["uploads", "accounts"];
13746
+ function _routeEvent(rootName, filename) {
13747
+ if (!filename) return null;
13748
+ const segs = filename.split(sep6).filter(Boolean);
13749
+ if (segs.length < 2) return null;
13750
+ const accountId = segs[0];
13751
+ if (!ACCOUNT_UUID_RE2.test(accountId)) return null;
13752
+ return { accountId, relativePath: `${rootName}/${segs.join("/")}` };
13753
+ }
13754
+ async function startFileWatcher(opts = {}) {
13755
+ const dataRoot = opts.dataRoot ?? DATA_ROOT;
13756
+ const coalesceMs = opts.coalesceMs ?? DEFAULT_COALESCE_MS;
13757
+ const indexFn = opts.index ?? indexFile;
13758
+ const dropFn = opts.drop ?? dropFileIndex;
13759
+ for (const r of ROOTS) {
13760
+ try {
13761
+ await fsp2.mkdir(resolve21(dataRoot, r), { recursive: true });
13762
+ } catch (err) {
13763
+ console.error(
13764
+ `[file-watcher] start-failed root="${r}" err="${err.message}" \u2014 index will be maintained by the 5-min reconcile backstop only`
13765
+ );
13766
+ return { stop: async () => {
13767
+ } };
13768
+ }
13769
+ }
13770
+ const controller = new AbortController();
13771
+ const timers = /* @__PURE__ */ new Map();
13772
+ function schedule(relativePath, accountId) {
13773
+ const existing = timers.get(relativePath);
13774
+ if (existing) clearTimeout(existing);
13775
+ const t = setTimeout(() => {
13776
+ timers.delete(relativePath);
13777
+ void runHook(relativePath, accountId);
13778
+ }, coalesceMs);
13779
+ timers.set(relativePath, t);
13780
+ }
13781
+ async function runHook(relativePath, accountId) {
13782
+ const absolute = resolve21(dataRoot, relativePath);
13783
+ let exists = false;
13784
+ try {
13785
+ const st = await fsp2.stat(absolute);
13786
+ exists = st.isFile();
13787
+ } catch (err) {
13788
+ const code = err.code;
13789
+ if (code !== "ENOENT") {
13790
+ console.error(`[file-watcher] stat-error path="${relativePath}" err="${err.message}"`);
13791
+ }
13792
+ }
13793
+ try {
13794
+ if (exists) {
13795
+ await indexFn(accountId, relativePath);
13796
+ } else {
13797
+ await dropFn(accountId, relativePath);
13798
+ }
13799
+ } catch (err) {
13800
+ console.error(`[file-watcher] hook-failed path="${relativePath}" err="${err.message}"`);
13801
+ } finally {
13802
+ opts.onSettled?.(relativePath);
13803
+ }
13804
+ }
13805
+ async function watchRoot(rootName) {
13806
+ const absRoot = resolve21(dataRoot, rootName);
13807
+ try {
13808
+ const iter = fsp2.watch(absRoot, { recursive: true, signal: controller.signal });
13809
+ for await (const event of iter) {
13810
+ const routed = _routeEvent(rootName, event.filename);
13811
+ if (!routed) continue;
13812
+ schedule(routed.relativePath, routed.accountId);
13813
+ }
13814
+ } catch (err) {
13815
+ const code = err.code;
13816
+ const name = err.name;
13817
+ if (code === "ABORT_ERR" || name === "AbortError") return;
13818
+ console.error(`[file-watcher] loop-error root="${rootName}" err="${err.message}"`);
13819
+ }
13820
+ }
13821
+ for (const r of ROOTS) void watchRoot(r);
13822
+ const sigHandler = () => {
13823
+ void stop();
13824
+ };
13825
+ process.once("SIGTERM", sigHandler);
13826
+ process.once("SIGINT", sigHandler);
13827
+ let stopped = false;
13828
+ async function stop() {
13829
+ if (stopped) return;
13830
+ stopped = true;
13831
+ process.off("SIGTERM", sigHandler);
13832
+ process.off("SIGINT", sigHandler);
13833
+ controller.abort();
13834
+ for (const t of timers.values()) clearTimeout(t);
13835
+ timers.clear();
13836
+ }
13837
+ console.error(
13838
+ `[file-watcher] started roots=${ROOTS.join(",")} dataRoot="${dataRoot}" coalesce-ms=${coalesceMs}`
13839
+ );
13840
+ return { stop };
13841
+ }
13842
+
13661
13843
  // app/lib/whatsapp/inbound/claude-bridge.ts
13662
13844
  var TAG27 = "[whatsapp-adaptor]";
13663
13845
  function whatsappTurnTimeoutMs() {
@@ -13719,7 +13901,7 @@ function broadcastAdminShutdown(reason) {
13719
13901
 
13720
13902
  // ../lib/entitlement/src/index.ts
13721
13903
  import { createPublicKey, createHash as createHash4, verify as cryptoVerify } from "crypto";
13722
- import { existsSync as existsSync22, readFileSync as readFileSync19, statSync as statSync8 } from "fs";
13904
+ import { existsSync as existsSync21, readFileSync as readFileSync19, statSync as statSync8 } from "fs";
13723
13905
  import { resolve as resolve22 } from "path";
13724
13906
 
13725
13907
  // ../lib/entitlement/src/canonicalize.ts
@@ -13768,11 +13950,11 @@ function resolveEntitlement(brand, account) {
13768
13950
  return logResolved(implicitTrust(account), null);
13769
13951
  }
13770
13952
  const entitlementPath = resolve22(brand.configDir, "entitlement.json");
13771
- if (!existsSync22(entitlementPath)) {
13953
+ if (!existsSync21(entitlementPath)) {
13772
13954
  return logResolved(anonymousFallback("missing"), { reason: "missing" });
13773
13955
  }
13774
- const stat7 = statSync8(entitlementPath);
13775
- const key = memoKey(stat7.mtimeMs, account);
13956
+ const stat8 = statSync8(entitlementPath);
13957
+ const key = memoKey(stat8.mtimeMs, account);
13776
13958
  if (memo && memo.key === key) {
13777
13959
  return memo.result;
13778
13960
  }
@@ -13960,10 +14142,10 @@ function clientFrom(c) {
13960
14142
  var PLATFORM_ROOT9 = process.env.MAXY_PLATFORM_ROOT || "";
13961
14143
  var BRAND_JSON_PATH = PLATFORM_ROOT9 ? join15(PLATFORM_ROOT9, "config", "brand.json") : "";
13962
14144
  var BRAND = { productName: "Maxy", hostname: "maxy", configDir: ".maxy", marketingUrl: "https://getmaxy.com" };
13963
- if (BRAND_JSON_PATH && !existsSync23(BRAND_JSON_PATH)) {
14145
+ if (BRAND_JSON_PATH && !existsSync22(BRAND_JSON_PATH)) {
13964
14146
  console.error(`[brand] WARNING: brand.json not found at ${BRAND_JSON_PATH} \u2014 using Maxy defaults`);
13965
14147
  }
13966
- if (BRAND_JSON_PATH && existsSync23(BRAND_JSON_PATH)) {
14148
+ if (BRAND_JSON_PATH && existsSync22(BRAND_JSON_PATH)) {
13967
14149
  try {
13968
14150
  const parsed = JSON.parse(readFileSync20(BRAND_JSON_PATH, "utf-8"));
13969
14151
  BRAND = { ...BRAND, ...parsed };
@@ -13987,7 +14169,7 @@ var brandLoginOpts = {
13987
14169
  var ALIAS_DOMAINS_PATH = join15(homedir2(), BRAND.configDir, "alias-domains.json");
13988
14170
  function loadAliasDomains() {
13989
14171
  try {
13990
- if (!existsSync23(ALIAS_DOMAINS_PATH)) return null;
14172
+ if (!existsSync22(ALIAS_DOMAINS_PATH)) return null;
13991
14173
  const parsed = JSON.parse(readFileSync20(ALIAS_DOMAINS_PATH, "utf-8"));
13992
14174
  if (!Array.isArray(parsed)) {
13993
14175
  console.error("[alias-domains] malformed alias-domains.json \u2014 expected array");
@@ -14012,9 +14194,9 @@ watchFile(ALIAS_DOMAINS_PATH, { interval: 2e3 }, () => {
14012
14194
  function isPublicHost(host) {
14013
14195
  return host.startsWith("public.") || aliasDomains.has(host);
14014
14196
  }
14015
- var app40 = new Hono();
14016
- app40.use("*", clientIpMiddleware);
14017
- app40.use("*", async (c, next) => {
14197
+ var app39 = new Hono();
14198
+ app39.use("*", clientIpMiddleware);
14199
+ app39.use("*", async (c, next) => {
14018
14200
  await next();
14019
14201
  c.header("X-Content-Type-Options", "nosniff");
14020
14202
  c.header("Referrer-Policy", "strict-origin-when-cross-origin");
@@ -14024,7 +14206,7 @@ app40.use("*", async (c, next) => {
14024
14206
  );
14025
14207
  });
14026
14208
  var HTTP_LOG_PATHS = /* @__PURE__ */ new Set(["/vnc-viewer.html", "/vnc-popout.html"]);
14027
- app40.use("*", async (c, next) => {
14209
+ app39.use("*", async (c, next) => {
14028
14210
  if (!HTTP_LOG_PATHS.has(c.req.path)) {
14029
14211
  await next();
14030
14212
  return;
@@ -14057,7 +14239,7 @@ var PUBLIC_ALLOWED_PREFIXES = [
14057
14239
  "/sites/"
14058
14240
  ];
14059
14241
  var PUBLIC_ALLOWED_EXACT = ["/favicon.ico"];
14060
- app40.use("*", async (c, next) => {
14242
+ app39.use("*", async (c, next) => {
14061
14243
  const host = (c.req.header("host") ?? "").split(":")[0];
14062
14244
  if (!isPublicHost(host)) {
14063
14245
  await next();
@@ -14097,7 +14279,7 @@ function resolveRemoteAuthOpts() {
14097
14279
  return brandLoginOpts;
14098
14280
  }
14099
14281
  var MAX_LOGIN_BODY = 8 * 1024;
14100
- app40.post("/__remote-auth/login", async (c) => {
14282
+ app39.post("/__remote-auth/login", async (c) => {
14101
14283
  const client = clientFrom(c);
14102
14284
  const clientIp = client.ip || "unknown";
14103
14285
  if (!requestIsTlsTerminated(c)) {
@@ -14142,7 +14324,7 @@ app40.post("/__remote-auth/login", async (c) => {
14142
14324
  }
14143
14325
  });
14144
14326
  });
14145
- app40.get("/__remote-auth/logout", (c) => {
14327
+ app39.get("/__remote-auth/logout", (c) => {
14146
14328
  const client = clientFrom(c);
14147
14329
  const clientIp = client.ip || "unknown";
14148
14330
  console.error(`[remote-auth] logout ip=${clientIp}`);
@@ -14155,7 +14337,7 @@ app40.get("/__remote-auth/logout", (c) => {
14155
14337
  }
14156
14338
  });
14157
14339
  });
14158
- app40.post("/__remote-auth/change-password", async (c) => {
14340
+ app39.post("/__remote-auth/change-password", async (c) => {
14159
14341
  const client = clientFrom(c);
14160
14342
  const clientIp = client.ip || "unknown";
14161
14343
  const rateLimited = checkRateLimit(client);
@@ -14206,13 +14388,13 @@ app40.post("/__remote-auth/change-password", async (c) => {
14206
14388
  return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(), mode: "change", changeError: "Failed to save password", redirect }), 200);
14207
14389
  }
14208
14390
  });
14209
- app40.get("/__remote-auth/setup", (c) => {
14391
+ app39.get("/__remote-auth/setup", (c) => {
14210
14392
  if (isRemoteAuthConfigured()) {
14211
14393
  return c.redirect("/");
14212
14394
  }
14213
14395
  return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(), mode: "setup" }), 200);
14214
14396
  });
14215
- app40.post("/__remote-auth/set-initial-password", async (c) => {
14397
+ app39.post("/__remote-auth/set-initial-password", async (c) => {
14216
14398
  if (isRemoteAuthConfigured()) {
14217
14399
  return c.redirect("/");
14218
14400
  }
@@ -14250,10 +14432,10 @@ app40.post("/__remote-auth/set-initial-password", async (c) => {
14250
14432
  return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(), mode: "setup", setupError: "Failed to save password. Please try again." }), 200);
14251
14433
  }
14252
14434
  });
14253
- app40.get("/api/remote-auth/status", (c) => {
14435
+ app39.get("/api/remote-auth/status", (c) => {
14254
14436
  return c.json({ configured: isRemoteAuthConfigured() });
14255
14437
  });
14256
- app40.post("/api/remote-auth/set-password", async (c) => {
14438
+ app39.post("/api/remote-auth/set-password", async (c) => {
14257
14439
  let body;
14258
14440
  try {
14259
14441
  body = await c.req.json();
@@ -14284,9 +14466,9 @@ app40.post("/api/remote-auth/set-password", async (c) => {
14284
14466
  return c.json({ error: "Failed to save password" }, 500);
14285
14467
  }
14286
14468
  });
14287
- app40.route("/api/_client-error", client_error_default);
14469
+ app39.route("/api/_client-error", client_error_default);
14288
14470
  console.log("[client-error-route] mounted");
14289
- app40.use("*", async (c, next) => {
14471
+ app39.use("*", async (c, next) => {
14290
14472
  const host = (c.req.header("host") ?? "").split(":")[0];
14291
14473
  const path2 = c.req.path;
14292
14474
  if (path2 === "/favicon.ico" || path2.startsWith("/assets/") || path2.startsWith("/brand/")) {
@@ -14319,12 +14501,12 @@ app40.use("*", async (c, next) => {
14319
14501
  console.error(`[remote-auth] login required ip=${clientIp} path=${path2} ${disambig}`);
14320
14502
  return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(), redirect: path2 }), 200);
14321
14503
  });
14322
- app40.route("/api/health", health_default);
14323
- app40.route("/api/chat", chat_default);
14324
- app40.route("/api/whatsapp", whatsapp_default);
14325
- app40.route("/api/onboarding", onboarding_default);
14326
- app40.route("/api/admin", admin_default);
14327
- app40.route("/api/access", access_default);
14504
+ app39.route("/api/health", health_default);
14505
+ app39.route("/api/chat", chat_default);
14506
+ app39.route("/api/whatsapp", whatsapp_default);
14507
+ app39.route("/api/onboarding", onboarding_default);
14508
+ app39.route("/api/admin", admin_default);
14509
+ app39.route("/api/access", access_default);
14328
14510
  var SAFE_SLUG_RE2 = /^[a-z][a-z0-9-]{2,49}$/;
14329
14511
  var SAFE_FILENAME_RE = /^[a-z0-9_][a-z0-9_.-]{0,99}$/i;
14330
14512
  var IMAGE_MIME = {
@@ -14336,7 +14518,7 @@ var IMAGE_MIME = {
14336
14518
  ".svg": "image/svg+xml",
14337
14519
  ".ico": "image/x-icon"
14338
14520
  };
14339
- app40.get("/agent-assets/:slug/:filename", (c) => {
14521
+ app39.get("/agent-assets/:slug/:filename", (c) => {
14340
14522
  const slug = c.req.param("slug");
14341
14523
  const filename = c.req.param("filename");
14342
14524
  if (!SAFE_SLUG_RE2.test(slug)) {
@@ -14358,7 +14540,7 @@ app40.get("/agent-assets/:slug/:filename", (c) => {
14358
14540
  console.error(`[agent-assets] path-traversal-rejected slug=${slug} file=${filename}`);
14359
14541
  return c.text("Forbidden", 403);
14360
14542
  }
14361
- if (!existsSync23(filePath)) {
14543
+ if (!existsSync22(filePath)) {
14362
14544
  console.error(`[agent-assets] serve slug=${slug} file=${filename} status=404`);
14363
14545
  return c.text("Not found", 404);
14364
14546
  }
@@ -14371,7 +14553,7 @@ app40.get("/agent-assets/:slug/:filename", (c) => {
14371
14553
  "Cache-Control": "public, max-age=3600"
14372
14554
  });
14373
14555
  });
14374
- app40.get("/generated/:filename", (c) => {
14556
+ app39.get("/generated/:filename", (c) => {
14375
14557
  const filename = c.req.param("filename");
14376
14558
  if (!SAFE_FILENAME_RE.test(filename) || filename.includes("..")) {
14377
14559
  console.error(`[generated] serve file=${filename} status=403`);
@@ -14388,7 +14570,7 @@ app40.get("/generated/:filename", (c) => {
14388
14570
  console.error(`[generated] serve file=${filename} status=403`);
14389
14571
  return c.text("Forbidden", 403);
14390
14572
  }
14391
- if (!existsSync23(filePath)) {
14573
+ if (!existsSync22(filePath)) {
14392
14574
  console.error(`[generated] serve file=${filename} status=404`);
14393
14575
  return c.text("Not found", 404);
14394
14576
  }
@@ -14401,14 +14583,14 @@ app40.get("/generated/:filename", (c) => {
14401
14583
  "Cache-Control": "public, max-age=86400"
14402
14584
  });
14403
14585
  });
14404
- app40.route("/sites", sites_default);
14405
- app40.route("/listings", listings_default);
14406
- app40.route("/v", visitor_event_default);
14407
- app40.route("/v", visitor_consent_default);
14586
+ app39.route("/sites", sites_default);
14587
+ app39.route("/listings", listings_default);
14588
+ app39.route("/v", visitor_event_default);
14589
+ app39.route("/v", visitor_consent_default);
14408
14590
  var htmlCache = /* @__PURE__ */ new Map();
14409
14591
  var brandLogoPath = "/brand/maxy-monochrome.png";
14410
14592
  var brandIconPath = "/brand/maxy-monochrome.png";
14411
- if (BRAND_JSON_PATH && existsSync23(BRAND_JSON_PATH)) {
14593
+ if (BRAND_JSON_PATH && existsSync22(BRAND_JSON_PATH)) {
14412
14594
  try {
14413
14595
  const fullBrand = JSON.parse(readFileSync20(BRAND_JSON_PATH, "utf-8"));
14414
14596
  if (fullBrand.assets?.logo) brandLogoPath = `/brand/${fullBrand.assets.logo}`;
@@ -14428,7 +14610,7 @@ function readInstalledVersion() {
14428
14610
  try {
14429
14611
  if (!PLATFORM_ROOT9) return "unknown";
14430
14612
  const versionFile = join15(PLATFORM_ROOT9, "config", `.${BRAND.hostname}-version`);
14431
- if (!existsSync23(versionFile)) return "unknown";
14613
+ if (!existsSync22(versionFile)) return "unknown";
14432
14614
  const content = readFileSync20(versionFile, "utf-8").trim();
14433
14615
  return content || "unknown";
14434
14616
  } catch {
@@ -14489,12 +14671,12 @@ function loadBrandingCache(agentSlug) {
14489
14671
  const configDir2 = join15(homedir2(), BRAND.configDir);
14490
14672
  try {
14491
14673
  const accountJsonPath = join15(configDir2, "account.json");
14492
- if (!existsSync23(accountJsonPath)) return null;
14674
+ if (!existsSync22(accountJsonPath)) return null;
14493
14675
  const account = JSON.parse(readFileSync20(accountJsonPath, "utf-8"));
14494
14676
  const accountId = account.accountId;
14495
14677
  if (!accountId) return null;
14496
14678
  const cachePath = join15(configDir2, "branding-cache", accountId, `${agentSlug}.json`);
14497
- if (!existsSync23(cachePath)) return null;
14679
+ if (!existsSync22(cachePath)) return null;
14498
14680
  return JSON.parse(readFileSync20(cachePath, "utf-8"));
14499
14681
  } catch {
14500
14682
  return null;
@@ -14504,7 +14686,7 @@ function resolveDefaultSlug() {
14504
14686
  try {
14505
14687
  const configDir2 = join15(homedir2(), BRAND.configDir);
14506
14688
  const accountJsonPath = join15(configDir2, "account.json");
14507
- if (!existsSync23(accountJsonPath)) return null;
14689
+ if (!existsSync22(accountJsonPath)) return null;
14508
14690
  const account = JSON.parse(readFileSync20(accountJsonPath, "utf-8"));
14509
14691
  return account.defaultAgent || null;
14510
14692
  } catch {
@@ -14541,7 +14723,7 @@ function brandedPublicHtml(agentSlug) {
14541
14723
  function escapeHtml(s) {
14542
14724
  return s.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
14543
14725
  }
14544
- app40.get("/", (c) => {
14726
+ app39.get("/", (c) => {
14545
14727
  const host = (c.req.header("host") ?? "").split(":")[0];
14546
14728
  if (isPublicHost(host)) {
14547
14729
  const defaultSlug = resolveDefaultSlug();
@@ -14549,12 +14731,12 @@ app40.get("/", (c) => {
14549
14731
  }
14550
14732
  return c.html(cachedHtml("index.html"));
14551
14733
  });
14552
- app40.get("/public", (c) => {
14734
+ app39.get("/public", (c) => {
14553
14735
  const host = (c.req.header("host") ?? "").split(":")[0];
14554
14736
  if (isPublicHost(host)) return c.text("Not found", 404);
14555
14737
  return c.html(cachedHtml("public.html"));
14556
14738
  });
14557
- app40.get("/chat", (c) => {
14739
+ app39.get("/chat", (c) => {
14558
14740
  const host = (c.req.header("host") ?? "").split(":")[0];
14559
14741
  if (isPublicHost(host)) return c.text("Not found", 404);
14560
14742
  return c.html(cachedHtml("public.html"));
@@ -14573,9 +14755,9 @@ async function logViewerFetch(c, next) {
14573
14755
  duration_ms: Date.now() - start
14574
14756
  });
14575
14757
  }
14576
- app40.use("/vnc-viewer.html", logViewerFetch);
14577
- app40.use("/vnc-popout.html", logViewerFetch);
14578
- app40.get("/vnc-popout.html", (c) => {
14758
+ app39.use("/vnc-viewer.html", logViewerFetch);
14759
+ app39.use("/vnc-popout.html", logViewerFetch);
14760
+ app39.get("/vnc-popout.html", (c) => {
14579
14761
  let html = htmlCache.get("vnc-popout.html");
14580
14762
  if (!html) {
14581
14763
  html = readFileSync20(resolve23(process.cwd(), "public", "vnc-popout.html"), "utf-8");
@@ -14588,7 +14770,7 @@ app40.get("/vnc-popout.html", (c) => {
14588
14770
  }
14589
14771
  return c.html(html);
14590
14772
  });
14591
- app40.post("/api/vnc/client-event", async (c) => {
14773
+ app39.post("/api/vnc/client-event", async (c) => {
14592
14774
  let body;
14593
14775
  try {
14594
14776
  body = await c.req.json();
@@ -14609,25 +14791,25 @@ app40.post("/api/vnc/client-event", async (c) => {
14609
14791
  });
14610
14792
  return c.json({ ok: true });
14611
14793
  });
14612
- app40.get("/g/:slug", (c) => {
14794
+ app39.get("/g/:slug", (c) => {
14613
14795
  return c.html(brandedPublicHtml());
14614
14796
  });
14615
- app40.get("/graph", (c) => {
14797
+ app39.get("/graph", (c) => {
14616
14798
  const host = (c.req.header("host") ?? "").split(":")[0];
14617
14799
  if (isPublicHost(host)) return c.text("Not found", 404);
14618
14800
  return c.html(cachedHtml("graph.html"));
14619
14801
  });
14620
- app40.get("/sessions", (c) => {
14802
+ app39.get("/sessions", (c) => {
14621
14803
  const host = (c.req.header("host") ?? "").split(":")[0];
14622
14804
  if (isPublicHost(host)) return c.text("Not found", 404);
14623
14805
  return c.html(cachedHtml("sessions.html"));
14624
14806
  });
14625
- app40.get("/data", (c) => {
14807
+ app39.get("/data", (c) => {
14626
14808
  const host = (c.req.header("host") ?? "").split(":")[0];
14627
14809
  if (isPublicHost(host)) return c.text("Not found", 404);
14628
14810
  return c.html(cachedHtml("data.html"));
14629
14811
  });
14630
- app40.get("/:slug", async (c, next) => {
14812
+ app39.get("/:slug", async (c, next) => {
14631
14813
  const slug = c.req.param("slug");
14632
14814
  if (AGENT_SLUG_PATTERN.test(`/${slug}`)) {
14633
14815
  const branding = loadBrandingCache(slug);
@@ -14637,15 +14819,15 @@ app40.get("/:slug", async (c, next) => {
14637
14819
  await next();
14638
14820
  });
14639
14821
  if (brandFaviconPath !== "/favicon.ico") {
14640
- app40.get("/favicon.ico", (c) => {
14822
+ app39.get("/favicon.ico", (c) => {
14641
14823
  c.header("Cache-Control", "public, max-age=300");
14642
14824
  return c.redirect(brandFaviconPath, 302);
14643
14825
  });
14644
14826
  }
14645
- app40.use("/*", serveStatic({ root: "./public" }));
14827
+ app39.use("/*", serveStatic({ root: "./public" }));
14646
14828
  var port = requirePortEnv("MAXY_UI_INTERNAL_PORT", { tag: "ui-server" });
14647
14829
  var hostname = process.env.HOSTNAME ?? "127.0.0.1";
14648
- var httpServer = serve({ fetch: app40.fetch, port, hostname });
14830
+ var httpServer = serve({ fetch: app39.fetch, port, hostname });
14649
14831
  console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
14650
14832
  {
14651
14833
  const loopHist = monitorEventLoopDelay({ resolution: 50 });
@@ -14681,7 +14863,7 @@ for (const m of SUBAPP_MANIFEST) {
14681
14863
  }
14682
14864
  try {
14683
14865
  const registered = [];
14684
- for (const r of app40.routes ?? []) {
14866
+ for (const r of app39.routes ?? []) {
14685
14867
  if (typeof r.path !== "string" || r.path.includes(":") || r.path.includes("*")) continue;
14686
14868
  if (AGENT_SLUG_PATTERN.test(r.path)) {
14687
14869
  registered.push({ method: (r.method ?? "ALL").toUpperCase(), path: r.path });
@@ -14703,7 +14885,7 @@ try {
14703
14885
  }
14704
14886
  (async () => {
14705
14887
  try {
14706
- if (!existsSync23(USERS_FILE)) return;
14888
+ if (!existsSync22(USERS_FILE)) return;
14707
14889
  const usersRaw = readFileSync20(USERS_FILE, "utf-8").trim();
14708
14890
  if (!usersRaw) return;
14709
14891
  const users = JSON.parse(usersRaw);
@@ -14720,6 +14902,9 @@ try {
14720
14902
  }
14721
14903
  })();
14722
14904
  startGraphHealthTimer();
14905
+ void startFileWatcher().catch((err) => {
14906
+ console.error(`[file-watcher] start-failed err="${err instanceof Error ? err.message : String(err)}"`);
14907
+ });
14723
14908
  try {
14724
14909
  const accounts = enumerateValidAccountIds(getAccountsDirFromEnv());
14725
14910
  console.error(`[graph-health] account-enumeration accounts=${accounts.length}`);
@@ -14738,7 +14923,7 @@ try {
14738
14923
  } catch (err) {
14739
14924
  console.error(`[graph-health] account-enumeration unavailable reason=${err instanceof Error ? err.message : String(err)}`);
14740
14925
  }
14741
- var configDirForWhatsApp = basename5(MAXY_DIR) || ".maxy";
14926
+ var configDirForWhatsApp = basename6(MAXY_DIR) || ".maxy";
14742
14927
  var bootAccount = resolveAccount();
14743
14928
  var bootAccountConfig = bootAccount?.config;
14744
14929
  var bootPublicAgent = bootAccount ? resolvePublicAgent(bootAccount.accountDir, { accountId: bootAccount.accountId })?.slug ?? null : null;