@rubytech/create-maxy 1.0.648 → 1.0.650

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 (143) hide show
  1. package/dist/index.js +65 -2
  2. package/package.json +1 -1
  3. package/payload/platform/plugins/docs/references/deployment.md +11 -0
  4. package/payload/platform/plugins/docs/references/getting-started.md +2 -0
  5. package/payload/platform/plugins/docs/references/memory-guide.md +2 -2
  6. package/payload/platform/plugins/docs/references/platform.md +6 -0
  7. package/payload/platform/plugins/docs/references/troubleshooting.md +16 -0
  8. package/payload/platform/plugins/memory/references/graph-primitives.md +5 -5
  9. package/payload/platform/templates/dotfiles/.tmux.conf +1 -0
  10. package/payload/platform/templates/systemd/maxy-ttyd.service +20 -0
  11. package/payload/server/public/assets/admin-S2KHPNe4.js +362 -0
  12. package/payload/server/public/assets/admin-kHJ-D0s7.css +1 -0
  13. package/payload/server/public/assets/{arc-DcrodP5U.js → arc-CRqJUbyK.js} +1 -1
  14. package/payload/server/public/assets/architecture-YZFGNWBL-CXIHKKCa.js +1 -0
  15. package/payload/server/public/assets/{architectureDiagram-Q4EWVU46-Cvo_8X6C.js → architectureDiagram-Q4EWVU46-DtICG195.js} +1 -1
  16. package/payload/server/public/assets/{blockDiagram-DXYQGD6D-CEK06TEn.js → blockDiagram-DXYQGD6D-nw1V7I38.js} +1 -1
  17. package/payload/server/public/assets/{c4Diagram-AHTNJAMY-QjYUSwTU.js → c4Diagram-AHTNJAMY-C1eEC43O.js} +1 -1
  18. package/payload/server/public/assets/channel-CA7njeKl.js +1 -0
  19. package/payload/server/public/assets/{chunk-2KRD3SAO-BBifNfFc.js → chunk-2KRD3SAO-Bybqj-wj.js} +1 -1
  20. package/payload/server/public/assets/{chunk-336JU56O-B-N_zWuf.js → chunk-336JU56O-Dszn2qEY.js} +2 -2
  21. package/payload/server/public/assets/chunk-426QAEUC-tWQOa3-I.js +1 -0
  22. package/payload/server/public/assets/{chunk-4BX2VUAB-CtDQKj9B.js → chunk-4BX2VUAB-DrKtrnWH.js} +1 -1
  23. package/payload/server/public/assets/{chunk-4TB4RGXK-Dnu9n3p1.js → chunk-4TB4RGXK-CBFzVYqS.js} +1 -1
  24. package/payload/server/public/assets/{chunk-55IACEB6-DSLoJJSj.js → chunk-55IACEB6-BNsOFSNf.js} +1 -1
  25. package/payload/server/public/assets/{chunk-5FUZZQ4R-rx-IvMNE.js → chunk-5FUZZQ4R-CXZykYh_.js} +1 -1
  26. package/payload/server/public/assets/{chunk-5PVQY5BW-B9w9AKCS.js → chunk-5PVQY5BW-CLNppenz.js} +1 -1
  27. package/payload/server/public/assets/{chunk-67CJDMHE-C1yEjtiu.js → chunk-67CJDMHE-DFyE0-n0.js} +1 -1
  28. package/payload/server/public/assets/{chunk-7N4EOEYR-C2f3zeVH.js → chunk-7N4EOEYR-BIKZD1_4.js} +1 -1
  29. package/payload/server/public/assets/{chunk-AA7GKIK3-B_U-NsDK.js → chunk-AA7GKIK3-D4_g24le.js} +1 -1
  30. package/payload/server/public/assets/{chunk-BSJP7CBP-DM6_wafW.js → chunk-BSJP7CBP-Cd9H-V61.js} +1 -1
  31. package/payload/server/public/assets/{chunk-CIAEETIT-DG7WkfNj.js → chunk-CIAEETIT-CAU9PIQi.js} +1 -1
  32. package/payload/server/public/assets/{chunk-EDXVE4YY-aS3_rdwQ.js → chunk-EDXVE4YY-CR1JfOwe.js} +1 -1
  33. package/payload/server/public/assets/{chunk-ENJZ2VHE-r1I0uoCf.js → chunk-ENJZ2VHE-CuXW3Isg.js} +1 -1
  34. package/payload/server/public/assets/{chunk-FMBD7UC4-CTm3YRE5.js → chunk-FMBD7UC4-BwGAtkIr.js} +1 -1
  35. package/payload/server/public/assets/{chunk-FOC6F5B3-CAIttx3K.js → chunk-FOC6F5B3-Cn0552qP.js} +1 -1
  36. package/payload/server/public/assets/{chunk-ICPOFSXX-DQFV4c1l.js → chunk-ICPOFSXX-DEZT2XyQ.js} +2 -2
  37. package/payload/server/public/assets/{chunk-K5T4RW27-B2WCPQBa.js → chunk-K5T4RW27-KwBFTzJ9.js} +1 -1
  38. package/payload/server/public/assets/{chunk-KGLVRYIC-C6w2sUOF.js → chunk-KGLVRYIC-1-3y582Z.js} +1 -1
  39. package/payload/server/public/assets/{chunk-LIHQZDEY-BT1hcDTK.js → chunk-LIHQZDEY-DXIBsDHL.js} +1 -1
  40. package/payload/server/public/assets/{chunk-ORNJ4GCN-Brl32BSe.js → chunk-ORNJ4GCN-CRbOike7.js} +1 -1
  41. package/payload/server/public/assets/{chunk-OYMX7WX6-BCO6n1CX.js → chunk-OYMX7WX6-CVT9itnY.js} +1 -1
  42. package/payload/server/public/assets/chunk-QZHKN3VN-BwkFBCAY.js +1 -0
  43. package/payload/server/public/assets/{chunk-U2HBQHQK-BvgC0fpb.js → chunk-U2HBQHQK-BLgNHWFf.js} +1 -1
  44. package/payload/server/public/assets/{chunk-X2U36JSP-3yLdcqYf.js → chunk-X2U36JSP-DHYLiYqc.js} +1 -1
  45. package/payload/server/public/assets/{chunk-XPW4576I-D876RWxK.js → chunk-XPW4576I-DBdiQ3Zy.js} +1 -1
  46. package/payload/server/public/assets/{chunk-YZCP3GAM-CiuA4hOC.js → chunk-YZCP3GAM-DXaosB5Z.js} +1 -1
  47. package/payload/server/public/assets/{chunk-ZZ45TVLE-COjEBPzv.js → chunk-ZZ45TVLE-B5dCmOpH.js} +1 -1
  48. package/payload/server/public/assets/classDiagram-6PBFFD2Q-CUZ9BU_6.js +1 -0
  49. package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-BGYsCDux.js +1 -0
  50. package/payload/server/public/assets/clone-BjouONkW.js +1 -0
  51. package/payload/server/public/assets/{cose-bilkent-S5V4N54A-Dap0yL0o.js → cose-bilkent-S5V4N54A-DaHtPQvk.js} +1 -1
  52. package/payload/server/public/assets/{dagre-KV5264BT-DEia9UJj.js → dagre-KV5264BT-CAL9V_HR.js} +1 -1
  53. package/payload/server/public/assets/{dagre-DBbjK-Cf.js → dagre-N8C5Xujx.js} +1 -1
  54. package/payload/server/public/assets/data-BajF3t1i.js +1 -0
  55. package/payload/server/public/assets/{diagram-5BDNPKRD-CWSP9MzJ.js → diagram-5BDNPKRD-pzBSPqlM.js} +1 -1
  56. package/payload/server/public/assets/{diagram-G4DWMVQ6-DWRsfitL.js → diagram-G4DWMVQ6-DStdLqos.js} +1 -1
  57. package/payload/server/public/assets/{diagram-MMDJMWI5-n-jyzS4D.js → diagram-MMDJMWI5-D-SfeX-6.js} +1 -1
  58. package/payload/server/public/assets/{diagram-TYMM5635-CLPTbfLq.js → diagram-TYMM5635-Cdr1DQ84.js} +1 -1
  59. package/payload/server/public/assets/{dist-Tkw8EOuG.js → dist-BKbAaes5.js} +1 -1
  60. package/payload/server/public/assets/{erDiagram-SMLLAGMA-CmPC9Cnc.js → erDiagram-SMLLAGMA-CIg1dDZT.js} +1 -1
  61. package/payload/server/public/assets/file-CRrDfnO1.js +1 -0
  62. package/payload/server/public/assets/{flatten-Db2kUB5j.js → flatten-CpKIi5d2.js} +1 -1
  63. package/payload/server/public/assets/{flowDiagram-DWJPFMVM-DKMNmUbX.js → flowDiagram-DWJPFMVM-CJtU1T6d.js} +1 -1
  64. package/payload/server/public/assets/{ganttDiagram-T4ZO3ILL-C5-y3w-l.js → ganttDiagram-T4ZO3ILL-R4fuRAT1.js} +1 -1
  65. package/payload/server/public/assets/gitGraph-7Q5UKJZL-tvzbaNdg.js +1 -0
  66. package/payload/server/public/assets/{gitGraphDiagram-UUTBAWPF-D9hG5kTg.js → gitGraphDiagram-UUTBAWPF-sifugSGn.js} +1 -1
  67. package/payload/server/public/assets/graph-CSBMZGGe.js +49 -0
  68. package/payload/server/public/assets/{graphlib-StP6GUhM.js → graphlib-DrlxPM8j.js} +1 -1
  69. package/payload/server/public/assets/house-D1CBraxB.js +1 -0
  70. package/payload/server/public/assets/info-OMHHGYJF-ByeBaFw5.js +1 -0
  71. package/payload/server/public/assets/infoDiagram-42DDH7IO-CjgCxerY.js +2 -0
  72. package/payload/server/public/assets/{isEmpty-CXH_nKTs.js → isEmpty-C3Vxk1It.js} +1 -1
  73. package/payload/server/public/assets/{ishikawaDiagram-UXIWVN3A--K4KOS61.js → ishikawaDiagram-UXIWVN3A-CYUJOA2c.js} +1 -1
  74. package/payload/server/public/assets/{journeyDiagram-VCZTEJTY-DE-28YrW.js → journeyDiagram-VCZTEJTY-UltrLajs.js} +1 -1
  75. package/payload/server/public/assets/{jsx-runtime-BQmd8XDE.css → jsx-runtime-CZtLX8NN.css} +1 -1
  76. package/payload/server/public/assets/{jsx-runtime-DwoXvzmf.js → jsx-runtime-Cb-WunFZ.js} +1 -1
  77. package/payload/server/public/assets/{kanban-definition-6JOO6SKY-CxSHjau2.js → kanban-definition-6JOO6SKY-BBaThtP3.js} +1 -1
  78. package/payload/server/public/assets/{line-DbcqYIG0.js → line-BhOwLD_o.js} +1 -1
  79. package/payload/server/public/assets/{linear-DXHoZSN3.js → linear-D76hoLvZ.js} +1 -1
  80. package/payload/server/public/assets/{mermaid-parser.core-CNGUA13J.js → mermaid-parser.core-D8n5xV7A.js} +2 -2
  81. package/payload/server/public/assets/{mermaid.core-IQgx_upQ.js → mermaid.core-C3TZA9fX.js} +3 -3
  82. package/payload/server/public/assets/{mindmap-definition-QFDTVHPH-BT9Up6-C.js → mindmap-definition-QFDTVHPH-Bv1kghvk.js} +1 -1
  83. package/payload/server/public/assets/{ordinal-CN3oz6oW.js → ordinal-BLrOss5K.js} +1 -1
  84. package/payload/server/public/assets/packet-4T2RLAQJ-Csybj5RO.js +1 -0
  85. package/payload/server/public/assets/pie-ZZUOXDRM-Iw1du1Bn.js +1 -0
  86. package/payload/server/public/assets/{pieDiagram-DEJITSTG-BuewQTi6.js → pieDiagram-DEJITSTG-BSYldcKa.js} +1 -1
  87. package/payload/server/public/assets/public-ukz9-gSe.js +5 -0
  88. package/payload/server/public/assets/{quadrantDiagram-34T5L4WZ-Cu8y2zQL.js → quadrantDiagram-34T5L4WZ-lCDshgz1.js} +1 -1
  89. package/payload/server/public/assets/radar-PYXPWWZC-rEet4TBV.js +1 -0
  90. package/payload/server/public/assets/{reduce-SDh8_UdG.js → reduce-C5tBOlxC.js} +1 -1
  91. package/payload/server/public/assets/{requirementDiagram-MS252O5E-DiT9bo27.js → requirementDiagram-MS252O5E-C7j42RrO.js} +1 -1
  92. package/payload/server/public/assets/{sankeyDiagram-XADWPNL6-EkRnGTxM.js → sankeyDiagram-XADWPNL6-plPbHhuF.js} +1 -1
  93. package/payload/server/public/assets/{sequenceDiagram-FGHM5R23-BUiQA3SI.js → sequenceDiagram-FGHM5R23-D3Y8MXiX.js} +1 -1
  94. package/payload/server/public/assets/share-2-DkF8neDM.js +1 -0
  95. package/payload/server/public/assets/{src-DQQCRlaQ.js → src-BvrHnOMG.js} +1 -1
  96. package/payload/server/public/assets/{stateDiagram-FHFEXIEX-Fij35Tic.js → stateDiagram-FHFEXIEX-D4BdhMPy.js} +1 -1
  97. package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-DhKxVkX3.js +1 -0
  98. package/payload/server/public/assets/{timeline-definition-GMOUNBTQ-BMsyr7wU.js → timeline-definition-GMOUNBTQ-Lh9jrYCl.js} +1 -1
  99. package/payload/server/public/assets/trash-2-Dde58AAR.js +1 -0
  100. package/payload/server/public/assets/treeView-SZITEDCU-PaLYyjtc.js +1 -0
  101. package/payload/server/public/assets/treemap-W4RFUUIX-CEhGYFbO.js +1 -0
  102. package/payload/server/public/assets/{useVoiceRecorder-C0Fvv_Bt.js → useVoiceRecorder-Hz9X6luB.js} +3 -3
  103. package/payload/server/public/assets/{vennDiagram-DHZGUBPP-6fegYFB3.js → vennDiagram-DHZGUBPP-Cx0v19iv.js} +1 -1
  104. package/payload/server/public/assets/wardley-RL74JXVD-xtJ4_o4d.js +1 -0
  105. package/payload/server/public/assets/{wardleyDiagram-NUSXRM2D-BtJ_B35h.js → wardleyDiagram-NUSXRM2D-BYewCTre.js} +1 -1
  106. package/payload/server/public/assets/x-D52qhSya.js +1 -0
  107. package/payload/server/public/assets/{xychartDiagram-5P7HB3ND-DJ20B4NY.js → xychartDiagram-5P7HB3ND-CkfIdbJu.js} +1 -1
  108. package/payload/server/public/data.html +8 -5
  109. package/payload/server/public/graph.html +19 -0
  110. package/payload/server/public/index.html +11 -7
  111. package/payload/server/public/public.html +7 -6
  112. package/payload/server/server.js +964 -737
  113. package/payload/server/public/assets/admin-uiqCo17I.js +0 -352
  114. package/payload/server/public/assets/architecture-YZFGNWBL-C38eyeNF.js +0 -1
  115. package/payload/server/public/assets/channel-D0dIwjlN.js +0 -1
  116. package/payload/server/public/assets/chunk-426QAEUC-C8oXXITm.js +0 -1
  117. package/payload/server/public/assets/chunk-QZHKN3VN-BE_lylks.js +0 -1
  118. package/payload/server/public/assets/classDiagram-6PBFFD2Q-DH37CWIF.js +0 -1
  119. package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-DNJ7bv8r.js +0 -1
  120. package/payload/server/public/assets/clone-rrGuX3ZR.js +0 -1
  121. package/payload/server/public/assets/data-D8kol1ed.js +0 -1
  122. package/payload/server/public/assets/gitGraph-7Q5UKJZL-CCjgA3FG.js +0 -1
  123. package/payload/server/public/assets/info-OMHHGYJF-B65K6dQJ.js +0 -1
  124. package/payload/server/public/assets/infoDiagram-42DDH7IO-DUJfTICr.js +0 -2
  125. package/payload/server/public/assets/packet-4T2RLAQJ-fp5ishAK.js +0 -1
  126. package/payload/server/public/assets/pie-ZZUOXDRM-Bc3VMuuU.js +0 -1
  127. package/payload/server/public/assets/public-CWuf8cLU.js +0 -5
  128. package/payload/server/public/assets/radar-PYXPWWZC-D9jy5QAa.js +0 -1
  129. package/payload/server/public/assets/share-2-wGga_ldi.js +0 -1
  130. package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-DQzhSd8K.js +0 -1
  131. package/payload/server/public/assets/treeView-SZITEDCU-CHyRL9e4.js +0 -1
  132. package/payload/server/public/assets/treemap-W4RFUUIX-DpQ_FOO6.js +0 -1
  133. package/payload/server/public/assets/wardley-RL74JXVD-CWBIAatW.js +0 -1
  134. /package/payload/server/public/assets/{_baseFor-D71p92tl.js → _baseFor-WfS9pKAn.js} +0 -0
  135. /package/payload/server/public/assets/{array-Bs_owIvv.js → array-HeX70jSN.js} +0 -0
  136. /package/payload/server/public/assets/{chunk-lgnzUk6H.js → chunk-DD-I1_y5.js} +0 -0
  137. /package/payload/server/public/assets/{cytoscape.esm-DLG5qhup.js → cytoscape.esm-CDZo0kst.js} +0 -0
  138. /package/payload/server/public/assets/{defaultLocale-Du_2bjyv.js → defaultLocale-GJwWH1Jr.js} +0 -0
  139. /package/payload/server/public/assets/{init-BYLBkHX_.js → init-BPLPMQ3Y.js} +0 -0
  140. /package/payload/server/public/assets/{katex-lkho_UhZ.js → katex-CKZ-HWMQ.js} +0 -0
  141. /package/payload/server/public/assets/{path-BO54iFkf.js → path-YdFzr2W6.js} +0 -0
  142. /package/payload/server/public/assets/{preload-helper-DWTEM3RW.js → preload-helper-BEFjQwLd.js} +0 -0
  143. /package/payload/server/public/assets/{rough.esm-BCiZEpQC.js → rough.esm-HAx67Hnb.js} +0 -0
@@ -1201,14 +1201,14 @@ var Hono = class _Hono {
1201
1201
  * app.route("/api", app2) // GET /api/user
1202
1202
  * ```
1203
1203
  */
1204
- route(path2, app29) {
1204
+ route(path2, app30) {
1205
1205
  const subApp = this.basePath(path2);
1206
- app29.routes.map((r) => {
1206
+ app30.routes.map((r) => {
1207
1207
  let handler;
1208
- if (app29.errorHandler === errorHandler) {
1208
+ if (app30.errorHandler === errorHandler) {
1209
1209
  handler = r.handler;
1210
1210
  } else {
1211
- handler = async (c, next) => (await compose([], app29.errorHandler)(c, () => r.handler(c, next))).res;
1211
+ handler = async (c, next) => (await compose([], app30.errorHandler)(c, () => r.handler(c, next))).res;
1212
1212
  handler[COMPOSED_HANDLER] = r.handler;
1213
1213
  }
1214
1214
  subApp.#addRoute(r.method, r.path, handler);
@@ -2526,7 +2526,7 @@ var responseViaResponseObject = async (res, outgoing, options = {}) => {
2526
2526
  });
2527
2527
  if (!chunk) {
2528
2528
  if (i === 1) {
2529
- await new Promise((resolve29) => setTimeout(resolve29));
2529
+ await new Promise((resolve30) => setTimeout(resolve30));
2530
2530
  maxReadCount = 3;
2531
2531
  continue;
2532
2532
  }
@@ -2892,8 +2892,8 @@ var serveStatic = (options = { root: "" }) => {
2892
2892
  };
2893
2893
 
2894
2894
  // server/index.ts
2895
- import { readFileSync as readFileSync26, existsSync as existsSync25, watchFile } from "fs";
2896
- import { resolve as resolve28, join as join13, basename as basename7 } from "path";
2895
+ import { readFileSync as readFileSync25, existsSync as existsSync24, watchFile } from "fs";
2896
+ import { resolve as resolve29, join as join13, basename as basename7 } from "path";
2897
2897
  import { homedir as homedir5 } from "os";
2898
2898
 
2899
2899
  // app/lib/vnc-logger.ts
@@ -2982,10 +2982,10 @@ var SCRYPT_R = 8;
2982
2982
  var SCRYPT_P = 1;
2983
2983
  var SCRYPT_KEYLEN = 64;
2984
2984
  function scryptAsync(password, salt) {
2985
- return new Promise((resolve29, reject) => {
2985
+ return new Promise((resolve30, reject) => {
2986
2986
  scrypt(password, salt, SCRYPT_KEYLEN, { N: SCRYPT_N, r: SCRYPT_R, p: SCRYPT_P }, (err, key) => {
2987
2987
  if (err) reject(err);
2988
- else resolve29(key);
2988
+ else resolve30(key);
2989
2989
  });
2990
2990
  });
2991
2991
  }
@@ -3826,9 +3826,33 @@ Content-Length: 0\r
3826
3826
  socket.destroy();
3827
3827
  }
3828
3828
 
3829
- // server/graph-proxy.ts
3829
+ // server/ws-proxy-terminal.ts
3830
3830
  import { createConnection as createConnection2 } from "net";
3831
- var GRAPH_PREFIX = "/graph";
3831
+
3832
+ // app/lib/terminal-logger.ts
3833
+ import { appendFileSync as appendFileSync2, mkdirSync as mkdirSync2 } from "fs";
3834
+ import { resolve as resolve3 } from "path";
3835
+ var TERMINAL_LOG_FILE = resolve3(LOG_DIR, "terminal.log");
3836
+ try {
3837
+ mkdirSync2(LOG_DIR, { recursive: true });
3838
+ } catch (err) {
3839
+ console.error(`[terminal-log-fail] mkdir ${LOG_DIR} failed: ${err.message}`);
3840
+ }
3841
+ function terminalLog(phase, fields = {}) {
3842
+ const ts = (/* @__PURE__ */ new Date()).toISOString();
3843
+ const kv = Object.entries(fields).map(([k, v]) => `${k}=${JSON.stringify(v)}`).join(" ");
3844
+ const line = kv.length > 0 ? `[${ts}] [terminal-${phase}] ${kv}
3845
+ ` : `[${ts}] [terminal-${phase}]
3846
+ `;
3847
+ try {
3848
+ appendFileSync2(TERMINAL_LOG_FILE, line);
3849
+ } catch (err) {
3850
+ console.error(`[terminal-log-fail] ${err.message} \u2014 dropped: ${line.slice(0, 300).trim()}`);
3851
+ }
3852
+ }
3853
+
3854
+ // server/ws-proxy-terminal.ts
3855
+ var WS_PATH2 = "/admin/terminal/ws";
3832
3856
  var UPSTREAM_TIMEOUT_MS2 = 5e3;
3833
3857
  var HOP_BY_HOP2 = /* @__PURE__ */ new Set([
3834
3858
  "connection",
@@ -3840,194 +3864,225 @@ var HOP_BY_HOP2 = /* @__PURE__ */ new Set([
3840
3864
  "transfer-encoding",
3841
3865
  "upgrade"
3842
3866
  ]);
3843
- var cachedUpstreamPort = null;
3844
- function resolveUpstreamPort() {
3845
- if (cachedUpstreamPort !== null) return cachedUpstreamPort;
3846
- const uri = process.env.NEO4J_URI;
3847
- if (!uri) {
3848
- throw new Error(
3849
- "[ui/graph-proxy] NEO4J_URI unset \u2014 refusing to default to bolt://localhost:7687 (Task 580)"
3850
- );
3851
- }
3852
- const m = uri.match(/:(\d+)$/);
3853
- const boltPort = m ? parseInt(m[1], 10) : 7687;
3854
- cachedUpstreamPort = boltPort - 213;
3855
- console.error(`[ui/graph-proxy] resolved neo4j_uri=${uri} http_port=${cachedUpstreamPort}`);
3856
- return cachedUpstreamPort;
3857
- }
3858
- var UPSTREAM_HOST = "127.0.0.1";
3859
- function attachGraphHttpRoutes(app29) {
3860
- const handler = async (c) => {
3861
- const raw2 = c.req.raw;
3862
- const url = new URL(raw2.url);
3863
- const upstreamPort = resolveUpstreamPort();
3864
- const pathAfterPrefix = url.pathname.slice(GRAPH_PREFIX.length) || "/";
3865
- const upstreamUrl = `http://${UPSTREAM_HOST}:${upstreamPort}${pathAfterPrefix}${url.search}`;
3866
- const upstreamHeaders = new Headers(raw2.headers);
3867
- for (const h of HOP_BY_HOP2) upstreamHeaders.delete(h);
3868
- upstreamHeaders.set("host", `${UPSTREAM_HOST}:${upstreamPort}`);
3869
- try {
3870
- const upstream = await fetch(upstreamUrl, {
3871
- method: raw2.method,
3872
- headers: upstreamHeaders,
3873
- body: raw2.body,
3874
- // `duplex: 'half'` is required when forwarding a streaming body; TS
3875
- // typings do not yet expose it but the undici runtime supports it.
3876
- ...raw2.body ? { duplex: "half" } : {},
3877
- redirect: "manual"
3878
- });
3879
- const resHeaders = new Headers(upstream.headers);
3880
- for (const h of HOP_BY_HOP2) resHeaders.delete(h);
3881
- const loc = resHeaders.get("location");
3882
- if (loc) {
3883
- const rewritten = rewriteLocation(loc);
3884
- if (rewritten !== loc) resHeaders.set("location", rewritten);
3885
- }
3886
- return new Response(upstream.body, {
3887
- status: upstream.status,
3888
- statusText: upstream.statusText,
3889
- headers: resHeaders
3890
- });
3891
- } catch (err) {
3892
- const msg = err instanceof Error ? err.message : String(err);
3893
- console.error(`[graph-proxy] upstream fetch failed for ${upstreamUrl}: ${msg}`);
3894
- return c.text(`Graph proxy upstream unreachable: ${msg}`, 502);
3895
- }
3896
- };
3897
- app29.all(GRAPH_PREFIX, handler);
3898
- app29.all(`${GRAPH_PREFIX}/*`, handler);
3899
- }
3900
- function attachGraphWsProxy(server, opts) {
3867
+ function attachTerminalWsProxy(server, opts) {
3868
+ const upstreamHost = opts.upstreamHost ?? "127.0.0.1";
3869
+ const upstreamPort = opts.upstreamPort ?? 7681;
3901
3870
  server.on("upgrade", (req, clientSocket, head) => {
3902
3871
  try {
3903
- const url = req.url ?? "";
3904
- const qsIndex = url.indexOf("?");
3905
- const pathname = qsIndex === -1 ? url : url.slice(0, qsIndex);
3906
- const isGraphPath = pathname === GRAPH_PREFIX || pathname.startsWith(`${GRAPH_PREFIX}/`);
3907
- if (!isGraphPath) {
3908
- if (pathname !== "/websockify") {
3909
- clientSocket.destroy();
3910
- }
3911
- return;
3912
- }
3913
- const hostHeader = (req.headers.host ?? "").split(":")[0];
3914
- const remote = req.socket.remoteAddress;
3915
- const xff = headerString2(req.headers["x-forwarded-for"]);
3916
- const cookie = headerString2(req.headers.cookie);
3917
- const decision = opts.canAccessAdmin({
3918
- host: hostHeader,
3919
- remoteAddress: remote,
3920
- xForwardedFor: xff,
3921
- cookieHeader: cookie,
3922
- isPublicHost: opts.isPublicHost
3923
- });
3924
- if (!decision.allow) {
3925
- const status = decision.reason === "public-host" ? 404 : 401;
3926
- writeStatusAndDestroy2(clientSocket, status, decision.reason === "public-host" ? "Not Found" : "Unauthorized");
3927
- return;
3928
- }
3929
- const originHeader = headerString2(req.headers.origin);
3930
- const originHost = parseOriginHost2(originHeader);
3931
- if (!originHost || originHost !== hostHeader || opts.isPublicHost(originHost)) {
3932
- writeStatusAndDestroy2(clientSocket, 403, "Forbidden");
3933
- return;
3934
- }
3935
- const rewrittenPath = pathname === GRAPH_PREFIX ? "/" : pathname.slice(GRAPH_PREFIX.length);
3936
- const rewrittenUrl = qsIndex === -1 ? rewrittenPath : rewrittenPath + url.slice(qsIndex);
3937
- const upstreamPort = resolveUpstreamPort();
3938
- const upstream = createConnection2({ host: UPSTREAM_HOST, port: upstreamPort });
3939
- upstream.setTimeout(UPSTREAM_TIMEOUT_MS2);
3940
- upstream.once("connect", () => {
3941
- upstream.setTimeout(0);
3942
- const lines = [];
3943
- lines.push(`${req.method ?? "GET"} ${rewrittenUrl} HTTP/${req.httpVersion}`);
3944
- lines.push(`host: ${UPSTREAM_HOST}:${upstreamPort}`);
3945
- for (const [name, value] of Object.entries(req.headers)) {
3946
- if (name === "host") continue;
3947
- if (HOP_BY_HOP2.has(name)) continue;
3948
- if (value == null) continue;
3949
- if (Array.isArray(value)) {
3950
- for (const v of value) lines.push(`${name}: ${v}`);
3951
- } else {
3952
- lines.push(`${name}: ${value}`);
3953
- }
3954
- }
3955
- const upgradeHeader = headerString2(req.headers.upgrade);
3956
- const connectionHeader = headerString2(req.headers.connection);
3957
- if (upgradeHeader) lines.push(`upgrade: ${upgradeHeader}`);
3958
- if (connectionHeader) lines.push(`connection: ${connectionHeader}`);
3959
- upstream.write(lines.join("\r\n") + "\r\n\r\n");
3960
- if (head && head.length > 0) upstream.write(head);
3961
- clientSocket.pipe(upstream);
3962
- upstream.pipe(clientSocket);
3963
- const teardown = () => {
3964
- clientSocket.destroy();
3965
- upstream.destroy();
3966
- };
3967
- clientSocket.once("close", teardown);
3968
- upstream.once("close", teardown);
3969
- clientSocket.once("error", teardown);
3970
- upstream.once("error", teardown);
3971
- });
3972
- upstream.once("timeout", () => {
3973
- writeStatusAndDestroy2(clientSocket, 504, "Gateway Timeout");
3974
- upstream.destroy();
3975
- });
3976
- upstream.once("error", (err) => {
3977
- console.error(`[graph-proxy] ws upstream error: ${err.message}`);
3978
- writeStatusAndDestroy2(clientSocket, 502, "Bad Gateway");
3979
- upstream.destroy();
3872
+ handleUpgrade2(req, clientSocket, head, {
3873
+ isPublicHost: opts.isPublicHost,
3874
+ upstreamHost,
3875
+ upstreamPort
3980
3876
  });
3981
3877
  } catch (err) {
3982
- console.error(`[graph-proxy] upgrade handler exception: ${err.message}`);
3878
+ terminalLog("ws-upgrade", {
3879
+ decision: "rejected",
3880
+ reason: "handler-exception",
3881
+ err: err.message
3882
+ });
3983
3883
  clientSocket.destroy();
3984
3884
  }
3985
3885
  });
3986
3886
  }
3987
- function graphAuthMiddleware(opts) {
3988
- return async (c, next) => {
3989
- const url = new URL(c.req.raw.url);
3990
- if (!url.pathname.startsWith(GRAPH_PREFIX)) return next();
3991
- const hostHeader = (c.req.header("host") ?? "").split(":")[0];
3992
- const incoming = c.env?.incoming;
3993
- const remote = incoming?.socket?.remoteAddress;
3994
- const xff = c.req.header("x-forwarded-for");
3995
- const cookie = c.req.header("cookie");
3996
- const decision = opts.canAccessAdmin({
3887
+ function handleUpgrade2(req, clientSocket, head, opts) {
3888
+ const url = req.url ?? "";
3889
+ const qsIndex = url.indexOf("?");
3890
+ const pathname = qsIndex === -1 ? url : url.slice(0, qsIndex);
3891
+ if (pathname !== WS_PATH2) {
3892
+ return;
3893
+ }
3894
+ const corrId = newCorrId();
3895
+ const query = qsIndex === -1 ? "" : url.slice(qsIndex + 1);
3896
+ const rawClientCorrId = parseQueryParam2(query, "corrId");
3897
+ const clientCorrId = sanitizeClientCorrId(rawClientCorrId);
3898
+ const hostHeader = (req.headers.host ?? "").split(":")[0];
3899
+ const originHeader = headerString2(req.headers.origin);
3900
+ const remote = req.socket.remoteAddress;
3901
+ const xff = headerString2(req.headers["x-forwarded-for"]);
3902
+ const decision = canAccessAdmin({
3903
+ host: hostHeader,
3904
+ remoteAddress: remote,
3905
+ xForwardedFor: xff,
3906
+ cookieHeader: headerString2(req.headers.cookie),
3907
+ isPublicHost: opts.isPublicHost
3908
+ });
3909
+ if (!decision.allow) {
3910
+ const status = decision.reason === "public-host" ? 404 : 401;
3911
+ terminalLog("ws-upgrade", {
3912
+ corrId,
3913
+ clientCorrId: clientCorrId ?? null,
3914
+ decision: "rejected",
3915
+ reason: decision.reason,
3916
+ ip: remote,
3917
+ xff: xff ?? null,
3918
+ origin: originHeader ?? null,
3919
+ host: hostHeader
3920
+ });
3921
+ writeStatusAndDestroy2(clientSocket, status, decision.reason === "public-host" ? "Not Found" : "Unauthorized");
3922
+ return;
3923
+ }
3924
+ const originHost = parseOriginHost2(originHeader);
3925
+ if (!originHost) {
3926
+ terminalLog("ws-upgrade", {
3927
+ corrId,
3928
+ clientCorrId: clientCorrId ?? null,
3929
+ decision: "rejected",
3930
+ reason: "origin-missing-or-invalid",
3931
+ origin: originHeader ?? null,
3997
3932
  host: hostHeader,
3998
- remoteAddress: remote,
3999
- xForwardedFor: xff,
4000
- cookieHeader: cookie,
4001
- isPublicHost: opts.isPublicHost
3933
+ ip: remote
4002
3934
  });
4003
- if (!decision.allow) {
4004
- return c.text(
4005
- decision.reason === "public-host" ? "Not Found" : "Unauthorized",
4006
- decision.reason === "public-host" ? 404 : 401
4007
- );
3935
+ writeStatusAndDestroy2(clientSocket, 403, "Forbidden");
3936
+ return;
3937
+ }
3938
+ if (originHost !== hostHeader) {
3939
+ terminalLog("ws-upgrade", {
3940
+ corrId,
3941
+ clientCorrId: clientCorrId ?? null,
3942
+ decision: "rejected",
3943
+ reason: "origin-mismatch",
3944
+ origin_host: originHost,
3945
+ host: hostHeader,
3946
+ ip: remote
3947
+ });
3948
+ writeStatusAndDestroy2(clientSocket, 403, "Forbidden");
3949
+ return;
3950
+ }
3951
+ if (opts.isPublicHost(originHost)) {
3952
+ terminalLog("ws-upgrade", {
3953
+ corrId,
3954
+ clientCorrId: clientCorrId ?? null,
3955
+ decision: "rejected",
3956
+ reason: "origin-public-host",
3957
+ origin_host: originHost,
3958
+ host: hostHeader,
3959
+ ip: remote
3960
+ });
3961
+ writeStatusAndDestroy2(clientSocket, 403, "Forbidden");
3962
+ return;
3963
+ }
3964
+ terminalLog("ws-upgrade", {
3965
+ corrId,
3966
+ clientCorrId: clientCorrId ?? null,
3967
+ decision: "accepted",
3968
+ ip: remote,
3969
+ xff: xff ?? null,
3970
+ origin: originHeader ?? null,
3971
+ host: hostHeader,
3972
+ sec_ws_version: headerString2(req.headers["sec-websocket-version"]) ?? null,
3973
+ sec_ws_protocol: headerString2(req.headers["sec-websocket-protocol"]) ?? null
3974
+ });
3975
+ const upstream = createConnection2({ host: opts.upstreamHost, port: opts.upstreamPort });
3976
+ upstream.setTimeout(UPSTREAM_TIMEOUT_MS2);
3977
+ let bytesClientToUpstream = 0;
3978
+ let bytesUpstreamToClient = 0;
3979
+ let closedBy = null;
3980
+ let proxyOpened = false;
3981
+ const finish = (side, reason) => {
3982
+ if (closedBy) return;
3983
+ closedBy = side;
3984
+ if (proxyOpened) {
3985
+ terminalLog("proxy-close", {
3986
+ corrId,
3987
+ closedBy: side,
3988
+ reason,
3989
+ clientBytes: bytesClientToUpstream,
3990
+ upstreamBytes: bytesUpstreamToClient
3991
+ });
4008
3992
  }
4009
- return next();
3993
+ clientSocket.destroy();
3994
+ upstream.destroy();
4010
3995
  };
3996
+ upstream.once("connect", () => {
3997
+ upstream.setTimeout(0);
3998
+ proxyOpened = true;
3999
+ terminalLog("proxy-open", {
4000
+ corrId,
4001
+ upstream: `${opts.upstreamHost}:${opts.upstreamPort}`
4002
+ });
4003
+ const lines = [];
4004
+ lines.push(`${req.method ?? "GET"} ${WS_PATH2} HTTP/${req.httpVersion}`);
4005
+ lines.push(`host: ${opts.upstreamHost}:${opts.upstreamPort}`);
4006
+ for (const [name, value] of Object.entries(req.headers)) {
4007
+ if (name === "host") continue;
4008
+ if (HOP_BY_HOP2.has(name)) continue;
4009
+ if (value == null) continue;
4010
+ if (Array.isArray(value)) {
4011
+ for (const v of value) lines.push(`${name}: ${v}`);
4012
+ } else {
4013
+ lines.push(`${name}: ${value}`);
4014
+ }
4015
+ }
4016
+ const upgradeHeader = headerString2(req.headers.upgrade);
4017
+ const connectionHeader = headerString2(req.headers.connection);
4018
+ if (upgradeHeader) lines.push(`upgrade: ${upgradeHeader}`);
4019
+ if (connectionHeader) lines.push(`connection: ${connectionHeader}`);
4020
+ upstream.write(lines.join("\r\n") + "\r\n\r\n");
4021
+ if (head && head.length > 0) upstream.write(head);
4022
+ clientSocket.on("data", (chunk) => {
4023
+ bytesClientToUpstream += chunk.length;
4024
+ });
4025
+ upstream.on("data", (chunk) => {
4026
+ bytesUpstreamToClient += chunk.length;
4027
+ });
4028
+ clientSocket.pipe(upstream);
4029
+ upstream.pipe(clientSocket);
4030
+ clientSocket.once("close", (hadError) => {
4031
+ finish("client", hadError ? "error" : "normal");
4032
+ });
4033
+ upstream.once("close", (hadError) => {
4034
+ finish("upstream", hadError ? "error" : "normal");
4035
+ });
4036
+ clientSocket.once("error", (err) => {
4037
+ terminalLog("proxy-error", { corrId, side: "client", err: err.message });
4038
+ finish("client", "error");
4039
+ });
4040
+ upstream.once("error", (err) => {
4041
+ terminalLog("proxy-error", { corrId, side: "upstream", err: err.message });
4042
+ finish("upstream", "error");
4043
+ });
4044
+ });
4045
+ upstream.once("timeout", () => {
4046
+ if (proxyOpened) return;
4047
+ terminalLog("proxy-error", {
4048
+ corrId,
4049
+ side: "upstream-connect",
4050
+ err: "timeout",
4051
+ timeout_ms: UPSTREAM_TIMEOUT_MS2
4052
+ });
4053
+ writeStatusAndDestroy2(clientSocket, 504, "Gateway Timeout");
4054
+ upstream.destroy();
4055
+ });
4056
+ upstream.once("error", (err) => {
4057
+ if (proxyOpened) return;
4058
+ terminalLog("proxy-error", {
4059
+ corrId,
4060
+ side: "upstream-connect",
4061
+ err: err.message
4062
+ });
4063
+ writeStatusAndDestroy2(clientSocket, 502, "Bad Gateway");
4064
+ upstream.destroy();
4065
+ });
4066
+ }
4067
+ function parseQueryParam2(query, key) {
4068
+ if (!query) return null;
4069
+ for (const pair of query.split("&")) {
4070
+ const eq = pair.indexOf("=");
4071
+ const k = eq === -1 ? pair : pair.slice(0, eq);
4072
+ if (k !== key) continue;
4073
+ const v = eq === -1 ? "" : pair.slice(eq + 1);
4074
+ try {
4075
+ return decodeURIComponent(v);
4076
+ } catch {
4077
+ return null;
4078
+ }
4079
+ }
4080
+ return null;
4011
4081
  }
4012
4082
  function headerString2(value) {
4013
4083
  if (value == null) return void 0;
4014
4084
  return Array.isArray(value) ? value[0] : value;
4015
4085
  }
4016
- function rewriteLocation(location) {
4017
- try {
4018
- const parsed = new URL(location);
4019
- if (parsed.hostname === UPSTREAM_HOST && parsed.port === String(resolveUpstreamPort())) {
4020
- const pathWithQuery = parsed.pathname + parsed.search + parsed.hash;
4021
- return pathWithQuery.startsWith(GRAPH_PREFIX) ? pathWithQuery : `${GRAPH_PREFIX}${pathWithQuery}`;
4022
- }
4023
- return location;
4024
- } catch {
4025
- if (location.startsWith("/") && !location.startsWith(`${GRAPH_PREFIX}/`) && location !== GRAPH_PREFIX) {
4026
- return `${GRAPH_PREFIX}${location}`;
4027
- }
4028
- return location;
4029
- }
4030
- }
4031
4086
  function parseOriginHost2(origin) {
4032
4087
  if (!origin) return null;
4033
4088
  try {
@@ -4057,9 +4112,9 @@ var AGENT_SLUG_PATTERN = /^\/([a-z][a-z0-9-]{2,49})$/;
4057
4112
  import Anthropic2 from "@anthropic-ai/sdk";
4058
4113
  import { spawn as spawn2, spawnSync as spawnSync2 } from "child_process";
4059
4114
  import { randomUUID as randomUUID2 } from "crypto";
4060
- import { resolve as resolve6, join as join4 } from "path";
4115
+ import { resolve as resolve7, join as join4 } from "path";
4061
4116
  import { platform as osPlatform } from "os";
4062
- import { readFileSync as readFileSync7, writeFileSync as writeFileSync5, readdirSync as readdirSync2, existsSync as existsSync6, mkdirSync as mkdirSync4, createWriteStream, statSync as statSync3, unlinkSync as unlinkSync2, cpSync, rmSync as rmSync2, appendFileSync as appendFileSync2, openSync as openSync2, readSync as readSync2, closeSync as closeSync2 } from "fs";
4117
+ import { readFileSync as readFileSync7, writeFileSync as writeFileSync5, readdirSync as readdirSync2, existsSync as existsSync6, mkdirSync as mkdirSync5, createWriteStream, statSync as statSync3, unlinkSync as unlinkSync2, cpSync, rmSync as rmSync2, appendFileSync as appendFileSync3, openSync as openSync2, readSync as readSync2, closeSync as closeSync2 } from "fs";
4063
4118
  import { lookup as dnsLookup } from "dns/promises";
4064
4119
  import { createConnection as netConnect } from "net";
4065
4120
  import { StringDecoder } from "string_decoder";
@@ -4068,12 +4123,12 @@ import { StringDecoder } from "string_decoder";
4068
4123
  var import_dist = __toESM(require_dist());
4069
4124
  import {
4070
4125
  existsSync as existsSync4,
4071
- mkdirSync as mkdirSync2,
4126
+ mkdirSync as mkdirSync3,
4072
4127
  readFileSync as readFileSync3,
4073
4128
  unlinkSync,
4074
4129
  writeFileSync as writeFileSync2
4075
4130
  } from "fs";
4076
- import { resolve as resolve3, join as join3, dirname } from "path";
4131
+ import { resolve as resolve4, join as join3, dirname } from "path";
4077
4132
  import { homedir as homedir2 } from "os";
4078
4133
  var cachedKeyFilePath = null;
4079
4134
  function resolveKeyFilePath() {
@@ -4104,7 +4159,7 @@ function resolveKeyFilePath() {
4104
4159
  throw err;
4105
4160
  }
4106
4161
  }
4107
- cachedKeyFilePath = resolve3(homedir2(), configDirName2, ".anthropic-api-key");
4162
+ cachedKeyFilePath = resolve4(homedir2(), configDirName2, ".anthropic-api-key");
4108
4163
  return cachedKeyFilePath;
4109
4164
  }
4110
4165
  function readKey() {
@@ -4391,10 +4446,10 @@ async function ensureAuth() {
4391
4446
  // app/lib/vnc.ts
4392
4447
  import { spawnSync, execFileSync } from "child_process";
4393
4448
  import { createConnection as createConnection3 } from "net";
4394
- import { mkdirSync as mkdirSync3, readFileSync as readFileSync5, writeFileSync as writeFileSync4 } from "fs";
4395
- import { resolve as resolve4 } from "path";
4396
- var PLATFORM_ROOT2 = process.env.MAXY_PLATFORM_ROOT ?? resolve4(process.cwd(), "..");
4397
- var VNC_SCRIPT = resolve4(PLATFORM_ROOT2, "scripts/vnc.sh");
4449
+ import { mkdirSync as mkdirSync4, readFileSync as readFileSync5, writeFileSync as writeFileSync4 } from "fs";
4450
+ import { resolve as resolve5 } from "path";
4451
+ var PLATFORM_ROOT2 = process.env.MAXY_PLATFORM_ROOT ?? resolve5(process.cwd(), "..");
4452
+ var VNC_SCRIPT = resolve5(PLATFORM_ROOT2, "scripts/vnc.sh");
4398
4453
  var displayMode = process.env.DISPLAY_MODE ?? "virtual";
4399
4454
  if (displayMode === "native") {
4400
4455
  console.log(`[vnc] DISPLAY_MODE=native \u2014 local requests use desktop display, remote requests use VNC`);
@@ -4506,10 +4561,10 @@ async function waitForPort(port2, timeoutMs = 12e3) {
4506
4561
  return false;
4507
4562
  }
4508
4563
  function ensureLogDir() {
4509
- mkdirSync3(LOG_DIR, { recursive: true });
4564
+ mkdirSync4(LOG_DIR, { recursive: true });
4510
4565
  }
4511
4566
  function logPath(name) {
4512
- return resolve4(LOG_DIR, `${name}.log`);
4567
+ return resolve5(LOG_DIR, `${name}.log`);
4513
4568
  }
4514
4569
  async function ensureVnc() {
4515
4570
  const up = await waitForPort(5900, 1e3);
@@ -4587,8 +4642,8 @@ function killChromium() {
4587
4642
  spawnSync("pkill", ["-f", "chromium"], { stdio: "pipe" });
4588
4643
  }
4589
4644
  function writeChromiumWrapper() {
4590
- mkdirSync3(BIN_DIR, { recursive: true });
4591
- const wrapperPath = resolve4(BIN_DIR, "chromium");
4645
+ mkdirSync4(BIN_DIR, { recursive: true });
4646
+ const wrapperPath = resolve5(BIN_DIR, "chromium");
4592
4647
  writeFileSync4(wrapperPath, `#!/bin/bash
4593
4648
  LOG="${LOG_DIR}/chromium.log"
4594
4649
  echo "==== [$(date)] chromium wrapper ====" >> "$LOG"
@@ -4674,12 +4729,12 @@ import neo4j from "neo4j-driver";
4674
4729
  import { randomUUID } from "crypto";
4675
4730
  import { spawn } from "child_process";
4676
4731
  import { readFileSync as readFileSync6, readdirSync, existsSync as existsSync5, openSync, readSync, closeSync, statSync as statSync2, rmSync } from "fs";
4677
- import { resolve as resolve5 } from "path";
4678
- var PLATFORM_ROOT3 = process.env.MAXY_PLATFORM_ROOT ?? resolve5(process.cwd(), "..");
4732
+ import { resolve as resolve6 } from "path";
4733
+ var PLATFORM_ROOT3 = process.env.MAXY_PLATFORM_ROOT ?? resolve6(process.cwd(), "..");
4679
4734
  var driver = null;
4680
4735
  function readPassword() {
4681
4736
  if (process.env.NEO4J_PASSWORD) return process.env.NEO4J_PASSWORD;
4682
- const passwordFile = resolve5(PLATFORM_ROOT3, "config/.neo4j-password");
4737
+ const passwordFile = resolve6(PLATFORM_ROOT3, "config/.neo4j-password");
4683
4738
  try {
4684
4739
  return readFileSync6(passwordFile, "utf-8").trim();
4685
4740
  } catch {
@@ -5355,7 +5410,7 @@ ${userContent}`;
5355
5410
  "dontAsk",
5356
5411
  prompt
5357
5412
  ];
5358
- return new Promise((resolve29) => {
5413
+ return new Promise((resolve30) => {
5359
5414
  let stdout = "";
5360
5415
  let stderr = "";
5361
5416
  const spawnFn = _spawnOverride ?? spawn;
@@ -5373,35 +5428,35 @@ ${userContent}`;
5373
5428
  const timer = setTimeout(() => {
5374
5429
  proc.kill("SIGTERM");
5375
5430
  console.error("[persist] autoLabel: haiku subprocess timed out");
5376
- resolve29(null);
5431
+ resolve30(null);
5377
5432
  }, SESSION_LABEL_TIMEOUT_MS);
5378
5433
  proc.on("error", (err) => {
5379
5434
  clearTimeout(timer);
5380
5435
  console.error(`[persist] autoLabel: subprocess error \u2014 ${err.message}`);
5381
- resolve29(null);
5436
+ resolve30(null);
5382
5437
  });
5383
5438
  proc.on("close", (code) => {
5384
5439
  clearTimeout(timer);
5385
5440
  if (code !== 0) {
5386
5441
  console.error(`[persist] autoLabel: subprocess exited code=${code}${stderr ? ` stderr=${stderr.trim().slice(0, 200)}` : ""}`);
5387
- resolve29(null);
5442
+ resolve30(null);
5388
5443
  return;
5389
5444
  }
5390
5445
  const text = stdout.trim();
5391
5446
  if (!text) {
5392
5447
  console.error("[persist] autoLabel: haiku returned empty response");
5393
- resolve29(null);
5448
+ resolve30(null);
5394
5449
  return;
5395
5450
  }
5396
5451
  if (text === "SKIP") {
5397
5452
  console.error("[persist] autoLabel: haiku returned SKIP \u2014 messages too vague");
5398
- resolve29(null);
5453
+ resolve30(null);
5399
5454
  return;
5400
5455
  }
5401
5456
  const words = text.split(/\s+/).slice(0, SESSION_LABEL_MAX_WORDS);
5402
5457
  const label = words.join(" ");
5403
5458
  console.error(`[persist] autoLabel: haiku response="${label}"`);
5404
- resolve29(label);
5459
+ resolve30(label);
5405
5460
  });
5406
5461
  });
5407
5462
  }
@@ -5673,9 +5728,9 @@ var MAX_RECENT_TOOL_FAILURES = 3;
5673
5728
  var RECENT_FAILURES_TAIL_BYTES = 10 * 1024;
5674
5729
  function readRecentToolFailures(accountId, conversationId) {
5675
5730
  try {
5676
- const platformRoot3 = process.env.MAXY_PLATFORM_ROOT ?? resolve5(process.cwd(), "..");
5677
- const logDir = resolve5(platformRoot3, "..", "data/accounts", accountId, "logs");
5678
- const logPath2 = resolve5(logDir, `claude-agent-stream-${conversationId}.log`);
5731
+ const platformRoot3 = process.env.MAXY_PLATFORM_ROOT ?? resolve6(process.cwd(), "..");
5732
+ const logDir = resolve6(platformRoot3, "..", "data/accounts", accountId, "logs");
5733
+ const logPath2 = resolve6(logDir, `claude-agent-stream-${conversationId}.log`);
5679
5734
  if (!existsSync5(logPath2)) {
5680
5735
  console.error(`[review-tail-skip] path=${logPath2} reason=file-missing \u2014 first turn of conversation, subprocess not yet spawned, or log rotated`);
5681
5736
  return [];
@@ -5832,13 +5887,13 @@ ${taskLines.join("\n")}`);
5832
5887
  let pendingCount = 0;
5833
5888
  let pendingLines = [];
5834
5889
  try {
5835
- const platformRoot3 = process.env.MAXY_PLATFORM_ROOT ?? resolve5(process.cwd(), "..");
5836
- const pendingDir = resolve5(platformRoot3, "..", "data/accounts", accountId, "pending-actions");
5890
+ const platformRoot3 = process.env.MAXY_PLATFORM_ROOT ?? resolve6(process.cwd(), "..");
5891
+ const pendingDir = resolve6(platformRoot3, "..", "data/accounts", accountId, "pending-actions");
5837
5892
  if (existsSync5(pendingDir)) {
5838
5893
  const files = readdirSync(pendingDir).filter((f) => f.endsWith(".json") && !f.startsWith("."));
5839
5894
  for (const file of files) {
5840
5895
  try {
5841
- const raw2 = readFileSync6(resolve5(pendingDir, file), "utf-8");
5896
+ const raw2 = readFileSync6(resolve6(pendingDir, file), "utf-8");
5842
5897
  const action = JSON.parse(raw2);
5843
5898
  if (action.state === "pending") {
5844
5899
  const inputSummary = JSON.stringify(action.hookPayload?.tool_input ?? {}).slice(0, 150);
@@ -5905,8 +5960,8 @@ ${sections.join("\n\n")}
5905
5960
  }
5906
5961
  }
5907
5962
  async function consumeStep7FlagUI(session, accountId) {
5908
- const accountDir = resolve5(PLATFORM_ROOT3, "..", "data/accounts", accountId);
5909
- const flagPath = resolve5(accountDir, "onboarding", "step7-complete");
5963
+ const accountDir = resolve6(PLATFORM_ROOT3, "..", "data/accounts", accountId);
5964
+ const flagPath = resolve6(accountDir, "onboarding", "step7-complete");
5910
5965
  if (!existsSync5(flagPath)) return false;
5911
5966
  let completedAt = (/* @__PURE__ */ new Date()).toISOString();
5912
5967
  try {
@@ -6487,20 +6542,20 @@ function agentLogStream(name, accountDir, conversationId) {
6487
6542
  if (!conversationId) {
6488
6543
  throw new Error(`agentLogStream: conversationId is required (name=${name}) \u2014 use preConversationLogStream for pre-session events`);
6489
6544
  }
6490
- const logDir = resolve6(accountDir, "logs");
6491
- mkdirSync4(logDir, { recursive: true });
6545
+ const logDir = resolve7(accountDir, "logs");
6546
+ mkdirSync5(logDir, { recursive: true });
6492
6547
  purgeOldLogs(logDir, `${name}-`);
6493
- const logPath2 = resolve6(logDir, `${name}-${conversationId}.log`);
6548
+ const logPath2 = resolve7(logDir, `${name}-${conversationId}.log`);
6494
6549
  const stream = createWriteStream(logPath2, { flags: "a" });
6495
6550
  registerStreamLog(stream, { path: logPath2, conversationId, name });
6496
6551
  return stream;
6497
6552
  }
6498
6553
  function preConversationLogStream(name, accountDir) {
6499
- const logDir = resolve6(accountDir, "logs");
6500
- mkdirSync4(logDir, { recursive: true });
6554
+ const logDir = resolve7(accountDir, "logs");
6555
+ mkdirSync5(logDir, { recursive: true });
6501
6556
  const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
6502
6557
  purgeOldLogs(logDir, `preconversation-${name}-`);
6503
- const logPath2 = resolve6(logDir, `preconversation-${name}-${date}.log`);
6558
+ const logPath2 = resolve7(logDir, `preconversation-${name}-${date}.log`);
6504
6559
  const stream = createWriteStream(logPath2, { flags: "a" });
6505
6560
  registerStreamLog(stream, { path: logPath2, conversationId: null, name: `preconversation-${name}` });
6506
6561
  return stream;
@@ -6519,7 +6574,7 @@ function sigtermFlushStreamLogs(reason, source) {
6519
6574
  const line = `[${ts}] [server-sigterm] reason=${reason}${convPart} name=${entry.name} source=${source}
6520
6575
  `;
6521
6576
  try {
6522
- appendFileSync2(entry.path, line);
6577
+ appendFileSync3(entry.path, line);
6523
6578
  } catch (err) {
6524
6579
  const msg = err instanceof Error ? err.message : String(err);
6525
6580
  console.error(`[server-sigterm-flush-err] path=${entry.path} reason=${msg}`);
@@ -6538,7 +6593,7 @@ function purgeOldLogs(logDir, prefix) {
6538
6593
  }
6539
6594
  for (const file of entries) {
6540
6595
  if (!file.startsWith(prefix)) continue;
6541
- const filePath = resolve6(logDir, file);
6596
+ const filePath = resolve7(logDir, file);
6542
6597
  try {
6543
6598
  if (statSync3(filePath).mtimeMs < cutoff) unlinkSync2(filePath);
6544
6599
  } catch (err) {
@@ -6656,8 +6711,8 @@ function sampleProcState(pid) {
6656
6711
  return `proc_err=${JSON.stringify(msg.slice(0, 60))}`;
6657
6712
  }
6658
6713
  }
6659
- var PLATFORM_ROOT4 = process.env.MAXY_PLATFORM_ROOT ?? resolve6(process.cwd(), "..");
6660
- var ACCOUNTS_DIR = resolve6(PLATFORM_ROOT4, "..", "data/accounts");
6714
+ var PLATFORM_ROOT4 = process.env.MAXY_PLATFORM_ROOT ?? resolve7(process.cwd(), "..");
6715
+ var ACCOUNTS_DIR = resolve7(PLATFORM_ROOT4, "..", "data/accounts");
6661
6716
  if (!existsSync6(PLATFORM_ROOT4)) {
6662
6717
  throw new Error(
6663
6718
  `PLATFORM_ROOT does not exist: ${PLATFORM_ROOT4}
@@ -6666,7 +6721,7 @@ Set the MAXY_PLATFORM_ROOT environment variable to the absolute path of the plat
6666
6721
  }
6667
6722
  function resolveAccount() {
6668
6723
  if (!existsSync6(ACCOUNTS_DIR)) return null;
6669
- const usersFilePath = resolve6(PLATFORM_ROOT4, "config", "users.json");
6724
+ const usersFilePath = resolve7(PLATFORM_ROOT4, "config", "users.json");
6670
6725
  let usersJsonUserId = null;
6671
6726
  if (existsSync6(usersFilePath)) {
6672
6727
  try {
@@ -6684,7 +6739,7 @@ function resolveAccount() {
6684
6739
  let fallback = null;
6685
6740
  for (const entry of entries) {
6686
6741
  if (!entry.isDirectory()) continue;
6687
- const configPath2 = resolve6(ACCOUNTS_DIR, entry.name, "account.json");
6742
+ const configPath2 = resolve7(ACCOUNTS_DIR, entry.name, "account.json");
6688
6743
  if (!existsSync6(configPath2)) continue;
6689
6744
  const raw2 = readFileSync7(configPath2, "utf-8");
6690
6745
  let config;
@@ -6701,7 +6756,7 @@ function resolveAccount() {
6701
6756
  }
6702
6757
  const result = {
6703
6758
  accountId: config.accountId,
6704
- accountDir: resolve6(ACCOUNTS_DIR, entry.name),
6759
+ accountDir: resolve7(ACCOUNTS_DIR, entry.name),
6705
6760
  config
6706
6761
  };
6707
6762
  if (usersJsonUserId && config.admins?.some((a) => a.userId === usersJsonUserId)) {
@@ -6719,7 +6774,7 @@ function resolveAccount() {
6719
6774
  return fallback;
6720
6775
  }
6721
6776
  function readAgentFile(accountDir, agentName, filename) {
6722
- const filePath = resolve6(accountDir, "agents", agentName, filename);
6777
+ const filePath = resolve7(accountDir, "agents", agentName, filename);
6723
6778
  if (!existsSync6(filePath)) return null;
6724
6779
  return readFileSync7(filePath, "utf-8");
6725
6780
  }
@@ -6737,7 +6792,7 @@ function validateAgentSlug(slug) {
6737
6792
  return true;
6738
6793
  }
6739
6794
  function resolveDefaultAgentSlug(accountDir) {
6740
- const configPath2 = resolve6(accountDir, "account.json");
6795
+ const configPath2 = resolve7(accountDir, "account.json");
6741
6796
  if (!existsSync6(configPath2)) {
6742
6797
  console.error("[agent-resolve] account.json not found \u2014 cannot resolve defaultAgent");
6743
6798
  return null;
@@ -6753,7 +6808,7 @@ function resolveDefaultAgentSlug(accountDir) {
6753
6808
  console.error("[agent-resolve] defaultAgent not configured in account.json \u2014 set it via the connect-whatsapp skill");
6754
6809
  return null;
6755
6810
  }
6756
- const agentConfigPath = resolve6(accountDir, "agents", config.defaultAgent, "config.json");
6811
+ const agentConfigPath = resolve7(accountDir, "agents", config.defaultAgent, "config.json");
6757
6812
  if (!existsSync6(agentConfigPath)) {
6758
6813
  console.error(`[agent-resolve] defaultAgent="${config.defaultAgent}" has no config.json at ${agentConfigPath}`);
6759
6814
  return null;
@@ -6826,9 +6881,9 @@ function resolveAgentConfig(accountDir, agentName) {
6826
6881
  }
6827
6882
  let knowledge = null;
6828
6883
  let knowledgeBaked = false;
6829
- const agentDir = resolve6(accountDir, "agents", agentName);
6830
- const knowledgePath = resolve6(agentDir, "KNOWLEDGE.md");
6831
- const summaryPath = resolve6(agentDir, "KNOWLEDGE-SUMMARY.md");
6884
+ const agentDir = resolve7(accountDir, "agents", agentName);
6885
+ const knowledgePath = resolve7(agentDir, "KNOWLEDGE.md");
6886
+ const summaryPath = resolve7(agentDir, "KNOWLEDGE-SUMMARY.md");
6832
6887
  const hasKnowledge = existsSync6(knowledgePath);
6833
6888
  const hasSummary = existsSync6(summaryPath);
6834
6889
  if (hasKnowledge && hasSummary) {
@@ -6864,7 +6919,7 @@ function resolveAgentConfig(accountDir, agentName) {
6864
6919
  return { model, plugins, status, displayName, image, imageShape, showAgentName, knowledge, knowledgeBaked, liveMemory, knowledgeKeywords, budget, accessMode };
6865
6920
  }
6866
6921
  function parsePluginFrontmatter(pluginDir) {
6867
- const pluginPath = resolve6(PLATFORM_ROOT4, "plugins", pluginDir, "PLUGIN.md");
6922
+ const pluginPath = resolve7(PLATFORM_ROOT4, "plugins", pluginDir, "PLUGIN.md");
6868
6923
  if (!existsSync6(pluginPath)) return null;
6869
6924
  let raw2;
6870
6925
  try {
@@ -6927,14 +6982,14 @@ function parsePluginFrontmatter(pluginDir) {
6927
6982
  function autoDeliverPremiumPlugins(purchasedPlugins) {
6928
6983
  if (!purchasedPlugins || purchasedPlugins.length === 0) return;
6929
6984
  const TAG18 = "[premium-auto-deliver]";
6930
- const stagingRoot = resolve6(PLATFORM_ROOT4, "../premium-plugins");
6931
- const pluginsDir = resolve6(PLATFORM_ROOT4, "plugins");
6985
+ const stagingRoot = resolve7(PLATFORM_ROOT4, "../premium-plugins");
6986
+ const pluginsDir = resolve7(PLATFORM_ROOT4, "plugins");
6932
6987
  if (!existsSync6(stagingRoot)) {
6933
6988
  console.log(`${TAG18} no staging directory \u2014 skipping`);
6934
6989
  return;
6935
6990
  }
6936
6991
  for (const pluginName of purchasedPlugins) {
6937
- const stagingDir = resolve6(stagingRoot, pluginName);
6992
+ const stagingDir = resolve7(stagingRoot, pluginName);
6938
6993
  if (!existsSync6(stagingDir)) {
6939
6994
  console.log(`${TAG18} ${pluginName}: not in staging \u2014 skipping`);
6940
6995
  continue;
@@ -6975,12 +7030,12 @@ function autoDeliverPremiumPlugins(purchasedPlugins) {
6975
7030
  let delivered = 0;
6976
7031
  let skipped = 0;
6977
7032
  for (const sub of subPlugins) {
6978
- const target = resolve6(pluginsDir, sub);
6979
- if (existsSync6(resolve6(target, "PLUGIN.md"))) {
7033
+ const target = resolve7(pluginsDir, sub);
7034
+ if (existsSync6(resolve7(target, "PLUGIN.md"))) {
6980
7035
  skipped++;
6981
7036
  continue;
6982
7037
  }
6983
- const source = resolve6(stagingDir, "plugins", sub);
7038
+ const source = resolve7(stagingDir, "plugins", sub);
6984
7039
  if (!existsSync6(source)) {
6985
7040
  console.log(`${TAG18} ${pluginName}/${sub}: source missing in staging \u2014 skipping`);
6986
7041
  continue;
@@ -6994,8 +7049,8 @@ function autoDeliverPremiumPlugins(purchasedPlugins) {
6994
7049
  }
6995
7050
  console.log(`${TAG18} ${pluginName} (bundle): ${delivered} delivered, ${skipped} already present`);
6996
7051
  } else {
6997
- const target = resolve6(pluginsDir, pluginName);
6998
- if (existsSync6(resolve6(target, "PLUGIN.md"))) {
7052
+ const target = resolve7(pluginsDir, pluginName);
7053
+ if (existsSync6(resolve7(target, "PLUGIN.md"))) {
6999
7054
  console.log(`${TAG18} ${pluginName}: already present \u2014 skipping`);
7000
7055
  continue;
7001
7056
  }
@@ -7035,7 +7090,7 @@ function migratePluginRenames(accountDir, config) {
7035
7090
  return name;
7036
7091
  });
7037
7092
  if (!changed) return;
7038
- const configPath2 = resolve6(accountDir, "account.json");
7093
+ const configPath2 = resolve7(accountDir, "account.json");
7039
7094
  try {
7040
7095
  const raw2 = readFileSync7(configPath2, "utf-8");
7041
7096
  const parsed = JSON.parse(raw2);
@@ -7046,9 +7101,9 @@ function migratePluginRenames(accountDir, config) {
7046
7101
  } catch (err) {
7047
7102
  console.error(`${TAG18} failed to update account.json \u2014 ${err instanceof Error ? err.message : String(err)}`);
7048
7103
  }
7049
- const pluginsDir = resolve6(PLATFORM_ROOT4, "plugins");
7104
+ const pluginsDir = resolve7(PLATFORM_ROOT4, "plugins");
7050
7105
  for (const oldName of Object.keys(PLUGIN_RENAMES)) {
7051
- const orphan = resolve6(pluginsDir, oldName);
7106
+ const orphan = resolve7(pluginsDir, oldName);
7052
7107
  if (existsSync6(orphan)) {
7053
7108
  try {
7054
7109
  rmSync2(orphan, { recursive: true });
@@ -7062,13 +7117,13 @@ function migratePluginRenames(accountDir, config) {
7062
7117
  function autoDeliverBundleAgents(accountDir, purchasedPlugins) {
7063
7118
  if (!purchasedPlugins || purchasedPlugins.length === 0) return;
7064
7119
  const TAG18 = "[bundle-agent-deliver]";
7065
- const stagingRoot = resolve6(PLATFORM_ROOT4, "../premium-plugins");
7066
- const specialistsDir = resolve6(accountDir, "specialists", "agents");
7120
+ const stagingRoot = resolve7(PLATFORM_ROOT4, "../premium-plugins");
7121
+ const specialistsDir = resolve7(accountDir, "specialists", "agents");
7067
7122
  if (!existsSync6(stagingRoot)) return;
7068
7123
  if (!existsSync6(specialistsDir)) {
7069
- mkdirSync4(specialistsDir, { recursive: true });
7124
+ mkdirSync5(specialistsDir, { recursive: true });
7070
7125
  }
7071
- const agentsmdPath = resolve6(accountDir, "agents", "admin", "AGENTS.md");
7126
+ const agentsmdPath = resolve7(accountDir, "agents", "admin", "AGENTS.md");
7072
7127
  let agentsmd = "";
7073
7128
  try {
7074
7129
  agentsmd = existsSync6(agentsmdPath) ? readFileSync7(agentsmdPath, "utf-8") : "";
@@ -7076,7 +7131,7 @@ function autoDeliverBundleAgents(accountDir, purchasedPlugins) {
7076
7131
  }
7077
7132
  let delivered = 0;
7078
7133
  for (const pluginName of purchasedPlugins) {
7079
- const bundleAgentsDir = resolve6(stagingRoot, pluginName, "agents");
7134
+ const bundleAgentsDir = resolve7(stagingRoot, pluginName, "agents");
7080
7135
  if (!existsSync6(bundleAgentsDir)) continue;
7081
7136
  let entries;
7082
7137
  try {
@@ -7085,9 +7140,9 @@ function autoDeliverBundleAgents(accountDir, purchasedPlugins) {
7085
7140
  continue;
7086
7141
  }
7087
7142
  for (const filename of entries) {
7088
- const target = resolve6(specialistsDir, filename);
7143
+ const target = resolve7(specialistsDir, filename);
7089
7144
  if (existsSync6(target)) continue;
7090
- const source = resolve6(bundleAgentsDir, filename);
7145
+ const source = resolve7(bundleAgentsDir, filename);
7091
7146
  try {
7092
7147
  cpSync(source, target);
7093
7148
  } catch (err) {
@@ -7127,8 +7182,8 @@ function autoDeliverBundleAgents(accountDir, purchasedPlugins) {
7127
7182
  }
7128
7183
  }
7129
7184
  function assemblePublicPluginContent(pluginDir) {
7130
- const pluginRoot = resolve6(PLATFORM_ROOT4, "plugins", pluginDir);
7131
- const pluginPath = resolve6(pluginRoot, "PLUGIN.md");
7185
+ const pluginRoot = resolve7(PLATFORM_ROOT4, "plugins", pluginDir);
7186
+ const pluginPath = resolve7(pluginRoot, "PLUGIN.md");
7132
7187
  let raw2;
7133
7188
  try {
7134
7189
  raw2 = readFileSync7(pluginPath, "utf-8");
@@ -7140,7 +7195,7 @@ function assemblePublicPluginContent(pluginDir) {
7140
7195
  const parts = [pluginBody];
7141
7196
  let skillCount = 0;
7142
7197
  let refCount = 0;
7143
- const skillsDir = resolve6(pluginRoot, "skills");
7198
+ const skillsDir = resolve7(pluginRoot, "skills");
7144
7199
  let skillDirs;
7145
7200
  try {
7146
7201
  skillDirs = readdirSync2(skillsDir).sort();
@@ -7148,8 +7203,8 @@ function assemblePublicPluginContent(pluginDir) {
7148
7203
  return { body: pluginBody, skillCount: 0, refCount: 0 };
7149
7204
  }
7150
7205
  for (const skillName of skillDirs) {
7151
- const skillDir = resolve6(skillsDir, skillName);
7152
- const skillMdPath = resolve6(skillDir, "SKILL.md");
7206
+ const skillDir = resolve7(skillsDir, skillName);
7207
+ const skillMdPath = resolve7(skillDir, "SKILL.md");
7153
7208
  let skillRaw;
7154
7209
  try {
7155
7210
  skillRaw = readFileSync7(skillMdPath, "utf-8");
@@ -7192,7 +7247,7 @@ function assemblePublicPluginContent(pluginDir) {
7192
7247
  parts.push(`
7193
7248
  <!-- skill: ${skillName} -->`);
7194
7249
  parts.push(skillBody);
7195
- const refsDir = resolve6(skillDir, "references");
7250
+ const refsDir = resolve7(skillDir, "references");
7196
7251
  let refFiles;
7197
7252
  try {
7198
7253
  refFiles = readdirSync2(refsDir).filter((f) => f.endsWith(".md")).filter((f) => !publicExcludeReferences.includes(f)).sort();
@@ -7203,7 +7258,7 @@ function assemblePublicPluginContent(pluginDir) {
7203
7258
  }
7204
7259
  for (const refFile of refFiles) {
7205
7260
  try {
7206
- const refContent = readFileSync7(resolve6(refsDir, refFile), "utf-8").trim();
7261
+ const refContent = readFileSync7(resolve7(refsDir, refFile), "utf-8").trim();
7207
7262
  if (refContent) {
7208
7263
  parts.push(`
7209
7264
  <!-- reference: ${refFile} -->`);
@@ -7221,7 +7276,7 @@ function assemblePublicPluginContent(pluginDir) {
7221
7276
  return { body: parts.join("\n"), skillCount, refCount };
7222
7277
  }
7223
7278
  function loadEmbeddedPlugins(agentType, selectedPlugins, enabledPlugins) {
7224
- const pluginsDir = resolve6(PLATFORM_ROOT4, "plugins");
7279
+ const pluginsDir = resolve7(PLATFORM_ROOT4, "plugins");
7225
7280
  let dirs;
7226
7281
  try {
7227
7282
  dirs = readdirSync2(pluginsDir);
@@ -7274,7 +7329,7 @@ function loadEmbeddedPlugins(agentType, selectedPlugins, enabledPlugins) {
7274
7329
  console.log(`[plugins] loaded ${dir} for public (${assembled.body.length} chars, ${assembled.skillCount} skills, ${assembled.refCount} refs)`);
7275
7330
  }
7276
7331
  } else {
7277
- const pluginPath = resolve6(pluginsDir, dir, "PLUGIN.md");
7332
+ const pluginPath = resolve7(pluginsDir, dir, "PLUGIN.md");
7278
7333
  let raw2;
7279
7334
  try {
7280
7335
  raw2 = readFileSync7(pluginPath, "utf-8");
@@ -7301,7 +7356,7 @@ var mcpToolsCache = /* @__PURE__ */ new Map();
7301
7356
  function fetchMcpToolsList(pluginDir) {
7302
7357
  const cached = mcpToolsCache.get(pluginDir);
7303
7358
  if (cached) return Promise.resolve(cached);
7304
- const serverPath = resolve6(PLATFORM_ROOT4, "plugins", pluginDir, "mcp/dist/index.js");
7359
+ const serverPath = resolve7(PLATFORM_ROOT4, "plugins", pluginDir, "mcp/dist/index.js");
7305
7360
  if (!existsSync6(serverPath)) return Promise.resolve([]);
7306
7361
  const startMs = Date.now();
7307
7362
  return new Promise((resolvePromise) => {
@@ -7414,7 +7469,7 @@ var SPECIALIST_PLUGIN_DOMAINS = {
7414
7469
  // agent, so it retains a full manifest entry for routing clarity.
7415
7470
  };
7416
7471
  async function buildPluginManifest(enabledPlugins) {
7417
- const pluginsDir = resolve6(PLATFORM_ROOT4, "plugins");
7472
+ const pluginsDir = resolve7(PLATFORM_ROOT4, "plugins");
7418
7473
  let dirs;
7419
7474
  try {
7420
7475
  dirs = readdirSync2(pluginsDir);
@@ -7501,16 +7556,16 @@ ${specialist}: ${plugins.join(", ")}`);
7501
7556
  for (let j = 0; j < adminPlugins.length; j++) {
7502
7557
  const { dir, parsed } = adminPlugins[j];
7503
7558
  const mcpTools = adminMcpResults[j];
7504
- const pluginRoot = resolve6(pluginsDir, dir);
7559
+ const pluginRoot = resolve7(pluginsDir, dir);
7505
7560
  const skills = [];
7506
7561
  const references = [];
7507
7562
  const scanDir = (base, prefix, target) => {
7508
- const scanPath = resolve6(pluginRoot, base);
7563
+ const scanPath = resolve7(pluginRoot, base);
7509
7564
  if (!existsSync6(scanPath)) return;
7510
7565
  try {
7511
7566
  const walk = (current, rel) => {
7512
7567
  for (const entry of readdirSync2(current)) {
7513
- const full = resolve6(current, entry);
7568
+ const full = resolve7(current, entry);
7514
7569
  try {
7515
7570
  const stat5 = statSync3(full);
7516
7571
  if (stat5.isDirectory()) {
@@ -7538,7 +7593,7 @@ ${specialist}: ${plugins.join(", ")}`);
7538
7593
  toolLines.push(desc ? ` ${tool.name} \u2014 ${desc}` : ` ${tool.name}`);
7539
7594
  }
7540
7595
  } else if (parsed.tools.length > 0) {
7541
- const serverPath = resolve6(PLATFORM_ROOT4, "plugins", dir, "mcp/dist/index.js");
7596
+ const serverPath = resolve7(PLATFORM_ROOT4, "plugins", dir, "mcp/dist/index.js");
7542
7597
  if (existsSync6(serverPath)) {
7543
7598
  fallbackSourced++;
7544
7599
  console.error(`[plugin-manifest] ${dir}: tools/list empty \u2014 fallback to frontmatter (${parsed.tools.length} tools)`);
@@ -7619,7 +7674,7 @@ function resolveUserAccounts(userId) {
7619
7674
  const entries = readdirSync2(ACCOUNTS_DIR, { withFileTypes: true });
7620
7675
  for (const entry of entries) {
7621
7676
  if (!entry.isDirectory()) continue;
7622
- const configPath2 = resolve6(ACCOUNTS_DIR, entry.name, "account.json");
7677
+ const configPath2 = resolve7(ACCOUNTS_DIR, entry.name, "account.json");
7623
7678
  if (!existsSync6(configPath2)) continue;
7624
7679
  let config;
7625
7680
  try {
@@ -7632,7 +7687,7 @@ function resolveUserAccounts(userId) {
7632
7687
  if (adminEntry) {
7633
7688
  results.push({
7634
7689
  accountId: config.accountId,
7635
- accountDir: resolve6(ACCOUNTS_DIR, entry.name),
7690
+ accountDir: resolve7(ACCOUNTS_DIR, entry.name),
7636
7691
  config,
7637
7692
  role: adminEntry.role
7638
7693
  });
@@ -7849,8 +7904,8 @@ function consumeStalledSubagents(sessionKey) {
7849
7904
  return stalls && stalls.length > 0 ? stalls : void 0;
7850
7905
  }
7851
7906
  function streamLogPathFor(accountId, conversationId) {
7852
- const logDir = resolve6(ACCOUNTS_DIR, accountId, "logs");
7853
- const streamLogPath = resolve6(logDir, `claude-agent-stream-${conversationId}.log`);
7907
+ const logDir = resolve7(ACCOUNTS_DIR, accountId, "logs");
7908
+ const streamLogPath = resolve7(logDir, `claude-agent-stream-${conversationId}.log`);
7854
7909
  return { logDir, streamLogPath };
7855
7910
  }
7856
7911
  function buildSpawnEnv(accountId, accountDir, conversationId) {
@@ -7871,7 +7926,7 @@ var cachedBrandHostname = null;
7871
7926
  function readBrandHostname() {
7872
7927
  if (cachedBrandHostname !== null) return cachedBrandHostname;
7873
7928
  try {
7874
- const brandPath = resolve6(PLATFORM_ROOT4, "config", "brand.json");
7929
+ const brandPath = resolve7(PLATFORM_ROOT4, "config", "brand.json");
7875
7930
  const parsed = JSON.parse(readFileSync7(brandPath, "utf-8"));
7876
7931
  cachedBrandHostname = typeof parsed.hostname === "string" && parsed.hostname.length > 0 ? parsed.hostname : "maxy";
7877
7932
  } catch {
@@ -7909,37 +7964,37 @@ function getMcpServers(accountId, conversationId, userId, enabledPlugins) {
7909
7964
  const servers = {
7910
7965
  "memory": {
7911
7966
  command: "node",
7912
- args: [resolve6(PLATFORM_ROOT4, "plugins/memory/mcp/dist/index.js")],
7967
+ args: [resolve7(PLATFORM_ROOT4, "plugins/memory/mcp/dist/index.js")],
7913
7968
  env: { ...baseEnv, ...userId ? { USER_ID: userId } : {} }
7914
7969
  },
7915
7970
  "contacts": {
7916
7971
  command: "node",
7917
- args: [resolve6(PLATFORM_ROOT4, "plugins/contacts/mcp/dist/index.js")],
7972
+ args: [resolve7(PLATFORM_ROOT4, "plugins/contacts/mcp/dist/index.js")],
7918
7973
  env: { ...baseEnv }
7919
7974
  },
7920
7975
  "whatsapp": {
7921
7976
  command: "node",
7922
- args: [resolve6(PLATFORM_ROOT4, "plugins/whatsapp/mcp/dist/index.js")],
7977
+ args: [resolve7(PLATFORM_ROOT4, "plugins/whatsapp/mcp/dist/index.js")],
7923
7978
  env: { ...baseEnv, PLATFORM_PORT: process.env.PORT ?? "19200" }
7924
7979
  },
7925
7980
  "admin": {
7926
7981
  command: "node",
7927
- args: [resolve6(PLATFORM_ROOT4, "plugins/admin/mcp/dist/index.js")],
7982
+ args: [resolve7(PLATFORM_ROOT4, "plugins/admin/mcp/dist/index.js")],
7928
7983
  env: { ...baseEnv, PLATFORM_PORT: process.env.PORT ?? "19200", ...userId ? { USER_ID: userId } : {} }
7929
7984
  },
7930
7985
  "scheduling": {
7931
7986
  command: "node",
7932
- args: [resolve6(PLATFORM_ROOT4, "plugins/scheduling/mcp/dist/index.js")],
7987
+ args: [resolve7(PLATFORM_ROOT4, "plugins/scheduling/mcp/dist/index.js")],
7933
7988
  env: { ...baseEnv }
7934
7989
  },
7935
7990
  "tasks": {
7936
7991
  command: "node",
7937
- args: [resolve6(PLATFORM_ROOT4, "plugins/tasks/mcp/dist/index.js")],
7992
+ args: [resolve7(PLATFORM_ROOT4, "plugins/tasks/mcp/dist/index.js")],
7938
7993
  env: { ...baseEnv }
7939
7994
  },
7940
7995
  "email": {
7941
7996
  command: "node",
7942
- args: [resolve6(PLATFORM_ROOT4, "plugins/email/mcp/dist/index.js")],
7997
+ args: [resolve7(PLATFORM_ROOT4, "plugins/email/mcp/dist/index.js")],
7943
7998
  env: { ...baseEnv }
7944
7999
  },
7945
8000
  // Workflows MCP — persistent admin-session server for list/get/update/delete/
@@ -7950,7 +8005,7 @@ function getMcpServers(accountId, conversationId, userId, enabledPlugins) {
7950
8005
  // ToolSearches fruitlessly before degrading to a task-create stand-in (Task 571).
7951
8006
  "workflows": {
7952
8007
  command: "node",
7953
- args: [resolve6(PLATFORM_ROOT4, "plugins/workflows/mcp/dist/index.js")],
8008
+ args: [resolve7(PLATFORM_ROOT4, "plugins/workflows/mcp/dist/index.js")],
7954
8009
  env: { ...baseEnv }
7955
8010
  },
7956
8011
  // Playwright MCP server — browser automation for browser-specialist.
@@ -7972,7 +8027,7 @@ function getMcpServers(accountId, conversationId, userId, enabledPlugins) {
7972
8027
  // MAXY-PRD.md:627, not in any application-layer filter).
7973
8028
  "graph": {
7974
8029
  command: "node",
7975
- args: [resolve6(PLATFORM_ROOT4, "lib/graph-mcp/dist/index.js")],
8030
+ args: [resolve7(PLATFORM_ROOT4, "lib/graph-mcp/dist/index.js")],
7976
8031
  env: {
7977
8032
  ...baseEnv,
7978
8033
  BRAND: readBrandHostname(),
@@ -7988,7 +8043,7 @@ function getMcpServers(accountId, conversationId, userId, enabledPlugins) {
7988
8043
  if (tgBotToken) {
7989
8044
  servers["telegram"] = {
7990
8045
  command: "node",
7991
- args: [resolve6(PLATFORM_ROOT4, "plugins/telegram/mcp/dist/index.js")],
8046
+ args: [resolve7(PLATFORM_ROOT4, "plugins/telegram/mcp/dist/index.js")],
7992
8047
  env: { ...baseEnv, TELEGRAM_BOT_TOKEN: tgBotToken }
7993
8048
  };
7994
8049
  } else {
@@ -7996,11 +8051,11 @@ function getMcpServers(accountId, conversationId, userId, enabledPlugins) {
7996
8051
  }
7997
8052
  servers["cloudflare"] = {
7998
8053
  command: "node",
7999
- args: [resolve6(PLATFORM_ROOT4, "plugins/cloudflare/mcp/dist/index.js")],
8054
+ args: [resolve7(PLATFORM_ROOT4, "plugins/cloudflare/mcp/dist/index.js")],
8000
8055
  env: { ...baseEnv, PLATFORM_PORT: process.env.PORT ?? "19200" }
8001
8056
  };
8002
8057
  if (Array.isArray(enabledPlugins) && enabledPlugins.length > 0) {
8003
- const pluginsDir = resolve6(PLATFORM_ROOT4, "plugins");
8058
+ const pluginsDir = resolve7(PLATFORM_ROOT4, "plugins");
8004
8059
  let dirs;
8005
8060
  try {
8006
8061
  dirs = readdirSync2(pluginsDir);
@@ -8023,7 +8078,7 @@ function getMcpServers(accountId, conversationId, userId, enabledPlugins) {
8023
8078
  continue;
8024
8079
  }
8025
8080
  }
8026
- const mcpEntry = resolve6(PLATFORM_ROOT4, "plugins", dir, "mcp/dist/index.js");
8081
+ const mcpEntry = resolve7(PLATFORM_ROOT4, "plugins", dir, "mcp/dist/index.js");
8027
8082
  if (!existsSync6(mcpEntry)) continue;
8028
8083
  servers[dir] = {
8029
8084
  command: "node",
@@ -8158,7 +8213,7 @@ var ADMIN_CORE_TOOLS = [
8158
8213
  function getAdminAllowedTools(enabledPlugins) {
8159
8214
  const tools = [...ADMIN_CORE_TOOLS];
8160
8215
  if (Array.isArray(enabledPlugins) && enabledPlugins.length > 0) {
8161
- const pluginsDir = resolve6(PLATFORM_ROOT4, "plugins");
8216
+ const pluginsDir = resolve7(PLATFORM_ROOT4, "plugins");
8162
8217
  let dirs;
8163
8218
  try {
8164
8219
  dirs = readdirSync2(pluginsDir);
@@ -8266,13 +8321,13 @@ ${message.slice(0, QUERY_CLASSIFIER_MSG_CAP)}`
8266
8321
  }
8267
8322
  }
8268
8323
  async function fetchMemoryContext(accountId, query, sessionKey, options) {
8269
- const serverPath = resolve6(PLATFORM_ROOT4, "plugins/memory/mcp/dist/index.js");
8324
+ const serverPath = resolve7(PLATFORM_ROOT4, "plugins/memory/mcp/dist/index.js");
8270
8325
  if (!existsSync6(serverPath)) {
8271
8326
  console.error(`[fetchMemoryContext] MCP server not found: ${serverPath}`);
8272
8327
  return null;
8273
8328
  }
8274
8329
  const startMs = Date.now();
8275
- return new Promise((resolve29) => {
8330
+ return new Promise((resolve30) => {
8276
8331
  const proc = spawn2(process.execPath, [serverPath], {
8277
8332
  env: {
8278
8333
  ...process.env,
@@ -8301,7 +8356,7 @@ async function fetchMemoryContext(accountId, query, sessionKey, options) {
8301
8356
  } else {
8302
8357
  console.error(`[fetchMemoryContext] failed: ${reason} (${elapsed}ms)${stderrBuf ? ` stderr: ${stderrBuf.slice(0, 500)}` : ""}`);
8303
8358
  }
8304
- resolve29(value);
8359
+ resolve30(value);
8305
8360
  };
8306
8361
  proc.stdout.on("data", (chunk) => {
8307
8362
  buffer += chunk.toString();
@@ -8364,7 +8419,7 @@ async function fetchMemoryContext(accountId, query, sessionKey, options) {
8364
8419
  }
8365
8420
  async function compactTrimmedMessages(accountId, trimmedMessages) {
8366
8421
  if (trimmedMessages.length === 0) return true;
8367
- const serverPath = resolve6(PLATFORM_ROOT4, "plugins/memory/mcp/dist/index.js");
8422
+ const serverPath = resolve7(PLATFORM_ROOT4, "plugins/memory/mcp/dist/index.js");
8368
8423
  if (!existsSync6(serverPath)) return false;
8369
8424
  const briefing = trimmedMessages.map((m) => `[${m.role.toUpperCase()}] ${m.content}`).join("\n\n");
8370
8425
  return new Promise((resolvePromise) => {
@@ -8682,7 +8737,7 @@ Then respond with only: [COMPACTED]`;
8682
8737
  var COMPACTION_TIMEOUT_MS = 45e3;
8683
8738
  async function* runCompactionTurn(accountDir, accountId, systemPrompt, resumeSessionId, adminModel, conversationId, enabledPlugins) {
8684
8739
  const mcpConfig = JSON.stringify({ mcpServers: getMcpServers(accountId, conversationId, void 0, enabledPlugins) });
8685
- const specialistsDir = resolve6(accountDir, "specialists");
8740
+ const specialistsDir = resolve7(accountDir, "specialists");
8686
8741
  if (!existsSync6(specialistsDir)) agentLogStream("claude-agent-compaction-stream", accountDir, conversationId).write(`[${isoTs()}] [warn] specialists plugin dir missing: ${specialistsDir}
8687
8742
  `);
8688
8743
  const args = [
@@ -8956,7 +9011,7 @@ async function* parseClaudeStream(proc, streamLog, adminModel, conversationId, a
8956
9011
  const { logDir } = streamLogPathFor(accountId, conversationId);
8957
9012
  const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
8958
9013
  for (const s of failed) {
8959
- const stderrPath = resolve6(logDir, `mcp-${s.name}-stderr-${date}.log`);
9014
+ const stderrPath = resolve7(logDir, `mcp-${s.name}-stderr-${date}.log`);
8960
9015
  let tail = "(no stderr file)";
8961
9016
  try {
8962
9017
  const stats = statSync3(stderrPath);
@@ -9598,7 +9653,7 @@ async function* invokeAdminAgent(message, systemPrompt, accountDir, accountId, a
9598
9653
  }
9599
9654
  const ccUserId = sessionKey ? getUserIdForSession(sessionKey) : void 0;
9600
9655
  const mcpConfig = JSON.stringify({ mcpServers: getMcpServers(accountId, spawnConvId, ccUserId, enabledPlugins) });
9601
- const specialistsDir = resolve6(accountDir, "specialists");
9656
+ const specialistsDir = resolve7(accountDir, "specialists");
9602
9657
  if (!existsSync6(specialistsDir)) agentLogStream("claude-agent-stream", accountDir, spawnConvId).write(`[${isoTs()}] [warn] specialists plugin dir missing: ${specialistsDir}
9603
9658
  `);
9604
9659
  const args = [
@@ -9939,7 +9994,7 @@ async function* invokeManagedAdminAgent(message, systemPrompt, accountDir, accou
9939
9994
  `);
9940
9995
  const managedUserId = getUserIdForSession(sessionKey);
9941
9996
  const mcpConfig = JSON.stringify({ mcpServers: getMcpServers(accountId, managedConvId, managedUserId, enabledPlugins) });
9942
- const specialistsDir = resolve6(accountDir, "specialists");
9997
+ const specialistsDir = resolve7(accountDir, "specialists");
9943
9998
  if (!existsSync6(specialistsDir)) streamLog.write(`[${isoTs()}] [warn] specialists plugin dir missing: ${specialistsDir}
9944
9999
  `);
9945
10000
  const fullMessage = attachments.length > 0 ? message + buildAttachmentMetaText(attachments) : message;
@@ -10602,7 +10657,7 @@ ${sessionContext}`;
10602
10657
  console.log(`[onboarding-inject] accountId=${accountId.slice(0, 8)}\u2026 error=neo4j-unreachable injected=false`);
10603
10658
  } else if (onboardingStep < 8) {
10604
10659
  const GENERIC_FALLBACK = "At every session start, call `onboarding-get`. If `currentStep` is less than 8, load the onboarding skill via `plugin-read` (find its path in the manifest under `admin`) and follow it \u2014 before any business setup. If `onboarding-get` fails (Neo4j unreachable), tell the user and skip onboarding for this session \u2014 it resumes automatically when the graph is available.";
10605
- const skillPath = resolve6(PLATFORM_ROOT4, "plugins/admin/skills/onboarding/SKILL.md");
10660
+ const skillPath = resolve7(PLATFORM_ROOT4, "plugins/admin/skills/onboarding/SKILL.md");
10606
10661
  let skillContent = "";
10607
10662
  try {
10608
10663
  skillContent = readFileSync7(skillPath, "utf-8");
@@ -10655,7 +10710,7 @@ ${body}`;
10655
10710
 
10656
10711
  ${manifest}`;
10657
10712
  }
10658
- const graphRefPath = resolve6(PLATFORM_ROOT4, "plugins/memory/references/graph-primitives.md");
10713
+ const graphRefPath = resolve7(PLATFORM_ROOT4, "plugins/memory/references/graph-primitives.md");
10659
10714
  try {
10660
10715
  const graphRef = readFileSync7(graphRefPath, "utf-8");
10661
10716
  baseSystemPrompt += `
@@ -10883,8 +10938,8 @@ function getLanIp() {
10883
10938
  import { basename as basename2 } from "path";
10884
10939
 
10885
10940
  // app/lib/review-detector/rules.ts
10886
- import { readFileSync as readFileSync8, writeFileSync as writeFileSync6, existsSync as existsSync7, statSync as statSync4, mkdirSync as mkdirSync5, renameSync } from "fs";
10887
- import { resolve as resolve7, dirname as dirname2 } from "path";
10941
+ import { readFileSync as readFileSync8, writeFileSync as writeFileSync6, existsSync as existsSync7, statSync as statSync4, mkdirSync as mkdirSync6, renameSync } from "fs";
10942
+ import { resolve as resolve8, dirname as dirname2 } from "path";
10888
10943
  var DEFAULT_SCAN_INTERVAL_MS = 5e3;
10889
10944
  var RATE_LIMIT_PATTERN = "rate[- ]?limit(?:ed| reached| hit)|(?:HTTP|status)[^a-z]{0,3}429|too many requests";
10890
10945
  var RATE_LIMIT_PATTERN_V1 = "\\b429\\b|rate.?limit|too.?many.?requests";
@@ -11313,12 +11368,12 @@ function defaultRules() {
11313
11368
  ];
11314
11369
  }
11315
11370
  function rulesFilePath(configDir2) {
11316
- return resolve7(configDir2, "review-rules.json");
11371
+ return resolve8(configDir2, "review-rules.json");
11317
11372
  }
11318
11373
  function ensureRulesFile(configDir2) {
11319
11374
  const path2 = rulesFilePath(configDir2);
11320
11375
  if (existsSync7(path2)) return { created: false, path: path2 };
11321
- mkdirSync5(dirname2(path2), { recursive: true });
11376
+ mkdirSync6(dirname2(path2), { recursive: true });
11322
11377
  const body = {
11323
11378
  scanIntervalMs: DEFAULT_SCAN_INTERVAL_MS,
11324
11379
  rules: defaultRules()
@@ -11496,10 +11551,10 @@ function validateRule(input, label, seenIds) {
11496
11551
  }
11497
11552
 
11498
11553
  // app/lib/review-detector/sources.ts
11499
- import { existsSync as existsSync8, readdirSync as readdirSync3, statSync as statSync5, writeFileSync as writeFileSync7, renameSync as renameSync2, mkdirSync as mkdirSync6, openSync as openSync3, readSync as readSync3, closeSync as closeSync3, readFileSync as readFileSync9 } from "fs";
11500
- import { resolve as resolve8, join as join5, basename, dirname as dirname3 } from "path";
11554
+ import { existsSync as existsSync8, readdirSync as readdirSync3, statSync as statSync5, writeFileSync as writeFileSync7, renameSync as renameSync2, mkdirSync as mkdirSync7, openSync as openSync3, readSync as readSync3, closeSync as closeSync3, readFileSync as readFileSync9 } from "fs";
11555
+ import { resolve as resolve9, join as join5, basename, dirname as dirname3 } from "path";
11501
11556
  function tailStatePath(configDir2) {
11502
- return resolve8(configDir2, "review-state.json");
11557
+ return resolve9(configDir2, "review-state.json");
11503
11558
  }
11504
11559
  function loadTailState(configDir2) {
11505
11560
  const path2 = tailStatePath(configDir2);
@@ -11523,25 +11578,25 @@ function loadTailState(configDir2) {
11523
11578
  }
11524
11579
  function saveTailState(configDir2, state) {
11525
11580
  const path2 = tailStatePath(configDir2);
11526
- mkdirSync6(dirname3(path2), { recursive: true });
11581
+ mkdirSync7(dirname3(path2), { recursive: true });
11527
11582
  const tmp = `${path2}.tmp.${process.pid}.${Date.now()}`;
11528
11583
  writeFileSync7(tmp, JSON.stringify(state, null, 2) + "\n", "utf-8");
11529
11584
  renameSync2(tmp, path2);
11530
11585
  }
11531
11586
  function discoverSourceFiles(configDir2, accountLogDir2, logicalSource) {
11532
11587
  if (logicalSource === "server") {
11533
- const p = resolve8(configDir2, "logs", "server.log");
11588
+ const p = resolve9(configDir2, "logs", "server.log");
11534
11589
  return existsSync8(p) ? [{ logicalSource: "server", filepath: p }] : [];
11535
11590
  }
11536
11591
  if (logicalSource === "vnc") {
11537
- const p = resolve8(configDir2, "logs", "vnc-boot.log");
11592
+ const p = resolve9(configDir2, "logs", "vnc-boot.log");
11538
11593
  return existsSync8(p) ? [{ logicalSource: "vnc", filepath: p }] : [];
11539
11594
  }
11540
11595
  if (logicalSource === "cloudflared") {
11541
11596
  const files2 = [];
11542
- const daemon = resolve8(configDir2, "logs", "cloudflared.log");
11597
+ const daemon = resolve9(configDir2, "logs", "cloudflared.log");
11543
11598
  if (existsSync8(daemon)) files2.push({ logicalSource: "cloudflared", filepath: daemon });
11544
- const login = resolve8(configDir2, "logs", "cloudflared-login.log");
11599
+ const login = resolve9(configDir2, "logs", "cloudflared-login.log");
11545
11600
  if (existsSync8(login)) files2.push({ logicalSource: "cloudflared", filepath: login });
11546
11601
  return files2;
11547
11602
  }
@@ -11668,31 +11723,31 @@ function fileLastWriteMs(path2) {
11668
11723
  }
11669
11724
  }
11670
11725
  function accountLogDir(accountDir) {
11671
- return resolve8(accountDir, "logs");
11726
+ return resolve9(accountDir, "logs");
11672
11727
  }
11673
11728
  function sourceKey(file) {
11674
11729
  return `${file.logicalSource}:${basename(file.filepath)}`;
11675
11730
  }
11676
11731
 
11677
11732
  // app/lib/review-detector/writer.ts
11678
- import { appendFileSync as appendFileSync3, existsSync as existsSync9, mkdirSync as mkdirSync7, readFileSync as readFileSync10, writeFileSync as writeFileSync8, renameSync as renameSync3, statSync as statSync6 } from "fs";
11679
- import { resolve as resolve9, dirname as dirname4 } from "path";
11733
+ import { appendFileSync as appendFileSync4, existsSync as existsSync9, mkdirSync as mkdirSync8, readFileSync as readFileSync10, writeFileSync as writeFileSync8, renameSync as renameSync3, statSync as statSync6 } from "fs";
11734
+ import { resolve as resolve10, dirname as dirname4 } from "path";
11680
11735
  import { randomUUID as randomUUID3 } from "crypto";
11681
11736
  function reviewLogPath(configDir2) {
11682
- return resolve9(configDir2, "logs", "review.log");
11737
+ return resolve10(configDir2, "logs", "review.log");
11683
11738
  }
11684
11739
  function pendingAlertsPath(configDir2) {
11685
- return resolve9(configDir2, "review-pending-alerts.jsonl");
11740
+ return resolve10(configDir2, "review-pending-alerts.jsonl");
11686
11741
  }
11687
11742
  function reviewLog(configDir2, event) {
11688
11743
  const path2 = reviewLogPath(configDir2);
11689
11744
  try {
11690
- mkdirSync7(dirname4(path2), { recursive: true });
11745
+ mkdirSync8(dirname4(path2), { recursive: true });
11691
11746
  const line = `${new Date(
11692
11747
  typeof event.ts === "number" ? event.ts : Date.now()
11693
11748
  ).toISOString()} [review] ${JSON.stringify(event)}
11694
11749
  `;
11695
- appendFileSync3(path2, line, "utf-8");
11750
+ appendFileSync4(path2, line, "utf-8");
11696
11751
  } catch (err) {
11697
11752
  console.error(`[review] failed to write review log at ${path2}: ${err instanceof Error ? err.message : String(err)}`);
11698
11753
  }
@@ -11804,9 +11859,9 @@ async function upsertReviewAlert(accountId, match2) {
11804
11859
  function queueAlert(configDir2, accountId, match2) {
11805
11860
  const path2 = pendingAlertsPath(configDir2);
11806
11861
  try {
11807
- mkdirSync7(dirname4(path2), { recursive: true });
11862
+ mkdirSync8(dirname4(path2), { recursive: true });
11808
11863
  const line = JSON.stringify({ accountId, match: match2 }) + "\n";
11809
- appendFileSync3(path2, line, "utf-8");
11864
+ appendFileSync4(path2, line, "utf-8");
11810
11865
  } catch (err) {
11811
11866
  console.error(`[review] failed to queue alert at ${path2}: ${err instanceof Error ? err.message : String(err)}`);
11812
11867
  }
@@ -11937,7 +11992,7 @@ async function bootDetector() {
11937
11992
  }
11938
11993
 
11939
11994
  // app/lib/review-detector/scan-loop.ts
11940
- import { resolve as resolve10 } from "path";
11995
+ import { resolve as resolve11 } from "path";
11941
11996
 
11942
11997
  // app/lib/review-detector/evaluator.ts
11943
11998
  var SAMPLE_MAX_CHARS = 500;
@@ -12231,14 +12286,14 @@ async function runScanCycle(runtime) {
12231
12286
  match2 = result.match;
12232
12287
  }
12233
12288
  } else if (rule.type === "file-write-storm") {
12234
- const dir = resolve10(runtime.configDir, rule.watchPath ?? "");
12289
+ const dir = resolve11(runtime.configDir, rule.watchPath ?? "");
12235
12290
  const sinceMs = cycleStart - rule.thresholdWindowMinutes * 6e4;
12236
12291
  const count = countRecentWrites(dir, sinceMs);
12237
12292
  const result = evaluateFileWriteStormRule(rule, count, state, cycleStart);
12238
12293
  state = result.state;
12239
12294
  match2 = result.match;
12240
12295
  } else if (rule.type === "stale-log") {
12241
- const trackedPath = resolve10(runtime.configDir, rule.watchPath ?? "");
12296
+ const trackedPath = resolve11(runtime.configDir, rule.watchPath ?? "");
12242
12297
  const lastMs = fileLastWriteMs(trackedPath);
12243
12298
  const result = evaluateStaleLogRule(rule, lastMs, state, cycleStart);
12244
12299
  state = result.state;
@@ -12478,10 +12533,10 @@ var WhatsAppConfigSchema = z.object({
12478
12533
 
12479
12534
  // app/lib/whatsapp/config-persist.ts
12480
12535
  import { readFileSync as readFileSync11, writeFileSync as writeFileSync9, existsSync as existsSync10 } from "fs";
12481
- import { resolve as resolve11, join as join6 } from "path";
12536
+ import { resolve as resolve12, join as join6 } from "path";
12482
12537
  var TAG2 = "[whatsapp:config]";
12483
12538
  function configPath(accountDir) {
12484
- return resolve11(accountDir, "account.json");
12539
+ return resolve12(accountDir, "account.json");
12485
12540
  }
12486
12541
  function readConfig(accountDir) {
12487
12542
  const path2 = configPath(accountDir);
@@ -12936,7 +12991,7 @@ var credsSaveQueue = Promise.resolve();
12936
12991
  async function drainCredsSaveQueue(timeoutMs = 5e3) {
12937
12992
  console.error(`${TAG4} draining credential save queue\u2026`);
12938
12993
  const timer = new Promise(
12939
- (resolve29) => setTimeout(() => resolve29("timeout"), timeoutMs)
12994
+ (resolve30) => setTimeout(() => resolve30("timeout"), timeoutMs)
12940
12995
  );
12941
12996
  const result = await Promise.race([
12942
12997
  credsSaveQueue.then(() => "drained"),
@@ -13064,11 +13119,11 @@ async function createWaSocket(opts) {
13064
13119
  return sock;
13065
13120
  }
13066
13121
  async function waitForConnection(sock) {
13067
- return new Promise((resolve29, reject) => {
13122
+ return new Promise((resolve30, reject) => {
13068
13123
  const handler = (update) => {
13069
13124
  if (update.connection === "open") {
13070
13125
  sock.ev.off("connection.update", handler);
13071
- resolve29();
13126
+ resolve30();
13072
13127
  }
13073
13128
  if (update.connection === "close") {
13074
13129
  sock.ev.off("connection.update", handler);
@@ -13182,14 +13237,14 @@ ${inspected}`;
13182
13237
  return inspect2(err, INSPECT_OPTS2);
13183
13238
  }
13184
13239
  function withTimeout(label, promise, timeoutMs) {
13185
- return new Promise((resolve29, reject) => {
13240
+ return new Promise((resolve30, reject) => {
13186
13241
  const timer = setTimeout(() => {
13187
13242
  reject(new Error(`${label} timed out after ${timeoutMs}ms`));
13188
13243
  }, timeoutMs);
13189
13244
  promise.then(
13190
13245
  (value) => {
13191
13246
  clearTimeout(timer);
13192
- resolve29(value);
13247
+ resolve30(value);
13193
13248
  },
13194
13249
  (err) => {
13195
13250
  clearTimeout(timer);
@@ -14389,11 +14444,11 @@ async function connectWithReconnect(conn) {
14389
14444
  console.error(
14390
14445
  `${TAG12} reconnecting account=${conn.accountId} in ${delay}ms (attempt ${decision.nextAttempts}/${maxAttempts})`
14391
14446
  );
14392
- await new Promise((resolve29) => {
14393
- const timer = setTimeout(resolve29, delay);
14447
+ await new Promise((resolve30) => {
14448
+ const timer = setTimeout(resolve30, delay);
14394
14449
  conn.abortController.signal.addEventListener("abort", () => {
14395
14450
  clearTimeout(timer);
14396
- resolve29();
14451
+ resolve30();
14397
14452
  }, { once: true });
14398
14453
  });
14399
14454
  }
@@ -14401,16 +14456,16 @@ async function connectWithReconnect(conn) {
14401
14456
  }
14402
14457
  }
14403
14458
  function waitForDisconnectEvent(conn) {
14404
- return new Promise((resolve29) => {
14459
+ return new Promise((resolve30) => {
14405
14460
  if (!conn.sock) {
14406
- resolve29();
14461
+ resolve30();
14407
14462
  return;
14408
14463
  }
14409
14464
  const sock = conn.sock;
14410
14465
  const handler = (update) => {
14411
14466
  if (update.connection === "close") {
14412
14467
  sock.ev.off("connection.update", handler);
14413
- resolve29();
14468
+ resolve30();
14414
14469
  }
14415
14470
  };
14416
14471
  sock.ev.on("connection.update", handler);
@@ -14620,8 +14675,8 @@ async function handleInboundMessage(conn, msg) {
14620
14675
  const conversationKey = isGroup ? remoteJid : senderPhone;
14621
14676
  const debounceKey = `${conn.accountId}:${conversationKey}:${senderPhone}`;
14622
14677
  let resolvePending;
14623
- const sttPending = new Promise((resolve29) => {
14624
- resolvePending = resolve29;
14678
+ const sttPending = new Promise((resolve30) => {
14679
+ resolvePending = resolve30;
14625
14680
  });
14626
14681
  if (conn.debouncer) conn.debouncer.registerPending(debounceKey, sttPending);
14627
14682
  try {
@@ -14728,20 +14783,20 @@ async function probeApiKey() {
14728
14783
  return result.status;
14729
14784
  }
14730
14785
  function checkPort(port2, timeoutMs = 500) {
14731
- return new Promise((resolve29) => {
14786
+ return new Promise((resolve30) => {
14732
14787
  const socket = createConnection4(port2, "127.0.0.1");
14733
14788
  socket.setTimeout(timeoutMs);
14734
14789
  socket.once("connect", () => {
14735
14790
  socket.destroy();
14736
- resolve29(true);
14791
+ resolve30(true);
14737
14792
  });
14738
14793
  socket.once("error", () => {
14739
14794
  socket.destroy();
14740
- resolve29(false);
14795
+ resolve30(false);
14741
14796
  });
14742
14797
  socket.once("timeout", () => {
14743
14798
  socket.destroy();
14744
- resolve29(false);
14799
+ resolve30(false);
14745
14800
  });
14746
14801
  });
14747
14802
  }
@@ -14838,14 +14893,14 @@ app.get("/", async (c) => {
14838
14893
  var health_default = app;
14839
14894
 
14840
14895
  // server/routes/session.ts
14841
- import { resolve as resolve12 } from "path";
14842
- import { existsSync as existsSync12, writeFileSync as writeFileSync10, mkdirSync as mkdirSync8 } from "fs";
14896
+ import { resolve as resolve13 } from "path";
14897
+ import { existsSync as existsSync12, writeFileSync as writeFileSync10, mkdirSync as mkdirSync9 } from "fs";
14843
14898
  var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
14844
14899
  function writeBrandingCache(accountId, agentSlug, branding) {
14845
14900
  try {
14846
- const cacheDir = resolve12(MAXY_DIR, "branding-cache", accountId);
14847
- mkdirSync8(cacheDir, { recursive: true });
14848
- writeFileSync10(resolve12(cacheDir, `${agentSlug}.json`), JSON.stringify(branding), "utf-8");
14901
+ const cacheDir = resolve13(MAXY_DIR, "branding-cache", accountId);
14902
+ mkdirSync9(cacheDir, { recursive: true });
14903
+ writeFileSync10(resolve13(cacheDir, `${agentSlug}.json`), JSON.stringify(branding), "utf-8");
14849
14904
  } catch (err) {
14850
14905
  console.error(`[branding] cache write failed: ${err instanceof Error ? err.message : String(err)}`);
14851
14906
  }
@@ -14915,8 +14970,8 @@ app2.post("/", async (c) => {
14915
14970
  }
14916
14971
  let agentConfig = null;
14917
14972
  if (account) {
14918
- const agentDir = resolve12(account.accountDir, "agents", agentSlug);
14919
- const agentConfigPath = resolve12(agentDir, "config.json");
14973
+ const agentDir = resolve13(account.accountDir, "agents", agentSlug);
14974
+ const agentConfigPath = resolve13(agentDir, "config.json");
14920
14975
  if (!existsSync12(agentDir) || !existsSync12(agentConfigPath)) {
14921
14976
  return c.json({ error: "Agent not found" }, 404);
14922
14977
  }
@@ -15163,9 +15218,9 @@ ${raw2}`;
15163
15218
  import { randomUUID as randomUUID6 } from "crypto";
15164
15219
  import { mkdir as mkdir2, readFile, stat as stat2, writeFile as writeFile2 } from "fs/promises";
15165
15220
  import { realpathSync } from "fs";
15166
- import { resolve as resolve13, extname, basename as basename3 } from "path";
15167
- var PLATFORM_ROOT5 = process.env.MAXY_PLATFORM_ROOT ?? resolve13(process.cwd(), "../platform");
15168
- var ATTACHMENTS_ROOT = resolve13(PLATFORM_ROOT5, "..", "data/uploads");
15221
+ import { resolve as resolve14, extname, basename as basename3 } from "path";
15222
+ var PLATFORM_ROOT5 = process.env.MAXY_PLATFORM_ROOT ?? resolve14(process.cwd(), "../platform");
15223
+ var ATTACHMENTS_ROOT = resolve14(PLATFORM_ROOT5, "..", "data/uploads");
15169
15224
  var SUPPORTED_MIME_TYPES = /* @__PURE__ */ new Set([
15170
15225
  "image/jpeg",
15171
15226
  "image/png",
@@ -15189,11 +15244,11 @@ function assertSupportedMime(mimeType) {
15189
15244
  }
15190
15245
  async function writeAttachment(scope, filename, mimeType, sizeBytes, buffer) {
15191
15246
  const attachmentId = randomUUID6();
15192
- const dir = resolve13(ATTACHMENTS_ROOT, scope, attachmentId);
15247
+ const dir = resolve14(ATTACHMENTS_ROOT, scope, attachmentId);
15193
15248
  await mkdir2(dir, { recursive: true });
15194
15249
  const ext = extname(filename) || "";
15195
- const storagePath = resolve13(dir, `${attachmentId}${ext}`);
15196
- const metaPath = resolve13(dir, `${attachmentId}.meta.json`);
15250
+ const storagePath = resolve14(dir, `${attachmentId}${ext}`);
15251
+ const metaPath = resolve14(dir, `${attachmentId}.meta.json`);
15197
15252
  const meta = {
15198
15253
  attachmentId,
15199
15254
  scope,
@@ -15871,13 +15926,13 @@ var group_default = app4;
15871
15926
  // app/lib/access-gate.ts
15872
15927
  import neo4j2 from "neo4j-driver";
15873
15928
  import { readFileSync as readFileSync13 } from "fs";
15874
- import { resolve as resolve14 } from "path";
15929
+ import { resolve as resolve15 } from "path";
15875
15930
  import { randomUUID as randomUUID7, randomInt } from "crypto";
15876
- var PLATFORM_ROOT6 = process.env.MAXY_PLATFORM_ROOT ?? resolve14(process.cwd(), "..");
15931
+ var PLATFORM_ROOT6 = process.env.MAXY_PLATFORM_ROOT ?? resolve15(process.cwd(), "..");
15877
15932
  var driver2 = null;
15878
15933
  function readPassword2() {
15879
15934
  if (process.env.NEO4J_PASSWORD) return process.env.NEO4J_PASSWORD;
15880
- const passwordFile = resolve14(PLATFORM_ROOT6, "config/.neo4j-password");
15935
+ const passwordFile = resolve15(PLATFORM_ROOT6, "config/.neo4j-password");
15881
15936
  try {
15882
15937
  return readFileSync13(passwordFile, "utf-8").trim();
15883
15938
  } catch {
@@ -16190,17 +16245,17 @@ async function findActiveGrantByContact(contactValue, agentSlug, accountId) {
16190
16245
  }
16191
16246
 
16192
16247
  // app/lib/brevo-sms.ts
16193
- import { readFileSync as readFileSync14, writeFileSync as writeFileSync11, mkdirSync as mkdirSync9, existsSync as existsSync13, chmodSync } from "fs";
16248
+ import { readFileSync as readFileSync14, writeFileSync as writeFileSync11, mkdirSync as mkdirSync10, existsSync as existsSync13, chmodSync } from "fs";
16194
16249
  import { dirname as dirname5 } from "path";
16195
- import { resolve as resolve15 } from "path";
16196
- var BREVO_API_KEY_FILE = resolve15(MAXY_DIR, ".brevo-api-key");
16250
+ import { resolve as resolve16 } from "path";
16251
+ var BREVO_API_KEY_FILE = resolve16(MAXY_DIR, ".brevo-api-key");
16197
16252
  var BREVO_API_URL = "https://api.brevo.com/v3/transactionalSMS/sms";
16198
16253
  var BREVO_TIMEOUT_MS = 1e4;
16199
16254
  var BREVO_SENDER = "Maxy";
16200
16255
  var platformRoot2 = process.env.MAXY_PLATFORM_ROOT;
16201
16256
  if (platformRoot2) {
16202
16257
  try {
16203
- const brandPath = resolve15(platformRoot2, "config", "brand.json");
16258
+ const brandPath = resolve16(platformRoot2, "config", "brand.json");
16204
16259
  if (existsSync13(brandPath)) {
16205
16260
  const brand = JSON.parse(readFileSync14(brandPath, "utf-8"));
16206
16261
  if (brand.productName) BREVO_SENDER = brand.productName;
@@ -16837,7 +16892,7 @@ app6.post("/webhook", async (c) => {
16837
16892
  var telegram_default = app6;
16838
16893
 
16839
16894
  // server/routes/whatsapp.ts
16840
- import { join as join9, resolve as resolve16, basename as basename4 } from "path";
16895
+ import { join as join9, resolve as resolve17, basename as basename4 } from "path";
16841
16896
  import { readFile as readFile2, stat as stat3 } from "fs/promises";
16842
16897
  import { realpathSync as realpathSync2, readdirSync as readdirSync4, readFileSync as readFileSync16, existsSync as existsSync15 } from "fs";
16843
16898
 
@@ -16945,8 +17000,8 @@ async function startLogin(opts) {
16945
17000
  resetActiveLogin(accountId);
16946
17001
  let resolveQr = null;
16947
17002
  let rejectQr = null;
16948
- const qrPromise = new Promise((resolve29, reject) => {
16949
- resolveQr = resolve29;
17003
+ const qrPromise = new Promise((resolve30, reject) => {
17004
+ resolveQr = resolve30;
16950
17005
  rejectQr = reject;
16951
17006
  });
16952
17007
  const qrTimer = setTimeout(
@@ -17317,14 +17372,14 @@ app7.post("/config", async (c) => {
17317
17372
  return c.json({ ok: true, slug: currentSlug });
17318
17373
  }
17319
17374
  case "list-public-agents": {
17320
- const agentsDir = resolve16(account.accountDir, "agents");
17375
+ const agentsDir = resolve17(account.accountDir, "agents");
17321
17376
  const agents = [];
17322
17377
  if (existsSync15(agentsDir)) {
17323
17378
  try {
17324
17379
  const entries = readdirSync4(agentsDir, { withFileTypes: true });
17325
17380
  for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
17326
17381
  if (!entry.isDirectory() || entry.name === "admin") continue;
17327
- const configPath2 = resolve16(agentsDir, entry.name, "config.json");
17382
+ const configPath2 = resolve17(agentsDir, entry.name, "config.json");
17328
17383
  if (!existsSync15(configPath2)) continue;
17329
17384
  try {
17330
17385
  const config = JSON.parse(readFileSync16(configPath2, "utf-8"));
@@ -17402,7 +17457,7 @@ app7.post("/send-document", async (c) => {
17402
17457
  if (!maxyAccountId || !PLATFORM_ROOT7) {
17403
17458
  return c.json({ error: "Cannot validate file path: missing account or platform context" }, 400);
17404
17459
  }
17405
- const accountDir = resolve16(PLATFORM_ROOT7, "..", "data/accounts", maxyAccountId);
17460
+ const accountDir = resolve17(PLATFORM_ROOT7, "..", "data/accounts", maxyAccountId);
17406
17461
  let resolvedPath;
17407
17462
  try {
17408
17463
  resolvedPath = realpathSync2(filePath);
@@ -17539,8 +17594,8 @@ var whatsapp_default = app7;
17539
17594
 
17540
17595
  // server/routes/onboarding.ts
17541
17596
  import { spawn as spawn3, execFileSync as execFileSync2 } from "child_process";
17542
- import { openSync as openSync4, closeSync as closeSync4, writeFileSync as writeFileSync12, writeSync, existsSync as existsSync16, mkdirSync as mkdirSync10, readFileSync as readFileSync17, unlinkSync as unlinkSync3 } from "fs";
17543
- import { resolve as resolve17, dirname as dirname6 } from "path";
17597
+ import { openSync as openSync4, closeSync as closeSync4, writeFileSync as writeFileSync12, writeSync, existsSync as existsSync16, mkdirSync as mkdirSync11, readFileSync as readFileSync17, unlinkSync as unlinkSync3 } from "fs";
17598
+ import { resolve as resolve18, dirname as dirname6 } from "path";
17544
17599
  import { createHash, randomUUID as randomUUID9 } from "crypto";
17545
17600
  var PLATFORM_ROOT8 = process.env.MAXY_PLATFORM_ROOT || "";
17546
17601
  function hashPin(pin) {
@@ -17653,7 +17708,7 @@ app8.post("/set-pin", async (c) => {
17653
17708
  }
17654
17709
  const hash = hashPin(body.pin);
17655
17710
  const userId = randomUUID9();
17656
- mkdirSync10(dirname6(USERS_FILE), { recursive: true });
17711
+ mkdirSync11(dirname6(USERS_FILE), { recursive: true });
17657
17712
  writeFileSync12(USERS_FILE, JSON.stringify([{ userId, name: "Owner", pin: hash }]), { mode: 384 });
17658
17713
  console.log(`[set-pin] created users.json: userId=${userId.slice(0, 8)}\u2026 hash=${hash.slice(0, 8)}\u2026`);
17659
17714
  const account = resolveAccount();
@@ -17714,7 +17769,7 @@ app8.post("/skip", async (c) => {
17714
17769
  }
17715
17770
  const { accountId, accountDir } = account;
17716
17771
  let agentName = "Maxy";
17717
- const brandPath = PLATFORM_ROOT8 ? resolve17(PLATFORM_ROOT8, "config", "brand.json") : "";
17772
+ const brandPath = PLATFORM_ROOT8 ? resolve18(PLATFORM_ROOT8, "config", "brand.json") : "";
17718
17773
  if (brandPath && existsSync16(brandPath)) {
17719
17774
  try {
17720
17775
  const brand = JSON.parse(readFileSync17(brandPath, "utf-8"));
@@ -17723,9 +17778,9 @@ app8.post("/skip", async (c) => {
17723
17778
  console.error(`[onboarding-skip] brand.json read failed: ${err instanceof Error ? err.message : String(err)}`);
17724
17779
  }
17725
17780
  }
17726
- const soulPath = resolve17(accountDir, "agents", "admin", "SOUL.md");
17781
+ const soulPath = resolve18(accountDir, "agents", "admin", "SOUL.md");
17727
17782
  try {
17728
- mkdirSync10(dirname6(soulPath), { recursive: true });
17783
+ mkdirSync11(dirname6(soulPath), { recursive: true });
17729
17784
  writeFileSync12(soulPath, `You are ${agentName}, an AI operations manager.
17730
17785
  `);
17731
17786
  console.log(`[onboarding-skip] wrote SOUL.md: ${soulPath}`);
@@ -17764,7 +17819,7 @@ app8.post("/skip", async (c) => {
17764
17819
  var onboarding_default = app8;
17765
17820
 
17766
17821
  // server/routes/client-error.ts
17767
- import { appendFileSync as appendFileSync4, existsSync as existsSync17, renameSync as renameSync4, statSync as statSync7 } from "fs";
17822
+ import { appendFileSync as appendFileSync5, existsSync as existsSync17, renameSync as renameSync4, statSync as statSync7 } from "fs";
17768
17823
  import { join as join10 } from "path";
17769
17824
  var CLIENT_ERRORS_LOG = join10(LOG_DIR, "client-errors.log");
17770
17825
  var MAX_LOG_SIZE = 10 * 1024 * 1024;
@@ -17892,7 +17947,7 @@ app9.post("/", async (c) => {
17892
17947
  tag: typeof body.tag === "string" ? truncate(body.tag, 32) : void 0,
17893
17948
  status: typeof body.status === "number" ? body.status : void 0
17894
17949
  };
17895
- appendFileSync4(CLIENT_ERRORS_LOG, JSON.stringify(payload) + "\n", "utf-8");
17950
+ appendFileSync5(CLIENT_ERRORS_LOG, JSON.stringify(payload) + "\n", "utf-8");
17896
17951
  } catch (err) {
17897
17952
  console.error(`[client-error] append failed: ${err instanceof Error ? err.message : String(err)}`);
17898
17953
  }
@@ -18004,12 +18059,14 @@ app10.post("/", async (c) => {
18004
18059
  var session_default2 = app10;
18005
18060
 
18006
18061
  // server/routes/admin/chat.ts
18007
- import { resolve as resolve18 } from "path";
18062
+ import { resolve as resolve19 } from "path";
18008
18063
 
18009
18064
  // app/lib/script-stream-tailer.ts
18010
- import { createReadStream as createReadStream2, statSync as statSync8 } from "fs";
18065
+ import * as childProcess from "child_process";
18066
+ import { appendFileSync as appendFileSync6, createReadStream as createReadStream2, mkdirSync as mkdirSync12, statSync as statSync8 } from "fs";
18067
+ import { dirname as dirname7 } from "path";
18011
18068
  import { StringDecoder as StringDecoder2 } from "string_decoder";
18012
- var SCRIPT_STREAM_RE = /^\[([^\]]+)\] \[(setup-tunnel|reset-tunnel)((?::[^\]]+)?)\] (.*)$/;
18069
+ var SCRIPT_STREAM_RE = /^\[([^\]]+)\] \[([a-z][a-z0-9-]*)((?::[a-z0-9:_-]+)?)\] (.*)$/;
18013
18070
  function parseLine(line) {
18014
18071
  const m = line.match(SCRIPT_STREAM_RE);
18015
18072
  if (!m) return void 0;
@@ -18102,6 +18159,106 @@ function startScriptStreamTailer(opts) {
18102
18159
  }
18103
18160
  };
18104
18161
  }
18162
+ var SCOPE_TOKEN_RE = /^[a-z][a-z0-9-]*$/;
18163
+ function writeRouteMilestone(streamLogPath, scope, line) {
18164
+ if (!SCOPE_TOKEN_RE.test(scope)) {
18165
+ throw new Error(
18166
+ `writeRouteMilestone: scope "${scope}" must match [a-z][a-z0-9-]* \u2014 the tailer regex won't match otherwise`
18167
+ );
18168
+ }
18169
+ const ts = (/* @__PURE__ */ new Date()).toISOString();
18170
+ try {
18171
+ mkdirSync12(dirname7(streamLogPath), { recursive: true });
18172
+ appendFileSync6(streamLogPath, `[${ts}] [${scope}] ${line}
18173
+ `);
18174
+ } catch (err) {
18175
+ console.error(
18176
+ `[script-stream-tailer] writeRouteMilestone failed path=${streamLogPath} scope=${scope}: ${err instanceof Error ? err.message : String(err)}`
18177
+ );
18178
+ }
18179
+ }
18180
+ function runFormSpawn(opts) {
18181
+ const { scriptPath, args, streamLogPath, correlationId, timeoutMs, log, logErr, broadcast } = opts;
18182
+ return new Promise((resolveP) => {
18183
+ let tailer = null;
18184
+ try {
18185
+ tailer = startScriptStreamTailer({
18186
+ path: streamLogPath,
18187
+ onEvent: (event) => {
18188
+ try {
18189
+ broadcast(correlationId, event);
18190
+ } catch (e) {
18191
+ logErr(
18192
+ `phase=tailer-broadcast-failed detail="${e instanceof Error ? e.message : String(e)}"`
18193
+ );
18194
+ }
18195
+ },
18196
+ onError: (e) => {
18197
+ logErr(`phase=tailer-error message="${e.message.slice(0, 160).replace(/"/g, "'")}"`);
18198
+ }
18199
+ });
18200
+ log(`phase=tailer-started`);
18201
+ } catch (e) {
18202
+ logErr(`phase=tailer-start-failed detail="${e instanceof Error ? e.message : String(e)}"`);
18203
+ tailer = null;
18204
+ }
18205
+ const child = childProcess.spawn(scriptPath, args, {
18206
+ env: { ...process.env, STREAM_LOG_PATH: streamLogPath },
18207
+ stdio: ["ignore", "pipe", "pipe"]
18208
+ });
18209
+ log(`phase=script-spawn pid=${child.pid ?? "unknown"} args="${args.join(" ")}"`);
18210
+ let stdout = "";
18211
+ let stderr = "";
18212
+ let timedOut = false;
18213
+ const killer = setTimeout(() => {
18214
+ timedOut = true;
18215
+ try {
18216
+ child.kill("SIGTERM");
18217
+ } catch {
18218
+ }
18219
+ }, timeoutMs);
18220
+ child.stdout?.on("data", (chunk) => {
18221
+ stdout += chunk.toString("utf-8");
18222
+ });
18223
+ child.stderr?.on("data", (chunk) => {
18224
+ stderr += chunk.toString("utf-8");
18225
+ });
18226
+ let finalized = false;
18227
+ const finalize = async (result) => {
18228
+ if (finalized) return;
18229
+ finalized = true;
18230
+ if (tailer) {
18231
+ try {
18232
+ await tailer.stop();
18233
+ log(`phase=tailer-stopped`);
18234
+ } catch (e) {
18235
+ logErr(
18236
+ `phase=tailer-stop-failed detail="${e instanceof Error ? e.message : String(e)}"`
18237
+ );
18238
+ }
18239
+ }
18240
+ resolveP(result);
18241
+ };
18242
+ child.on("error", (e) => {
18243
+ clearTimeout(killer);
18244
+ void finalize({
18245
+ code: null,
18246
+ signal: null,
18247
+ stdout,
18248
+ stderr: `${stderr}
18249
+ ${e.message}`,
18250
+ timedOut: false
18251
+ });
18252
+ });
18253
+ child.on("exit", (code, signal) => {
18254
+ clearTimeout(killer);
18255
+ log(
18256
+ `phase=script-exit code=${code ?? "null"} signal=${signal ?? "null"} timedOut=${timedOut}`
18257
+ );
18258
+ void finalize({ code, signal, stdout, stderr, timedOut });
18259
+ });
18260
+ });
18261
+ }
18105
18262
 
18106
18263
  // app/lib/admin-sse-registry.ts
18107
18264
  var activeAdminSSEControllers = /* @__PURE__ */ new Set();
@@ -18342,7 +18499,7 @@ app11.post("/", requireAdminSession, async (c) => {
18342
18499
  try {
18343
18500
  registerAdminSSE(sseEntry);
18344
18501
  if (sseConvId) {
18345
- const streamLogPath = resolve18(account.accountDir, "logs", `claude-agent-stream-${sseConvId}.log`);
18502
+ const streamLogPath = resolve19(account.accountDir, "logs", `claude-agent-stream-${sseConvId}.log`);
18346
18503
  tailer = startScriptStreamTailer({
18347
18504
  path: streamLogPath,
18348
18505
  onEvent: (event) => {
@@ -18468,7 +18625,7 @@ var compact_default = app12;
18468
18625
 
18469
18626
  // server/routes/admin/logs.ts
18470
18627
  import { existsSync as existsSync19, readdirSync as readdirSync5, readFileSync as readFileSync19, statSync as statSync9 } from "fs";
18471
- import { resolve as resolve19, basename as basename5 } from "path";
18628
+ import { resolve as resolve20, basename as basename5 } from "path";
18472
18629
  var TAIL_BYTES = 8192;
18473
18630
  var app13 = new Hono2();
18474
18631
  app13.get("/", async (c) => {
@@ -18477,13 +18634,13 @@ app13.get("/", async (c) => {
18477
18634
  const conversationIdParam = c.req.query("conversationId");
18478
18635
  const download = c.req.query("download") === "1";
18479
18636
  const account = resolveAccount();
18480
- const accountLogDir2 = account ? resolve19(account.accountDir, "logs") : null;
18637
+ const accountLogDir2 = account ? resolve20(account.accountDir, "logs") : null;
18481
18638
  if (fileParam) {
18482
18639
  const safe = basename5(fileParam);
18483
18640
  const searched = [];
18484
18641
  for (const dir of [accountLogDir2, LOG_DIR]) {
18485
18642
  if (!dir) continue;
18486
- const filePath = resolve19(dir, safe);
18643
+ const filePath = resolve20(dir, safe);
18487
18644
  searched.push(filePath);
18488
18645
  try {
18489
18646
  const content = readFileSync19(filePath, "utf-8");
@@ -18525,7 +18682,7 @@ app13.get("/", async (c) => {
18525
18682
  const searched = [];
18526
18683
  for (const dir of [accountLogDir2, LOG_DIR]) {
18527
18684
  if (!dir) continue;
18528
- const filePath = resolve19(dir, fileName);
18685
+ const filePath = resolve20(dir, fileName);
18529
18686
  searched.push(filePath);
18530
18687
  try {
18531
18688
  const content = readFileSync19(filePath, "utf-8");
@@ -18552,10 +18709,10 @@ app13.get("/", async (c) => {
18552
18709
  console.warn(`[admin/logs] readdir-fail dir=${dir} reason=${reason}`);
18553
18710
  continue;
18554
18711
  }
18555
- files.filter((f) => !seen.has(f)).map((f) => ({ name: f, mtime: statSync9(resolve19(dir, f)).mtimeMs })).sort((a, b) => b.mtime - a.mtime).forEach(({ name }) => {
18712
+ files.filter((f) => !seen.has(f)).map((f) => ({ name: f, mtime: statSync9(resolve20(dir, f)).mtimeMs })).sort((a, b) => b.mtime - a.mtime).forEach(({ name }) => {
18556
18713
  seen.add(name);
18557
18714
  try {
18558
- const content = readFileSync19(resolve19(dir, name));
18715
+ const content = readFileSync19(resolve20(dir, name));
18559
18716
  const tail = content.length > TAIL_BYTES ? content.subarray(content.length - TAIL_BYTES).toString("utf-8") : content.toString("utf-8");
18560
18717
  logs[name] = tail.trim() || "(empty)";
18561
18718
  } catch (err) {
@@ -18596,7 +18753,7 @@ var claude_info_default = app14;
18596
18753
  // server/routes/admin/attachment.ts
18597
18754
  import { readFile as readFile3, readdir } from "fs/promises";
18598
18755
  import { existsSync as existsSync20 } from "fs";
18599
- import { resolve as resolve20 } from "path";
18756
+ import { resolve as resolve21 } from "path";
18600
18757
  var app15 = new Hono2();
18601
18758
  app15.get("/:attachmentId", requireAdminSession, async (c) => {
18602
18759
  const attachmentId = c.req.param("attachmentId");
@@ -18608,11 +18765,11 @@ app15.get("/:attachmentId", requireAdminSession, async (c) => {
18608
18765
  if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/.test(attachmentId)) {
18609
18766
  return new Response("Not found", { status: 404 });
18610
18767
  }
18611
- const dir = resolve20(ATTACHMENTS_ROOT, accountId, attachmentId);
18768
+ const dir = resolve21(ATTACHMENTS_ROOT, accountId, attachmentId);
18612
18769
  if (!existsSync20(dir)) {
18613
18770
  return new Response("Not found", { status: 404 });
18614
18771
  }
18615
- const metaPath = resolve20(dir, `${attachmentId}.meta.json`);
18772
+ const metaPath = resolve21(dir, `${attachmentId}.meta.json`);
18616
18773
  if (!existsSync20(metaPath)) {
18617
18774
  return new Response("Not found", { status: 404 });
18618
18775
  }
@@ -18627,7 +18784,7 @@ app15.get("/:attachmentId", requireAdminSession, async (c) => {
18627
18784
  if (!dataFile) {
18628
18785
  return new Response("Not found", { status: 404 });
18629
18786
  }
18630
- const filePath = resolve20(dir, dataFile);
18787
+ const filePath = resolve21(dir, dataFile);
18631
18788
  const buffer = await readFile3(filePath);
18632
18789
  return new Response(new Uint8Array(buffer), {
18633
18790
  headers: {
@@ -18641,7 +18798,7 @@ var attachment_default = app15;
18641
18798
 
18642
18799
  // server/routes/admin/account.ts
18643
18800
  import { readFileSync as readFileSync20, writeFileSync as writeFileSync13 } from "fs";
18644
- import { resolve as resolve21 } from "path";
18801
+ import { resolve as resolve22 } from "path";
18645
18802
  var VALID_CONTEXT_MODES = ["managed", "claude-code"];
18646
18803
  var app16 = new Hono2();
18647
18804
  app16.patch("/", requireAdminSession, async (c) => {
@@ -18657,7 +18814,7 @@ app16.patch("/", requireAdminSession, async (c) => {
18657
18814
  }
18658
18815
  const account = resolveAccount();
18659
18816
  if (!account) return c.json({ error: "No account configured" }, 500);
18660
- const configPath2 = resolve21(account.accountDir, "account.json");
18817
+ const configPath2 = resolve22(account.accountDir, "account.json");
18661
18818
  try {
18662
18819
  const raw2 = readFileSync20(configPath2, "utf-8");
18663
18820
  const config = JSON.parse(raw2);
@@ -18673,13 +18830,13 @@ app16.patch("/", requireAdminSession, async (c) => {
18673
18830
  var account_default = app16;
18674
18831
 
18675
18832
  // server/routes/admin/agents.ts
18676
- import { resolve as resolve22 } from "path";
18833
+ import { resolve as resolve23 } from "path";
18677
18834
  import { readdirSync as readdirSync6, readFileSync as readFileSync21, existsSync as existsSync21, rmSync as rmSync3 } from "fs";
18678
18835
  var app17 = new Hono2();
18679
18836
  app17.get("/", (c) => {
18680
18837
  const account = resolveAccount();
18681
18838
  if (!account) return c.json({ agents: [] });
18682
- const agentsDir = resolve22(account.accountDir, "agents");
18839
+ const agentsDir = resolve23(account.accountDir, "agents");
18683
18840
  if (!existsSync21(agentsDir)) return c.json({ agents: [] });
18684
18841
  const agents = [];
18685
18842
  try {
@@ -18687,7 +18844,7 @@ app17.get("/", (c) => {
18687
18844
  for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
18688
18845
  if (!entry.isDirectory()) continue;
18689
18846
  if (entry.name === "admin") continue;
18690
- const configPath2 = resolve22(agentsDir, entry.name, "config.json");
18847
+ const configPath2 = resolve23(agentsDir, entry.name, "config.json");
18691
18848
  if (!existsSync21(configPath2)) continue;
18692
18849
  try {
18693
18850
  const config = JSON.parse(readFileSync21(configPath2, "utf-8"));
@@ -18716,7 +18873,7 @@ app17.delete("/:slug", (c) => {
18716
18873
  if (slug.includes("/") || slug.includes("..") || slug.includes("\\")) {
18717
18874
  return c.json({ error: "Invalid agent slug" }, 400);
18718
18875
  }
18719
- const agentDir = resolve22(account.accountDir, "agents", slug);
18876
+ const agentDir = resolve23(account.accountDir, "agents", slug);
18720
18877
  if (!existsSync21(agentDir)) {
18721
18878
  return c.json({ error: "Agent not found" }, 404);
18722
18879
  }
@@ -18732,74 +18889,27 @@ app17.delete("/:slug", (c) => {
18732
18889
  var agents_default = app17;
18733
18890
 
18734
18891
  // server/routes/admin/version.ts
18735
- import { spawn as spawn4 } from "child_process";
18736
- import { existsSync as existsSync23, statSync as statSync10, writeFileSync as writeFileSync14, readFileSync as readFileSync23, openSync as openSync5, closeSync as closeSync5 } from "fs";
18737
- import { resolve as resolve23, join as join11 } from "path";
18738
-
18739
- // app/lib/upgrade-progress-parser.ts
18740
18892
  import { existsSync as existsSync22, readFileSync as readFileSync22 } from "fs";
18741
- var STEP_RE = /\[(\d+)\/(\d+)\]\s+(.+)/;
18742
- var ANSI_RE = /\x1b\[[0-9;?]*[a-zA-Z]/g;
18743
- var stripAnsi = (s) => s.replace(ANSI_RE, "").replace(/\r/g, "").trimEnd();
18744
- var MAX_SUBSTEPS = 20;
18745
- function parseUpgradeProgress(content) {
18746
- const rawLines = content.split("\n");
18747
- let step = 0;
18748
- let total = 0;
18749
- let label = "";
18750
- let markerIdx = -1;
18751
- for (let i = rawLines.length - 1; i >= 0; i--) {
18752
- const match2 = rawLines[i].match(STEP_RE);
18753
- if (match2) {
18754
- step = parseInt(match2[1], 10);
18755
- total = parseInt(match2[2], 10);
18756
- label = match2[3].replace(/\.{3}$/, "").trim();
18757
- markerIdx = i;
18758
- break;
18759
- }
18760
- }
18761
- const subSteps = markerIdx >= 0 ? rawLines.slice(markerIdx + 1).map(stripAnsi).filter((l) => l.length > 0).slice(-MAX_SUBSTEPS) : [];
18762
- const tail = rawLines.slice(-20).join("\n");
18763
- const finished = tail.includes("Open in your browser:");
18764
- const failed = tail.includes("Setup failed:");
18765
- return { step, total, label, started: true, finished, failed, subSteps };
18766
- }
18767
- function readProgressLog(path2) {
18768
- if (!existsSync22(path2)) {
18769
- return { step: 0, total: 0, label: "", started: false, subSteps: [] };
18770
- }
18771
- let content;
18772
- try {
18773
- content = readFileSync22(path2, "utf-8");
18774
- } catch {
18775
- return { step: 0, total: 0, label: "", started: false, subSteps: [] };
18776
- }
18777
- return parseUpgradeProgress(content);
18778
- }
18779
-
18780
- // server/routes/admin/version.ts
18781
- var PLATFORM_ROOT9 = process.env.MAXY_PLATFORM_ROOT ?? resolve23(process.cwd(), "..");
18893
+ import { resolve as resolve24, join as join11 } from "path";
18894
+ var PLATFORM_ROOT9 = process.env.MAXY_PLATFORM_ROOT ?? resolve24(process.cwd(), "..");
18782
18895
  var brandHostname = "maxy";
18783
18896
  var brandNpmPackage = "@rubytech/create-maxy";
18784
18897
  var brandJsonPath = join11(PLATFORM_ROOT9, "config", "brand.json");
18785
- if (existsSync23(brandJsonPath)) {
18898
+ if (existsSync22(brandJsonPath)) {
18786
18899
  try {
18787
- const brand = JSON.parse(readFileSync23(brandJsonPath, "utf-8"));
18900
+ const brand = JSON.parse(readFileSync22(brandJsonPath, "utf-8"));
18788
18901
  if (brand.hostname) brandHostname = brand.hostname;
18789
18902
  if (brand.npm?.packageName) brandNpmPackage = brand.npm.packageName;
18790
18903
  } catch {
18791
18904
  }
18792
18905
  }
18793
- var VERSION_FILE = resolve23(PLATFORM_ROOT9, `config/.${brandHostname}-version`);
18906
+ var VERSION_FILE = resolve24(PLATFORM_ROOT9, `config/.${brandHostname}-version`);
18794
18907
  var NPM_PACKAGE = brandNpmPackage;
18795
18908
  var REGISTRY_URL = `https://registry.npmjs.org/${NPM_PACKAGE}/latest`;
18796
18909
  var FETCH_TIMEOUT_MS = 5e3;
18797
- var LOCK_FILE = `/tmp/${brandHostname}-upgrade.lock`;
18798
- var LOG_FILE = `/tmp/${brandHostname}-upgrade.log`;
18799
- var LOCK_MAX_AGE_MS = 20 * 60 * 1e3;
18800
18910
  function readInstalled() {
18801
- if (!existsSync23(VERSION_FILE)) return "unknown";
18802
- const content = readFileSync23(VERSION_FILE, "utf-8").trim();
18911
+ if (!existsSync22(VERSION_FILE)) return "unknown";
18912
+ const content = readFileSync22(VERSION_FILE, "utf-8").trim();
18803
18913
  return content || "unknown";
18804
18914
  }
18805
18915
  async function fetchLatest() {
@@ -18837,23 +18947,6 @@ function isNewer(latest, installed) {
18837
18947
  }
18838
18948
  return false;
18839
18949
  }
18840
- function isLockFresh() {
18841
- if (!existsSync23(LOCK_FILE)) return false;
18842
- try {
18843
- const st = statSync10(LOCK_FILE);
18844
- if (Date.now() - st.mtimeMs >= LOCK_MAX_AGE_MS) return false;
18845
- if (existsSync23(LOG_FILE)) {
18846
- const content = readFileSync23(LOG_FILE, "utf-8");
18847
- const tail = content.slice(-4e3);
18848
- if (tail.includes("Setup failed:") || tail.includes("Open in your browser:")) {
18849
- return false;
18850
- }
18851
- }
18852
- return true;
18853
- } catch {
18854
- return false;
18855
- }
18856
- }
18857
18950
  var app18 = new Hono2();
18858
18951
  app18.get("/", async (c) => {
18859
18952
  const installed = readInstalled();
@@ -18866,56 +18959,6 @@ app18.get("/", async (c) => {
18866
18959
  );
18867
18960
  return c.json({ installed, latest, updateAvailable, clientErrors24h });
18868
18961
  });
18869
- app18.post("/upgrade", requireAdminSession, (c) => {
18870
- if (isLockFresh()) {
18871
- return c.json({ ok: false, error: "upgrade already in progress" }, 409);
18872
- }
18873
- const installerScope = `upgrade-${brandHostname}`;
18874
- try {
18875
- writeFileSync14(LOCK_FILE, String(Date.now()));
18876
- } catch (err) {
18877
- console.error("[admin/version/upgrade] failed to write lock file:", err);
18878
- }
18879
- try {
18880
- writeFileSync14(LOG_FILE, "");
18881
- } catch (err) {
18882
- console.error("[admin/version/upgrade] failed to truncate upgrade log:", err);
18883
- return c.json(
18884
- { ok: false, error: err instanceof Error ? err.message : "failed to prepare upgrade log" },
18885
- 500
18886
- );
18887
- }
18888
- try {
18889
- const logFd = openSync5(LOG_FILE, "a");
18890
- const child = spawn4("systemd-run", [
18891
- "--user",
18892
- "--scope",
18893
- `--unit=${installerScope}`,
18894
- "--",
18895
- "npx",
18896
- "-y",
18897
- `${NPM_PACKAGE}@latest`
18898
- ], {
18899
- detached: true,
18900
- stdio: ["ignore", logFd, logFd],
18901
- env: { ...process.env, npm_config_yes: "true" },
18902
- cwd: resolve23(process.cwd(), "..")
18903
- });
18904
- child.unref();
18905
- closeSync5(logFd);
18906
- console.log(`[admin/version/upgrade] spawned upgrade process (pid ${child.pid} scope ${installerScope})`);
18907
- return c.json({ ok: true, started: true });
18908
- } catch (err) {
18909
- console.error("[admin/version/upgrade] failed to spawn upgrade process:", err);
18910
- return c.json(
18911
- { ok: false, error: err instanceof Error ? err.message : "failed to start upgrade" },
18912
- 500
18913
- );
18914
- }
18915
- });
18916
- app18.get("/upgrade/progress", (c) => {
18917
- return c.json(readProgressLog(LOG_FILE));
18918
- });
18919
18962
  app18.post("/alert-surfaced", async (c) => {
18920
18963
  let installed = "unknown";
18921
18964
  let latest = null;
@@ -19299,10 +19342,9 @@ app22.post("/", async (c) => {
19299
19342
  var events_default = app22;
19300
19343
 
19301
19344
  // server/routes/admin/cloudflare.ts
19302
- import * as childProcess from "child_process";
19303
19345
  import { homedir as homedir4 } from "os";
19304
- import { resolve as resolve25 } from "path";
19305
- import { readFileSync as readFileSync25 } from "fs";
19346
+ import { resolve as resolve26 } from "path";
19347
+ import { readFileSync as readFileSync24 } from "fs";
19306
19348
 
19307
19349
  // app/lib/dns-label.ts
19308
19350
  var VALID_LABEL = /^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$/;
@@ -19318,14 +19360,14 @@ function isValidDomain(value) {
19318
19360
  }
19319
19361
 
19320
19362
  // app/lib/alias-domains.ts
19321
- import { existsSync as existsSync24, mkdirSync as mkdirSync11, readFileSync as readFileSync24, writeFileSync as writeFileSync15 } from "fs";
19322
- import { dirname as dirname7 } from "path";
19323
- import { resolve as resolve24 } from "path";
19324
- var ALIAS_DOMAINS_PATH = resolve24(MAXY_DIR, "alias-domains.json");
19363
+ import { existsSync as existsSync23, mkdirSync as mkdirSync13, readFileSync as readFileSync23, writeFileSync as writeFileSync14 } from "fs";
19364
+ import { dirname as dirname8 } from "path";
19365
+ import { resolve as resolve25 } from "path";
19366
+ var ALIAS_DOMAINS_PATH = resolve25(MAXY_DIR, "alias-domains.json");
19325
19367
  function readExisting() {
19326
- if (!existsSync24(ALIAS_DOMAINS_PATH)) return /* @__PURE__ */ new Set();
19368
+ if (!existsSync23(ALIAS_DOMAINS_PATH)) return /* @__PURE__ */ new Set();
19327
19369
  try {
19328
- const parsed = JSON.parse(readFileSync24(ALIAS_DOMAINS_PATH, "utf-8"));
19370
+ const parsed = JSON.parse(readFileSync23(ALIAS_DOMAINS_PATH, "utf-8"));
19329
19371
  if (!Array.isArray(parsed)) return /* @__PURE__ */ new Set();
19330
19372
  return new Set(parsed.filter((h) => typeof h === "string"));
19331
19373
  } catch {
@@ -19336,17 +19378,18 @@ function addAliasDomain(hostname2) {
19336
19378
  const existing = readExisting();
19337
19379
  if (existing.has(hostname2)) return;
19338
19380
  existing.add(hostname2);
19339
- mkdirSync11(dirname7(ALIAS_DOMAINS_PATH), { recursive: true });
19340
- writeFileSync15(ALIAS_DOMAINS_PATH, JSON.stringify([...existing], null, 2) + "\n", "utf-8");
19381
+ mkdirSync13(dirname8(ALIAS_DOMAINS_PATH), { recursive: true });
19382
+ writeFileSync14(ALIAS_DOMAINS_PATH, JSON.stringify([...existing], null, 2) + "\n", "utf-8");
19341
19383
  }
19342
19384
 
19343
19385
  // server/routes/admin/cloudflare.ts
19344
- var SCRIPT_TIMEOUT_MS = 10 * 60 * 1e3;
19386
+ var SETUP_TIMEOUT_MS = 10 * 60 * 1e3;
19387
+ var DOMAINS_TIMEOUT_MS = 40 * 1e3;
19345
19388
  function loadBrandInfo() {
19346
- const platformRoot3 = process.env.MAXY_PLATFORM_ROOT ?? resolve25(process.cwd(), "..");
19347
- const brandPath = resolve25(platformRoot3, "config", "brand.json");
19389
+ const platformRoot3 = process.env.MAXY_PLATFORM_ROOT ?? resolve26(process.cwd(), "..");
19390
+ const brandPath = resolve26(platformRoot3, "config", "brand.json");
19348
19391
  try {
19349
- const parsed = JSON.parse(readFileSync25(brandPath, "utf-8"));
19392
+ const parsed = JSON.parse(readFileSync24(brandPath, "utf-8"));
19350
19393
  const hostname2 = typeof parsed.hostname === "string" && parsed.hostname ? parsed.hostname : "maxy";
19351
19394
  const configDir2 = typeof parsed.configDir === "string" && parsed.configDir ? parsed.configDir : ".maxy";
19352
19395
  return { hostname: hostname2, configDir: configDir2 };
@@ -19389,78 +19432,7 @@ function validateBody(body) {
19389
19432
  }
19390
19433
  return null;
19391
19434
  }
19392
- function runScript(scriptPath, args, streamLogPath, log) {
19393
- return new Promise((resolveP) => {
19394
- const child = childProcess.spawn(scriptPath, args, {
19395
- env: { ...process.env, STREAM_LOG_PATH: streamLogPath },
19396
- stdio: ["ignore", "pipe", "pipe"]
19397
- });
19398
- log(`phase=script-spawn pid=${child.pid ?? "unknown"} args="${args.join(" ")}"`);
19399
- let stdout = "";
19400
- let stderr = "";
19401
- let timedOut = false;
19402
- const killer = setTimeout(() => {
19403
- timedOut = true;
19404
- try {
19405
- child.kill("SIGTERM");
19406
- } catch {
19407
- }
19408
- }, SCRIPT_TIMEOUT_MS);
19409
- child.stdout?.on("data", (chunk) => {
19410
- stdout += chunk.toString("utf-8");
19411
- });
19412
- child.stderr?.on("data", (chunk) => {
19413
- stderr += chunk.toString("utf-8");
19414
- });
19415
- child.on("error", (e) => {
19416
- clearTimeout(killer);
19417
- resolveP({ code: null, signal: null, stdout, stderr: `${stderr}
19418
- ${e.message}`, timedOut: false });
19419
- });
19420
- child.on("exit", (code, signal) => {
19421
- clearTimeout(killer);
19422
- log(`phase=script-exit code=${code ?? "null"} signal=${signal ?? "null"} timedOut=${timedOut}`);
19423
- resolveP({ code, signal, stdout, stderr, timedOut });
19424
- });
19425
- });
19426
- }
19427
19435
  var app23 = new Hono2();
19428
- var DOMAINS_TIMEOUT_MS = 40 * 1e3;
19429
- function runListScript(scriptPath, args, streamLogPath, log) {
19430
- return new Promise((resolveP) => {
19431
- const child = childProcess.spawn(scriptPath, args, {
19432
- env: { ...process.env, STREAM_LOG_PATH: streamLogPath },
19433
- stdio: ["ignore", "pipe", "pipe"]
19434
- });
19435
- log(`phase=script-spawn pid=${child.pid ?? "unknown"} args="${args.join(" ")}"`);
19436
- let stdout = "";
19437
- let stderr = "";
19438
- let timedOut = false;
19439
- const killer = setTimeout(() => {
19440
- timedOut = true;
19441
- try {
19442
- child.kill("SIGTERM");
19443
- } catch {
19444
- }
19445
- }, DOMAINS_TIMEOUT_MS);
19446
- child.stdout?.on("data", (chunk) => {
19447
- stdout += chunk.toString("utf-8");
19448
- });
19449
- child.stderr?.on("data", (chunk) => {
19450
- stderr += chunk.toString("utf-8");
19451
- });
19452
- child.on("error", (e) => {
19453
- clearTimeout(killer);
19454
- resolveP({ code: null, signal: null, stdout, stderr: `${stderr}
19455
- ${e.message}`, timedOut: false });
19456
- });
19457
- child.on("exit", (code, signal) => {
19458
- clearTimeout(killer);
19459
- log(`phase=script-exit code=${code ?? "null"} signal=${signal ?? "null"} timedOut=${timedOut}`);
19460
- resolveP({ code, signal, stdout, stderr, timedOut });
19461
- });
19462
- });
19463
- }
19464
19436
  function fieldFromReason(reason) {
19465
19437
  switch (reason) {
19466
19438
  case "not-signed-in":
@@ -19495,6 +19467,13 @@ app23.get("/domains", requireAdminSession, async (c) => {
19495
19467
  }
19496
19468
  function err(field, message, output) {
19497
19469
  logErr(`phase=error field=${field} reason="${message.slice(0, 160).replace(/"/g, "'")}"`);
19470
+ if (streamLogPath) {
19471
+ writeRouteMilestone(
19472
+ streamLogPath,
19473
+ "cloudflare-domains",
19474
+ `phase=error field=${field} reason="${message.slice(0, 160).replace(/"/g, "'")}"`
19475
+ );
19476
+ }
19498
19477
  const body = {
19499
19478
  ok: false,
19500
19479
  field,
@@ -19513,8 +19492,17 @@ app23.get("/domains", requireAdminSession, async (c) => {
19513
19492
  streamLogPath = streamLogPathFor(accountId, correlationId).streamLogPath;
19514
19493
  log(`phase=stream-log-resolved path=${streamLogPath}`);
19515
19494
  const brand = loadBrandInfo();
19516
- const scriptPath = resolve25(homedir4(), "list-cf-domains.sh");
19517
- const result = await runListScript(scriptPath, [brand.hostname], streamLogPath, log);
19495
+ const scriptPath = resolve26(homedir4(), "list-cf-domains.sh");
19496
+ const result = await runFormSpawn({
19497
+ scriptPath,
19498
+ args: [brand.hostname],
19499
+ streamLogPath,
19500
+ correlationId,
19501
+ timeoutMs: DOMAINS_TIMEOUT_MS,
19502
+ log,
19503
+ logErr,
19504
+ broadcast: broadcastToConversation
19505
+ });
19518
19506
  const combined = `${result.stdout}${result.stderr ? `
19519
19507
  ---
19520
19508
  ${result.stderr}` : ""}`;
@@ -19547,6 +19535,11 @@ ${result.stderr}` : ""}`;
19547
19535
  };
19548
19536
  const total = Date.now() - started;
19549
19537
  log(`phase=response-sent total_ms=${total} count=${domains.length}`);
19538
+ writeRouteMilestone(
19539
+ streamLogPath,
19540
+ "cloudflare-domains",
19541
+ `phase=response-sent total_ms=${total} count=${domains.length}`
19542
+ );
19550
19543
  return c.json(success, 200);
19551
19544
  });
19552
19545
  app23.post("/setup", requireAdminSession, async (c) => {
@@ -19567,6 +19560,13 @@ app23.post("/setup", requireAdminSession, async (c) => {
19567
19560
  }
19568
19561
  function err(field, message, output) {
19569
19562
  logErr(`phase=error field=${field} reason="${message.slice(0, 160).replace(/"/g, "'")}"`);
19563
+ if (streamLogPath) {
19564
+ writeRouteMilestone(
19565
+ streamLogPath,
19566
+ "cloudflare-setup",
19567
+ `phase=error field=${field} reason="${message.slice(0, 160).replace(/"/g, "'")}"`
19568
+ );
19569
+ }
19570
19570
  const body2 = {
19571
19571
  ok: false,
19572
19572
  field,
@@ -19617,47 +19617,25 @@ app23.post("/setup", requireAdminSession, async (c) => {
19617
19617
  }
19618
19618
  streamLogPath = streamLogPathFor(accountId, correlationId).streamLogPath;
19619
19619
  log(`phase=stream-log-resolved path=${streamLogPath}`);
19620
- let tailer = null;
19621
- try {
19622
- tailer = startScriptStreamTailer({
19623
- path: streamLogPath,
19624
- onEvent: (event) => {
19625
- if (correlationId) broadcastToConversation(correlationId, event);
19626
- },
19627
- onError: (e) => {
19628
- logErr(`phase=tailer-error message="${e.message.slice(0, 160).replace(/"/g, "'")}"`);
19629
- }
19630
- });
19631
- log(`phase=tailer-started`);
19632
- } catch (e) {
19633
- logErr(`phase=tailer-start-failed detail="${e instanceof Error ? e.message : String(e)}"`);
19634
- tailer = null;
19635
- }
19636
- const scriptPath = resolve25(homedir4(), "setup-tunnel.sh");
19620
+ const scriptPath = resolve26(homedir4(), "setup-tunnel.sh");
19637
19621
  const args = [brand.hostname, String(port2), adminFqdn];
19638
19622
  if (publicFqdn) args.push(publicFqdn);
19639
19623
  if (apex) args.push(apex);
19640
- let result = null;
19641
- try {
19642
- result = await runScript(scriptPath, args, streamLogPath, log);
19643
- } finally {
19644
- if (tailer) {
19645
- try {
19646
- await tailer.stop();
19647
- log(`phase=tailer-stopped`);
19648
- } catch (e) {
19649
- logErr(`phase=tailer-stop-failed detail="${e instanceof Error ? e.message : String(e)}"`);
19650
- }
19651
- }
19652
- }
19653
- if (!result) {
19654
- return err("script", "Script spawn failed before producing a result");
19655
- }
19624
+ const result = await runFormSpawn({
19625
+ scriptPath,
19626
+ args,
19627
+ streamLogPath,
19628
+ correlationId,
19629
+ timeoutMs: SETUP_TIMEOUT_MS,
19630
+ log,
19631
+ logErr,
19632
+ broadcast: broadcastToConversation
19633
+ });
19656
19634
  const combined = `${result.stdout}${result.stderr ? `
19657
19635
  ---
19658
19636
  ${result.stderr}` : ""}`;
19659
19637
  if (result.code !== 0) {
19660
- const reason = result.timedOut ? `Timed out after ${SCRIPT_TIMEOUT_MS / 1e3}s` : `Exited with code ${result.code ?? "null"}${result.signal ? ` (signal ${result.signal})` : ""}`;
19638
+ const reason = result.timedOut ? `Timed out after ${SETUP_TIMEOUT_MS / 1e3}s` : `Exited with code ${result.code ?? "null"}${result.signal ? ` (signal ${result.signal})` : ""}`;
19661
19639
  return err("script", reason, combined);
19662
19640
  }
19663
19641
  const candidates = [publicFqdn, apex].filter((h) => typeof h === "string" && h.length > 0);
@@ -19685,6 +19663,11 @@ ${result.stderr}` : ""}`;
19685
19663
  }
19686
19664
  const total = Date.now() - started;
19687
19665
  log(`phase=done total_ms=${total} aliases=${aliasesWritten.length}`);
19666
+ writeRouteMilestone(
19667
+ streamLogPath,
19668
+ "cloudflare-setup",
19669
+ `phase=done total_ms=${total} aliases=${aliasesWritten.length}`
19670
+ );
19688
19671
  const success = {
19689
19672
  ok: true,
19690
19673
  output: combined,
@@ -19701,19 +19684,19 @@ var cloudflare_default = app23;
19701
19684
 
19702
19685
  // server/routes/admin/files.ts
19703
19686
  import { createReadStream as createReadStream3 } from "fs";
19704
- import { readdir as readdir2, readFile as readFile4, stat as stat4, mkdir as mkdir3, writeFile as writeFile4 } from "fs/promises";
19687
+ import { readdir as readdir2, readFile as readFile4, stat as stat4, mkdir as mkdir3, writeFile as writeFile4, unlink as unlink2 } from "fs/promises";
19705
19688
  import { realpathSync as realpathSync4 } from "fs";
19706
- import { basename as basename6, join as join12, resolve as resolve27, sep as sep2 } from "path";
19689
+ import { basename as basename6, dirname as dirname9, join as join12, resolve as resolve28, sep as sep2 } from "path";
19707
19690
  import { Readable as Readable3 } from "stream";
19708
19691
 
19709
19692
  // app/lib/data-path.ts
19710
19693
  import { realpathSync as realpathSync3 } from "fs";
19711
- import { resolve as resolve26, normalize, sep, relative } from "path";
19712
- var PLATFORM_ROOT10 = process.env.MAXY_PLATFORM_ROOT ?? resolve26(process.cwd(), "../platform");
19713
- var DATA_ROOT = resolve26(PLATFORM_ROOT10, "..", "data");
19694
+ import { resolve as resolve27, normalize, sep, relative } from "path";
19695
+ var PLATFORM_ROOT10 = process.env.MAXY_PLATFORM_ROOT ?? resolve27(process.cwd(), "../platform");
19696
+ var DATA_ROOT = resolve27(PLATFORM_ROOT10, "..", "data");
19714
19697
  function resolveDataPath(raw2) {
19715
19698
  const cleaned = normalize("/" + (raw2 ?? "").replace(/\\/g, "/")).replace(/^\/+/, "");
19716
- const absolute = resolve26(DATA_ROOT, cleaned);
19699
+ const absolute = resolve27(DATA_ROOT, cleaned);
19717
19700
  let dataRootReal;
19718
19701
  try {
19719
19702
  dataRootReal = realpathSync3(DATA_ROOT);
@@ -19761,12 +19744,44 @@ async function readMeta(absDir, baseName) {
19761
19744
  }
19762
19745
  return null;
19763
19746
  }
19764
- async function enrich(absolute, entry) {
19747
+ async function readAccountNames() {
19748
+ const map = /* @__PURE__ */ new Map();
19749
+ const accountsDir = resolve28(DATA_ROOT, "accounts");
19750
+ let names;
19751
+ try {
19752
+ names = await readdir2(accountsDir);
19753
+ } catch {
19754
+ return map;
19755
+ }
19756
+ for (const name of names) {
19757
+ if (!UUID_RE2.test(name)) continue;
19758
+ const configPath2 = resolve28(accountsDir, name, "account.json");
19759
+ try {
19760
+ const raw2 = await readFile4(configPath2, "utf8");
19761
+ const parsed = JSON.parse(raw2);
19762
+ if (typeof parsed?.name === "string" && parsed.name.length > 0) {
19763
+ map.set(name, parsed.name);
19764
+ } else {
19765
+ console.error(`[data] scope-resolve-miss uuid="${name}" reason="no-name"`);
19766
+ }
19767
+ } catch (err) {
19768
+ const code = err.code;
19769
+ console.error(`[data] scope-resolve-miss uuid="${name}" reason="${code === "ENOENT" ? "enoent" : "parse"}"`);
19770
+ }
19771
+ }
19772
+ return map;
19773
+ }
19774
+ async function enrich(absolute, entry, accountNames) {
19765
19775
  if (entry.kind === "directory" && UUID_RE2.test(entry.name)) {
19766
19776
  const meta = await readMeta(join12(absolute, entry.name), entry.name);
19767
19777
  if (meta?.filename) {
19768
19778
  entry.displayName = meta.filename;
19769
19779
  entry.mimeType = meta.mimeType;
19780
+ return;
19781
+ }
19782
+ const accountName = accountNames.get(entry.name);
19783
+ if (accountName) {
19784
+ entry.displayName = accountName;
19770
19785
  }
19771
19786
  return;
19772
19787
  }
@@ -19782,6 +19797,13 @@ async function enrich(absolute, entry) {
19782
19797
  }
19783
19798
  }
19784
19799
  }
19800
+ function buildDisplayPath(relPath, accountNames) {
19801
+ if (relPath === "." || relPath === "") return [];
19802
+ return relPath.split("/").filter(Boolean).map((seg) => {
19803
+ const dn = UUID_RE2.test(seg) ? accountNames.get(seg) : void 0;
19804
+ return dn ? { name: seg, displayName: dn } : { name: seg };
19805
+ });
19806
+ }
19785
19807
  var app24 = new Hono2();
19786
19808
  app24.get("/", requireAdminSession, async (c) => {
19787
19809
  const sessionKey = c.var.sessionKey;
@@ -19822,15 +19844,17 @@ app24.get("/", requireAdminSession, async (c) => {
19822
19844
  entries.push({ name, kind: "other", sizeBytes: null, modifiedAt: (/* @__PURE__ */ new Date(0)).toISOString() });
19823
19845
  }
19824
19846
  }
19825
- await Promise.all(entries.map((e) => enrich(absolute, e)));
19847
+ const accountNames = await readAccountNames();
19848
+ await Promise.all(entries.map((e) => enrich(absolute, e, accountNames)));
19826
19849
  entries.sort((a, b) => {
19827
19850
  if (a.kind !== b.kind) return a.kind === "directory" ? -1 : 1;
19828
19851
  const aKey = a.displayName ?? a.name;
19829
19852
  const bKey = b.displayName ?? b.name;
19830
19853
  return aKey.localeCompare(bKey);
19831
19854
  });
19855
+ const displayPath = buildDisplayPath(relPath, accountNames);
19832
19856
  console.error(`[data] file-list path="${relPath}" entries=${entries.length}`);
19833
- return c.json({ path: relPath, entries });
19857
+ return c.json({ path: relPath, displayPath, entries });
19834
19858
  } catch (err) {
19835
19859
  const code = err.code;
19836
19860
  if (code === "ENOENT") {
@@ -19922,8 +19946,8 @@ app24.post("/upload", requireAdminSession, async (c) => {
19922
19946
  }
19923
19947
  const safeName = basename6(file.name).replace(/[\0/\\]/g, "_");
19924
19948
  const finalName = `${Date.now()}-${safeName}`;
19925
- const destDir = resolve27(DATA_ROOT, "uploads", accountId);
19926
- const destPath = resolve27(destDir, finalName);
19949
+ const destDir = resolve28(DATA_ROOT, "uploads", accountId);
19950
+ const destPath = resolve28(destDir, finalName);
19927
19951
  try {
19928
19952
  await mkdir3(destDir, { recursive: true });
19929
19953
  const dataRootReal = realpathSync4(DATA_ROOT);
@@ -19948,6 +19972,59 @@ app24.post("/upload", requireAdminSession, async (c) => {
19948
19972
  mimeType: file.type
19949
19973
  });
19950
19974
  });
19975
+ app24.delete("/", requireAdminSession, async (c) => {
19976
+ const sessionKey = c.var.sessionKey;
19977
+ if (!getAccountIdForSession(sessionKey)) {
19978
+ console.error(`[data] auth-rejected endpoint="DELETE /api/admin/files" reason="no account for session"`);
19979
+ return c.json({ error: "Account not found for session" }, 401);
19980
+ }
19981
+ const rawPath = c.req.query("path") ?? "";
19982
+ if (!rawPath) return c.json({ error: "path required" }, 400);
19983
+ const resolution = resolveDataPath(rawPath);
19984
+ if (!resolution.ok) {
19985
+ if (resolution.status === 403) {
19986
+ console.error(`[data] path-traversal-blocked requested="${rawPath}" resolved="${resolution.resolved ?? "n/a"}"`);
19987
+ }
19988
+ return c.json({ error: resolution.error }, resolution.status);
19989
+ }
19990
+ const { absolute, relative: relPath } = resolution;
19991
+ const base = basename6(absolute);
19992
+ const segments = relPath.split("/").filter(Boolean);
19993
+ if (base === "account.json" || segments.includes(".git")) {
19994
+ console.error(`[data] file-delete blocked path="${relPath}" reason="protected"`);
19995
+ return c.json({ error: "Protected file \u2014 refusing to delete" }, 403);
19996
+ }
19997
+ try {
19998
+ const info = await stat4(absolute);
19999
+ if (info.isDirectory()) {
20000
+ return c.json({ error: "Directory deletion not supported" }, 400);
20001
+ }
20002
+ if (!info.isFile()) {
20003
+ return c.json({ error: "Not a regular file" }, 400);
20004
+ }
20005
+ const dot = base.lastIndexOf(".");
20006
+ const stem = dot === -1 ? base : base.slice(0, dot);
20007
+ const sidecarPath = UUID_RE2.test(stem) && base !== `${stem}.meta.json` ? join12(dirname9(absolute), `${stem}.meta.json`) : null;
20008
+ await unlink2(absolute);
20009
+ if (sidecarPath) {
20010
+ try {
20011
+ await unlink2(sidecarPath);
20012
+ } catch {
20013
+ }
20014
+ }
20015
+ console.error(`[data] file-delete path="${relPath}" bytes=${info.size}`);
20016
+ return c.json({ ok: true });
20017
+ } catch (err) {
20018
+ const code = err.code;
20019
+ if (code === "ENOENT") {
20020
+ console.error(`[data] file-delete not-found path="${relPath}"`);
20021
+ return c.json({ error: "Not found" }, 404);
20022
+ }
20023
+ const message = err instanceof Error ? err.message : String(err);
20024
+ console.error(`[data] file-delete error path="${relPath}" err="${message}"`);
20025
+ return c.json({ error: message }, 500);
20026
+ }
20027
+ });
19951
20028
  var files_default = app24;
19952
20029
 
19953
20030
  // server/routes/admin/graph-search.ts
@@ -19981,9 +20058,152 @@ app25.get("/", requireAdminSession, async (c) => {
19981
20058
  });
19982
20059
  var graph_search_default = app25;
19983
20060
 
19984
- // server/routes/admin/file-attach.ts
20061
+ // app/lib/graph-labels.ts
20062
+ var GRAPH_LABEL_COLOURS = {
20063
+ // Business identity
20064
+ LocalBusiness: "#2E5090",
20065
+ Service: "#3A6BB0",
20066
+ PriceSpecification: "#5B8DD0",
20067
+ OpeningHoursSpecification: "#7AAFE8",
20068
+ // People
20069
+ Person: "#C2410C",
20070
+ UserProfile: "#EA580C",
20071
+ Preference: "#F97316",
20072
+ AdminUser: "#9A3412",
20073
+ AccessGrant: "#7C2D12",
20074
+ // Knowledge
20075
+ KnowledgeDocument: "#16A34A",
20076
+ Section: "#22C55E",
20077
+ Chunk: "#4ADE80",
20078
+ DigitalDocument: "#15803D",
20079
+ CreativeWork: "#86EFAC",
20080
+ Question: "#A7F3D0",
20081
+ FAQPage: "#34D399",
20082
+ DefinedTerm: "#10B981",
20083
+ Review: "#059669",
20084
+ ImageObject: "#6EE7B7",
20085
+ // Conversational
20086
+ Conversation: "#7C3AED",
20087
+ Message: "#A78BFA",
20088
+ ToolCall: "#C4B5FD",
20089
+ // Tasks / projects / events
20090
+ Task: "#DB2777",
20091
+ Project: "#BE185D",
20092
+ Event: "#EC4899",
20093
+ // Workflows
20094
+ Workflow: "#0891B2",
20095
+ WorkflowStep: "#06B6D4",
20096
+ WorkflowRun: "#22D3EE",
20097
+ StepResult: "#67E8F9",
20098
+ // Onboarding
20099
+ OnboardingState: "#8B5CF6",
20100
+ // Email
20101
+ Email: "#65A30D",
20102
+ EmailAccount: "#84CC16"
20103
+ };
20104
+ var ALL_GRAPH_LABELS = Object.freeze(
20105
+ Object.keys(GRAPH_LABEL_COLOURS)
20106
+ );
20107
+ function isKnownLabel(label) {
20108
+ return Object.prototype.hasOwnProperty.call(GRAPH_LABEL_COLOURS, label);
20109
+ }
20110
+
20111
+ // server/routes/admin/graph-subgraph.ts
20112
+ var DEFAULT_LIMIT2 = 200;
20113
+ var MAX_LIMIT2 = 500;
20114
+ var STRIPPED_PROPERTIES = /* @__PURE__ */ new Set([
20115
+ "embedding",
20116
+ "passwordHash",
20117
+ "magicToken",
20118
+ "otpCode",
20119
+ "sessionKey"
20120
+ ]);
19985
20121
  var app26 = new Hono2();
19986
- app26.post("/", async (c) => {
20122
+ app26.get("/", requireAdminSession, async (c) => {
20123
+ const sessionKey = c.var.sessionKey;
20124
+ const accountId = getAccountIdForSession(sessionKey);
20125
+ if (!accountId) {
20126
+ console.error('[graph-page] auth-rejected reason="no account for session"');
20127
+ return c.json({ error: "Account not found for session" }, 401);
20128
+ }
20129
+ const rawLimit = c.req.query("limit");
20130
+ const parsedLimit = rawLimit ? parseInt(rawLimit, 10) : DEFAULT_LIMIT2;
20131
+ const limit = Number.isFinite(parsedLimit) && parsedLimit > 0 ? Math.min(parsedLimit, MAX_LIMIT2) : DEFAULT_LIMIT2;
20132
+ const rawLabels = c.req.query("labels");
20133
+ const labels = rawLabels ? rawLabels.split(",").map((s) => s.trim()).filter(Boolean) : [];
20134
+ for (const label of labels) {
20135
+ if (!isKnownLabel(label)) {
20136
+ return c.json(
20137
+ { error: `unknown label "${label}" \u2014 not registered in graph-labels.ts` },
20138
+ 400
20139
+ );
20140
+ }
20141
+ }
20142
+ const started = Date.now();
20143
+ const session = getSession();
20144
+ try {
20145
+ const cypher = labels.length > 0 ? buildCypher(true) : buildCypher(false);
20146
+ const params = { accountId, limit };
20147
+ if (labels.length > 0) params.labels = labels;
20148
+ const result = await session.run(cypher, params);
20149
+ const record = result.records[0];
20150
+ const rawNodes = record?.get("nodes") ?? [];
20151
+ const rawEdges = record?.get("edges") ?? [];
20152
+ const nodes = rawNodes.map(pruneNode);
20153
+ const edges = rawEdges.filter((e) => e && e.id != null);
20154
+ const elapsed = Date.now() - started;
20155
+ console.error(
20156
+ `[graph-page] load account=${accountId} nodes=${nodes.length} edges=${edges.length} ms=${elapsed}`
20157
+ );
20158
+ return c.json({ nodes, edges });
20159
+ } catch (err) {
20160
+ const elapsed = Date.now() - started;
20161
+ const message = err instanceof Error ? err.message : String(err);
20162
+ console.error(`[graph-page] error stage=fetch reason="${message}" ms=${elapsed}`);
20163
+ return c.json({ error: `Graph data unavailable: ${message}` }, 503);
20164
+ } finally {
20165
+ try {
20166
+ await session.close();
20167
+ } catch {
20168
+ }
20169
+ }
20170
+ });
20171
+ function buildCypher(filtered) {
20172
+ const labelGuard = filtered ? "AND any(lbl IN labels(n) WHERE lbl IN $labels)" : "";
20173
+ return `
20174
+ MATCH (n)
20175
+ WHERE n.accountId = $accountId ${labelGuard}
20176
+ WITH n ORDER BY coalesce(n.updatedAt, n.createdAt, '') DESC
20177
+ LIMIT toInteger($limit)
20178
+ WITH collect(n) AS nodes, collect(elementId(n)) AS nodeIds
20179
+ UNWIND nodes AS n
20180
+ OPTIONAL MATCH (n)-[r]-(m)
20181
+ WHERE elementId(m) IN nodeIds
20182
+ WITH nodes,
20183
+ collect(DISTINCT CASE WHEN r IS NULL THEN null ELSE {
20184
+ id: elementId(r),
20185
+ from: elementId(startNode(r)),
20186
+ to: elementId(endNode(r)),
20187
+ type: type(r)
20188
+ } END) AS rawEdges
20189
+ RETURN
20190
+ [x IN nodes | {id: elementId(x), labels: labels(x), properties: properties(x)}] AS nodes,
20191
+ [e IN rawEdges WHERE e IS NOT NULL] AS edges
20192
+ `;
20193
+ }
20194
+ function pruneNode(node) {
20195
+ const properties = {};
20196
+ for (const [key, value] of Object.entries(node.properties ?? {})) {
20197
+ if (STRIPPED_PROPERTIES.has(key)) continue;
20198
+ properties[key] = value;
20199
+ }
20200
+ return { id: node.id, labels: node.labels, properties };
20201
+ }
20202
+ var graph_subgraph_default = app26;
20203
+
20204
+ // server/routes/admin/file-attach.ts
20205
+ var app27 = new Hono2();
20206
+ app27.post("/", async (c) => {
19987
20207
  try {
19988
20208
  const body = await c.req.json();
19989
20209
  const { filePath, accountId } = body;
@@ -20006,39 +20226,40 @@ app26.post("/", async (c) => {
20006
20226
  return c.json({ error: message }, 500);
20007
20227
  }
20008
20228
  });
20009
- var file_attach_default = app26;
20229
+ var file_attach_default = app27;
20010
20230
 
20011
20231
  // server/routes/admin/index.ts
20012
- var app27 = new Hono2();
20013
- app27.route("/session", session_default2);
20014
- app27.route("/chat", chat_default2);
20015
- app27.route("/compact", compact_default);
20016
- app27.route("/logs", logs_default);
20017
- app27.route("/claude-info", claude_info_default);
20018
- app27.route("/attachment", attachment_default);
20019
- app27.route("/account", account_default);
20020
- app27.route("/agents", agents_default);
20021
- app27.route("/version", version_default);
20022
- app27.route("/sessions", sessions_default);
20023
- app27.route("/browser", browser_default);
20024
- app27.route("/device-browser", device_browser_default);
20025
- app27.route("/events", events_default);
20026
- app27.route("/cloudflare", cloudflare_default);
20027
- app27.route("/files", files_default);
20028
- app27.route("/graph-search", graph_search_default);
20029
- app27.route("/file-attach", file_attach_default);
20030
- var admin_default = app27;
20232
+ var app28 = new Hono2();
20233
+ app28.route("/session", session_default2);
20234
+ app28.route("/chat", chat_default2);
20235
+ app28.route("/compact", compact_default);
20236
+ app28.route("/logs", logs_default);
20237
+ app28.route("/claude-info", claude_info_default);
20238
+ app28.route("/attachment", attachment_default);
20239
+ app28.route("/account", account_default);
20240
+ app28.route("/agents", agents_default);
20241
+ app28.route("/version", version_default);
20242
+ app28.route("/sessions", sessions_default);
20243
+ app28.route("/browser", browser_default);
20244
+ app28.route("/device-browser", device_browser_default);
20245
+ app28.route("/events", events_default);
20246
+ app28.route("/cloudflare", cloudflare_default);
20247
+ app28.route("/files", files_default);
20248
+ app28.route("/graph-search", graph_search_default);
20249
+ app28.route("/graph-subgraph", graph_subgraph_default);
20250
+ app28.route("/file-attach", file_attach_default);
20251
+ var admin_default = app28;
20031
20252
 
20032
20253
  // server/index.ts
20033
20254
  var PLATFORM_ROOT11 = process.env.MAXY_PLATFORM_ROOT || "";
20034
20255
  var BRAND_JSON_PATH = PLATFORM_ROOT11 ? join13(PLATFORM_ROOT11, "config", "brand.json") : "";
20035
20256
  var BRAND = { productName: "Maxy", hostname: "maxy", configDir: ".maxy", domain: "getmaxy.com" };
20036
- if (BRAND_JSON_PATH && !existsSync25(BRAND_JSON_PATH)) {
20257
+ if (BRAND_JSON_PATH && !existsSync24(BRAND_JSON_PATH)) {
20037
20258
  console.error(`[brand] WARNING: brand.json not found at ${BRAND_JSON_PATH} \u2014 using Maxy defaults`);
20038
20259
  }
20039
- if (BRAND_JSON_PATH && existsSync25(BRAND_JSON_PATH)) {
20260
+ if (BRAND_JSON_PATH && existsSync24(BRAND_JSON_PATH)) {
20040
20261
  try {
20041
- const parsed = JSON.parse(readFileSync26(BRAND_JSON_PATH, "utf-8"));
20262
+ const parsed = JSON.parse(readFileSync25(BRAND_JSON_PATH, "utf-8"));
20042
20263
  BRAND = { ...BRAND, ...parsed };
20043
20264
  } catch (err) {
20044
20265
  console.error(`[brand] Failed to parse brand.json: ${err.message}`);
@@ -20060,8 +20281,8 @@ var brandLoginOpts = {
20060
20281
  var ALIAS_DOMAINS_PATH2 = join13(homedir5(), BRAND.configDir, "alias-domains.json");
20061
20282
  function loadAliasDomains() {
20062
20283
  try {
20063
- if (!existsSync25(ALIAS_DOMAINS_PATH2)) return null;
20064
- const parsed = JSON.parse(readFileSync26(ALIAS_DOMAINS_PATH2, "utf-8"));
20284
+ if (!existsSync24(ALIAS_DOMAINS_PATH2)) return null;
20285
+ const parsed = JSON.parse(readFileSync25(ALIAS_DOMAINS_PATH2, "utf-8"));
20065
20286
  if (!Array.isArray(parsed)) {
20066
20287
  console.error("[alias-domains] malformed alias-domains.json \u2014 expected array");
20067
20288
  return null;
@@ -20085,9 +20306,9 @@ watchFile(ALIAS_DOMAINS_PATH2, { interval: 2e3 }, () => {
20085
20306
  function isPublicHost(host) {
20086
20307
  return host.startsWith("public.") || aliasDomains.has(host);
20087
20308
  }
20088
- var app28 = new Hono2();
20089
- app28.use("*", clientIpMiddleware);
20090
- app28.use("*", async (c, next) => {
20309
+ var app29 = new Hono2();
20310
+ app29.use("*", clientIpMiddleware);
20311
+ app29.use("*", async (c, next) => {
20091
20312
  await next();
20092
20313
  c.header("X-Content-Type-Options", "nosniff");
20093
20314
  c.header("Referrer-Policy", "strict-origin-when-cross-origin");
@@ -20110,7 +20331,7 @@ var PUBLIC_ALLOWED_PREFIXES = [
20110
20331
  "/g/"
20111
20332
  ];
20112
20333
  var PUBLIC_ALLOWED_EXACT = ["/favicon.ico"];
20113
- app28.use("*", async (c, next) => {
20334
+ app29.use("*", async (c, next) => {
20114
20335
  const host = (c.req.header("host") ?? "").split(":")[0];
20115
20336
  if (!isPublicHost(host)) {
20116
20337
  await next();
@@ -20150,7 +20371,7 @@ function resolveRemoteAuthOpts() {
20150
20371
  return brandLoginOpts;
20151
20372
  }
20152
20373
  var MAX_LOGIN_BODY = 8 * 1024;
20153
- app28.post("/__remote-auth/login", async (c) => {
20374
+ app29.post("/__remote-auth/login", async (c) => {
20154
20375
  const clientIp = c.var.clientIp || "unknown";
20155
20376
  const rateLimited = checkRateLimit(clientIp);
20156
20377
  if (rateLimited) {
@@ -20186,7 +20407,7 @@ app28.post("/__remote-auth/login", async (c) => {
20186
20407
  }
20187
20408
  });
20188
20409
  });
20189
- app28.get("/__remote-auth/logout", (c) => {
20410
+ app29.get("/__remote-auth/logout", (c) => {
20190
20411
  const cookieHeader = c.req.header("cookie");
20191
20412
  const token = parseCookie(cookieHeader, "__remote_session");
20192
20413
  if (token) invalidateRemoteSession(token);
@@ -20199,7 +20420,7 @@ app28.get("/__remote-auth/logout", (c) => {
20199
20420
  }
20200
20421
  });
20201
20422
  });
20202
- app28.post("/__remote-auth/change-password", async (c) => {
20423
+ app29.post("/__remote-auth/change-password", async (c) => {
20203
20424
  const clientIp = c.var.clientIp || "unknown";
20204
20425
  const rateLimited = checkRateLimit(clientIp);
20205
20426
  if (rateLimited) {
@@ -20248,13 +20469,13 @@ app28.post("/__remote-auth/change-password", async (c) => {
20248
20469
  return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(), mode: "change", changeError: "Failed to save password", redirect }), 200);
20249
20470
  }
20250
20471
  });
20251
- app28.get("/__remote-auth/setup", (c) => {
20472
+ app29.get("/__remote-auth/setup", (c) => {
20252
20473
  if (isRemoteAuthConfigured()) {
20253
20474
  return c.redirect("/");
20254
20475
  }
20255
20476
  return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(), mode: "setup" }), 200);
20256
20477
  });
20257
- app28.post("/__remote-auth/set-initial-password", async (c) => {
20478
+ app29.post("/__remote-auth/set-initial-password", async (c) => {
20258
20479
  if (isRemoteAuthConfigured()) {
20259
20480
  return c.redirect("/");
20260
20481
  }
@@ -20290,10 +20511,10 @@ app28.post("/__remote-auth/set-initial-password", async (c) => {
20290
20511
  return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(), mode: "setup", setupError: "Failed to save password. Please try again." }), 200);
20291
20512
  }
20292
20513
  });
20293
- app28.get("/api/remote-auth/status", (c) => {
20514
+ app29.get("/api/remote-auth/status", (c) => {
20294
20515
  return c.json({ configured: isRemoteAuthConfigured() });
20295
20516
  });
20296
- app28.post("/api/remote-auth/set-password", async (c) => {
20517
+ app29.post("/api/remote-auth/set-password", async (c) => {
20297
20518
  let body;
20298
20519
  try {
20299
20520
  body = await c.req.json();
@@ -20323,9 +20544,9 @@ app28.post("/api/remote-auth/set-password", async (c) => {
20323
20544
  return c.json({ error: "Failed to save password" }, 500);
20324
20545
  }
20325
20546
  });
20326
- app28.route("/api/_client-error", client_error_default);
20547
+ app29.route("/api/_client-error", client_error_default);
20327
20548
  console.log("[client-error-route] mounted");
20328
- app28.use("*", async (c, next) => {
20549
+ app29.use("*", async (c, next) => {
20329
20550
  const host = (c.req.header("host") ?? "").split(":")[0];
20330
20551
  const path2 = c.req.path;
20331
20552
  if (path2 === "/favicon.ico" || path2.startsWith("/assets/") || path2.startsWith("/brand/")) {
@@ -20365,15 +20586,15 @@ function parseCookie(cookieHeader, name) {
20365
20586
  return null;
20366
20587
  }
20367
20588
  }
20368
- app28.route("/api/health", health_default);
20369
- app28.route("/api/session", session_default);
20370
- app28.route("/api/chat", chat_default);
20371
- app28.route("/api/group", group_default);
20372
- app28.route("/api/access", access_default);
20373
- app28.route("/api/telegram", telegram_default);
20374
- app28.route("/api/whatsapp", whatsapp_default);
20375
- app28.route("/api/onboarding", onboarding_default);
20376
- app28.route("/api/admin", admin_default);
20589
+ app29.route("/api/health", health_default);
20590
+ app29.route("/api/session", session_default);
20591
+ app29.route("/api/chat", chat_default);
20592
+ app29.route("/api/group", group_default);
20593
+ app29.route("/api/access", access_default);
20594
+ app29.route("/api/telegram", telegram_default);
20595
+ app29.route("/api/whatsapp", whatsapp_default);
20596
+ app29.route("/api/onboarding", onboarding_default);
20597
+ app29.route("/api/admin", admin_default);
20377
20598
  var SAFE_SLUG_RE = /^[a-z][a-z0-9-]{2,49}$/;
20378
20599
  var SAFE_FILENAME_RE = /^[a-z0-9_][a-z0-9_.-]{0,99}$/i;
20379
20600
  var IMAGE_MIME = {
@@ -20385,7 +20606,7 @@ var IMAGE_MIME = {
20385
20606
  ".svg": "image/svg+xml",
20386
20607
  ".ico": "image/x-icon"
20387
20608
  };
20388
- app28.get("/agent-assets/:slug/:filename", (c) => {
20609
+ app29.get("/agent-assets/:slug/:filename", (c) => {
20389
20610
  const slug = c.req.param("slug");
20390
20611
  const filename = c.req.param("filename");
20391
20612
  if (!SAFE_SLUG_RE.test(slug)) {
@@ -20401,26 +20622,26 @@ app28.get("/agent-assets/:slug/:filename", (c) => {
20401
20622
  console.error(`[agent-assets] no-account slug=${slug} file=${filename}`);
20402
20623
  return c.text("Not found", 404);
20403
20624
  }
20404
- const filePath = resolve28(account.accountDir, "agents", slug, "assets", filename);
20405
- const expectedDir = resolve28(account.accountDir, "agents", slug, "assets");
20625
+ const filePath = resolve29(account.accountDir, "agents", slug, "assets", filename);
20626
+ const expectedDir = resolve29(account.accountDir, "agents", slug, "assets");
20406
20627
  if (!filePath.startsWith(expectedDir + "/")) {
20407
20628
  console.error(`[agent-assets] path-traversal-rejected slug=${slug} file=${filename}`);
20408
20629
  return c.text("Forbidden", 403);
20409
20630
  }
20410
- if (!existsSync25(filePath)) {
20631
+ if (!existsSync24(filePath)) {
20411
20632
  console.error(`[agent-assets] serve slug=${slug} file=${filename} status=404`);
20412
20633
  return c.text("Not found", 404);
20413
20634
  }
20414
20635
  const ext = "." + filename.split(".").pop()?.toLowerCase();
20415
20636
  const contentType = IMAGE_MIME[ext] || "application/octet-stream";
20416
20637
  console.log(`[agent-assets] serve slug=${slug} file=${filename} status=200`);
20417
- const body = readFileSync26(filePath);
20638
+ const body = readFileSync25(filePath);
20418
20639
  return c.body(body, 200, {
20419
20640
  "Content-Type": contentType,
20420
20641
  "Cache-Control": "public, max-age=3600"
20421
20642
  });
20422
20643
  });
20423
- app28.get("/generated/:filename", (c) => {
20644
+ app29.get("/generated/:filename", (c) => {
20424
20645
  const filename = c.req.param("filename");
20425
20646
  if (!SAFE_FILENAME_RE.test(filename) || filename.includes("..")) {
20426
20647
  console.error(`[generated] serve file=${filename} status=403`);
@@ -20431,20 +20652,20 @@ app28.get("/generated/:filename", (c) => {
20431
20652
  console.error(`[generated] serve file=${filename} status=404`);
20432
20653
  return c.text("Not found", 404);
20433
20654
  }
20434
- const filePath = resolve28(account.accountDir, "generated", filename);
20435
- const expectedDir = resolve28(account.accountDir, "generated");
20655
+ const filePath = resolve29(account.accountDir, "generated", filename);
20656
+ const expectedDir = resolve29(account.accountDir, "generated");
20436
20657
  if (!filePath.startsWith(expectedDir + "/")) {
20437
20658
  console.error(`[generated] serve file=${filename} status=403`);
20438
20659
  return c.text("Forbidden", 403);
20439
20660
  }
20440
- if (!existsSync25(filePath)) {
20661
+ if (!existsSync24(filePath)) {
20441
20662
  console.error(`[generated] serve file=${filename} status=404`);
20442
20663
  return c.text("Not found", 404);
20443
20664
  }
20444
20665
  const ext = "." + filename.split(".").pop()?.toLowerCase();
20445
20666
  const contentType = IMAGE_MIME[ext] || "application/octet-stream";
20446
20667
  console.log(`[generated] serve file=${filename} status=200`);
20447
- const body = readFileSync26(filePath);
20668
+ const body = readFileSync25(filePath);
20448
20669
  return c.body(body, 200, {
20449
20670
  "Content-Type": contentType,
20450
20671
  "Cache-Control": "public, max-age=86400"
@@ -20453,9 +20674,9 @@ app28.get("/generated/:filename", (c) => {
20453
20674
  var htmlCache = /* @__PURE__ */ new Map();
20454
20675
  var brandLogoPath = "/brand/maxy-monochrome.png";
20455
20676
  var brandIconPath = "/brand/maxy-monochrome.png";
20456
- if (BRAND_JSON_PATH && existsSync25(BRAND_JSON_PATH)) {
20677
+ if (BRAND_JSON_PATH && existsSync24(BRAND_JSON_PATH)) {
20457
20678
  try {
20458
- const fullBrand = JSON.parse(readFileSync26(BRAND_JSON_PATH, "utf-8"));
20679
+ const fullBrand = JSON.parse(readFileSync25(BRAND_JSON_PATH, "utf-8"));
20459
20680
  if (fullBrand.assets?.logo) brandLogoPath = `/brand/${fullBrand.assets.logo}`;
20460
20681
  brandIconPath = fullBrand.assets?.icon ? `/brand/${fullBrand.assets.icon}` : brandLogoPath;
20461
20682
  } catch {
@@ -20473,8 +20694,8 @@ function readInstalledVersion() {
20473
20694
  try {
20474
20695
  if (!PLATFORM_ROOT11) return "unknown";
20475
20696
  const versionFile = join13(PLATFORM_ROOT11, "config", `.${BRAND.hostname}-version`);
20476
- if (!existsSync25(versionFile)) return "unknown";
20477
- const content = readFileSync26(versionFile, "utf-8").trim();
20697
+ if (!existsSync24(versionFile)) return "unknown";
20698
+ const content = readFileSync25(versionFile, "utf-8").trim();
20478
20699
  return content || "unknown";
20479
20700
  } catch {
20480
20701
  return "unknown";
@@ -20515,7 +20736,7 @@ var clientErrorReporterScript = `<script>
20515
20736
  function cachedHtml(file) {
20516
20737
  let html = htmlCache.get(file);
20517
20738
  if (!html) {
20518
- html = readFileSync26(resolve28(process.cwd(), "public", file), "utf-8");
20739
+ html = readFileSync25(resolve29(process.cwd(), "public", file), "utf-8");
20519
20740
  html = html.replace("<title>Maxy</title>", `<title>${escapeHtml2(BRAND.productName)}</title>`);
20520
20741
  html = html.replace('href="/favicon.ico"', `href="${escapeHtml2(brandFaviconPath)}"`);
20521
20742
  const headInjection = file === "index.html" ? `${brandScript}
@@ -20533,13 +20754,13 @@ function loadBrandingCache(agentSlug) {
20533
20754
  const configDir2 = join13(homedir5(), BRAND.configDir);
20534
20755
  try {
20535
20756
  const accountJsonPath = join13(configDir2, "account.json");
20536
- if (!existsSync25(accountJsonPath)) return null;
20537
- const account = JSON.parse(readFileSync26(accountJsonPath, "utf-8"));
20757
+ if (!existsSync24(accountJsonPath)) return null;
20758
+ const account = JSON.parse(readFileSync25(accountJsonPath, "utf-8"));
20538
20759
  const accountId = account.accountId;
20539
20760
  if (!accountId) return null;
20540
20761
  const cachePath = join13(configDir2, "branding-cache", accountId, `${agentSlug}.json`);
20541
- if (!existsSync25(cachePath)) return null;
20542
- return JSON.parse(readFileSync26(cachePath, "utf-8"));
20762
+ if (!existsSync24(cachePath)) return null;
20763
+ return JSON.parse(readFileSync25(cachePath, "utf-8"));
20543
20764
  } catch {
20544
20765
  return null;
20545
20766
  }
@@ -20548,8 +20769,8 @@ function resolveDefaultSlug() {
20548
20769
  try {
20549
20770
  const configDir2 = join13(homedir5(), BRAND.configDir);
20550
20771
  const accountJsonPath = join13(configDir2, "account.json");
20551
- if (!existsSync25(accountJsonPath)) return null;
20552
- const account = JSON.parse(readFileSync26(accountJsonPath, "utf-8"));
20772
+ if (!existsSync24(accountJsonPath)) return null;
20773
+ const account = JSON.parse(readFileSync25(accountJsonPath, "utf-8"));
20553
20774
  return account.defaultAgent || null;
20554
20775
  } catch {
20555
20776
  return null;
@@ -20585,7 +20806,7 @@ function brandedPublicHtml(agentSlug) {
20585
20806
  function escapeHtml2(s) {
20586
20807
  return s.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
20587
20808
  }
20588
- app28.get("/", (c) => {
20809
+ app29.get("/", (c) => {
20589
20810
  const host = (c.req.header("host") ?? "").split(":")[0];
20590
20811
  if (isPublicHost(host)) {
20591
20812
  const defaultSlug = resolveDefaultSlug();
@@ -20593,12 +20814,12 @@ app28.get("/", (c) => {
20593
20814
  }
20594
20815
  return c.html(cachedHtml("index.html"));
20595
20816
  });
20596
- app28.get("/public", (c) => {
20817
+ app29.get("/public", (c) => {
20597
20818
  const host = (c.req.header("host") ?? "").split(":")[0];
20598
20819
  if (isPublicHost(host)) return c.text("Not found", 404);
20599
20820
  return c.html(cachedHtml("public.html"));
20600
20821
  });
20601
- app28.get("/chat", (c) => {
20822
+ app29.get("/chat", (c) => {
20602
20823
  const host = (c.req.header("host") ?? "").split(":")[0];
20603
20824
  if (isPublicHost(host)) return c.text("Not found", 404);
20604
20825
  return c.html(cachedHtml("public.html"));
@@ -20617,12 +20838,12 @@ async function logViewerFetch(c, next) {
20617
20838
  duration_ms: Date.now() - start
20618
20839
  });
20619
20840
  }
20620
- app28.use("/vnc-viewer.html", logViewerFetch);
20621
- app28.use("/vnc-popout.html", logViewerFetch);
20622
- app28.get("/vnc-popout.html", (c) => {
20841
+ app29.use("/vnc-viewer.html", logViewerFetch);
20842
+ app29.use("/vnc-popout.html", logViewerFetch);
20843
+ app29.get("/vnc-popout.html", (c) => {
20623
20844
  let html = htmlCache.get("vnc-popout.html");
20624
20845
  if (!html) {
20625
- html = readFileSync26(resolve28(process.cwd(), "public", "vnc-popout.html"), "utf-8");
20846
+ html = readFileSync25(resolve29(process.cwd(), "public", "vnc-popout.html"), "utf-8");
20626
20847
  const name = escapeHtml2(BRAND.productName);
20627
20848
  html = html.replace("<title>Browser \u2014 Maxy</title>", `<title>${name}</title>`);
20628
20849
  html = html.replace("</head>", ` ${brandScript}
@@ -20632,7 +20853,7 @@ app28.get("/vnc-popout.html", (c) => {
20632
20853
  }
20633
20854
  return c.html(html);
20634
20855
  });
20635
- app28.post("/api/vnc/client-event", async (c) => {
20856
+ app29.post("/api/vnc/client-event", async (c) => {
20636
20857
  let body;
20637
20858
  try {
20638
20859
  body = await c.req.json();
@@ -20653,18 +20874,20 @@ app28.post("/api/vnc/client-event", async (c) => {
20653
20874
  });
20654
20875
  return c.json({ ok: true });
20655
20876
  });
20656
- app28.get("/g/:slug", (c) => {
20877
+ app29.get("/g/:slug", (c) => {
20657
20878
  return c.html(brandedPublicHtml());
20658
20879
  });
20659
- app28.use("/graph/*", graphAuthMiddleware({ isPublicHost, canAccessAdmin }));
20660
- app28.use("/graph", graphAuthMiddleware({ isPublicHost, canAccessAdmin }));
20661
- attachGraphHttpRoutes(app28);
20662
- app28.get("/data", (c) => {
20880
+ app29.get("/graph", (c) => {
20881
+ const host = (c.req.header("host") ?? "").split(":")[0];
20882
+ if (isPublicHost(host)) return c.text("Not found", 404);
20883
+ return c.html(cachedHtml("graph.html"));
20884
+ });
20885
+ app29.get("/data", (c) => {
20663
20886
  const host = (c.req.header("host") ?? "").split(":")[0];
20664
20887
  if (isPublicHost(host)) return c.text("Not found", 404);
20665
20888
  return c.html(cachedHtml("data.html"));
20666
20889
  });
20667
- app28.get("/:slug", async (c, next) => {
20890
+ app29.get("/:slug", async (c, next) => {
20668
20891
  const slug = c.req.param("slug");
20669
20892
  if (AGENT_SLUG_PATTERN.test(`/${slug}`)) {
20670
20893
  const branding = loadBrandingCache(slug);
@@ -20673,16 +20896,20 @@ app28.get("/:slug", async (c, next) => {
20673
20896
  }
20674
20897
  await next();
20675
20898
  });
20676
- app28.use("/*", serveStatic({ root: "./public" }));
20899
+ app29.use("/*", serveStatic({ root: "./public" }));
20677
20900
  var port = parseInt(process.env.PORT ?? "19200", 10);
20678
20901
  var hostname = process.env.HOSTNAME ?? "0.0.0.0";
20679
- var httpServer = serve({ fetch: app28.fetch, port, hostname });
20902
+ var httpServer = serve({ fetch: app29.fetch, port, hostname });
20680
20903
  attachVncWsProxy(httpServer, {
20681
20904
  isPublicHost,
20682
20905
  upstreamHost: "127.0.0.1",
20683
20906
  upstreamPort: 6080
20684
20907
  });
20685
- attachGraphWsProxy(httpServer, { isPublicHost, canAccessAdmin });
20908
+ attachTerminalWsProxy(httpServer, {
20909
+ isPublicHost,
20910
+ upstreamHost: "127.0.0.1",
20911
+ upstreamPort: 7681
20912
+ });
20686
20913
  console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
20687
20914
  var SUBAPP_MANIFEST = [
20688
20915
  { prefix: "/api/health", file: "server/routes/health.ts", subapp: health_default },
@@ -20702,7 +20929,7 @@ for (const m of SUBAPP_MANIFEST) {
20702
20929
  }
20703
20930
  try {
20704
20931
  const registered = [];
20705
- for (const r of app28.routes ?? []) {
20932
+ for (const r of app29.routes ?? []) {
20706
20933
  if (typeof r.path !== "string" || r.path.includes(":") || r.path.includes("*")) continue;
20707
20934
  if (AGENT_SLUG_PATTERN.test(r.path)) {
20708
20935
  registered.push({ method: (r.method ?? "ALL").toUpperCase(), path: r.path });
@@ -20716,8 +20943,8 @@ try {
20716
20943
  (async () => {
20717
20944
  try {
20718
20945
  let userId = "";
20719
- if (existsSync25(USERS_FILE)) {
20720
- const users = JSON.parse(readFileSync26(USERS_FILE, "utf-8").trim() || "[]");
20946
+ if (existsSync24(USERS_FILE)) {
20947
+ const users = JSON.parse(readFileSync25(USERS_FILE, "utf-8").trim() || "[]");
20721
20948
  userId = users[0]?.userId ?? "";
20722
20949
  }
20723
20950
  await backfillNullUserIdConversations(userId);
@@ -20743,7 +20970,7 @@ if (bootAccountConfig?.whatsapp) {
20743
20970
  }
20744
20971
  init({
20745
20972
  configDir: configDirForWhatsApp,
20746
- platformRoot: resolve28(process.env.MAXY_PLATFORM_ROOT ?? join13(__dirname, "..")),
20973
+ platformRoot: resolve29(process.env.MAXY_PLATFORM_ROOT ?? join13(__dirname, "..")),
20747
20974
  accountConfig: bootAccountConfig,
20748
20975
  onMessage: async (msg) => {
20749
20976
  try {