@rubytech/create-maxy-code 0.1.181 → 0.1.183

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 (186) hide show
  1. package/dist/__tests__/bundler-rewrite-platform-lib.test.js +68 -0
  2. package/dist/bundler-rewrite-platform-lib.js +29 -0
  3. package/package.json +1 -1
  4. package/payload/platform/lib/graph-search/dist/index.d.ts +10 -0
  5. package/payload/platform/lib/graph-search/dist/index.d.ts.map +1 -1
  6. package/payload/platform/lib/graph-search/dist/index.js +57 -30
  7. package/payload/platform/lib/graph-search/dist/index.js.map +1 -1
  8. package/payload/platform/lib/graph-search/src/__tests__/hybrid.test.ts +95 -2
  9. package/payload/platform/lib/graph-search/src/__tests__/vector-index-coverage.test.ts +198 -0
  10. package/payload/platform/lib/graph-search/src/index.ts +73 -29
  11. package/payload/platform/neo4j/schema.cypher +36 -0
  12. package/payload/platform/plugins/admin/PLUGIN.md +2 -0
  13. package/payload/platform/plugins/admin/hooks/admin-authoring-observer.sh +155 -0
  14. package/payload/platform/plugins/admin/skills/access-manager/SKILL.md +7 -5
  15. package/payload/platform/plugins/admin/skills/access-manager/references/operations.md +27 -55
  16. package/payload/platform/plugins/admin/skills/professional-document/SKILL.md +178 -0
  17. package/payload/platform/plugins/admin/skills/specialist-management/SKILL.md +3 -2
  18. package/payload/platform/plugins/aeo/mcp/dist/index.js +3 -0
  19. package/payload/platform/plugins/aeo/mcp/dist/index.js.map +1 -1
  20. package/payload/platform/plugins/contacts/PLUGIN.md +1 -0
  21. package/payload/platform/plugins/contacts/mcp/dist/index.js +3 -0
  22. package/payload/platform/plugins/contacts/mcp/dist/index.js.map +1 -1
  23. package/payload/platform/plugins/docs/references/access-control.md +29 -22
  24. package/payload/platform/plugins/email/mcp/dist/scripts/send-transactional.d.ts +2 -0
  25. package/payload/platform/plugins/email/mcp/dist/scripts/send-transactional.d.ts.map +1 -0
  26. package/payload/platform/plugins/email/mcp/dist/scripts/send-transactional.js +56 -0
  27. package/payload/platform/plugins/email/mcp/dist/scripts/send-transactional.js.map +1 -0
  28. package/payload/platform/plugins/memory/.claude-plugin/plugin.json +1 -1
  29. package/payload/platform/plugins/memory/PLUGIN.md +5 -1
  30. package/payload/platform/plugins/memory/mcp/dist/index.js +76 -2
  31. package/payload/platform/plugins/memory/mcp/dist/index.js.map +1 -1
  32. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/typed-edge-schema.test.js +30 -1
  33. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/typed-edge-schema.test.js.map +1 -1
  34. package/payload/platform/plugins/memory/mcp/dist/lib/section-types.d.ts +15 -0
  35. package/payload/platform/plugins/memory/mcp/dist/lib/section-types.d.ts.map +1 -1
  36. package/payload/platform/plugins/memory/mcp/dist/lib/typed-edge-schema.d.ts +10 -0
  37. package/payload/platform/plugins/memory/mcp/dist/lib/typed-edge-schema.d.ts.map +1 -1
  38. package/payload/platform/plugins/memory/mcp/dist/lib/typed-edge-schema.js +35 -4
  39. package/payload/platform/plugins/memory/mcp/dist/lib/typed-edge-schema.js.map +1 -1
  40. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest.test.js +61 -0
  41. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest.test.js.map +1 -1
  42. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-update-by-name.test.d.ts +2 -0
  43. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-update-by-name.test.d.ts.map +1 -0
  44. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-update-by-name.test.js +125 -0
  45. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-update-by-name.test.js.map +1 -0
  46. package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.d.ts.map +1 -1
  47. package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.js +65 -5
  48. package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.js.map +1 -1
  49. package/payload/platform/plugins/memory/mcp/dist/tools/memory-update-by-name.d.ts +15 -0
  50. package/payload/platform/plugins/memory/mcp/dist/tools/memory-update-by-name.d.ts.map +1 -0
  51. package/payload/platform/plugins/memory/mcp/dist/tools/memory-update-by-name.js +45 -0
  52. package/payload/platform/plugins/memory/mcp/dist/tools/memory-update-by-name.js.map +1 -0
  53. package/payload/platform/plugins/memory/mcp/dist/tools/memory-update.js +24 -1
  54. package/payload/platform/plugins/memory/mcp/dist/tools/memory-update.js.map +1 -1
  55. package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.d.ts.map +1 -1
  56. package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.js +16 -0
  57. package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.js.map +1 -1
  58. package/payload/platform/plugins/memory/references/graph-primitives.md +1 -0
  59. package/payload/platform/plugins/memory/references/schema-base.md +4 -2
  60. package/payload/platform/plugins/memory/skills/document-ingest/SKILL.md +24 -0
  61. package/payload/platform/plugins/scheduling/PLUGIN.md +1 -0
  62. package/payload/platform/plugins/scheduling/mcp/dist/index.js +3 -0
  63. package/payload/platform/plugins/scheduling/mcp/dist/index.js.map +1 -1
  64. package/payload/platform/plugins/work/PLUGIN.md +1 -0
  65. package/payload/platform/plugins/work/mcp/dist/index.js +4 -0
  66. package/payload/platform/plugins/work/mcp/dist/index.js.map +1 -1
  67. package/payload/platform/plugins/workflows/PLUGIN.md +1 -0
  68. package/payload/platform/plugins/workflows/mcp/dist/index.js +3 -0
  69. package/payload/platform/plugins/workflows/mcp/dist/index.js.map +1 -1
  70. package/payload/platform/scripts/__tests__/agents-md-bootstrap.test.sh +111 -0
  71. package/payload/platform/scripts/lib/agents-md-bootstrap.sh +49 -0
  72. package/payload/platform/scripts/setup-account.sh +18 -14
  73. package/payload/platform/services/claude-session-manager/dist/fs-watcher.d.ts +8 -0
  74. package/payload/platform/services/claude-session-manager/dist/fs-watcher.d.ts.map +1 -1
  75. package/payload/platform/services/claude-session-manager/dist/fs-watcher.js.map +1 -1
  76. package/payload/platform/services/claude-session-manager/dist/http-server.d.ts.map +1 -1
  77. package/payload/platform/services/claude-session-manager/dist/http-server.js +12 -0
  78. package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -1
  79. package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts +32 -4
  80. package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts.map +1 -1
  81. package/payload/platform/services/claude-session-manager/dist/pty-spawner.js +154 -53
  82. package/payload/platform/services/claude-session-manager/dist/pty-spawner.js.map +1 -1
  83. package/payload/platform/templates/agents/admin/AGENTS.md +18 -7
  84. package/payload/platform/templates/agents/admin/IDENTITY.md +2 -2
  85. package/payload/platform/templates/specialists/agents/archive-ingest-operator.md +1 -1
  86. package/payload/platform/templates/specialists/agents/citation-auditor.md +1 -1
  87. package/payload/platform/templates/specialists/agents/compiled-truth-rewriter.md +1 -1
  88. package/payload/platform/templates/specialists/agents/content-producer.md +3 -1
  89. package/payload/platform/templates/specialists/agents/database-operator.md +2 -2
  90. package/payload/platform/templates/specialists/agents/librarian.md +1 -1
  91. package/payload/platform/templates/specialists/agents/personal-assistant.md +1 -1
  92. package/payload/platform/templates/specialists/agents/project-manager.md +1 -1
  93. package/payload/platform/templates/specialists/agents/public-session-reviewer.md +40 -0
  94. package/payload/platform/templates/specialists/agents/research-assistant.md +1 -1
  95. package/payload/platform/templates/specialists/agents/typed-edge-classifier.md +1 -1
  96. package/payload/premium-plugins/writer-craft/mcp/dist/lib/voice-corpus.d.ts +13 -0
  97. package/payload/premium-plugins/writer-craft/mcp/dist/lib/voice-corpus.d.ts.map +1 -1
  98. package/payload/premium-plugins/writer-craft/mcp/dist/lib/voice-corpus.js +15 -1
  99. package/payload/premium-plugins/writer-craft/mcp/dist/lib/voice-corpus.js.map +1 -1
  100. package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-distil-profile.js +1 -1
  101. package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-distil-profile.js.map +1 -1
  102. package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-retrieve-conditioning.js +1 -1
  103. package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-retrieve-conditioning.js.map +1 -1
  104. package/payload/premium-plugins/writer-craft/mcp/src/lib/voice-corpus.ts +15 -1
  105. package/payload/premium-plugins/writer-craft/mcp/src/tools/voice-distil-profile.ts +1 -1
  106. package/payload/premium-plugins/writer-craft/mcp/src/tools/voice-retrieve-conditioning.ts +1 -1
  107. package/payload/server/public/assets/ChatInput-DzacFNMk.css +1 -0
  108. package/payload/server/public/assets/{Checkbox-YrQovXpN.js → Checkbox-BJKNkUYu.js} +1 -1
  109. package/payload/server/public/assets/{admin-DH5jp-1u.js → admin-BNy4N2d6.js} +1 -1
  110. package/payload/server/public/assets/{architectureDiagram-Q4EWVU46-Do2zYONG.js → architectureDiagram-Q4EWVU46-DZoOzmjE.js} +1 -1
  111. package/payload/server/public/assets/{blockDiagram-DXYQGD6D-DpIX_bRu.js → blockDiagram-DXYQGD6D-DG3yufCR.js} +1 -1
  112. package/payload/server/public/assets/{c4Diagram-AHTNJAMY-Cz335ZSY.js → c4Diagram-AHTNJAMY-BBx5wXuZ.js} +1 -1
  113. package/payload/server/public/assets/channel-DdtUtt5g.js +1 -0
  114. package/payload/server/public/assets/{chunk-336JU56O-Dmknn3Wo.js → chunk-336JU56O-DwP7g5ns.js} +2 -2
  115. package/payload/server/public/assets/{chunk-426QAEUC-ClfGjizN.js → chunk-426QAEUC-DXzGdc22.js} +1 -1
  116. package/payload/server/public/assets/{chunk-4TB4RGXK-BPKctJ5c.js → chunk-4TB4RGXK-1LDRjk0E.js} +1 -1
  117. package/payload/server/public/assets/{chunk-5FUZZQ4R-BWDCO9zw.js → chunk-5FUZZQ4R-BHjWhwhn.js} +1 -1
  118. package/payload/server/public/assets/{chunk-5PVQY5BW-BE34_Db0.js → chunk-5PVQY5BW-CwPHRVN8.js} +1 -1
  119. package/payload/server/public/assets/{chunk-EDXVE4YY-BysA-_6v.js → chunk-EDXVE4YY-7443hrYZ.js} +1 -1
  120. package/payload/server/public/assets/{chunk-ENJZ2VHE-BMVyMsqA.js → chunk-ENJZ2VHE-BTnmmnmX.js} +1 -1
  121. package/payload/server/public/assets/{chunk-ICPOFSXX-C5KRdLV4.js → chunk-ICPOFSXX-CPnMJplL.js} +1 -1
  122. package/payload/server/public/assets/{chunk-OYMX7WX6-DVZD-AVc.js → chunk-OYMX7WX6-DCQ5Q4RS.js} +1 -1
  123. package/payload/server/public/assets/{chunk-U2HBQHQK-DYmQdx8x.js → chunk-U2HBQHQK-BmfZG8e4.js} +1 -1
  124. package/payload/server/public/assets/{chunk-X2U36JSP-DrZenIl3.js → chunk-X2U36JSP-Da2yJL2u.js} +1 -1
  125. package/payload/server/public/assets/{chunk-YZCP3GAM-CDuGbvMq.js → chunk-YZCP3GAM-BdUmMUJ6.js} +1 -1
  126. package/payload/server/public/assets/{chunk-ZZ45TVLE-COztbiu5.js → chunk-ZZ45TVLE-abkGirrZ.js} +1 -1
  127. package/payload/server/public/assets/classDiagram-6PBFFD2Q-CKk2DFZe.js +1 -0
  128. package/payload/server/public/assets/classDiagram-v2-HSJHXN6E--YvfDQCh.js +1 -0
  129. package/payload/server/public/assets/clone-Cw6UfPGM.js +1 -0
  130. package/payload/server/public/assets/{dagre-Cv9Ib2OM.js → dagre-BVVuAEsU.js} +1 -1
  131. package/payload/server/public/assets/{dagre-KV5264BT-CoEd61KZ.js → dagre-KV5264BT-VG2kNj19.js} +1 -1
  132. package/payload/server/public/assets/data-uBpwaldK.js +1 -0
  133. package/payload/server/public/assets/{diagram-5BDNPKRD-WXNzw3uW.js → diagram-5BDNPKRD-DRiVy69K.js} +1 -1
  134. package/payload/server/public/assets/{diagram-G4DWMVQ6-C6IuQ2VV.js → diagram-G4DWMVQ6-DtpiRTYX.js} +1 -1
  135. package/payload/server/public/assets/{diagram-MMDJMWI5-CdJdir5A.js → diagram-MMDJMWI5-Da3t99fY.js} +1 -1
  136. package/payload/server/public/assets/{diagram-TYMM5635-DxWKQH5T.js → diagram-TYMM5635-BzQmLTJQ.js} +1 -1
  137. package/payload/server/public/assets/{erDiagram-SMLLAGMA-C9fvs33K.js → erDiagram-SMLLAGMA-D3Ll-GDw.js} +1 -1
  138. package/payload/server/public/assets/{flowDiagram-DWJPFMVM-BO7Yrbqv.js → flowDiagram-DWJPFMVM-D17bje6d.js} +1 -1
  139. package/payload/server/public/assets/{ganttDiagram-T4ZO3ILL-CQJyWBZq.js → ganttDiagram-T4ZO3ILL-BjeQ-Bun.js} +1 -1
  140. package/payload/server/public/assets/{gitGraphDiagram-UUTBAWPF-DZfQHrYw.js → gitGraphDiagram-UUTBAWPF-LtX4pvZS.js} +1 -1
  141. package/payload/server/public/assets/{graph-labels-BMU8LsOD.js → graph-labels-Cn9jAE6I.js} +1 -1
  142. package/payload/server/public/assets/graph-tvTPvBaz.js +1 -0
  143. package/payload/server/public/assets/{graphlib-C40H-Q5E.js → graphlib-CzwsfZOw.js} +1 -1
  144. package/payload/server/public/assets/{infoDiagram-42DDH7IO-CakfV5f9.js → infoDiagram-42DDH7IO-DOgH4i52.js} +1 -1
  145. package/payload/server/public/assets/{ishikawaDiagram-UXIWVN3A-CPuBPyfa.js → ishikawaDiagram-UXIWVN3A-DYqy5BFj.js} +1 -1
  146. package/payload/server/public/assets/{journeyDiagram-VCZTEJTY-zpZm2Mmo.js → journeyDiagram-VCZTEJTY-CsFpIULJ.js} +1 -1
  147. package/payload/server/public/assets/{kanban-definition-6JOO6SKY-DfJduQCo.js → kanban-definition-6JOO6SKY-5uZuArw7.js} +1 -1
  148. package/payload/server/public/assets/{lib-Bnh57com.js → lib-p4ylk2XS.js} +1 -1
  149. package/payload/server/public/assets/{line-BpYQqO5R.js → line-BY8PmCSW.js} +1 -1
  150. package/payload/server/public/assets/{mermaid-parser.core-C5Mwi14D.js → mermaid-parser.core-CJ_9xqwK.js} +1 -1
  151. package/payload/server/public/assets/{mermaid.core-D8qBtvvT.js → mermaid.core-CXznBkX7.js} +3 -3
  152. package/payload/server/public/assets/{mindmap-definition-QFDTVHPH-Bli9JhIN.js → mindmap-definition-QFDTVHPH-DZbHBud5.js} +1 -1
  153. package/payload/server/public/assets/{page-0lzgQWnS.js → page-B3VYaBcf.js} +1 -1
  154. package/payload/server/public/assets/{page-C29S3cBt.js → page-CfN7nw1J.js} +1 -1
  155. package/payload/server/public/assets/{pieDiagram-DEJITSTG-MsLXTSLg.js → pieDiagram-DEJITSTG-QmOi4DRG.js} +1 -1
  156. package/payload/server/public/assets/public-Bbew78md.js +7 -0
  157. package/payload/server/public/assets/{quadrantDiagram-34T5L4WZ-De1sh2K0.js → quadrantDiagram-34T5L4WZ-Cpidq6Xj.js} +1 -1
  158. package/payload/server/public/assets/{requirementDiagram-MS252O5E-D5Sfg1t_.js → requirementDiagram-MS252O5E-CBqtfbzR.js} +1 -1
  159. package/payload/server/public/assets/{sankeyDiagram-XADWPNL6-DHA3LIbv.js → sankeyDiagram-XADWPNL6-4BiY4dV9.js} +1 -1
  160. package/payload/server/public/assets/{sequenceDiagram-FGHM5R23-BE_QlG1B.js → sequenceDiagram-FGHM5R23-DVlL-l2u.js} +1 -1
  161. package/payload/server/public/assets/{stateDiagram-FHFEXIEX-NIwp3F4u.js → stateDiagram-FHFEXIEX-CAm9dW68.js} +1 -1
  162. package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-B2EzXi50.js +1 -0
  163. package/payload/server/public/assets/{timeline-definition-GMOUNBTQ-CPE9jyDF.js → timeline-definition-GMOUNBTQ-CD-tZc4y.js} +1 -1
  164. package/payload/server/public/assets/{vennDiagram-DHZGUBPP-CKfoyYmH.js → vennDiagram-DHZGUBPP-Davcm0TF.js} +1 -1
  165. package/payload/server/public/assets/{wardleyDiagram-NUSXRM2D-B9yJek-o.js → wardleyDiagram-NUSXRM2D-40LBYw_Y.js} +1 -1
  166. package/payload/server/public/assets/{xychartDiagram-5P7HB3ND-DuCcSLqX.js → xychartDiagram-5P7HB3ND-DEPT-__v.js} +1 -1
  167. package/payload/server/public/data.html +5 -5
  168. package/payload/server/public/graph.html +6 -6
  169. package/payload/server/public/index.html +8 -8
  170. package/payload/server/public/public.html +5 -5
  171. package/payload/server/server.js +1158 -337
  172. package/payload/premium-plugins/writer-craft/lib/graph-trash/dist/index.d.ts +0 -106
  173. package/payload/premium-plugins/writer-craft/lib/graph-trash/dist/index.d.ts.map +0 -1
  174. package/payload/premium-plugins/writer-craft/lib/graph-trash/dist/index.js +0 -340
  175. package/payload/premium-plugins/writer-craft/lib/graph-trash/dist/index.js.map +0 -1
  176. package/payload/premium-plugins/writer-craft/lib/graph-trash/package.json +0 -7
  177. package/payload/server/public/assets/ChatInput-CJ50oqWt.css +0 -1
  178. package/payload/server/public/assets/channel-PC4_IjI1.js +0 -1
  179. package/payload/server/public/assets/classDiagram-6PBFFD2Q-B_pMa-SX.js +0 -1
  180. package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-BdBAgF20.js +0 -1
  181. package/payload/server/public/assets/clone-CkNt9L9r.js +0 -1
  182. package/payload/server/public/assets/data-CtanHhM2.js +0 -1
  183. package/payload/server/public/assets/graph-6DPD6be1.js +0 -1
  184. package/payload/server/public/assets/public-DltgEfA9.js +0 -7
  185. package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-pfk2F6st.js +0 -1
  186. /package/payload/server/public/assets/{ChatInput-CJo_77bp.js → ChatInput-CvRaT05l.js} +0 -0
@@ -816,8 +816,8 @@ var serveStatic = (options = { root: "" }) => {
816
816
  };
817
817
 
818
818
  // server/index.ts
819
- import { readFileSync as readFileSync20, existsSync as existsSync24, watchFile } from "fs";
820
- import { resolve as resolve20, join as join15, basename as basename5 } from "path";
819
+ import { readFileSync as readFileSync21, existsSync as existsSync24, watchFile } from "fs";
820
+ import { resolve as resolve22, join as join15, basename as basename5 } from "path";
821
821
  import { homedir as homedir3 } from "os";
822
822
  import { monitorEventLoopDelay } from "perf_hooks";
823
823
 
@@ -1275,7 +1275,7 @@ var credsSaveQueue = Promise.resolve();
1275
1275
  async function drainCredsSaveQueue(timeoutMs = 5e3) {
1276
1276
  console.error(`${TAG2} draining credential save queue\u2026`);
1277
1277
  const timer2 = new Promise(
1278
- (resolve21) => setTimeout(() => resolve21("timeout"), timeoutMs)
1278
+ (resolve23) => setTimeout(() => resolve23("timeout"), timeoutMs)
1279
1279
  );
1280
1280
  const result = await Promise.race([
1281
1281
  credsSaveQueue.then(() => "drained"),
@@ -1403,11 +1403,11 @@ async function createWaSocket(opts) {
1403
1403
  return sock;
1404
1404
  }
1405
1405
  async function waitForConnection(sock) {
1406
- return new Promise((resolve21, reject) => {
1406
+ return new Promise((resolve23, reject) => {
1407
1407
  const handler = (update) => {
1408
1408
  if (update.connection === "open") {
1409
1409
  sock.ev.off("connection.update", handler);
1410
- resolve21();
1410
+ resolve23();
1411
1411
  }
1412
1412
  if (update.connection === "close") {
1413
1413
  sock.ev.off("connection.update", handler);
@@ -1521,14 +1521,14 @@ ${inspected}`;
1521
1521
  return inspect2(err, INSPECT_OPTS2);
1522
1522
  }
1523
1523
  function withTimeout(label, promise, timeoutMs) {
1524
- return new Promise((resolve21, reject) => {
1524
+ return new Promise((resolve23, reject) => {
1525
1525
  const timer2 = setTimeout(() => {
1526
1526
  reject(new Error(`${label} timed out after ${timeoutMs}ms`));
1527
1527
  }, timeoutMs);
1528
1528
  promise.then(
1529
1529
  (value) => {
1530
1530
  clearTimeout(timer2);
1531
- resolve21(value);
1531
+ resolve23(value);
1532
1532
  },
1533
1533
  (err) => {
1534
1534
  clearTimeout(timer2);
@@ -2063,8 +2063,8 @@ async function persistWhatsAppMessage(input) {
2063
2063
  const { givenName, familyName } = splitName(input.pushName);
2064
2064
  const prev = sessionWriteLocks.get(input.cacheKey);
2065
2065
  let release;
2066
- const mine = new Promise((resolve21) => {
2067
- release = resolve21;
2066
+ const mine = new Promise((resolve23) => {
2067
+ release = resolve23;
2068
2068
  });
2069
2069
  const chained = (prev ?? Promise.resolve()).then(() => mine);
2070
2070
  sessionWriteLocks.set(input.cacheKey, chained);
@@ -3100,11 +3100,11 @@ async function connectWithReconnect(conn) {
3100
3100
  console.error(
3101
3101
  `${TAG12} reconnecting account=${conn.accountId} in ${delay}ms (attempt ${decision.nextAttempts}/${maxAttempts})`
3102
3102
  );
3103
- await new Promise((resolve21) => {
3104
- const timer2 = setTimeout(resolve21, delay);
3103
+ await new Promise((resolve23) => {
3104
+ const timer2 = setTimeout(resolve23, delay);
3105
3105
  conn.abortController.signal.addEventListener("abort", () => {
3106
3106
  clearTimeout(timer2);
3107
- resolve21();
3107
+ resolve23();
3108
3108
  }, { once: true });
3109
3109
  });
3110
3110
  }
@@ -3112,16 +3112,16 @@ async function connectWithReconnect(conn) {
3112
3112
  }
3113
3113
  }
3114
3114
  function waitForDisconnectEvent(conn) {
3115
- return new Promise((resolve21) => {
3115
+ return new Promise((resolve23) => {
3116
3116
  if (!conn.sock) {
3117
- resolve21();
3117
+ resolve23();
3118
3118
  return;
3119
3119
  }
3120
3120
  const sock = conn.sock;
3121
3121
  const handler = (update) => {
3122
3122
  if (update.connection === "close") {
3123
3123
  sock.ev.off("connection.update", handler);
3124
- resolve21();
3124
+ resolve23();
3125
3125
  }
3126
3126
  };
3127
3127
  sock.ev.on("connection.update", handler);
@@ -3383,8 +3383,8 @@ async function handleInboundMessage(conn, msg) {
3383
3383
  const conversationKey = isGroup ? remoteJid : senderPhone;
3384
3384
  const debounceKey = `${conn.accountId}:${conversationKey}:${senderPhone}`;
3385
3385
  let resolvePending;
3386
- const sttPending = new Promise((resolve21) => {
3387
- resolvePending = resolve21;
3386
+ const sttPending = new Promise((resolve23) => {
3387
+ resolvePending = resolve23;
3388
3388
  });
3389
3389
  if (conn.debouncer) conn.debouncer.registerPending(debounceKey, sttPending);
3390
3390
  try {
@@ -3788,20 +3788,20 @@ function buildX11Env(chromiumWrapperPath, transport = "vnc") {
3788
3788
 
3789
3789
  // server/routes/health.ts
3790
3790
  function checkPort(port2, timeoutMs = 500) {
3791
- return new Promise((resolve21) => {
3791
+ return new Promise((resolve23) => {
3792
3792
  const socket = createConnection2(port2, "127.0.0.1");
3793
3793
  socket.setTimeout(timeoutMs);
3794
3794
  socket.once("connect", () => {
3795
3795
  socket.destroy();
3796
- resolve21(true);
3796
+ resolve23(true);
3797
3797
  });
3798
3798
  socket.once("error", () => {
3799
3799
  socket.destroy();
3800
- resolve21(false);
3800
+ resolve23(false);
3801
3801
  });
3802
3802
  socket.once("timeout", () => {
3803
3803
  socket.destroy();
3804
- resolve21(false);
3804
+ resolve23(false);
3805
3805
  });
3806
3806
  });
3807
3807
  }
@@ -4114,7 +4114,9 @@ async function managerSpawn(opts) {
4114
4114
  aboutOwner: opts.aboutOwner,
4115
4115
  dormantPlugins: opts.dormantPlugins,
4116
4116
  activePlugins: opts.activePlugins,
4117
- specialistDomains: opts.specialistDomains
4117
+ specialistDomains: opts.specialistDomains,
4118
+ sliceToken: opts.sliceToken,
4119
+ personId: opts.personId
4118
4120
  })
4119
4121
  }).catch((err) => ({ __throw: err instanceof Error ? err.message : String(err) }));
4120
4122
  if ("__throw" in res) {
@@ -4145,6 +4147,146 @@ function managerLogFollowUrl(sessionId) {
4145
4147
  return `${managerBase()}/${sessionId}/log?follow=1`;
4146
4148
  }
4147
4149
 
4150
+ // app/lib/channel-pty-bridge/public-session-end-review.ts
4151
+ var TAG14 = "[public-session-review]";
4152
+ function managerBase2() {
4153
+ const port2 = requirePortEnv("CLAUDE_SESSION_MANAGER_PORT", { tag: "public-session-end-review:manager" });
4154
+ return `http://127.0.0.1:${port2}`;
4155
+ }
4156
+ function uiBase() {
4157
+ const port2 = requirePortEnv("MAXY_UI_INTERNAL_PORT", { tag: "public-session-end-review:ui" });
4158
+ return `http://127.0.0.1:${port2}`;
4159
+ }
4160
+ async function fetchJsonl(sessionId) {
4161
+ try {
4162
+ const res = await fetch(`${managerBase2()}/${sessionId}/log?download=1`);
4163
+ if (!res.ok) return null;
4164
+ return await res.text();
4165
+ } catch (err) {
4166
+ console.error(
4167
+ `${TAG14} jsonl-fetch-failed sessionId=${sessionId} err="${err instanceof Error ? err.message : String(err)}"`
4168
+ );
4169
+ return null;
4170
+ }
4171
+ }
4172
+ function countOperatorTurns(jsonl) {
4173
+ let count = 0;
4174
+ for (const raw of jsonl.split("\n")) {
4175
+ const line = raw.trim();
4176
+ if (!line) continue;
4177
+ try {
4178
+ const obj = JSON.parse(line);
4179
+ if (obj.type === "user") count += 1;
4180
+ } catch {
4181
+ }
4182
+ }
4183
+ return count;
4184
+ }
4185
+ async function fetchPriorWrites(sliceToken, accountId) {
4186
+ const url = `${uiBase()}/api/admin/public-session-context?sliceToken=${encodeURIComponent(sliceToken)}&accountId=${encodeURIComponent(accountId)}`;
4187
+ try {
4188
+ const res = await fetch(url);
4189
+ if (!res.ok) {
4190
+ console.error(
4191
+ `${TAG14} prior-writes-fetch-failed sliceToken=${sliceToken.slice(0, 8)} status=${res.status}`
4192
+ );
4193
+ return [];
4194
+ }
4195
+ const body = await res.json();
4196
+ return Array.isArray(body.writes) ? body.writes : [];
4197
+ } catch (err) {
4198
+ console.error(
4199
+ `${TAG14} prior-writes-fetch-threw sliceToken=${sliceToken.slice(0, 8)} err="${err instanceof Error ? err.message : String(err)}"`
4200
+ );
4201
+ return [];
4202
+ }
4203
+ }
4204
+ function composeInitialMessage(input) {
4205
+ const priorJson = JSON.stringify(input.priorWrites, null, 2);
4206
+ return [
4207
+ `The gated public-agent session for agent "${input.agentSlug}" has just been reaped. Review the transcript and decide what is worth saving to the visitor's per-visitor memory slice. Dispatch \`database-operator\` for each individual write via the Task tool. Do not call memory-write or memory-update directly.`,
4208
+ "",
4209
+ "<slice-token>",
4210
+ input.sliceToken,
4211
+ "</slice-token>",
4212
+ "",
4213
+ "<person-id>",
4214
+ input.personId ?? "(unknown)",
4215
+ "</person-id>",
4216
+ "",
4217
+ "<prior-writes>",
4218
+ priorJson,
4219
+ "</prior-writes>",
4220
+ "",
4221
+ "<transcript>",
4222
+ input.jsonl,
4223
+ "</transcript>"
4224
+ ].join("\n");
4225
+ }
4226
+ async function dispatchReviewer(input, initialMessage) {
4227
+ try {
4228
+ const res = await fetch(`${managerBase2()}/spawn`, {
4229
+ method: "POST",
4230
+ headers: { "content-type": "application/json" },
4231
+ body: JSON.stringify({
4232
+ senderId: `public-session-reviewer-${input.sessionId}`,
4233
+ role: "admin",
4234
+ channel: "webchat",
4235
+ accountId: input.accountId,
4236
+ specialist: "public-session-reviewer",
4237
+ hidden: true,
4238
+ initialMessage,
4239
+ sliceToken: input.sliceToken,
4240
+ personId: input.personId ?? void 0
4241
+ })
4242
+ });
4243
+ if (!res.ok) {
4244
+ return { ok: false, reason: `spawn-status-${res.status}` };
4245
+ }
4246
+ const body = await res.json();
4247
+ return { ok: true, managerSessionId: body.sessionId ?? "unknown" };
4248
+ } catch (err) {
4249
+ return { ok: false, reason: `spawn-threw-${err instanceof Error ? err.message : String(err)}` };
4250
+ }
4251
+ }
4252
+ async function firePublicSessionEndReview(input) {
4253
+ const sliceShort = input.sliceToken.slice(0, 8);
4254
+ const personField = input.personId ?? "none";
4255
+ const dispatchedAt = Date.now();
4256
+ const jsonl = await fetchJsonl(input.sessionId);
4257
+ if (!jsonl) {
4258
+ console.log(
4259
+ `${TAG14} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-jsonl-missing`
4260
+ );
4261
+ return;
4262
+ }
4263
+ const operatorTurns = countOperatorTurns(jsonl);
4264
+ if (operatorTurns === 0) {
4265
+ console.log(
4266
+ `${TAG14} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-no-turns`
4267
+ );
4268
+ return;
4269
+ }
4270
+ const priorWrites = await fetchPriorWrites(input.sliceToken, input.accountId);
4271
+ const initialMessage = composeInitialMessage({
4272
+ agentSlug: input.agentSlug,
4273
+ sliceToken: input.sliceToken,
4274
+ personId: input.personId,
4275
+ jsonl,
4276
+ priorWrites
4277
+ });
4278
+ const dispatched = await dispatchReviewer(input, initialMessage);
4279
+ if (!dispatched.ok) {
4280
+ console.error(
4281
+ `${TAG14} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-spawn-failed reason=${dispatched.reason}`
4282
+ );
4283
+ return;
4284
+ }
4285
+ console.log(
4286
+ `${TAG14} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=dispatched managerSessionId=${dispatched.managerSessionId} operatorTurns=${operatorTurns} priorWrites=${priorWrites.length} ms=${Date.now() - dispatchedAt}`
4287
+ );
4288
+ }
4289
+
4148
4290
  // app/lib/channel-pty-bridge/follower.ts
4149
4291
  function startFollower(opts) {
4150
4292
  const abort = new AbortController();
@@ -4235,12 +4377,15 @@ function publicIdleMs() {
4235
4377
  return Number(process.env.CHANNEL_PTY_IDLE_MS ?? String(30 * 6e4));
4236
4378
  }
4237
4379
  var index = /* @__PURE__ */ new Map();
4238
- function indexKey(accountId, agentSlug, senderId, liveMemory) {
4239
- return `${accountId}:${agentSlug}:${liveMemory ? "lm1" : "lm0"}:${senderId}`;
4380
+ function indexKey(accountId, agentSlug, senderId, liveMemory, sliceToken = "") {
4381
+ const slice = sliceToken && sliceToken.length > 0 ? sliceToken : "open";
4382
+ return `${accountId}:${agentSlug}:${liveMemory ? "lm1" : "lm0"}:${slice}:${senderId}`;
4240
4383
  }
4241
4384
  async function ensureEntry(input) {
4242
4385
  startReaper();
4243
- const key = indexKey(input.accountId, input.agentSlug, input.senderId, input.liveMemory);
4386
+ const sliceTokenValue = input.sliceToken ?? "";
4387
+ const personIdValue = input.personId ?? null;
4388
+ const key = indexKey(input.accountId, input.agentSlug, input.senderId, input.liveMemory, sliceTokenValue);
4244
4389
  const existing = index.get(key);
4245
4390
  const tag = tagFor(input.channel);
4246
4391
  if (existing) {
@@ -4264,7 +4409,9 @@ async function ensureEntry(input) {
4264
4409
  accountId: input.accountId,
4265
4410
  agentSlug: input.agentSlug,
4266
4411
  liveMemory: input.liveMemory,
4267
- attachmentDir
4412
+ attachmentDir,
4413
+ sliceToken: sliceTokenValue.length > 0 ? sliceTokenValue : void 0,
4414
+ personId: personIdValue ?? void 0
4268
4415
  });
4269
4416
  if ("error" in spawned) {
4270
4417
  if (spawned.error !== "claude-auth-dead") {
@@ -4287,7 +4434,9 @@ async function ensureEntry(input) {
4287
4434
  pendingTurnText: "",
4288
4435
  followerAbort: null,
4289
4436
  followerRunning: false,
4290
- liveMemory: input.liveMemory
4437
+ liveMemory: input.liveMemory,
4438
+ sliceToken: sliceTokenValue,
4439
+ personId: personIdValue
4291
4440
  };
4292
4441
  entry.followerAbort = startFollower({
4293
4442
  entry,
@@ -4314,14 +4463,16 @@ async function writeInput(entry, text) {
4314
4463
  `${tag} session-resolved senderId=${entry.senderId} action=respawn reason=session-gone-${result.status} sessionId=${entry.sessionId.slice(0, 8)}`
4315
4464
  );
4316
4465
  if (entry.followerAbort) entry.followerAbort.abort();
4317
- index.delete(indexKey(entry.accountId, entry.agentSlug, entry.senderId, entry.liveMemory));
4466
+ index.delete(indexKey(entry.accountId, entry.agentSlug, entry.senderId, entry.liveMemory, entry.sliceToken));
4318
4467
  const respawned = await ensureEntry({
4319
4468
  accountId: entry.accountId,
4320
4469
  senderId: entry.senderId,
4321
4470
  role: entry.role,
4322
4471
  channel: entry.channel,
4323
4472
  agentSlug: entry.agentSlug,
4324
- liveMemory: entry.liveMemory
4473
+ liveMemory: entry.liveMemory,
4474
+ sliceToken: entry.sliceToken.length > 0 ? entry.sliceToken : void 0,
4475
+ personId: entry.personId
4325
4476
  });
4326
4477
  if (!respawned) return false;
4327
4478
  for (const cb of entry.subscribers) respawned.subscribers.add(cb);
@@ -4359,16 +4510,18 @@ async function dispatchOnce(input) {
4359
4510
  role: input.role,
4360
4511
  channel: input.channel,
4361
4512
  agentSlug: input.agentSlug,
4362
- liveMemory: input.liveMemory
4513
+ liveMemory: input.liveMemory,
4514
+ sliceToken: input.sliceToken,
4515
+ personId: input.personId
4363
4516
  });
4364
4517
  if (!entry) return { error: "spawn-failed" };
4365
4518
  entry.lastInboundAt = Date.now();
4366
- let resolve21;
4519
+ let resolve23;
4367
4520
  const turnPromise = new Promise((r) => {
4368
- resolve21 = r;
4521
+ resolve23 = r;
4369
4522
  });
4370
4523
  const listener = (text) => {
4371
- resolve21(text);
4524
+ resolve23(text);
4372
4525
  };
4373
4526
  entry.subscribers.add(listener);
4374
4527
  const writeOk = await writeInput(entry, input.text);
@@ -4418,12 +4571,49 @@ function startReaper() {
4418
4571
  );
4419
4572
  if (entry.followerAbort) entry.followerAbort.abort();
4420
4573
  index.delete(key);
4574
+ if (entry.role === "public" && entry.sliceToken.length > 0) {
4575
+ void firePublicSessionEndReview({
4576
+ sessionId: entry.sessionId,
4577
+ sliceToken: entry.sliceToken,
4578
+ personId: entry.personId,
4579
+ accountId: entry.accountId,
4580
+ agentSlug: entry.agentSlug,
4581
+ dispatchFor: "eviction"
4582
+ });
4583
+ }
4421
4584
  void managerDelete(entry.sessionId);
4422
4585
  }
4423
4586
  }, 6e4);
4424
4587
  if (reaperHandle.unref) reaperHandle.unref();
4425
4588
  }
4426
4589
 
4590
+ // app/lib/access-session-store.ts
4591
+ import { randomUUID as randomUUID4 } from "crypto";
4592
+ var sessions = /* @__PURE__ */ new Map();
4593
+ function createAccessSession(input) {
4594
+ const sessionId = randomUUID4();
4595
+ const session = {
4596
+ ...input,
4597
+ sessionId,
4598
+ createdAt: Date.now()
4599
+ };
4600
+ sessions.set(sessionId, session);
4601
+ return session;
4602
+ }
4603
+ function getAccessSession(sessionId) {
4604
+ return sessions.get(sessionId) ?? null;
4605
+ }
4606
+ function evictAccessSessionsByGrant(grantId) {
4607
+ let dropped = 0;
4608
+ for (const [sessionId, session] of sessions) {
4609
+ if (session.grantId === grantId) {
4610
+ sessions.delete(sessionId);
4611
+ dropped += 1;
4612
+ }
4613
+ }
4614
+ return dropped;
4615
+ }
4616
+
4427
4617
  // server/routes/chat.ts
4428
4618
  var WEBCHAT_TAG = "[webchat-adaptor]";
4429
4619
  var SESSION_KEY_RE = /^[A-Za-z0-9][A-Za-z0-9_-]{7,127}$/;
@@ -4545,13 +4735,37 @@ ${lines}]`;
4545
4735
  return c.json({ error: "No default agent configured" }, 500);
4546
4736
  }
4547
4737
  let liveMemory = false;
4738
+ let accessMode = "open";
4548
4739
  try {
4549
- liveMemory = resolveAgentConfig(account.accountDir, agentSlug).liveMemory;
4740
+ const config = resolveAgentConfig(account.accountDir, agentSlug);
4741
+ liveMemory = config.liveMemory;
4742
+ accessMode = config.accessMode;
4550
4743
  } catch (err) {
4551
4744
  const m = err instanceof Error ? err.message : String(err);
4552
4745
  console.error(`${WEBCHAT_TAG} reject reason=agent-config-failed agentSlug=${agentSlug} message=${m}`);
4553
4746
  return c.json({ error: "Agent config invalid" }, 500);
4554
4747
  }
4748
+ let sliceToken;
4749
+ let personId = null;
4750
+ if (accessMode === "gated" || accessMode === "paid") {
4751
+ const cookieHeader = c.req.header("cookie") ?? "";
4752
+ const accessSessionId = cookieHeader.split(";").map((p) => p.trim()).find((p) => p.startsWith("__access_session="))?.slice("__access_session=".length);
4753
+ const session = accessSessionId ? getAccessSession(accessSessionId) : null;
4754
+ if (!session) {
4755
+ console.error(
4756
+ `${WEBCHAT_TAG} reject reason=gate-required agentSlug=${agentSlug} senderId=${session_key.slice(0, 8)} cookie=${accessSessionId ? "unknown-id" : "absent"}`
4757
+ );
4758
+ return c.json({ error: "gate-required", agentSlug }, 401);
4759
+ }
4760
+ if (session.agentSlug !== agentSlug) {
4761
+ console.error(
4762
+ `${WEBCHAT_TAG} reject reason=gate-agent-mismatch agentSlug=${agentSlug} cookieAgent=${session.agentSlug}`
4763
+ );
4764
+ return c.json({ error: "gate-required", agentSlug }, 401);
4765
+ }
4766
+ sliceToken = session.sliceToken;
4767
+ personId = session.personId;
4768
+ }
4555
4769
  const sk = session_key.slice(0, 8);
4556
4770
  console.error(
4557
4771
  `${WEBCHAT_TAG} inbound from=${sk} bytes=${Buffer.byteLength(fullMessage, "utf8")} attachments=${attachmentCount} accountId=${account.accountId}`
@@ -4566,6 +4780,8 @@ ${lines}]`;
4566
4780
  channel: "webchat",
4567
4781
  agentSlug,
4568
4782
  liveMemory,
4783
+ sliceToken,
4784
+ personId,
4569
4785
  text: fullMessage,
4570
4786
  timeoutMs: webchatTurnTimeoutMs()
4571
4787
  });
@@ -4617,15 +4833,15 @@ import { readFile as readFile2, stat as stat3 } from "fs/promises";
4617
4833
  import { realpathSync as realpathSync2, readdirSync as readdirSync2, readFileSync as readFileSync6, existsSync as existsSync4 } from "fs";
4618
4834
 
4619
4835
  // app/lib/whatsapp/login.ts
4620
- import { randomUUID as randomUUID4 } from "crypto";
4621
- var TAG14 = "[whatsapp:login]";
4836
+ import { randomUUID as randomUUID5 } from "crypto";
4837
+ var TAG15 = "[whatsapp:login]";
4622
4838
  var ACTIVE_LOGIN_TTL_MS = 3 * 6e4;
4623
4839
  var activeLogins = /* @__PURE__ */ new Map();
4624
4840
  function closeSocket(sock) {
4625
4841
  try {
4626
4842
  sock.ws?.close?.();
4627
4843
  } catch (err) {
4628
- console.warn(`${TAG14} socket close error during cleanup: ${String(err)}`);
4844
+ console.warn(`${TAG15} socket close error during cleanup: ${String(err)}`);
4629
4845
  }
4630
4846
  }
4631
4847
  function resetActiveLogin(accountId) {
@@ -4648,7 +4864,7 @@ async function loginConnectionLoop(accountId, login) {
4648
4864
  const current = activeLogins.get(accountId);
4649
4865
  if (current?.id === login.id) {
4650
4866
  current.connected = true;
4651
- console.error(`${TAG14} loginConnectionLoop: connected account=${accountId} attempt=${attempt}`);
4867
+ console.error(`${TAG15} loginConnectionLoop: connected account=${accountId} attempt=${attempt}`);
4652
4868
  }
4653
4869
  return;
4654
4870
  } catch (err) {
@@ -4658,7 +4874,7 @@ async function loginConnectionLoop(accountId, login) {
4658
4874
  if (!classification.shouldRetry || attempt >= LOGIN_MAX_RECONNECTS) {
4659
4875
  if (attempt >= LOGIN_MAX_RECONNECTS) {
4660
4876
  console.error(
4661
- `${TAG14} login reconnect attempts exhausted (${attempt}/${LOGIN_MAX_RECONNECTS}) \u2014 surfacing error to agent`
4877
+ `${TAG15} login reconnect attempts exhausted (${attempt}/${LOGIN_MAX_RECONNECTS}) \u2014 surfacing error to agent`
4662
4878
  );
4663
4879
  current.error = `Login failed after ${attempt} reconnect attempts: ${formatError(err)}`;
4664
4880
  } else {
@@ -4670,7 +4886,7 @@ async function loginConnectionLoop(accountId, login) {
4670
4886
  attempt++;
4671
4887
  const delay = LOGIN_RECONNECT_DELAYS[attempt - 1] ?? 8e3;
4672
4888
  console.error(
4673
- `${TAG14} status=${classification.statusCode ?? "unknown"} restart required \u2014 reconnecting with saved creds (attempt ${attempt}/${LOGIN_MAX_RECONNECTS}) delay=${delay}ms`
4889
+ `${TAG15} status=${classification.statusCode ?? "unknown"} restart required \u2014 reconnecting with saved creds (attempt ${attempt}/${LOGIN_MAX_RECONNECTS}) delay=${delay}ms`
4674
4890
  );
4675
4891
  closeSocket(current.sock);
4676
4892
  await new Promise((r) => setTimeout(r, delay));
@@ -4681,7 +4897,7 @@ async function loginConnectionLoop(accountId, login) {
4681
4897
  current.sock = newSock;
4682
4898
  } catch (sockErr) {
4683
4899
  console.error(
4684
- `${TAG14} reconnect socket creation failed (attempt ${attempt}/${LOGIN_MAX_RECONNECTS}): ${String(sockErr)}`
4900
+ `${TAG15} reconnect socket creation failed (attempt ${attempt}/${LOGIN_MAX_RECONNECTS}): ${String(sockErr)}`
4685
4901
  );
4686
4902
  current.error = `Reconnection failed: ${String(sockErr)}`;
4687
4903
  return;
@@ -4695,7 +4911,7 @@ async function startLogin(opts) {
4695
4911
  const hasAuth = await authExists(authDir);
4696
4912
  const selfId = readSelfId(authDir);
4697
4913
  console.error(
4698
- `${TAG14} startLogin account=${accountId} force=${!!force} hasAuth=${hasAuth}` + (existing0 ? ` activeLogin={id=${existing0.id.slice(0, 8)},age=${Math.round((Date.now() - existing0.startedAt) / 1e3)}s,hasQr=${!!existing0.qr}}` : " activeLogin=none")
4914
+ `${TAG15} startLogin account=${accountId} force=${!!force} hasAuth=${hasAuth}` + (existing0 ? ` activeLogin={id=${existing0.id.slice(0, 8)},age=${Math.round((Date.now() - existing0.startedAt) / 1e3)}s,hasQr=${!!existing0.qr}}` : " activeLogin=none")
4699
4915
  );
4700
4916
  if (hasAuth && !force) {
4701
4917
  const who = selfId.e164 ?? selfId.jid ?? "unknown";
@@ -4707,7 +4923,7 @@ async function startLogin(opts) {
4707
4923
  await clearAuth(authDir);
4708
4924
  const existing = activeLogins.get(accountId);
4709
4925
  if (existing && isLoginFresh(existing) && existing.qrDataUrl && !force) {
4710
- console.error(`${TAG14} startLogin account=${accountId} guard: returning existing QR (age=${Math.round((Date.now() - existing.startedAt) / 1e3)}s)`);
4926
+ console.error(`${TAG15} startLogin account=${accountId} guard: returning existing QR (age=${Math.round((Date.now() - existing.startedAt) / 1e3)}s)`);
4711
4927
  return {
4712
4928
  qrDataUrl: existing.qrDataUrl,
4713
4929
  qrRaw: existing.qr,
@@ -4715,13 +4931,13 @@ async function startLogin(opts) {
4715
4931
  };
4716
4932
  }
4717
4933
  if (existing) {
4718
- console.error(`${TAG14} startLogin account=${accountId} ${force ? "force override" : "stale/no-QR"}, resetting active login`);
4934
+ console.error(`${TAG15} startLogin account=${accountId} ${force ? "force override" : "stale/no-QR"}, resetting active login`);
4719
4935
  }
4720
4936
  resetActiveLogin(accountId);
4721
4937
  let resolveQr = null;
4722
4938
  let rejectQr = null;
4723
- const qrPromise = new Promise((resolve21, reject) => {
4724
- resolveQr = resolve21;
4939
+ const qrPromise = new Promise((resolve23, reject) => {
4940
+ resolveQr = resolve23;
4725
4941
  rejectQr = reject;
4726
4942
  });
4727
4943
  const qrTimer = setTimeout(
@@ -4737,14 +4953,14 @@ async function startLogin(opts) {
4737
4953
  onQr: (qr2) => {
4738
4954
  loginQrCount++;
4739
4955
  if (pendingQr) {
4740
- console.error(`${TAG14} QR rotation #${loginQrCount} received for account=${accountId} \u2014 not forwarded (initial QR already captured)`);
4956
+ console.error(`${TAG15} QR rotation #${loginQrCount} received for account=${accountId} \u2014 not forwarded (initial QR already captured)`);
4741
4957
  return;
4742
4958
  }
4743
4959
  pendingQr = qr2;
4744
4960
  const current = activeLogins.get(accountId);
4745
4961
  if (current && !current.qr) current.qr = qr2;
4746
4962
  clearTimeout(qrTimer);
4747
- console.error(`${TAG14} QR #${loginQrCount} received for account=${accountId} \u2014 forwarding to caller`);
4963
+ console.error(`${TAG15} QR #${loginQrCount} received for account=${accountId} \u2014 forwarding to caller`);
4748
4964
  resolveQr?.(qr2);
4749
4965
  }
4750
4966
  });
@@ -4756,7 +4972,7 @@ async function startLogin(opts) {
4756
4972
  const login = {
4757
4973
  accountId,
4758
4974
  authDir,
4759
- id: randomUUID4(),
4975
+ id: randomUUID5(),
4760
4976
  sock,
4761
4977
  startedAt: Date.now(),
4762
4978
  connected: false
@@ -4764,7 +4980,7 @@ async function startLogin(opts) {
4764
4980
  activeLogins.set(accountId, login);
4765
4981
  if (pendingQr && !login.qr) login.qr = pendingQr;
4766
4982
  loginConnectionLoop(accountId, login).catch((err) => {
4767
- console.error(`${TAG14} loginConnectionLoop unexpected error: ${String(err)}`);
4983
+ console.error(`${TAG15} loginConnectionLoop unexpected error: ${String(err)}`);
4768
4984
  const current = activeLogins.get(accountId);
4769
4985
  if (current?.id === login.id) {
4770
4986
  current.error = `Unexpected login error: ${String(err)}`;
@@ -4789,7 +5005,7 @@ async function waitForLogin(opts) {
4789
5005
  const { accountId, timeoutMs = 6e4 } = opts;
4790
5006
  const login = activeLogins.get(accountId);
4791
5007
  console.error(
4792
- `${TAG14} waitForLogin account=${accountId} timeout=${timeoutMs}ms` + (login ? ` login={id=${login.id.slice(0, 8)},age=${Math.round((Date.now() - login.startedAt) / 1e3)}s,connected=${login.connected},hasQr=${!!login.qr}}` : " login=none")
5008
+ `${TAG15} waitForLogin account=${accountId} timeout=${timeoutMs}ms` + (login ? ` login={id=${login.id.slice(0, 8)},age=${Math.round((Date.now() - login.startedAt) / 1e3)}s,connected=${login.connected},hasQr=${!!login.qr}}` : " login=none")
4793
5009
  );
4794
5010
  if (!login) {
4795
5011
  return { connected: false, message: "No active WhatsApp login in progress." };
@@ -4802,7 +5018,7 @@ async function waitForLogin(opts) {
4802
5018
  while (Date.now() < deadline) {
4803
5019
  if (login.connected) {
4804
5020
  const selfId = readSelfId(login.authDir);
4805
- console.error(`${TAG14} login complete for account=${accountId} phone=${selfId.e164 ?? "unknown"}`);
5021
+ console.error(`${TAG15} login complete for account=${accountId} phone=${selfId.e164 ?? "unknown"}`);
4806
5022
  const sock = login.sock;
4807
5023
  const authDir = login.authDir;
4808
5024
  activeLogins.delete(accountId);
@@ -4822,7 +5038,7 @@ async function waitForLogin(opts) {
4822
5038
  await new Promise((r) => setTimeout(r, 1e3));
4823
5039
  }
4824
5040
  const elapsed = Math.round((Date.now() - (deadline - timeoutMs)) / 1e3);
4825
- console.error(`${TAG14} waitForLogin timeout account=${accountId} elapsed=${elapsed}s \u2014 cleaning up active login`);
5041
+ console.error(`${TAG15} waitForLogin timeout account=${accountId} elapsed=${elapsed}s \u2014 cleaning up active login`);
4826
5042
  resetActiveLogin(accountId);
4827
5043
  return { connected: false, message: "Login timed out. Try generating a new QR." };
4828
5044
  }
@@ -4830,7 +5046,7 @@ async function waitForLogin(opts) {
4830
5046
  // app/lib/whatsapp/config-persist.ts
4831
5047
  import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, existsSync as existsSync3 } from "fs";
4832
5048
  import { resolve as resolve5, join as join4 } from "path";
4833
- var TAG15 = "[whatsapp:config]";
5049
+ var TAG16 = "[whatsapp:config]";
4834
5050
  function configPath(accountDir) {
4835
5051
  return resolve5(accountDir, "account.json");
4836
5052
  }
@@ -4847,9 +5063,9 @@ function reloadManagerConfig(accountDir) {
4847
5063
  try {
4848
5064
  const config = readConfig(accountDir);
4849
5065
  reloadConfig(config);
4850
- console.error(`${TAG15} reloaded manager config`);
5066
+ console.error(`${TAG16} reloaded manager config`);
4851
5067
  } catch (err) {
4852
- console.error(`${TAG15} manager config reload failed: ${String(err)}`);
5068
+ console.error(`${TAG16} manager config reload failed: ${String(err)}`);
4853
5069
  }
4854
5070
  }
4855
5071
  var E164_PATTERN = /^\+\d{7,15}$/;
@@ -4875,25 +5091,25 @@ function persistAfterPairing(accountDir, accountId, selfPhone) {
4875
5091
  const adminPhones = wa.adminPhones;
4876
5092
  if (!adminPhones.includes(normalized)) {
4877
5093
  adminPhones.push(normalized);
4878
- console.error(`${TAG15} added selfPhone=${normalized} to adminPhones`);
5094
+ console.error(`${TAG16} added selfPhone=${normalized} to adminPhones`);
4879
5095
  }
4880
5096
  } else {
4881
- console.error(`${TAG15} skipping adminPhones \u2014 selfPhone is null account=${accountId}`);
5097
+ console.error(`${TAG16} skipping adminPhones \u2014 selfPhone is null account=${accountId}`);
4882
5098
  }
4883
5099
  const parsed = WhatsAppConfigSchema.safeParse(wa);
4884
5100
  if (!parsed.success) {
4885
5101
  const msg = parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ");
4886
- console.error(`${TAG15} validation failed after pairing: ${msg}`);
5102
+ console.error(`${TAG16} validation failed after pairing: ${msg}`);
4887
5103
  return { ok: false, error: `Validation failed: ${msg}` };
4888
5104
  }
4889
5105
  config.whatsapp = parsed.data;
4890
5106
  writeConfig(accountDir, config);
4891
- console.error(`${TAG15} persisted after pairing account=${accountId} phone=${selfPhone ?? "null"}`);
5107
+ console.error(`${TAG16} persisted after pairing account=${accountId} phone=${selfPhone ?? "null"}`);
4892
5108
  reloadManagerConfig(accountDir);
4893
5109
  return { ok: true };
4894
5110
  } catch (err) {
4895
5111
  const msg = err instanceof Error ? err.message : String(err);
4896
- console.error(`${TAG15} persist failed account=${accountId}: ${msg}`);
5112
+ console.error(`${TAG16} persist failed account=${accountId}: ${msg}`);
4897
5113
  return { ok: false, error: msg };
4898
5114
  }
4899
5115
  }
@@ -4923,12 +5139,12 @@ function addAdminPhone(accountDir, phone) {
4923
5139
  }
4924
5140
  config.whatsapp = parsed.data;
4925
5141
  writeConfig(accountDir, config);
4926
- console.error(`${TAG15} added admin phone=${normalized}`);
5142
+ console.error(`${TAG16} added admin phone=${normalized}`);
4927
5143
  reloadManagerConfig(accountDir);
4928
5144
  return { ok: true, message: `Added ${normalized} as admin phone. Messages from this number will route to the admin agent.` };
4929
5145
  } catch (err) {
4930
5146
  const msg = err instanceof Error ? err.message : String(err);
4931
- console.error(`${TAG15} addAdminPhone failed: ${msg}`);
5147
+ console.error(`${TAG16} addAdminPhone failed: ${msg}`);
4932
5148
  return { ok: false, error: msg };
4933
5149
  }
4934
5150
  }
@@ -4956,12 +5172,12 @@ function removeAdminPhone(accountDir, phone) {
4956
5172
  }
4957
5173
  config.whatsapp = parsed.data;
4958
5174
  writeConfig(accountDir, config);
4959
- console.error(`${TAG15} removed admin phone=${normalized}`);
5175
+ console.error(`${TAG16} removed admin phone=${normalized}`);
4960
5176
  reloadManagerConfig(accountDir);
4961
5177
  return { ok: true, message: `Removed ${normalized} from admin phones. Messages from this number will now route to the public agent.` };
4962
5178
  } catch (err) {
4963
5179
  const msg = err instanceof Error ? err.message : String(err);
4964
- console.error(`${TAG15} removeAdminPhone failed: ${msg}`);
5180
+ console.error(`${TAG16} removeAdminPhone failed: ${msg}`);
4965
5181
  return { ok: false, error: msg };
4966
5182
  }
4967
5183
  }
@@ -4999,12 +5215,12 @@ function setPublicAgent(accountDir, slug) {
4999
5215
  }
5000
5216
  config.whatsapp = parsed.data;
5001
5217
  writeConfig(accountDir, config);
5002
- console.error(`${TAG15} publicAgent set to ${trimmed}`);
5218
+ console.error(`${TAG16} publicAgent set to ${trimmed}`);
5003
5219
  reloadManagerConfig(accountDir);
5004
5220
  return { ok: true, message: `Public agent set to "${trimmed}". WhatsApp messages from non-admin phones will be handled by this agent.` };
5005
5221
  } catch (err) {
5006
5222
  const msg = err instanceof Error ? err.message : String(err);
5007
- console.error(`${TAG15} setPublicAgent failed: ${msg}`);
5223
+ console.error(`${TAG16} setPublicAgent failed: ${msg}`);
5008
5224
  return { ok: false, error: msg };
5009
5225
  }
5010
5226
  }
@@ -5068,12 +5284,12 @@ function setGroupPublicAgent(accountDir, accountId, groupJid, slug) {
5068
5284
  }
5069
5285
  config.whatsapp = parsed.data;
5070
5286
  writeConfig(accountDir, config);
5071
- console.error(`${TAG15} setGroupPublicAgent account=${trimmedAccount} groupJid=${trimmedGroup} slug=${trimmedSlug}`);
5287
+ console.error(`${TAG16} setGroupPublicAgent account=${trimmedAccount} groupJid=${trimmedGroup} slug=${trimmedSlug}`);
5072
5288
  reloadManagerConfig(accountDir);
5073
5289
  return { ok: true, message: `Per-group public agent set to "${trimmedSlug}" for group ${trimmedGroup}.` };
5074
5290
  } catch (err) {
5075
5291
  const msg = err instanceof Error ? err.message : String(err);
5076
- console.error(`${TAG15} setGroupPublicAgent failed: ${msg}`);
5292
+ console.error(`${TAG16} setGroupPublicAgent failed: ${msg}`);
5077
5293
  return { ok: false, error: msg };
5078
5294
  }
5079
5295
  }
@@ -5100,12 +5316,12 @@ function unsetGroupPublicAgent(accountDir, accountId, groupJid) {
5100
5316
  }
5101
5317
  config.whatsapp = parsed.data;
5102
5318
  writeConfig(accountDir, config);
5103
- console.error(`${TAG15} unsetGroupPublicAgent account=${trimmedAccount} groupJid=${trimmedGroup}`);
5319
+ console.error(`${TAG16} unsetGroupPublicAgent account=${trimmedAccount} groupJid=${trimmedGroup}`);
5104
5320
  reloadManagerConfig(accountDir);
5105
5321
  return { ok: true, message: `Per-group public agent override removed for group ${trimmedGroup}.` };
5106
5322
  } catch (err) {
5107
5323
  const msg = err instanceof Error ? err.message : String(err);
5108
- console.error(`${TAG15} unsetGroupPublicAgent failed: ${msg}`);
5324
+ console.error(`${TAG16} unsetGroupPublicAgent failed: ${msg}`);
5109
5325
  return { ok: false, error: msg };
5110
5326
  }
5111
5327
  }
@@ -5126,17 +5342,17 @@ function updateConfig(accountDir, fields) {
5126
5342
  const parsed = WhatsAppConfigSchema.safeParse(wa);
5127
5343
  if (!parsed.success) {
5128
5344
  const msg = parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ");
5129
- console.error(`${TAG15} update validation failed: ${msg}`);
5345
+ console.error(`${TAG16} update validation failed: ${msg}`);
5130
5346
  return { ok: false, error: `Validation failed: ${msg}` };
5131
5347
  }
5132
5348
  config.whatsapp = parsed.data;
5133
5349
  writeConfig(accountDir, config);
5134
- console.error(`${TAG15} updated fields=[${fieldNames.join(",")}]`);
5350
+ console.error(`${TAG16} updated fields=[${fieldNames.join(",")}]`);
5135
5351
  reloadManagerConfig(accountDir);
5136
5352
  return { ok: true, message: `Updated WhatsApp config: ${fieldNames.join(", ")}.` };
5137
5353
  } catch (err) {
5138
5354
  const msg = err instanceof Error ? err.message : String(err);
5139
- console.error(`${TAG15} updateConfig failed: ${msg}`);
5355
+ console.error(`${TAG16} updateConfig failed: ${msg}`);
5140
5356
  return { ok: false, error: msg };
5141
5357
  }
5142
5358
  }
@@ -5262,17 +5478,17 @@ function serializeWhatsAppSchema() {
5262
5478
  }
5263
5479
 
5264
5480
  // server/routes/whatsapp.ts
5265
- var TAG16 = "[whatsapp:api]";
5481
+ var TAG17 = "[whatsapp:api]";
5266
5482
  var PLATFORM_ROOT4 = process.env.MAXY_PLATFORM_ROOT || "";
5267
5483
  var app3 = new Hono();
5268
5484
  app3.get("/status", (c) => {
5269
5485
  try {
5270
5486
  const status = getStatus();
5271
5487
  const summary = status.map((a) => `${a.accountId}:${a.connected ? "up" : "down"}`).join(", ");
5272
- console.error(`${TAG16} status accounts=${status.length} [${summary}]`);
5488
+ console.error(`${TAG17} status accounts=${status.length} [${summary}]`);
5273
5489
  return c.json({ accounts: status });
5274
5490
  } catch (err) {
5275
- console.error(`${TAG16} status error: ${String(err)}`);
5491
+ console.error(`${TAG17} status error: ${String(err)}`);
5276
5492
  return c.json({ error: String(err) }, 500);
5277
5493
  }
5278
5494
  });
@@ -5283,10 +5499,10 @@ app3.post("/login/start", async (c) => {
5283
5499
  const force = body.force ?? false;
5284
5500
  const authDir = join5(MAXY_DIR, "credentials", "whatsapp", accountId);
5285
5501
  const result = await startLogin({ accountId, authDir, force });
5286
- console.error(`${TAG16} login/start result account=${accountId} hasQr=${!!result.qrRaw}${result.selfPhone ? ` phone=${result.selfPhone}` : ""}`);
5502
+ console.error(`${TAG17} login/start result account=${accountId} hasQr=${!!result.qrRaw}${result.selfPhone ? ` phone=${result.selfPhone}` : ""}`);
5287
5503
  return c.json(result);
5288
5504
  } catch (err) {
5289
- console.error(`${TAG16} login/start error: ${String(err)}`);
5505
+ console.error(`${TAG17} login/start error: ${String(err)}`);
5290
5506
  return c.json({ error: String(err) }, 500);
5291
5507
  }
5292
5508
  });
@@ -5301,7 +5517,7 @@ app3.post("/login/wait", async (c) => {
5301
5517
  try {
5302
5518
  await registerLoginSocket(accountId, result.sock, result.authDir);
5303
5519
  } catch (regErr) {
5304
- console.error(`${TAG16} registerLoginSocket failed account=${accountId}: ${String(regErr)}`);
5520
+ console.error(`${TAG17} registerLoginSocket failed account=${accountId}: ${String(regErr)}`);
5305
5521
  }
5306
5522
  try {
5307
5523
  const account = resolveAccount();
@@ -5309,16 +5525,16 @@ app3.post("/login/wait", async (c) => {
5309
5525
  const persistResult = persistAfterPairing(account.accountDir, accountId, result.selfPhone ?? null);
5310
5526
  configPersisted = persistResult.ok;
5311
5527
  if (!persistResult.ok) {
5312
- console.error(`${TAG16} config persist failed account=${accountId}: ${persistResult.error}`);
5528
+ console.error(`${TAG17} config persist failed account=${accountId}: ${persistResult.error}`);
5313
5529
  }
5314
5530
  } else {
5315
- console.error(`${TAG16} config persist skipped \u2014 no account resolved`);
5531
+ console.error(`${TAG17} config persist skipped \u2014 no account resolved`);
5316
5532
  }
5317
5533
  } catch (persistErr) {
5318
- console.error(`${TAG16} config persist error account=${accountId}: ${String(persistErr)}`);
5534
+ console.error(`${TAG17} config persist error account=${accountId}: ${String(persistErr)}`);
5319
5535
  }
5320
5536
  }
5321
- console.error(`${TAG16} login/wait result account=${accountId} connected=${result.connected}${result.selfPhone ? ` phone=${result.selfPhone}` : ""} configPersisted=${configPersisted}`);
5537
+ console.error(`${TAG17} login/wait result account=${accountId} connected=${result.connected}${result.selfPhone ? ` phone=${result.selfPhone}` : ""} configPersisted=${configPersisted}`);
5322
5538
  return c.json({
5323
5539
  connected: result.connected,
5324
5540
  message: result.message,
@@ -5326,7 +5542,7 @@ app3.post("/login/wait", async (c) => {
5326
5542
  configPersisted
5327
5543
  });
5328
5544
  } catch (err) {
5329
- console.error(`${TAG16} login/wait error: ${String(err)}`);
5545
+ console.error(`${TAG17} login/wait error: ${String(err)}`);
5330
5546
  return c.json({ error: String(err) }, 500);
5331
5547
  }
5332
5548
  });
@@ -5337,7 +5553,7 @@ app3.post("/disconnect", async (c) => {
5337
5553
  await stopConnection(accountId);
5338
5554
  return c.json({ disconnected: true, accountId });
5339
5555
  } catch (err) {
5340
- console.error(`${TAG16} disconnect error: ${String(err)}`);
5556
+ console.error(`${TAG17} disconnect error: ${String(err)}`);
5341
5557
  return c.json({ error: String(err) }, 500);
5342
5558
  }
5343
5559
  });
@@ -5348,7 +5564,7 @@ app3.post("/reconnect", async (c) => {
5348
5564
  await startConnection(accountId);
5349
5565
  return c.json({ reconnecting: true, accountId });
5350
5566
  } catch (err) {
5351
- console.error(`${TAG16} reconnect error: ${String(err)}`);
5567
+ console.error(`${TAG17} reconnect error: ${String(err)}`);
5352
5568
  return c.json({ error: String(err) }, 500);
5353
5569
  }
5354
5570
  });
@@ -5367,7 +5583,7 @@ app3.post("/send", async (c) => {
5367
5583
  const result = await sendTextMessage(sock, to, text, { accountId });
5368
5584
  return c.json(result);
5369
5585
  } catch (err) {
5370
- console.error(`${TAG16} send error: ${String(err)}`);
5586
+ console.error(`${TAG17} send error: ${String(err)}`);
5371
5587
  return c.json({ error: String(err) }, 500);
5372
5588
  }
5373
5589
  });
@@ -5388,7 +5604,7 @@ app3.post("/config", async (c) => {
5388
5604
  return c.json({ ok: false, error: 'Missing required field "phone" (E.164 format, e.g. +441234567890).' }, 400);
5389
5605
  }
5390
5606
  const result = addAdminPhone(account.accountDir, phone);
5391
- console.error(`${TAG16} config action=add-admin-phone phone=${phone} ok=${result.ok}`);
5607
+ console.error(`${TAG17} config action=add-admin-phone phone=${phone} ok=${result.ok}`);
5392
5608
  return c.json(result, result.ok ? 200 : 400);
5393
5609
  }
5394
5610
  case "remove-admin-phone": {
@@ -5396,12 +5612,12 @@ app3.post("/config", async (c) => {
5396
5612
  return c.json({ ok: false, error: 'Missing required field "phone".' }, 400);
5397
5613
  }
5398
5614
  const result = removeAdminPhone(account.accountDir, phone);
5399
- console.error(`${TAG16} config action=remove-admin-phone phone=${phone} ok=${result.ok}`);
5615
+ console.error(`${TAG17} config action=remove-admin-phone phone=${phone} ok=${result.ok}`);
5400
5616
  return c.json(result, result.ok ? 200 : 400);
5401
5617
  }
5402
5618
  case "list-admin-phones": {
5403
5619
  const phones = readAdminPhones(account.accountDir);
5404
- console.error(`${TAG16} config action=list-admin-phones count=${phones.length}`);
5620
+ console.error(`${TAG17} config action=list-admin-phones count=${phones.length}`);
5405
5621
  return c.json({ ok: true, phones });
5406
5622
  }
5407
5623
  case "set-public-agent": {
@@ -5409,14 +5625,14 @@ app3.post("/config", async (c) => {
5409
5625
  return c.json({ ok: false, error: 'Missing required field "slug" (the agent directory name, e.g. "my-agent").' }, 400);
5410
5626
  }
5411
5627
  const result = setPublicAgent(account.accountDir, slug);
5412
- console.error(`${TAG16} config action=set-public-agent slug=${slug} ok=${result.ok}`);
5628
+ console.error(`${TAG17} config action=set-public-agent slug=${slug} ok=${result.ok}`);
5413
5629
  return c.json(result, result.ok ? 200 : 400);
5414
5630
  }
5415
5631
  case "get-public-agent": {
5416
5632
  const targetAccount = typeof accountId === "string" && accountId.trim() ? accountId.trim() : "default";
5417
5633
  const targetGroup = typeof groupJid === "string" && groupJid.trim() ? groupJid.trim() : void 0;
5418
5634
  const resolved = resolvePublicAgent(account.accountDir, { accountId: targetAccount, groupJid: targetGroup });
5419
- console.error(`${TAG16} config action=get-public-agent accountId=${targetAccount} groupJid=${targetGroup ?? "none"} slug=${resolved?.slug ?? "none"} source=${resolved?.source ?? "none"}`);
5635
+ console.error(`${TAG17} config action=get-public-agent accountId=${targetAccount} groupJid=${targetGroup ?? "none"} slug=${resolved?.slug ?? "none"} source=${resolved?.source ?? "none"}`);
5420
5636
  return c.json({ ok: true, slug: resolved?.slug ?? null, source: resolved?.source ?? null });
5421
5637
  }
5422
5638
  case "set-group-public-agent": {
@@ -5428,7 +5644,7 @@ app3.post("/config", async (c) => {
5428
5644
  }
5429
5645
  const targetAccount = typeof accountId === "string" && accountId.trim() ? accountId.trim() : "default";
5430
5646
  const result = setGroupPublicAgent(account.accountDir, targetAccount, groupJid, slug);
5431
- console.error(`${TAG16} config action=set-group-public-agent accountId=${targetAccount} groupJid=${groupJid} slug=${slug} ok=${result.ok}`);
5647
+ console.error(`${TAG17} config action=set-group-public-agent accountId=${targetAccount} groupJid=${groupJid} slug=${slug} ok=${result.ok}`);
5432
5648
  return c.json(result, result.ok ? 200 : 400);
5433
5649
  }
5434
5650
  case "unset-group-public-agent": {
@@ -5437,7 +5653,7 @@ app3.post("/config", async (c) => {
5437
5653
  }
5438
5654
  const targetAccount = typeof accountId === "string" && accountId.trim() ? accountId.trim() : "default";
5439
5655
  const result = unsetGroupPublicAgent(account.accountDir, targetAccount, groupJid);
5440
- console.error(`${TAG16} config action=unset-group-public-agent accountId=${targetAccount} groupJid=${groupJid} ok=${result.ok}`);
5656
+ console.error(`${TAG17} config action=unset-group-public-agent accountId=${targetAccount} groupJid=${groupJid} ok=${result.ok}`);
5441
5657
  return c.json(result, result.ok ? 200 : 400);
5442
5658
  }
5443
5659
  case "list-public-agents": {
@@ -5454,26 +5670,26 @@ app3.post("/config", async (c) => {
5454
5670
  const config = JSON.parse(readFileSync6(configPath2, "utf-8"));
5455
5671
  agents.push({ slug: entry.name, displayName: config.displayName ?? entry.name });
5456
5672
  } catch {
5457
- console.error(`${TAG16} config action=list-public-agents error="failed to parse config.json for agent ${entry.name}" \u2014 skipping`);
5673
+ console.error(`${TAG17} config action=list-public-agents error="failed to parse config.json for agent ${entry.name}" \u2014 skipping`);
5458
5674
  }
5459
5675
  }
5460
5676
  } catch (err) {
5461
- console.error(`${TAG16} config action=list-public-agents error="failed to scan agents directory: ${String(err)}"`);
5677
+ console.error(`${TAG17} config action=list-public-agents error="failed to scan agents directory: ${String(err)}"`);
5462
5678
  }
5463
5679
  }
5464
- console.error(`${TAG16} config action=list-public-agents count=${agents.length}`);
5680
+ console.error(`${TAG17} config action=list-public-agents count=${agents.length}`);
5465
5681
  return c.json({ ok: true, agents });
5466
5682
  }
5467
5683
  case "schema": {
5468
5684
  const text = serializeWhatsAppSchema();
5469
- console.error(`${TAG16} config action=schema`);
5685
+ console.error(`${TAG17} config action=schema`);
5470
5686
  return c.json({ ok: true, text });
5471
5687
  }
5472
5688
  case "list-groups": {
5473
5689
  const groupAccountId = accountId ?? "default";
5474
5690
  const sock = getSocket(groupAccountId);
5475
5691
  if (!sock) {
5476
- console.error(`${TAG16} config action=list-groups error="not connected" accountId=${groupAccountId}`);
5692
+ console.error(`${TAG17} config action=list-groups error="not connected" accountId=${groupAccountId}`);
5477
5693
  return c.json({ ok: false, error: `WhatsApp account "${groupAccountId}" is not connected. Connect first, then retry.` });
5478
5694
  }
5479
5695
  try {
@@ -5483,10 +5699,10 @@ app3.post("/config", async (c) => {
5483
5699
  name: g.subject ?? g.id,
5484
5700
  participantCount: Array.isArray(g.participants) ? g.participants.length : 0
5485
5701
  }));
5486
- console.error(`${TAG16} config action=list-groups count=${groups.length} accountId=${groupAccountId}`);
5702
+ console.error(`${TAG17} config action=list-groups count=${groups.length} accountId=${groupAccountId}`);
5487
5703
  return c.json({ ok: true, groups });
5488
5704
  } catch (err) {
5489
- console.error(`${TAG16} config action=list-groups error="${String(err)}" accountId=${groupAccountId}`);
5705
+ console.error(`${TAG17} config action=list-groups error="${String(err)}" accountId=${groupAccountId}`);
5490
5706
  return c.json({ ok: false, error: `Failed to fetch groups: ${String(err)}` });
5491
5707
  }
5492
5708
  }
@@ -5496,12 +5712,12 @@ app3.post("/config", async (c) => {
5496
5712
  }
5497
5713
  const result = updateConfig(account.accountDir, fields);
5498
5714
  const fieldNames = Object.keys(fields);
5499
- console.error(`${TAG16} config action=update-config fields=[${fieldNames.join(",")}] ok=${result.ok}`);
5715
+ console.error(`${TAG17} config action=update-config fields=[${fieldNames.join(",")}] ok=${result.ok}`);
5500
5716
  return c.json(result, result.ok ? 200 : 400);
5501
5717
  }
5502
5718
  case "get-config": {
5503
5719
  const waConfig = getConfig(account.accountDir);
5504
- console.error(`${TAG16} config action=get-config`);
5720
+ console.error(`${TAG17} config action=get-config`);
5505
5721
  return c.json({ ok: true, config: waConfig });
5506
5722
  }
5507
5723
  default:
@@ -5511,7 +5727,7 @@ app3.post("/config", async (c) => {
5511
5727
  );
5512
5728
  }
5513
5729
  } catch (err) {
5514
- console.error(`${TAG16} config error: ${String(err)}`);
5730
+ console.error(`${TAG17} config error: ${String(err)}`);
5515
5731
  return c.json({ ok: false, error: String(err) }, 500);
5516
5732
  }
5517
5733
  });
@@ -5533,16 +5749,16 @@ app3.post("/send-document", async (c) => {
5533
5749
  const accountResolved = realpathSync2(accountDir);
5534
5750
  if (!resolvedPath.startsWith(accountResolved + "/")) {
5535
5751
  const sanitised = filePath.replace(accountDir, "<account>/");
5536
- console.error(`${TAG16} send-document REJECTED path=${sanitised} reason=outside_account_directory`);
5752
+ console.error(`${TAG17} send-document REJECTED path=${sanitised} reason=outside_account_directory`);
5537
5753
  return c.json({ error: "Access denied: file is outside the account directory" }, 403);
5538
5754
  }
5539
5755
  } catch (err) {
5540
5756
  const code = err.code;
5541
5757
  if (code === "ENOENT") {
5542
- console.error(`${TAG16} send-document ENOENT path=${filePath}`);
5758
+ console.error(`${TAG17} send-document ENOENT path=${filePath}`);
5543
5759
  return c.json({ error: `File not found: ${filePath}` }, 404);
5544
5760
  }
5545
- console.error(`${TAG16} send-document path error: ${String(err)}`);
5761
+ console.error(`${TAG17} send-document path error: ${String(err)}`);
5546
5762
  return c.json({ error: String(err) }, 500);
5547
5763
  }
5548
5764
  const fileStat = await stat3(resolvedPath);
@@ -5564,11 +5780,11 @@ app3.post("/send-document", async (c) => {
5564
5780
  caption
5565
5781
  }, { accountId });
5566
5782
  console.error(
5567
- `${TAG16} send-document to=${to} size=${fileStat.size} mime=${mimetype} ok=${result.success}` + (result.messageId ? ` id=${result.messageId}` : "")
5783
+ `${TAG17} send-document to=${to} size=${fileStat.size} mime=${mimetype} ok=${result.success}` + (result.messageId ? ` id=${result.messageId}` : "")
5568
5784
  );
5569
5785
  return c.json(result);
5570
5786
  } catch (err) {
5571
- console.error(`${TAG16} send-document error: ${String(err)}`);
5787
+ console.error(`${TAG17} send-document error: ${String(err)}`);
5572
5788
  return c.json({ error: String(err) }, 500);
5573
5789
  }
5574
5790
  });
@@ -5578,11 +5794,11 @@ app3.get("/activity", (c) => {
5578
5794
  const result = getChannelActivity(accountId);
5579
5795
  const total = result.accounts.reduce((sum, a) => sum + a.total, 0);
5580
5796
  console.error(
5581
- `${TAG16} activity accounts=${result.accounts.length} total=${total} recentEvents=${result.recentEvents.length}` + (accountId ? ` filter=${accountId}` : "")
5797
+ `${TAG17} activity accounts=${result.accounts.length} total=${total} recentEvents=${result.recentEvents.length}` + (accountId ? ` filter=${accountId}` : "")
5582
5798
  );
5583
5799
  return c.json(result);
5584
5800
  } catch (err) {
5585
- console.error(`${TAG16} activity error: ${String(err)}`);
5801
+ console.error(`${TAG17} activity error: ${String(err)}`);
5586
5802
  return c.json({ error: String(err) }, 500);
5587
5803
  }
5588
5804
  });
@@ -5601,10 +5817,10 @@ app3.get("/conversations", (c) => {
5601
5817
  };
5602
5818
  });
5603
5819
  conversations.sort((a, b) => (b.lastMessageTimestamp ?? 0) - (a.lastMessageTimestamp ?? 0));
5604
- console.error(`${TAG16} conversations account=${accountId} count=${conversations.length}`);
5820
+ console.error(`${TAG17} conversations account=${accountId} count=${conversations.length}`);
5605
5821
  return c.json({ conversations });
5606
5822
  } catch (err) {
5607
- console.error(`${TAG16} conversations error: ${String(err)}`);
5823
+ console.error(`${TAG17} conversations error: ${String(err)}`);
5608
5824
  return c.json({ error: String(err) }, 500);
5609
5825
  }
5610
5826
  });
@@ -5619,10 +5835,10 @@ app3.get("/messages", (c) => {
5619
5835
  const limit = limitParam ? parseInt(limitParam, 10) : void 0;
5620
5836
  const effectiveLimit = limit && !Number.isNaN(limit) && limit > 0 ? limit : void 0;
5621
5837
  const messages = getMessages(accountId, jid, effectiveLimit);
5622
- console.error(`${TAG16} messages account=${accountId} jid=${jid} limit=${effectiveLimit ?? "all"} returned=${messages.length}`);
5838
+ console.error(`${TAG17} messages account=${accountId} jid=${jid} limit=${effectiveLimit ?? "all"} returned=${messages.length}`);
5623
5839
  return c.json({ messages });
5624
5840
  } catch (err) {
5625
- console.error(`${TAG16} messages error: ${String(err)}`);
5841
+ console.error(`${TAG17} messages error: ${String(err)}`);
5626
5842
  return c.json({ error: String(err) }, 500);
5627
5843
  }
5628
5844
  });
@@ -5703,7 +5919,7 @@ app3.get("/conversation-graph-state", async (c) => {
5703
5919
  }
5704
5920
  } catch (err) {
5705
5921
  const msg = err instanceof Error ? err.message : String(err);
5706
- console.error(`${TAG16} conversation-graph-state ERR cacheKey=${cacheKey} reason=${msg}`);
5922
+ console.error(`${TAG17} conversation-graph-state ERR cacheKey=${cacheKey} reason=${msg}`);
5707
5923
  return c.json({ error: `Graph query failed: ${msg}`, cacheKey, cypher: cypher.trim() }, 500);
5708
5924
  }
5709
5925
  const ms = Date.now() - t0;
@@ -5716,7 +5932,7 @@ app3.get("/conversation-graph-state", async (c) => {
5716
5932
  ms
5717
5933
  });
5718
5934
  } catch (err) {
5719
- console.error(`${TAG16} conversation-graph-state error: ${String(err)}`);
5935
+ console.error(`${TAG17} conversation-graph-state error: ${String(err)}`);
5720
5936
  return c.json({ error: String(err) }, 500);
5721
5937
  }
5722
5938
  });
@@ -5728,12 +5944,12 @@ app3.get("/group-info", async (c) => {
5728
5944
  return c.json({ error: "Missing required parameter: jid" }, 400);
5729
5945
  }
5730
5946
  if (!isGroupJid(jid)) {
5731
- console.error(`${TAG16} group-info error="not a group JID" jid=${jid} account=${accountId}`);
5947
+ console.error(`${TAG17} group-info error="not a group JID" jid=${jid} account=${accountId}`);
5732
5948
  return c.json({ error: `"${jid}" is not a group JID. Group JIDs end with @g.us.` }, 400);
5733
5949
  }
5734
5950
  const sock = getSocket(accountId);
5735
5951
  if (!sock) {
5736
- console.error(`${TAG16} group-info error="not connected" account=${accountId}`);
5952
+ console.error(`${TAG17} group-info error="not connected" account=${accountId}`);
5737
5953
  return c.json({ error: `WhatsApp account "${accountId}" is not connected. Connect first, then retry.` }, 400);
5738
5954
  }
5739
5955
  const meta = await sock.groupMetadata(jid);
@@ -5746,10 +5962,10 @@ app3.get("/group-info", async (c) => {
5746
5962
  participantCount: meta.participants.length,
5747
5963
  participants: meta.participants.map((p) => ({ jid: p.id, admin: p.admin ?? null }))
5748
5964
  };
5749
- console.error(`${TAG16} group-info jid=${jid} subject="${meta.subject}" participants=${meta.participants.length} account=${accountId}`);
5965
+ console.error(`${TAG17} group-info jid=${jid} subject="${meta.subject}" participants=${meta.participants.length} account=${accountId}`);
5750
5966
  return c.json(result);
5751
5967
  } catch (err) {
5752
- console.error(`${TAG16} group-info error="${String(err)}" jid=${jid} account=${accountId}`);
5968
+ console.error(`${TAG17} group-info error="${String(err)}" jid=${jid} account=${accountId}`);
5753
5969
  return c.json({ error: `Failed to fetch group info: ${String(err)}` }, 500);
5754
5970
  }
5755
5971
  });
@@ -5758,7 +5974,7 @@ var whatsapp_default = app3;
5758
5974
  // server/routes/onboarding.ts
5759
5975
  import { spawn, spawnSync as spawnSync2, execFileSync as execFileSync2 } from "child_process";
5760
5976
  import { openSync, closeSync, writeFileSync as writeFileSync5, writeSync, existsSync as existsSync6, readFileSync as readFileSync8, unlinkSync } from "fs";
5761
- import { createHash, randomUUID as randomUUID5 } from "crypto";
5977
+ import { createHash, randomUUID as randomUUID6 } from "crypto";
5762
5978
 
5763
5979
  // ../lib/admins-write/src/index.ts
5764
5980
  import { existsSync as existsSync5, readFileSync as readFileSync7, writeFileSync as writeFileSync4, renameSync, mkdirSync as mkdirSync2, readdirSync as readdirSync3, statSync as statSync2 } from "fs";
@@ -6034,7 +6250,7 @@ app4.post("/set-pin", async (c) => {
6034
6250
  const hash = hashPin(body.pin);
6035
6251
  const account = resolveAccount();
6036
6252
  const existingOwnerUserId = account?.config.admins?.find((a) => a.role === "owner")?.userId;
6037
- const userId = existingOwnerUserId ?? randomUUID5();
6253
+ const userId = existingOwnerUserId ?? randomUUID6();
6038
6254
  if (existingOwnerUserId) {
6039
6255
  console.log(`[set-pin] reusing existing owner userId=${userId.slice(0, 8)}\u2026 (change-PIN preserves identity)`);
6040
6256
  } else {
@@ -6295,7 +6511,7 @@ var client_error_default = app5;
6295
6511
  import { readFileSync as readFileSync9, readdirSync as readdirSync4, statSync as statSync4, existsSync as existsSync8 } from "fs";
6296
6512
  import { join as join8 } from "path";
6297
6513
  import { createHash as createHash2 } from "crypto";
6298
- import { randomUUID as randomUUID6 } from "crypto";
6514
+ import { randomUUID as randomUUID7 } from "crypto";
6299
6515
  function hashPin2(pin) {
6300
6516
  return createHash2("sha256").update(pin).digest("hex");
6301
6517
  }
@@ -6352,7 +6568,7 @@ async function resolveUserIdentity(accountId, userId) {
6352
6568
  async function createAdminSession(accountId, thinkingView, userId, userName, role, avatar) {
6353
6569
  const account = resolveAccount();
6354
6570
  const effectiveThinkingView = thinkingView ?? account?.config.thinkingView ?? "default";
6355
- const signedSessionToken = randomUUID6();
6571
+ const signedSessionToken = randomUUID7();
6356
6572
  const cacheKey = fingerprintSessionKey(signedSessionToken);
6357
6573
  registerSession(cacheKey, "admin", accountId, void 0, userId, userName, role);
6358
6574
  if (userId) setWantsPriorConversation(cacheKey);
@@ -7223,7 +7439,7 @@ app14.get("/", requireAdminSession, async (c) => {
7223
7439
  if (!userId) return c.json({ error: "User identity required \u2014 authenticate with users.json PIN" }, 401);
7224
7440
  try {
7225
7441
  const flushed = await listAdminSessions(accountId, userId, 20);
7226
- const sessions = flushed.map((r) => ({
7442
+ const sessions2 = flushed.map((r) => ({
7227
7443
  sessionId: r.sessionId,
7228
7444
  cacheKey: null,
7229
7445
  name: r.name,
@@ -7231,15 +7447,15 @@ app14.get("/", requireAdminSession, async (c) => {
7231
7447
  phase: "flushed",
7232
7448
  channel: r.channel
7233
7449
  })).sort((a, b) => a.updatedAt < b.updatedAt ? 1 : a.updatedAt > b.updatedAt ? -1 : 0).slice(0, 20);
7234
- const channelCounts = sessions.reduce((acc, s) => {
7450
+ const channelCounts = sessions2.reduce((acc, s) => {
7235
7451
  const k = s.channel ?? "unknown";
7236
7452
  acc[k] = (acc[k] ?? 0) + 1;
7237
7453
  return acc;
7238
7454
  }, {});
7239
7455
  console.error(
7240
- `[conversations-list] render rows=${sessions.length} channels=${JSON.stringify(channelCounts)}`
7456
+ `[conversations-list] render rows=${sessions2.length} channels=${JSON.stringify(channelCounts)}`
7241
7457
  );
7242
- return c.json({ sessions });
7458
+ return c.json({ sessions: sessions2 });
7243
7459
  } catch (err) {
7244
7460
  console.error(`[sessions-list] Failed: ${err instanceof Error ? err.message : String(err)}`);
7245
7461
  return c.json({ error: "Failed to fetch sessions" }, 500);
@@ -7298,11 +7514,11 @@ app14.delete("/:id", requireAdminSession, async (c) => {
7298
7514
  const owned = await verifyConversationOwnership(sessionId, accountId);
7299
7515
  if (!owned) return c.json({ error: "Conversation not found" }, 404);
7300
7516
  const managerPort = requirePortEnv("CLAUDE_SESSION_MANAGER_PORT", { tag: "admin-conversation-delete" });
7301
- const managerBase3 = `http://127.0.0.1:${managerPort}`;
7517
+ const managerBase4 = `http://127.0.0.1:${managerPort}`;
7302
7518
  const outcome = await cascadeAdminConversationDelete({
7303
7519
  sessionId,
7304
7520
  accountId,
7305
- managerBase: managerBase3
7521
+ managerBase: managerBase4
7306
7522
  });
7307
7523
  if (outcome.ok) return c.json({ ok: true, claudeSessionIds: outcome.claudeSessionIds });
7308
7524
  if (outcome.reason === "pty-still-alive") {
@@ -7948,7 +8164,7 @@ function resolveTunnelUrl() {
7948
8164
  if (!state) return null;
7949
8165
  return `https://${hostname2}.${state.domain}`;
7950
8166
  }
7951
- var TAG17 = "[claude-session-manager:wrapper]";
8167
+ var TAG18 = "[claude-session-manager:wrapper]";
7952
8168
  async function refuseIfClaudeAuthDead(c, route, sessionId) {
7953
8169
  const auth = await ensureAuth();
7954
8170
  if (auth.status !== "dead" && auth.status !== "missing") return null;
@@ -7960,7 +8176,7 @@ async function refuseIfClaudeAuthDead(c, route, sessionId) {
7960
8176
  503
7961
8177
  );
7962
8178
  }
7963
- function managerBase2() {
8179
+ function managerBase3() {
7964
8180
  const port2 = requirePortEnv("CLAUDE_SESSION_MANAGER_PORT", { tag: "claude-session-manager:wrapper" });
7965
8181
  return `http://127.0.0.1:${port2}`;
7966
8182
  }
@@ -7971,12 +8187,12 @@ async function performSpawnWithInitialMessage(args) {
7971
8187
  const aboutOwner = await resolveOwnerProfileBlock(args.senderId, args.userId);
7972
8188
  const ownerMs = Date.now() - ownerStart;
7973
8189
  const aboutOwnerStatus = aboutOwner == null ? "absent" : "ok" in aboutOwner && aboutOwner.ok === false ? `unresolved:${aboutOwner.reason}` : "ok";
7974
- console.log(`${TAG17} about-owner-resolved status=${aboutOwnerStatus} ms=${ownerMs}`);
8190
+ console.log(`${TAG18} about-owner-resolved status=${aboutOwnerStatus} ms=${ownerMs}`);
7975
8191
  const dormantPlugins = computeDormantPlugins(args.senderId);
7976
8192
  const activePlugins = computeActivePlugins(args.senderId);
7977
8193
  const specialistDomains = computeSpecialistDomains(args.senderId);
7978
8194
  const tunnelUrl = resolveTunnelUrl();
7979
- console.log(`${TAG17} tunnel-url-resolved value=${tunnelUrl ?? "null"}`);
8195
+ console.log(`${TAG18} tunnel-url-resolved value=${tunnelUrl ?? "null"}`);
7980
8196
  const upstreamPayload = JSON.stringify({
7981
8197
  senderId: args.senderId,
7982
8198
  // Task 205 — pass userId through to the manager so it lands as
@@ -8003,24 +8219,24 @@ async function performSpawnWithInitialMessage(args) {
8003
8219
  // unshapely values.
8004
8220
  conversationNodeId: args.conversationNodeId
8005
8221
  });
8006
- console.log(`${TAG17} forward-spawn-start managerBase=${managerBase2()} bytes=${upstreamPayload.length} hidden=${args.hidden} specialist=${args.specialist ?? "none"}`);
8222
+ console.log(`${TAG18} forward-spawn-start managerBase=${managerBase3()} bytes=${upstreamPayload.length} hidden=${args.hidden} specialist=${args.specialist ?? "none"}`);
8007
8223
  const forwardStart = Date.now();
8008
- const upstream = await fetch(`${managerBase2()}/spawn`, {
8224
+ const upstream = await fetch(`${managerBase3()}/spawn`, {
8009
8225
  method: "POST",
8010
8226
  headers: { "content-type": "application/json" },
8011
8227
  body: upstreamPayload
8012
8228
  }).catch((err) => {
8013
- console.error(`${TAG17} fetch-failed op=spawn message=${err instanceof Error ? err.message : String(err)} ms=${Date.now() - forwardStart}`);
8229
+ console.error(`${TAG18} fetch-failed op=spawn message=${err instanceof Error ? err.message : String(err)} ms=${Date.now() - forwardStart}`);
8014
8230
  return null;
8015
8231
  });
8016
8232
  if (!upstream) return {
8017
8233
  response: new Response(JSON.stringify({ error: "manager-unreachable" }), { status: 503, headers: { "content-type": "application/json" } }),
8018
8234
  claudeSessionId: null
8019
8235
  };
8020
- console.log(`${TAG17} forward-spawn-done status=${upstream.status} ms=${Date.now() - forwardStart}`);
8236
+ console.log(`${TAG18} forward-spawn-done status=${upstream.status} ms=${Date.now() - forwardStart}`);
8021
8237
  if (args.initialMessage) {
8022
8238
  const inputBytes = Buffer.byteLength(args.initialMessage, "utf8");
8023
- console.log(`${TAG17} initial-message-inlined bytes=${inputBytes}`);
8239
+ console.log(`${TAG18} initial-message-inlined bytes=${inputBytes}`);
8024
8240
  }
8025
8241
  const bodyText = await upstream.text().catch(() => "");
8026
8242
  let claudeSessionId = null;
@@ -8069,7 +8285,7 @@ app15.post("/", async (c) => {
8069
8285
  authSurface = "cookie";
8070
8286
  senderId = getAccountIdForSession(cacheKey) ?? "";
8071
8287
  if (!senderId) {
8072
- console.error(`${TAG17} reject reason=no-account-id cacheKey-prefix=${cacheKey.slice(0, 8)}`);
8288
+ console.error(`${TAG18} reject reason=no-account-id cacheKey-prefix=${cacheKey.slice(0, 8)}`);
8073
8289
  return c.json({ error: "admin-account-not-resolved" }, 500);
8074
8290
  }
8075
8291
  userId = getUserIdForSession(cacheKey) ?? void 0;
@@ -8080,9 +8296,9 @@ app15.post("/", async (c) => {
8080
8296
  }
8081
8297
  const lookupSessionId = body.adminSessionId;
8082
8298
  const operatorMeta = await fetch(
8083
- `${managerBase2()}/${encodeURIComponent(lookupSessionId)}/meta`
8299
+ `${managerBase3()}/${encodeURIComponent(lookupSessionId)}/meta`
8084
8300
  ).then((r) => r.ok ? r.json() : null).catch((err) => {
8085
- console.error(`${TAG17} fetch-failed op=operator-meta-lookup message=${err instanceof Error ? err.message : String(err)}`);
8301
+ console.error(`${TAG18} fetch-failed op=operator-meta-lookup message=${err instanceof Error ? err.message : String(err)}`);
8086
8302
  return null;
8087
8303
  });
8088
8304
  const operatorSenderId = operatorMeta && typeof operatorMeta.senderId === "string" && operatorMeta.senderId.length > 0 ? operatorMeta.senderId : null;
@@ -8097,7 +8313,7 @@ app15.post("/", async (c) => {
8097
8313
  const permissionMode = typeof body.permissionMode === "string" ? body.permissionMode : void 0;
8098
8314
  const specialist = typeof body.specialist === "string" && /^[A-Za-z0-9_-]{1,64}$/.test(body.specialist) ? body.specialist : void 0;
8099
8315
  const model = typeof body.model === "string" && /^[A-Za-z0-9._-]{1,64}$/.test(body.model) ? body.model : void 0;
8100
- console.log(`${TAG17} spawn-request-in surface=${authSurface} accountId=${senderId.slice(0, 8)} userId=${userId ? userId.slice(0, 8) : "absent"} channel=${channel} permissionMode=${permissionMode ?? "default"} specialist=${specialist ?? "none"} model=${model ?? "default"} initialMessage=${initialMessage ? "yes" : "no"}`);
8316
+ console.log(`${TAG18} spawn-request-in surface=${authSurface} accountId=${senderId.slice(0, 8)} userId=${userId ? userId.slice(0, 8) : "absent"} channel=${channel} permissionMode=${permissionMode ?? "default"} specialist=${specialist ?? "none"} model=${model ?? "default"} initialMessage=${initialMessage ? "yes" : "no"}`);
8101
8317
  const conversationNodeId = cacheKey ? getSessionIdForSession(cacheKey) : void 0;
8102
8318
  const { response, claudeSessionId } = await performSpawnWithInitialMessage({
8103
8319
  senderId,
@@ -8116,14 +8332,14 @@ app15.post("/", async (c) => {
8116
8332
  claudeSessionId,
8117
8333
  senderId
8118
8334
  );
8119
- console.log(`${TAG17} route-done surface=${authSurface} status=${response.status} route-ms=${Date.now() - routeStart}`);
8335
+ console.log(`${TAG18} route-done surface=${authSurface} status=${response.status} route-ms=${Date.now() - routeStart}`);
8120
8336
  return response;
8121
8337
  });
8122
8338
  app15.get("/events", async (c) => {
8123
- const upstream = await fetch(`${managerBase2()}/events`, {
8339
+ const upstream = await fetch(`${managerBase3()}/events`, {
8124
8340
  headers: { accept: "text/event-stream" }
8125
8341
  }).catch((err) => {
8126
- console.error(`${TAG17} fetch-failed op=events message=${err instanceof Error ? err.message : String(err)}`);
8342
+ console.error(`${TAG18} fetch-failed op=events message=${err instanceof Error ? err.message : String(err)}`);
8127
8343
  return null;
8128
8344
  });
8129
8345
  if (!upstream || !upstream.body) return c.json({ error: "manager-unreachable" }, 503);
@@ -8142,9 +8358,9 @@ app15.get("/", async (c) => {
8142
8358
  const senderId = getAccountIdForSession(cacheKey) ?? "";
8143
8359
  if (!senderId) return c.json({ error: "admin-account-not-resolved" }, 500);
8144
8360
  const upstream = await fetch(
8145
- `${managerBase2()}/list?senderId=${encodeURIComponent(senderId)}`
8361
+ `${managerBase3()}/list?senderId=${encodeURIComponent(senderId)}`
8146
8362
  ).catch((err) => {
8147
- console.error(`${TAG17} fetch-failed op=list message=${err instanceof Error ? err.message : String(err)}`);
8363
+ console.error(`${TAG18} fetch-failed op=list message=${err instanceof Error ? err.message : String(err)}`);
8148
8364
  return null;
8149
8365
  });
8150
8366
  if (!upstream) return c.json({ error: "manager-unreachable" }, 503);
@@ -8173,7 +8389,7 @@ app15.post("/resume", async (c) => {
8173
8389
  const activePlugins = computeActivePlugins(senderId);
8174
8390
  const specialistDomains = computeSpecialistDomains(senderId);
8175
8391
  const tunnelUrl = resolveTunnelUrl();
8176
- const upstream = await fetch(`${managerBase2()}/resume`, {
8392
+ const upstream = await fetch(`${managerBase3()}/resume`, {
8177
8393
  method: "POST",
8178
8394
  headers: { "content-type": "application/json" },
8179
8395
  body: JSON.stringify({
@@ -8198,7 +8414,7 @@ app15.post("/resume", async (c) => {
8198
8414
  conversationNodeId: getSessionIdForSession(cacheKey)
8199
8415
  })
8200
8416
  }).catch((err) => {
8201
- console.error(`${TAG17} fetch-failed op=resume message=${err instanceof Error ? err.message : String(err)}`);
8417
+ console.error(`${TAG18} fetch-failed op=resume message=${err instanceof Error ? err.message : String(err)}`);
8202
8418
  return null;
8203
8419
  });
8204
8420
  if (!upstream) return c.json({ error: "manager-unreachable" }, 503);
@@ -8223,10 +8439,10 @@ app15.post("/resume", async (c) => {
8223
8439
  app15.post("/:sessionId/stop", async (c) => {
8224
8440
  const sessionId = c.req.param("sessionId");
8225
8441
  const upstream = await fetch(
8226
- `${managerBase2()}/${encodeURIComponent(sessionId)}/stop`,
8442
+ `${managerBase3()}/${encodeURIComponent(sessionId)}/stop`,
8227
8443
  { method: "POST" }
8228
8444
  ).catch((err) => {
8229
- console.error(`${TAG17} fetch-failed op=stop message=${err instanceof Error ? err.message : String(err)}`);
8445
+ console.error(`${TAG18} fetch-failed op=stop message=${err instanceof Error ? err.message : String(err)}`);
8230
8446
  return null;
8231
8447
  });
8232
8448
  if (!upstream) return c.json({ error: "manager-unreachable" }, 503);
@@ -8236,14 +8452,14 @@ app15.post("/:sessionId/rename", async (c) => {
8236
8452
  const sessionId = c.req.param("sessionId");
8237
8453
  const body = await c.req.text();
8238
8454
  const upstream = await fetch(
8239
- `${managerBase2()}/${encodeURIComponent(sessionId)}/rename`,
8455
+ `${managerBase3()}/${encodeURIComponent(sessionId)}/rename`,
8240
8456
  {
8241
8457
  method: "POST",
8242
8458
  headers: { "content-type": "application/json" },
8243
8459
  body
8244
8460
  }
8245
8461
  ).catch((err) => {
8246
- console.error(`${TAG17} fetch-failed op=rename message=${err instanceof Error ? err.message : String(err)}`);
8462
+ console.error(`${TAG18} fetch-failed op=rename message=${err instanceof Error ? err.message : String(err)}`);
8247
8463
  return null;
8248
8464
  });
8249
8465
  if (!upstream) return c.json({ error: "manager-unreachable" }, 503);
@@ -8258,10 +8474,10 @@ app15.delete("/:sessionId", async (c) => {
8258
8474
  if (!sessionId) {
8259
8475
  console.log(`[admin-conversation-delete] reason=no-conversation-mapping claudeSessionId=${claudeSessionId.slice(0, 8)}`);
8260
8476
  const upstream = await fetch(
8261
- `${managerBase2()}/${encodeURIComponent(claudeSessionId)}`,
8477
+ `${managerBase3()}/${encodeURIComponent(claudeSessionId)}`,
8262
8478
  { method: "DELETE" }
8263
8479
  ).catch((err) => {
8264
- console.error(`${TAG17} fetch-failed op=delete message=${err instanceof Error ? err.message : String(err)}`);
8480
+ console.error(`${TAG18} fetch-failed op=delete message=${err instanceof Error ? err.message : String(err)}`);
8265
8481
  return null;
8266
8482
  });
8267
8483
  if (!upstream) return c.json({ error: "manager-unreachable" }, 503);
@@ -8277,7 +8493,7 @@ app15.delete("/:sessionId", async (c) => {
8277
8493
  const outcome = await cascadeAdminConversationDelete({
8278
8494
  sessionId,
8279
8495
  accountId,
8280
- managerBase: managerBase2(),
8496
+ managerBase: managerBase3(),
8281
8497
  claudeSessionIds: all
8282
8498
  });
8283
8499
  if (outcome.ok) return c.json({ ok: true, claudeSessionIds: outcome.claudeSessionIds });
@@ -8316,14 +8532,14 @@ app15.post("/:sessionId/archive", async (c) => {
8316
8532
  const sessionId = c.req.param("sessionId");
8317
8533
  const body = await c.req.text();
8318
8534
  const upstream = await fetch(
8319
- `${managerBase2()}/${encodeURIComponent(sessionId)}/archive`,
8535
+ `${managerBase3()}/${encodeURIComponent(sessionId)}/archive`,
8320
8536
  {
8321
8537
  method: "POST",
8322
8538
  headers: { "content-type": "application/json" },
8323
8539
  body
8324
8540
  }
8325
8541
  ).catch((err) => {
8326
- console.error(`${TAG17} fetch-failed op=archive message=${err instanceof Error ? err.message : String(err)}`);
8542
+ console.error(`${TAG18} fetch-failed op=archive message=${err instanceof Error ? err.message : String(err)}`);
8327
8543
  return null;
8328
8544
  });
8329
8545
  if (!upstream) return c.json({ error: "manager-unreachable" }, 503);
@@ -8332,9 +8548,9 @@ app15.post("/:sessionId/archive", async (c) => {
8332
8548
  app15.get("/:sessionId/meta", async (c) => {
8333
8549
  const sessionId = c.req.param("sessionId");
8334
8550
  const upstream = await fetch(
8335
- `${managerBase2()}/${encodeURIComponent(sessionId)}/meta`
8551
+ `${managerBase3()}/${encodeURIComponent(sessionId)}/meta`
8336
8552
  ).catch((err) => {
8337
- console.error(`${TAG17} fetch-failed op=meta message=${err instanceof Error ? err.message : String(err)}`);
8553
+ console.error(`${TAG18} fetch-failed op=meta message=${err instanceof Error ? err.message : String(err)}`);
8338
8554
  return null;
8339
8555
  });
8340
8556
  if (!upstream) return c.json({ error: "manager-unreachable" }, 503);
@@ -8344,14 +8560,14 @@ app15.post("/:sessionId/input", async (c) => {
8344
8560
  const sessionId = c.req.param("sessionId");
8345
8561
  const rawBody = await c.req.text();
8346
8562
  const upstream = await fetch(
8347
- `${managerBase2()}/${encodeURIComponent(sessionId)}/input`,
8563
+ `${managerBase3()}/${encodeURIComponent(sessionId)}/input`,
8348
8564
  {
8349
8565
  method: "POST",
8350
8566
  headers: { "content-type": "application/json" },
8351
8567
  body: rawBody
8352
8568
  }
8353
8569
  ).catch((err) => {
8354
- console.error(`${TAG17} fetch-failed op=input message=${err instanceof Error ? err.message : String(err)}`);
8570
+ console.error(`${TAG18} fetch-failed op=input message=${err instanceof Error ? err.message : String(err)}`);
8355
8571
  return null;
8356
8572
  });
8357
8573
  if (!upstream) return c.json({ error: "manager-unreachable" }, 503);
@@ -8364,9 +8580,9 @@ app15.get("/:sessionId/log", async (c) => {
8364
8580
  if (c.req.query("download") === "1") search.set("download", "1");
8365
8581
  const qs = search.toString() ? `?${search.toString()}` : "";
8366
8582
  const upstream = await fetch(
8367
- `${managerBase2()}/${encodeURIComponent(sessionId)}/log${qs}`
8583
+ `${managerBase3()}/${encodeURIComponent(sessionId)}/log${qs}`
8368
8584
  ).catch((err) => {
8369
- console.error(`${TAG17} fetch-failed op=log message=${err instanceof Error ? err.message : String(err)}`);
8585
+ console.error(`${TAG18} fetch-failed op=log message=${err instanceof Error ? err.message : String(err)}`);
8370
8586
  return null;
8371
8587
  });
8372
8588
  if (!upstream) return c.json({ error: "manager-unreachable" }, 503);
@@ -8375,7 +8591,7 @@ app15.get("/:sessionId/log", async (c) => {
8375
8591
  var claude_sessions_default = app15;
8376
8592
 
8377
8593
  // server/routes/admin/log-ingest.ts
8378
- var TAG18 = "[log-ingest]";
8594
+ var TAG19 = "[log-ingest]";
8379
8595
  var TAG_PATTERN = /^[A-Za-z0-9_:-]{1,32}$/;
8380
8596
  var LEVELS = /* @__PURE__ */ new Set(["debug", "info", "warn", "error"]);
8381
8597
  var MAX_LINE_BYTES = 4096;
@@ -8386,7 +8602,7 @@ function isLoopbackAddr(addr) {
8386
8602
  app16.post("/", async (c) => {
8387
8603
  const remoteAddr = c.env?.incoming?.socket?.remoteAddress ?? "";
8388
8604
  if (!isLoopbackAddr(remoteAddr)) {
8389
- console.error(`${TAG18} reject reason=non-loopback remoteAddr=${remoteAddr}`);
8605
+ console.error(`${TAG19} reject reason=non-loopback remoteAddr=${remoteAddr}`);
8390
8606
  return c.json({ error: "log-ingest-loopback-only" }, 403);
8391
8607
  }
8392
8608
  const xffHeader = c.env?.incoming?.headers?.["x-forwarded-for"];
@@ -8395,7 +8611,7 @@ app16.post("/", async (c) => {
8395
8611
  const tokens = xffRaw.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
8396
8612
  const offender = tokens.find((t) => !isLoopbackAddr(t));
8397
8613
  if (offender !== void 0) {
8398
- console.error(`${TAG18} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
8614
+ console.error(`${TAG19} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
8399
8615
  return c.json({ error: "log-ingest-loopback-only" }, 403);
8400
8616
  }
8401
8617
  }
@@ -8437,18 +8653,18 @@ var ALLOWED_EVENTS = /* @__PURE__ */ new Set([
8437
8653
  ]);
8438
8654
  var app17 = new Hono();
8439
8655
  app17.post("/", async (c) => {
8440
- const TAG22 = "[admin:events]";
8656
+ const TAG27 = "[admin:events]";
8441
8657
  let body;
8442
8658
  try {
8443
8659
  body = await c.req.json();
8444
8660
  } catch (err) {
8445
8661
  const detail = err instanceof Error ? err.message : String(err);
8446
- console.error(`${TAG22} reject reason=body-not-json detail=${detail}`);
8662
+ console.error(`${TAG27} reject reason=body-not-json detail=${detail}`);
8447
8663
  return c.json({ ok: false, detail: "Request body was not valid JSON" }, 400);
8448
8664
  }
8449
8665
  const event = typeof body.event === "string" ? body.event : "";
8450
8666
  if (!ALLOWED_EVENTS.has(event)) {
8451
- console.error(`${TAG22} reject reason=event-not-allowed event=${JSON.stringify(event)}`);
8667
+ console.error(`${TAG27} reject reason=event-not-allowed event=${JSON.stringify(event)}`);
8452
8668
  return c.json({ ok: false, detail: `Event "${event}" is not allowed` }, 400);
8453
8669
  }
8454
8670
  const rawFields = body.fields && typeof body.fields === "object" ? body.fields : {};
@@ -9326,6 +9542,27 @@ function pickFulltextIndex(allowedScopes) {
9326
9542
  function isPublicOnlyScope(allowedScopes) {
9327
9543
  return Array.isArray(allowedScopes) && allowedScopes.length === 1 && allowedScopes[0] === "public";
9328
9544
  }
9545
+ function buildScopeAndSliceClause(allowedScopes, sliceToken, alias = "node") {
9546
+ const hasSlice = typeof sliceToken === "string" && sliceToken.length > 0;
9547
+ const hasScopes = Array.isArray(allowedScopes) && allowedScopes.length > 0;
9548
+ if (!hasSlice && !hasScopes) return { clause: "", params: {} };
9549
+ if (hasSlice && hasScopes) {
9550
+ return {
9551
+ clause: `AND (${alias}.sliceToken = $sliceToken OR ${alias}.scope IS NULL OR ${alias}.scope IN $allowedScopes)`,
9552
+ params: { sliceToken, allowedScopes }
9553
+ };
9554
+ }
9555
+ if (hasSlice) {
9556
+ return {
9557
+ clause: `AND (${alias}.sliceToken = $sliceToken OR ${alias}.scope IS NULL)`,
9558
+ params: { sliceToken }
9559
+ };
9560
+ }
9561
+ return {
9562
+ clause: `AND (${alias}.scope IS NULL OR ${alias}.scope IN $allowedScopes)`,
9563
+ params: { allowedScopes }
9564
+ };
9565
+ }
9329
9566
  function escapeLucene(query) {
9330
9567
  return query.replace(/[+\-&|!(){}[\]^"~*?:\\/]/g, "\\$&");
9331
9568
  }
@@ -9361,8 +9598,9 @@ function buildKeywordFilter(keywords, keywordMatch = "any") {
9361
9598
  };
9362
9599
  }
9363
9600
  async function bm25Only(session, params) {
9364
- const { query, accountId, limit, allowedScopes, agentSlug, keywords, keywordMatch, labels } = params;
9365
- const scopeClause = allowedScopes ? "AND (node.scope IS NULL OR node.scope IN $allowedScopes)" : "";
9601
+ const { query, accountId, limit, allowedScopes, agentSlug, keywords, keywordMatch, labels, sliceToken } = params;
9602
+ const scope = buildScopeAndSliceClause(allowedScopes, sliceToken);
9603
+ const scopeClause = scope.clause;
9366
9604
  const agentClause = agentSlug ? "AND node.agents IS NOT NULL AND $agentSlug IN node.agents" : "";
9367
9605
  const labelClause = labels && labels.length > 0 ? "AND any(l IN labels(node) WHERE l IN $labels)" : "";
9368
9606
  const keywordFilter = buildKeywordFilter(keywords, keywordMatch);
@@ -9386,7 +9624,7 @@ async function bm25Only(session, params) {
9386
9624
  query: escaped,
9387
9625
  accountId,
9388
9626
  limit: int(limit),
9389
- ...allowedScopes ? { allowedScopes } : {},
9627
+ ...scope.params,
9390
9628
  ...agentSlug ? { agentSlug } : {},
9391
9629
  ...labels && labels.length > 0 ? { labels } : {},
9392
9630
  ...keywordFilter?.params ?? {}
@@ -9418,6 +9656,7 @@ async function hybrid(session, embed2, params) {
9418
9656
  accountId,
9419
9657
  limit,
9420
9658
  allowedScopes,
9659
+ sliceToken,
9421
9660
  keywords,
9422
9661
  keywordMatch = "any",
9423
9662
  agentSlug,
@@ -9496,22 +9735,18 @@ async function hybrid(session, embed2, params) {
9496
9735
  if (effectiveLabels && effectiveLabels.length > 0) {
9497
9736
  indexesToQuery = effectiveLabels.map((l) => labelToIndex.get(l)).filter((idx) => idx !== void 0);
9498
9737
  if (indexesToQuery.length === 0) {
9499
- return {
9500
- mode: useRrf ? "rrf" : "hybrid",
9501
- results: [],
9502
- expandMs: 0,
9503
- rawMerged: 0,
9504
- suppressed: 0,
9505
- bm25Bypass: 0,
9506
- threshold: params.vectorThreshold ?? null,
9507
- stageCounts: emptyStageCounts
9508
- };
9738
+ const missing = effectiveLabels.filter((l) => !labelToIndex.has(l));
9739
+ process.stderr.write(
9740
+ `[graph-search] labels-without-vector-index=${missing.join(",")} accountId=${(accountId ?? "").slice(0, 8)} \u2014 falling through to BM25-only for these labels
9741
+ `
9742
+ );
9509
9743
  }
9510
9744
  } else {
9511
9745
  indexesToQuery = [...new Set(labelToIndex.values())];
9512
9746
  }
9513
- const scopeClause = allowedScopes ? "AND (node.scope IS NULL OR node.scope IN $allowedScopes)" : "";
9514
- const scopeParams = allowedScopes ? { allowedScopes } : {};
9747
+ const hybridScope = buildScopeAndSliceClause(allowedScopes, sliceToken);
9748
+ const scopeClause = hybridScope.clause;
9749
+ const scopeParams = hybridScope.params;
9515
9750
  const agentClause = agentSlug ? "AND node.agents IS NOT NULL AND $agentSlug IN node.agents" : "";
9516
9751
  const agentParams = agentSlug ? { agentSlug } : {};
9517
9752
  const keywordFilter = buildKeywordFilter(keywords, keywordMatch);
@@ -9626,7 +9861,8 @@ async function hybrid(session, embed2, params) {
9626
9861
  query: kw,
9627
9862
  accountId,
9628
9863
  limit,
9629
- allowedScopes
9864
+ allowedScopes,
9865
+ sliceToken
9630
9866
  });
9631
9867
  if (kwHits.length === 0) continue;
9632
9868
  const rawScores = kwHits.map((h) => h.score);
@@ -9635,21 +9871,21 @@ async function hybrid(session, embed2, params) {
9635
9871
  mergeBm25Hit(scoreMap, kwHits[i], normalised[i]);
9636
9872
  }
9637
9873
  }
9638
- const propScopeClause = allowedScopes ? "AND (node.scope IS NULL OR node.scope IN $allowedScopes)" : "";
9874
+ const propScope = buildScopeAndSliceClause(allowedScopes, sliceToken);
9639
9875
  const propResult = await session.run(
9640
9876
  `MATCH (node)
9641
9877
  WHERE node.accountId = $accountId
9642
9878
  AND ${(0, import_dist.notTrashed)("node")}
9643
9879
  AND node.keywords IS NOT NULL
9644
9880
  AND ANY(kw IN $kwSubs WHERE ANY(nk IN node.keywords WHERE toLower(nk) = kw))
9645
- ${propScopeClause}
9881
+ ${propScope.clause}
9646
9882
  RETURN node, labels(node) AS nodeLabels, elementId(node) AS nodeId
9647
9883
  LIMIT $limit`,
9648
9884
  {
9649
9885
  accountId,
9650
9886
  kwSubs: keywordSubscriptions,
9651
9887
  limit: int(limit),
9652
- ...allowedScopes ? { allowedScopes } : {}
9888
+ ...propScope.params
9653
9889
  }
9654
9890
  );
9655
9891
  for (const record of propResult.records) {
@@ -9677,7 +9913,8 @@ async function hybrid(session, embed2, params) {
9677
9913
  query: kw,
9678
9914
  accountId,
9679
9915
  limit,
9680
- allowedScopes
9916
+ allowedScopes,
9917
+ sliceToken
9681
9918
  });
9682
9919
  if (kwHits.length === 0) continue;
9683
9920
  const rawScores = kwHits.map((h) => h.score);
@@ -9686,21 +9923,21 @@ async function hybrid(session, embed2, params) {
9686
9923
  mergeBm25Hit(scoreMap, kwHits[i], normalised[i]);
9687
9924
  }
9688
9925
  }
9689
- const propScopeClause = allowedScopes ? "AND (node.scope IS NULL OR node.scope IN $allowedScopes)" : "";
9926
+ const propScope = buildScopeAndSliceClause(allowedScopes, sliceToken);
9690
9927
  const propResult = await session.run(
9691
9928
  `MATCH (node)
9692
9929
  WHERE node.accountId = $accountId
9693
9930
  AND ${(0, import_dist.notTrashed)("node")}
9694
9931
  AND node.keywords IS NOT NULL
9695
9932
  AND ANY(kw IN $kwSubs WHERE ANY(nk IN node.keywords WHERE toLower(nk) = kw))
9696
- ${propScopeClause}
9933
+ ${propScope.clause}
9697
9934
  RETURN node, labels(node) AS nodeLabels, elementId(node) AS nodeId
9698
9935
  LIMIT $limit`,
9699
9936
  {
9700
9937
  accountId,
9701
9938
  kwSubs: keywordSubscriptions,
9702
9939
  limit: int(limit),
9703
- ...allowedScopes ? { allowedScopes } : {}
9940
+ ...propScope.params
9704
9941
  }
9705
9942
  );
9706
9943
  for (const record of propResult.records) {
@@ -10731,7 +10968,7 @@ app21.post("/", requireAdminSession, async (c) => {
10731
10968
  return c.json({ error: "AdminConversation node is missing sessionId" }, 500);
10732
10969
  }
10733
10970
  const managerPort = requirePortEnv("CLAUDE_SESSION_MANAGER_PORT", { tag: "graph-page-cascade" });
10734
- const managerBase3 = `http://127.0.0.1:${managerPort}`;
10971
+ const managerBase4 = `http://127.0.0.1:${managerPort}`;
10735
10972
  try {
10736
10973
  await session.close();
10737
10974
  } catch {
@@ -10739,7 +10976,7 @@ app21.post("/", requireAdminSession, async (c) => {
10739
10976
  const outcome = await cascadeAdminConversationDelete({
10740
10977
  sessionId,
10741
10978
  accountId,
10742
- managerBase: managerBase3
10979
+ managerBase: managerBase4
10743
10980
  });
10744
10981
  const elapsed2 = Date.now() - started;
10745
10982
  const labelSummary2 = preflightLabels.join(",");
@@ -11848,7 +12085,7 @@ app31.get("/", async (c) => {
11848
12085
  var health_default2 = app31;
11849
12086
 
11850
12087
  // server/routes/admin/linkedin-ingest.ts
11851
- var TAG19 = "[linkedin-ingest-route]";
12088
+ var TAG20 = "[linkedin-ingest-route]";
11852
12089
  var UUID = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;
11853
12090
  var ISO = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,3})?(?:Z|[+-]\d{2}:\d{2})$/;
11854
12091
  var LINKEDIN_URL = /^https:\/\/www\.linkedin\.com\//;
@@ -11947,25 +12184,25 @@ app32.post("/", requireAdminSession, async (c) => {
11947
12184
  try {
11948
12185
  body = await c.req.json();
11949
12186
  } catch {
11950
- console.error(TAG19 + " rejected status=400 reason=schema:body-not-json");
12187
+ console.error(TAG20 + " rejected status=400 reason=schema:body-not-json");
11951
12188
  return c.json({ ok: false, error: "schema", reason: "body-not-json" }, 400);
11952
12189
  }
11953
12190
  const v = validate(body);
11954
12191
  if (!v.ok) {
11955
- console.error(TAG19 + " rejected status=" + v.status + " reason=" + v.reason + " missing=" + v.missing.join(","));
12192
+ console.error(TAG20 + " rejected status=" + v.status + " reason=" + v.reason + " missing=" + v.missing.join(","));
11956
12193
  return c.json({ ok: false, error: v.error, reason: v.reason, missing: v.missing }, v.status);
11957
12194
  }
11958
12195
  const envelope = v.envelope;
11959
12196
  const cacheKey = c.var.cacheKey ?? "";
11960
12197
  const senderId = getAccountIdForSession(cacheKey) ?? "";
11961
12198
  if (!senderId) {
11962
- console.error(TAG19 + " rejected status=500 reason=admin-account-not-resolved");
12199
+ console.error(TAG20 + " rejected status=500 reason=admin-account-not-resolved");
11963
12200
  return c.json({ ok: false, error: "admin-account-not-resolved" }, 500);
11964
12201
  }
11965
12202
  const userId = getUserIdForSession(cacheKey) ?? void 0;
11966
12203
  const payloadBytes = JSON.stringify(envelope).length;
11967
12204
  console.log(
11968
- TAG19 + " received kind=" + envelope.kind + " account=" + senderId.slice(0, 8) + " pageUrl=" + envelope.pageUrl + " dispatchId=" + envelope.dispatchId + " payloadBytes=" + payloadBytes
12205
+ TAG20 + " received kind=" + envelope.kind + " account=" + senderId.slice(0, 8) + " pageUrl=" + envelope.pageUrl + " dispatchId=" + envelope.dispatchId + " payloadBytes=" + payloadBytes
11969
12206
  );
11970
12207
  const initialMessage = buildInitialMessage(envelope);
11971
12208
  const spawnStart = Date.now();
@@ -11987,12 +12224,12 @@ app32.post("/", requireAdminSession, async (c) => {
11987
12224
  if (!response.ok) {
11988
12225
  const detail = await response.text().catch(() => "");
11989
12226
  console.error(
11990
- TAG19 + " dispatch-failed dispatchId=" + envelope.dispatchId + " status=" + response.status + " ms=" + (Date.now() - spawnStart) + " detail=" + detail.slice(0, 200)
12227
+ TAG20 + " dispatch-failed dispatchId=" + envelope.dispatchId + " status=" + response.status + " ms=" + (Date.now() - spawnStart) + " detail=" + detail.slice(0, 200)
11991
12228
  );
11992
12229
  return c.json({ ok: false, error: "dispatch-failed", upstreamStatus: response.status, detail: detail.slice(0, 500) }, 502);
11993
12230
  }
11994
12231
  console.log(
11995
- TAG19 + " dispatched dispatchId=" + envelope.dispatchId + " taskId=" + (claudeSessionId ?? "unknown") + " ms=" + (Date.now() - spawnStart)
12232
+ TAG20 + " dispatched dispatchId=" + envelope.dispatchId + " taskId=" + (claudeSessionId ?? "unknown") + " ms=" + (Date.now() - spawnStart)
11996
12233
  );
11997
12234
  return c.json({ ok: true, dispatchId: envelope.dispatchId, taskId: claudeSessionId }, 202);
11998
12235
  });
@@ -12000,7 +12237,7 @@ var linkedin_ingest_default = app32;
12000
12237
 
12001
12238
  // server/routes/admin/post-turn-context.ts
12002
12239
  import neo4j3 from "neo4j-driver";
12003
- var TAG20 = "[post-turn-context]";
12240
+ var TAG21 = "[post-turn-context]";
12004
12241
  var STRIPPED_PROPERTIES2 = /* @__PURE__ */ new Set([
12005
12242
  "embedding",
12006
12243
  "passwordHash",
@@ -12042,7 +12279,7 @@ var app33 = new Hono();
12042
12279
  app33.get("/", async (c) => {
12043
12280
  const remoteAddr = c.env?.incoming?.socket?.remoteAddress ?? "";
12044
12281
  if (!isLoopbackAddr2(remoteAddr)) {
12045
- console.error(`${TAG20} reject reason=non-loopback remoteAddr=${remoteAddr}`);
12282
+ console.error(`${TAG21} reject reason=non-loopback remoteAddr=${remoteAddr}`);
12046
12283
  return c.json({ error: "post-turn-context-loopback-only" }, 403);
12047
12284
  }
12048
12285
  const xffHeader = c.env?.incoming?.headers?.["x-forwarded-for"];
@@ -12051,7 +12288,7 @@ app33.get("/", async (c) => {
12051
12288
  const tokens = xffRaw.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
12052
12289
  const offender = tokens.find((t) => !isLoopbackAddr2(t));
12053
12290
  if (offender !== void 0) {
12054
- console.error(`${TAG20} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
12291
+ console.error(`${TAG21} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
12055
12292
  return c.json({ error: "post-turn-context-loopback-only" }, 403);
12056
12293
  }
12057
12294
  }
@@ -12083,7 +12320,7 @@ app33.get("/", async (c) => {
12083
12320
  writes.sort((a, b) => a.sortKey.localeCompare(b.sortKey));
12084
12321
  const total = Date.now() - started;
12085
12322
  console.log(
12086
- `${TAG20} sessionId=${sessionId} accountId=${accountId} writes=${writes.length} ms=${total}`
12323
+ `${TAG21} sessionId=${sessionId} accountId=${accountId} writes=${writes.length} ms=${total}`
12087
12324
  );
12088
12325
  return c.json({
12089
12326
  writes: writes.map(({ elementId, labels, properties }) => ({ elementId, labels, properties }))
@@ -12092,7 +12329,7 @@ app33.get("/", async (c) => {
12092
12329
  const elapsed = Date.now() - started;
12093
12330
  const message = err instanceof Error ? err.message : String(err);
12094
12331
  console.error(
12095
- `${TAG20} neo4j-unreachable sessionId=${sessionId} ms=${elapsed} err="${message}"`
12332
+ `${TAG21} neo4j-unreachable sessionId=${sessionId} ms=${elapsed} err="${message}"`
12096
12333
  );
12097
12334
  return c.json({ error: `post-turn-context unavailable: ${message}` }, 503);
12098
12335
  } finally {
@@ -12101,41 +12338,623 @@ app33.get("/", async (c) => {
12101
12338
  });
12102
12339
  var post_turn_context_default = app33;
12103
12340
 
12104
- // server/routes/admin/index.ts
12341
+ // server/routes/admin/public-session-context.ts
12342
+ import neo4j4 from "neo4j-driver";
12343
+ var TAG22 = "[public-session-context]";
12344
+ var STRIPPED_PROPERTIES3 = /* @__PURE__ */ new Set([
12345
+ "embedding",
12346
+ "passwordHash",
12347
+ "magicToken",
12348
+ "otpCode",
12349
+ "cacheKey"
12350
+ ]);
12351
+ function isLoopbackAddr3(addr) {
12352
+ return addr === "127.0.0.1" || addr === "::1" || addr === "::ffff:127.0.0.1";
12353
+ }
12354
+ function convertValue2(value) {
12355
+ if (neo4j4.isDateTime(value) || neo4j4.isDate(value) || neo4j4.isLocalDateTime(value) || neo4j4.isLocalTime(value) || neo4j4.isTime(value) || neo4j4.isDuration(value)) {
12356
+ return value.toString();
12357
+ }
12358
+ if (neo4j4.isInt(value)) {
12359
+ return value.inSafeRange() ? value.toNumber() : value.toString();
12360
+ }
12361
+ if (Array.isArray(value)) {
12362
+ return value.map((v) => convertValue2(v));
12363
+ }
12364
+ if (value !== null && typeof value === "object" && value.constructor === Object) {
12365
+ const out = {};
12366
+ for (const [k, v] of Object.entries(value)) {
12367
+ out[k] = convertValue2(v);
12368
+ }
12369
+ return out;
12370
+ }
12371
+ return value;
12372
+ }
12373
+ function pruneProperties2(raw) {
12374
+ const out = {};
12375
+ for (const [k, v] of Object.entries(raw)) {
12376
+ if (STRIPPED_PROPERTIES3.has(k)) continue;
12377
+ out[k] = convertValue2(v);
12378
+ }
12379
+ return out;
12380
+ }
12105
12381
  var app34 = new Hono();
12106
- app34.route("/session", session_default);
12107
- app34.route("/new-session-failure", new_session_failure_default);
12108
- app34.route("/new-session-submit", new_session_submit_default);
12109
- app34.route("/logs", logs_default);
12110
- app34.route("/claude-info", claude_info_default);
12111
- app34.route("/claude-capabilities", claude_capabilities_default);
12112
- app34.route("/attachment", attachment_default);
12113
- app34.route("/agents", agents_default);
12114
- app34.route("/sessions", sessions_default);
12115
- app34.route("/claude-sessions", claude_sessions_default);
12116
- app34.route("/log-ingest", log_ingest_default);
12117
- app34.route("/events", events_default);
12118
- app34.route("/files", files_default);
12119
- app34.route("/graph-search", graph_search_default);
12120
- app34.route("/graph-subgraph", graph_subgraph_default);
12121
- app34.route("/graph-delete", graph_delete_default);
12122
- app34.route("/graph-restore", graph_restore_default);
12123
- app34.route("/graph-labels-in-graph", graph_labels_in_graph_default);
12124
- app34.route("/graph-default-view", graph_default_view_default);
12125
- app34.route("/session-defaults", session_defaults_default);
12126
- app34.route("/file-attach", file_attach_default);
12127
- app34.route("/sidebar-artefacts", sidebar_artefacts_default);
12128
- app34.route("/sidebar-artefact-save", sidebar_artefact_save_default);
12129
- app34.route("/sidebar-artefact-content", sidebar_artefact_content_default);
12130
- app34.route("/system-stats", system_stats_default);
12131
- app34.route("/health-brand", health_default2);
12132
- app34.route("/linkedin-ingest", linkedin_ingest_default);
12133
- app34.route("/post-turn-context", post_turn_context_default);
12134
- var admin_default = app34;
12382
+ app34.get("/", async (c) => {
12383
+ const remoteAddr = c.env?.incoming?.socket?.remoteAddress ?? "";
12384
+ if (!isLoopbackAddr3(remoteAddr)) {
12385
+ console.error(`${TAG22} reject reason=non-loopback remoteAddr=${remoteAddr}`);
12386
+ return c.json({ error: "public-session-context-loopback-only" }, 403);
12387
+ }
12388
+ const xffHeader = c.env?.incoming?.headers?.["x-forwarded-for"];
12389
+ const xffRaw = Array.isArray(xffHeader) ? xffHeader.join(",") : typeof xffHeader === "string" ? xffHeader : "";
12390
+ if (xffRaw.length > 0) {
12391
+ const tokens = xffRaw.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
12392
+ const offender = tokens.find((t) => !isLoopbackAddr3(t));
12393
+ if (offender !== void 0) {
12394
+ console.error(`${TAG22} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
12395
+ return c.json({ error: "public-session-context-loopback-only" }, 403);
12396
+ }
12397
+ }
12398
+ const sliceToken = (c.req.query("sliceToken") ?? "").trim();
12399
+ const accountId = (c.req.query("accountId") ?? "").trim();
12400
+ if (!sliceToken) return c.json({ error: "sliceToken required" }, 400);
12401
+ if (!accountId) return c.json({ error: "accountId required" }, 400);
12402
+ const session = getSession();
12403
+ const started = Date.now();
12404
+ try {
12405
+ const cypher = `
12406
+ MATCH (n)
12407
+ WHERE n.sliceToken = $sliceToken AND n.accountId = $accountId
12408
+ RETURN elementId(n) AS elementId,
12409
+ labels(n) AS labels,
12410
+ properties(n) AS properties,
12411
+ coalesce(n.createdAt, n.observedAt, '') AS sortKey
12412
+ `;
12413
+ const res = await session.executeRead(
12414
+ (tx) => tx.run(cypher, { sliceToken, accountId })
12415
+ );
12416
+ const writes = res.records.map((r) => ({
12417
+ elementId: r.get("elementId"),
12418
+ labels: r.get("labels"),
12419
+ properties: pruneProperties2(r.get("properties")),
12420
+ sortKey: String(r.get("sortKey") ?? "")
12421
+ }));
12422
+ writes.sort((a, b) => a.sortKey.localeCompare(b.sortKey));
12423
+ const total = Date.now() - started;
12424
+ console.log(
12425
+ `${TAG22} sliceToken=${sliceToken.slice(0, 8)} writes=${writes.length} ms=${total}`
12426
+ );
12427
+ return c.json({
12428
+ writes: writes.map(({ elementId, labels, properties }) => ({ elementId, labels, properties }))
12429
+ });
12430
+ } catch (err) {
12431
+ const elapsed = Date.now() - started;
12432
+ const message = err instanceof Error ? err.message : String(err);
12433
+ console.error(
12434
+ `${TAG22} neo4j-unreachable sliceToken=${sliceToken.slice(0, 8)} ms=${elapsed} err="${message}"`
12435
+ );
12436
+ return c.json({ error: `public-session-context unavailable: ${message}` }, 503);
12437
+ } finally {
12438
+ await session.close();
12439
+ }
12440
+ });
12441
+ var public_session_context_default = app34;
12135
12442
 
12136
- // server/routes/sites.ts
12137
- import { existsSync as existsSync21, readFileSync as readFileSync16, realpathSync as realpathSync5, statSync as statSync7 } from "fs";
12443
+ // server/routes/admin/access-session-evict.ts
12444
+ var TAG23 = "[access-session-evict]";
12445
+ function isLoopbackAddr4(addr) {
12446
+ return addr === "127.0.0.1" || addr === "::1" || addr === "::ffff:127.0.0.1";
12447
+ }
12448
+ var app35 = new Hono();
12449
+ app35.post("/", async (c) => {
12450
+ const remoteAddr = c.env?.incoming?.socket?.remoteAddress ?? "";
12451
+ if (!isLoopbackAddr4(remoteAddr)) {
12452
+ console.error(`${TAG23} reject reason=non-loopback remoteAddr=${remoteAddr}`);
12453
+ return c.json({ error: "access-session-evict-loopback-only" }, 403);
12454
+ }
12455
+ const xffHeader = c.env?.incoming?.headers?.["x-forwarded-for"];
12456
+ const xffRaw = Array.isArray(xffHeader) ? xffHeader.join(",") : typeof xffHeader === "string" ? xffHeader : "";
12457
+ if (xffRaw.length > 0) {
12458
+ const tokens = xffRaw.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
12459
+ const offender = tokens.find((t) => !isLoopbackAddr4(t));
12460
+ if (offender !== void 0) {
12461
+ console.error(`${TAG23} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
12462
+ return c.json({ error: "access-session-evict-loopback-only" }, 403);
12463
+ }
12464
+ }
12465
+ let body;
12466
+ try {
12467
+ body = await c.req.json();
12468
+ } catch {
12469
+ return c.json({ error: "invalid-json" }, 400);
12470
+ }
12471
+ const grantId = typeof body.grantId === "string" ? body.grantId.trim() : "";
12472
+ if (!grantId) return c.json({ error: "grantId required" }, 400);
12473
+ const dropped = evictAccessSessionsByGrant(grantId);
12474
+ console.log(`${TAG23} grantId=${grantId} dropped=${dropped}`);
12475
+ return c.json({ ok: true, dropped });
12476
+ });
12477
+ var access_session_evict_default = app35;
12478
+
12479
+ // server/routes/admin/index.ts
12480
+ var app36 = new Hono();
12481
+ app36.route("/session", session_default);
12482
+ app36.route("/new-session-failure", new_session_failure_default);
12483
+ app36.route("/new-session-submit", new_session_submit_default);
12484
+ app36.route("/logs", logs_default);
12485
+ app36.route("/claude-info", claude_info_default);
12486
+ app36.route("/claude-capabilities", claude_capabilities_default);
12487
+ app36.route("/attachment", attachment_default);
12488
+ app36.route("/agents", agents_default);
12489
+ app36.route("/sessions", sessions_default);
12490
+ app36.route("/claude-sessions", claude_sessions_default);
12491
+ app36.route("/log-ingest", log_ingest_default);
12492
+ app36.route("/events", events_default);
12493
+ app36.route("/files", files_default);
12494
+ app36.route("/graph-search", graph_search_default);
12495
+ app36.route("/graph-subgraph", graph_subgraph_default);
12496
+ app36.route("/graph-delete", graph_delete_default);
12497
+ app36.route("/graph-restore", graph_restore_default);
12498
+ app36.route("/graph-labels-in-graph", graph_labels_in_graph_default);
12499
+ app36.route("/graph-default-view", graph_default_view_default);
12500
+ app36.route("/session-defaults", session_defaults_default);
12501
+ app36.route("/file-attach", file_attach_default);
12502
+ app36.route("/sidebar-artefacts", sidebar_artefacts_default);
12503
+ app36.route("/sidebar-artefact-save", sidebar_artefact_save_default);
12504
+ app36.route("/sidebar-artefact-content", sidebar_artefact_content_default);
12505
+ app36.route("/system-stats", system_stats_default);
12506
+ app36.route("/health-brand", health_default2);
12507
+ app36.route("/linkedin-ingest", linkedin_ingest_default);
12508
+ app36.route("/post-turn-context", post_turn_context_default);
12509
+ app36.route("/public-session-context", public_session_context_default);
12510
+ app36.route("/access-session-evict", access_session_evict_default);
12511
+ var admin_default = app36;
12512
+
12513
+ // app/lib/access-gate.ts
12514
+ import neo4j5 from "neo4j-driver";
12515
+ import { readFileSync as readFileSync16 } from "fs";
12138
12516
  import { resolve as resolve18 } from "path";
12517
+ import { randomUUID as randomUUID8 } from "crypto";
12518
+ var PLATFORM_ROOT7 = process.env.MAXY_PLATFORM_ROOT ?? resolve18(process.cwd(), "..");
12519
+ var driver = null;
12520
+ function readPassword() {
12521
+ if (process.env.NEO4J_PASSWORD) return process.env.NEO4J_PASSWORD;
12522
+ const passwordFile = resolve18(PLATFORM_ROOT7, "config/.neo4j-password");
12523
+ try {
12524
+ return readFileSync16(passwordFile, "utf-8").trim();
12525
+ } catch {
12526
+ throw new Error(
12527
+ `Neo4j password not found. Expected at ${passwordFile} or in NEO4J_PASSWORD env var.`
12528
+ );
12529
+ }
12530
+ }
12531
+ function getDriver() {
12532
+ if (!driver) {
12533
+ const uri = process.env.NEO4J_URI;
12534
+ if (!uri) {
12535
+ throw new Error(
12536
+ "[ui/access-gate] NEO4J_URI unset \u2014 refusing to default to bolt://localhost:7687"
12537
+ );
12538
+ }
12539
+ const user = process.env.NEO4J_USER ?? "neo4j";
12540
+ const password = readPassword();
12541
+ console.error(`[ui/access-gate] resolved neo4j_uri=${uri}`);
12542
+ driver = neo4j5.driver(uri, neo4j5.auth.basic(user, password), {
12543
+ maxConnectionPoolSize: 5
12544
+ });
12545
+ }
12546
+ return driver;
12547
+ }
12548
+ function getSession3() {
12549
+ return getDriver().session();
12550
+ }
12551
+ process.on("SIGINT", async () => {
12552
+ if (driver) {
12553
+ await driver.close();
12554
+ driver = null;
12555
+ }
12556
+ });
12557
+ var rateLimitMap = /* @__PURE__ */ new Map();
12558
+ var ACCESS_MAX_ATTEMPTS = 5;
12559
+ var ACCESS_LOCKOUT_MS = 15 * 60 * 1e3;
12560
+ var ACCESS_WINDOW_MS = 15 * 60 * 1e3;
12561
+ function checkAccessRateLimit(ip, agentSlug) {
12562
+ const key = `${ip}:${agentSlug}`;
12563
+ const entry = rateLimitMap.get(key);
12564
+ if (!entry) return null;
12565
+ const now = Date.now();
12566
+ if (entry.lockedUntil && now < entry.lockedUntil) {
12567
+ const remainingMs = entry.lockedUntil - now;
12568
+ const remainingS = Math.ceil(remainingMs / 1e3);
12569
+ return `Too many attempts. Try again in ${remainingS}s`;
12570
+ }
12571
+ if (now - entry.firstAttempt > ACCESS_WINDOW_MS) {
12572
+ rateLimitMap.delete(key);
12573
+ return null;
12574
+ }
12575
+ return null;
12576
+ }
12577
+ function recordAccessFailedAttempt(ip, agentSlug) {
12578
+ const key = `${ip}:${agentSlug}`;
12579
+ const now = Date.now();
12580
+ const entry = rateLimitMap.get(key);
12581
+ if (!entry || now - entry.firstAttempt > ACCESS_WINDOW_MS) {
12582
+ rateLimitMap.set(key, { attempts: 1, firstAttempt: now });
12583
+ return;
12584
+ }
12585
+ entry.attempts++;
12586
+ if (entry.attempts >= ACCESS_MAX_ATTEMPTS) {
12587
+ entry.lockedUntil = now + ACCESS_LOCKOUT_MS;
12588
+ }
12589
+ }
12590
+ function clearAccessRateLimit(ip, agentSlug) {
12591
+ rateLimitMap.delete(`${ip}:${agentSlug}`);
12592
+ }
12593
+ var contactRateMap = /* @__PURE__ */ new Map();
12594
+ var REQUEST_LINK_MAX = 3;
12595
+ var REQUEST_LINK_WINDOW_MS = 60 * 60 * 1e3;
12596
+ function checkRequestLinkRateLimit(contactValue) {
12597
+ const entry = contactRateMap.get(contactValue);
12598
+ if (!entry) return null;
12599
+ const now = Date.now();
12600
+ if (now - entry.windowStart > REQUEST_LINK_WINDOW_MS) {
12601
+ contactRateMap.delete(contactValue);
12602
+ return null;
12603
+ }
12604
+ if (entry.count >= REQUEST_LINK_MAX) {
12605
+ const remainingMs = REQUEST_LINK_WINDOW_MS - (now - entry.windowStart);
12606
+ const remainingM = Math.ceil(remainingMs / 6e4);
12607
+ return `Too many requests. Try again in ${remainingM} minute(s)`;
12608
+ }
12609
+ return null;
12610
+ }
12611
+ function recordRequestLinkAttempt(contactValue) {
12612
+ const now = Date.now();
12613
+ const entry = contactRateMap.get(contactValue);
12614
+ if (!entry || now - entry.windowStart > REQUEST_LINK_WINDOW_MS) {
12615
+ contactRateMap.set(contactValue, { count: 1, windowStart: now });
12616
+ return;
12617
+ }
12618
+ entry.count++;
12619
+ }
12620
+ function maskContact(value) {
12621
+ if (value.includes("@")) {
12622
+ const [local, domain] = value.split("@");
12623
+ return `${local[0]}***@${domain}`;
12624
+ }
12625
+ return "****";
12626
+ }
12627
+ function readGrant(record) {
12628
+ const g = record.get("g").properties;
12629
+ const personId = record.get("personId");
12630
+ return {
12631
+ grantId: record.get("grantId"),
12632
+ agentSlug: String(g.agentSlug),
12633
+ accountId: String(g.accountId),
12634
+ contactValue: String(g.contactValue),
12635
+ displayName: record.get("givenName") ?? null,
12636
+ personId,
12637
+ expiresAt: g.expiresAt ? new Date(String(g.expiresAt)).getTime() : null,
12638
+ status: String(g.status),
12639
+ sliceToken: String(g.sliceToken ?? "")
12640
+ };
12641
+ }
12642
+ async function findGrantByMagicToken(token) {
12643
+ const session = getSession3();
12644
+ try {
12645
+ const result = await session.run(
12646
+ `MATCH (p:Person)-[:HAS_ACCESS]->(g:AccessGrant {magicToken: $token})
12647
+ RETURN g, p.givenName AS givenName,
12648
+ elementId(g) AS grantId,
12649
+ elementId(p) AS personId`,
12650
+ { token }
12651
+ );
12652
+ if (result.records.length === 0) return null;
12653
+ return readGrant(result.records[0]);
12654
+ } finally {
12655
+ await session.close();
12656
+ }
12657
+ }
12658
+ async function findActiveGrantByContact(contactValue, agentSlug, accountId) {
12659
+ const session = getSession3();
12660
+ try {
12661
+ const result = await session.run(
12662
+ `MATCH (p:Person)-[:HAS_ACCESS]->(g:AccessGrant {
12663
+ contactValue: $contactValue,
12664
+ agentSlug: $agentSlug,
12665
+ accountId: $accountId
12666
+ })
12667
+ WHERE g.status IN ['invited', 'active']
12668
+ RETURN g, p.givenName AS givenName,
12669
+ elementId(g) AS grantId,
12670
+ elementId(p) AS personId`,
12671
+ { contactValue, agentSlug, accountId }
12672
+ );
12673
+ if (result.records.length === 0) return null;
12674
+ return readGrant(result.records[0]);
12675
+ } finally {
12676
+ await session.close();
12677
+ }
12678
+ }
12679
+ async function consumeMagicTokenAndActivate(grantId) {
12680
+ const session = getSession3();
12681
+ try {
12682
+ const result = await session.run(
12683
+ `MATCH (g:AccessGrant) WHERE elementId(g) = $grantId
12684
+ SET g.magicToken = null,
12685
+ g.magicTokenExpiresAt = null,
12686
+ g.status = "active"
12687
+ RETURN count(g) AS affected`,
12688
+ { grantId }
12689
+ );
12690
+ const affected = result.records[0]?.get("affected");
12691
+ const count = typeof affected === "number" ? affected : affected?.toNumber?.() ?? 0;
12692
+ if (count === 0) {
12693
+ throw new Error(`[access-gate] consumeMagicTokenAndActivate: no grant matched grantId=${grantId}`);
12694
+ }
12695
+ } finally {
12696
+ await session.close();
12697
+ }
12698
+ }
12699
+ async function generateNewMagicToken(grantId) {
12700
+ const token = randomUUID8();
12701
+ const session = getSession3();
12702
+ try {
12703
+ const result = await session.run(
12704
+ `MATCH (g:AccessGrant) WHERE elementId(g) = $grantId
12705
+ SET g.magicToken = $token,
12706
+ g.magicTokenExpiresAt = datetime() + duration('PT15M')
12707
+ RETURN count(g) AS affected`,
12708
+ { grantId, token }
12709
+ );
12710
+ const affected = result.records[0]?.get("affected");
12711
+ const count = typeof affected === "number" ? affected : affected?.toNumber?.() ?? 0;
12712
+ if (count === 0) {
12713
+ throw new Error(`[access-gate] generateNewMagicToken: no grant matched grantId=${grantId}`);
12714
+ }
12715
+ } finally {
12716
+ await session.close();
12717
+ }
12718
+ return token;
12719
+ }
12720
+
12721
+ // server/routes/access/verify-token.ts
12722
+ var TAG24 = "[access-verify]";
12723
+ var MINT_TAG = "[access-session-mint]";
12724
+ var COOKIE_NAME = "__access_session";
12725
+ var app37 = new Hono();
12726
+ app37.post("/", async (c) => {
12727
+ const ip = c.var.clientIp || "unknown";
12728
+ let body;
12729
+ try {
12730
+ body = await c.req.json();
12731
+ } catch {
12732
+ return c.json({ error: "invalid-json" }, 400);
12733
+ }
12734
+ const token = typeof body.token === "string" ? body.token.trim() : "";
12735
+ const agentSlug = typeof body.agentSlug === "string" ? body.agentSlug.trim() : "";
12736
+ if (!token || !agentSlug) {
12737
+ return c.json({ error: "token and agentSlug required" }, 400);
12738
+ }
12739
+ const rateMsg = checkAccessRateLimit(ip, agentSlug);
12740
+ if (rateMsg) {
12741
+ console.error(`${TAG24} grantId=- agentSlug=${agentSlug} result=rate-limited ip=${ip}`);
12742
+ return c.json({ error: rateMsg }, 429);
12743
+ }
12744
+ const grant = await findGrantByMagicToken(token);
12745
+ if (!grant) {
12746
+ recordAccessFailedAttempt(ip, agentSlug);
12747
+ console.error(`${TAG24} grantId=- agentSlug=${agentSlug} result=notfound ip=${ip}`);
12748
+ return c.json({ error: "invalid-or-expired-link" }, 401);
12749
+ }
12750
+ if (grant.agentSlug !== agentSlug) {
12751
+ recordAccessFailedAttempt(ip, agentSlug);
12752
+ console.error(
12753
+ `${TAG24} grantId=${grant.grantId} agentSlug=${agentSlug} result=agent-mismatch grantAgent=${grant.agentSlug} ip=${ip}`
12754
+ );
12755
+ return c.json({ error: "invalid-or-expired-link" }, 401);
12756
+ }
12757
+ if (grant.status === "expired" || grant.status === "revoked") {
12758
+ recordAccessFailedAttempt(ip, agentSlug);
12759
+ console.error(
12760
+ `${TAG24} grantId=${grant.grantId} agentSlug=${agentSlug} result=expired status=${grant.status} ip=${ip}`
12761
+ );
12762
+ return c.json({ error: "access-no-longer-valid" }, 401);
12763
+ }
12764
+ if (grant.expiresAt !== null && grant.expiresAt < Date.now()) {
12765
+ recordAccessFailedAttempt(ip, agentSlug);
12766
+ console.error(
12767
+ `${TAG24} grantId=${grant.grantId} agentSlug=${agentSlug} result=expired reason=expiresAt-past ip=${ip}`
12768
+ );
12769
+ return c.json({ error: "access-no-longer-valid" }, 401);
12770
+ }
12771
+ if (!grant.sliceToken) {
12772
+ console.error(
12773
+ `${TAG24} grantId=${grant.grantId} agentSlug=${agentSlug} result=no-slice-token reason=schema-violation`
12774
+ );
12775
+ return c.json({ error: "grant-misconfigured" }, 500);
12776
+ }
12777
+ const session = createAccessSession({
12778
+ grantId: grant.grantId,
12779
+ sliceToken: grant.sliceToken,
12780
+ agentSlug: grant.agentSlug,
12781
+ accountId: grant.accountId,
12782
+ personId: grant.personId,
12783
+ contactValue: grant.contactValue
12784
+ });
12785
+ try {
12786
+ await consumeMagicTokenAndActivate(grant.grantId);
12787
+ } catch (err) {
12788
+ console.error(
12789
+ `${TAG24} grantId=${grant.grantId} agentSlug=${agentSlug} result=consume-failed err="${err instanceof Error ? err.message : String(err)}"`
12790
+ );
12791
+ return c.json({ error: "verification-failed" }, 500);
12792
+ }
12793
+ clearAccessRateLimit(ip, agentSlug);
12794
+ console.log(`${TAG24} grantId=${grant.grantId} agentSlug=${agentSlug} result=ok ip=${ip}`);
12795
+ console.log(
12796
+ `${MINT_TAG} grantId=${grant.grantId} sliceToken=${grant.sliceToken.slice(0, 8)} agentSlug=${agentSlug} personId=${grant.personId ?? "none"}`
12797
+ );
12798
+ c.header(
12799
+ "Set-Cookie",
12800
+ `${COOKIE_NAME}=${session.sessionId}; HttpOnly; Secure; SameSite=Lax; Path=/`
12801
+ );
12802
+ return c.json({
12803
+ ok: true,
12804
+ agentSlug,
12805
+ displayName: grant.displayName
12806
+ });
12807
+ });
12808
+ var verify_token_default = app37;
12809
+
12810
+ // app/lib/access-email.ts
12811
+ import { spawn as spawn2 } from "child_process";
12812
+ import { resolve as resolve19 } from "path";
12813
+ var PLATFORM_ROOT8 = process.env.MAXY_PLATFORM_ROOT ?? resolve19(process.cwd(), "..");
12814
+ var SEND_SCRIPT = resolve19(
12815
+ PLATFORM_ROOT8,
12816
+ "plugins",
12817
+ "email",
12818
+ "mcp",
12819
+ "dist",
12820
+ "scripts",
12821
+ "send-transactional.js"
12822
+ );
12823
+ async function sendMagicLinkEmail(payload) {
12824
+ return new Promise((resolveResult) => {
12825
+ const child = spawn2("node", [SEND_SCRIPT], {
12826
+ stdio: ["pipe", "pipe", "pipe"],
12827
+ env: process.env
12828
+ });
12829
+ let stdout = "";
12830
+ let stderr = "";
12831
+ let settled = false;
12832
+ const timeout = setTimeout(() => {
12833
+ if (settled) return;
12834
+ settled = true;
12835
+ try {
12836
+ child.kill("SIGTERM");
12837
+ } catch {
12838
+ }
12839
+ resolveResult({ ok: false, error: "send-transactional timeout (30s)" });
12840
+ }, 3e4);
12841
+ child.stdout.on("data", (chunk) => {
12842
+ stdout += chunk.toString();
12843
+ });
12844
+ child.stderr.on("data", (chunk) => {
12845
+ stderr += chunk.toString();
12846
+ });
12847
+ child.on("error", (err) => {
12848
+ if (settled) return;
12849
+ settled = true;
12850
+ clearTimeout(timeout);
12851
+ resolveResult({ ok: false, error: `spawn-failed: ${err.message}` });
12852
+ });
12853
+ child.on("exit", (code) => {
12854
+ if (settled) return;
12855
+ settled = true;
12856
+ clearTimeout(timeout);
12857
+ if (code === 0) {
12858
+ const match = stdout.match(/^ok messageId=(.+)$/m);
12859
+ resolveResult({ ok: true, messageId: match?.[1]?.trim() ?? "unknown" });
12860
+ } else {
12861
+ resolveResult({ ok: false, error: stderr.trim() || `exit-${code}` });
12862
+ }
12863
+ });
12864
+ child.stdin.write(JSON.stringify(payload));
12865
+ child.stdin.end();
12866
+ });
12867
+ }
12868
+
12869
+ // server/routes/access/request-magic-link.ts
12870
+ var TAG25 = "[access-request-link]";
12871
+ var app38 = new Hono();
12872
+ var VISITOR_MESSAGE = "If that email is on the invite list, a fresh link is on the way.";
12873
+ app38.post("/", async (c) => {
12874
+ let body;
12875
+ try {
12876
+ body = await c.req.json();
12877
+ } catch {
12878
+ return c.json({ error: "invalid-json" }, 400);
12879
+ }
12880
+ const contactValue = typeof body.contactValue === "string" ? body.contactValue.trim().toLowerCase() : "";
12881
+ const agentSlug = typeof body.agentSlug === "string" ? body.agentSlug.trim() : "";
12882
+ if (!contactValue || !agentSlug) {
12883
+ return c.json({ error: "contactValue and agentSlug required" }, 400);
12884
+ }
12885
+ const rateMsg = checkRequestLinkRateLimit(contactValue);
12886
+ if (rateMsg) {
12887
+ console.error(`${TAG25} contactValue=${maskContact(contactValue)} result=rate-limited`);
12888
+ return c.json({ error: rateMsg }, 429);
12889
+ }
12890
+ recordRequestLinkAttempt(contactValue);
12891
+ const accountId = process.env.ACCOUNT_ID ?? "";
12892
+ if (!accountId) {
12893
+ console.error(`${TAG25} contactValue=${maskContact(contactValue)} result=no-account-id`);
12894
+ return c.json({ message: VISITOR_MESSAGE }, 200);
12895
+ }
12896
+ const grant = await findActiveGrantByContact(contactValue, agentSlug, accountId);
12897
+ if (!grant) {
12898
+ console.log(`${TAG25} contactValue=${maskContact(contactValue)} result=notfound`);
12899
+ return c.json({ message: VISITOR_MESSAGE }, 200);
12900
+ }
12901
+ let token;
12902
+ try {
12903
+ token = await generateNewMagicToken(grant.grantId);
12904
+ } catch (err) {
12905
+ console.error(
12906
+ `${TAG25} contactValue=${maskContact(contactValue)} result=mint-failed err="${err instanceof Error ? err.message : String(err)}"`
12907
+ );
12908
+ return c.json({ message: VISITOR_MESSAGE }, 200);
12909
+ }
12910
+ const host = c.req.header("host") ?? "";
12911
+ const proto = (c.req.header("x-forwarded-proto") ?? "https").toLowerCase();
12912
+ const magicUrl = `${proto}://${host}/${agentSlug}?token=${token}`;
12913
+ const subject = `Your link to ${grant.agentSlug}`;
12914
+ const bodyText = [
12915
+ grant.displayName ? `Hi ${grant.displayName},` : "Hello,",
12916
+ "",
12917
+ `Here's a fresh link to access ${grant.agentSlug}:`,
12918
+ "",
12919
+ magicUrl,
12920
+ "",
12921
+ "This link expires in 15 minutes."
12922
+ ].join("\n");
12923
+ const htmlBody = `
12924
+ <p>${grant.displayName ? `Hi ${grant.displayName},` : "Hello,"}</p>
12925
+ <p>Here's a fresh link to access <strong>${grant.agentSlug}</strong>:</p>
12926
+ <p><a href="${magicUrl}" style="display:inline-block;padding:12px 24px;background:#000;color:#fff;text-decoration:none;border-radius:6px;">Open ${grant.agentSlug}</a></p>
12927
+ <p style="color:#888;font-size:14px;">This link expires in 15 minutes.</p>
12928
+ `;
12929
+ const sendResult = await sendMagicLinkEmail({
12930
+ accountId,
12931
+ to: grant.contactValue,
12932
+ subject,
12933
+ body: bodyText,
12934
+ html: htmlBody
12935
+ });
12936
+ if (!sendResult.ok) {
12937
+ console.error(
12938
+ `${TAG25} contactValue=${maskContact(contactValue)} result=send-failed err="${sendResult.error}"`
12939
+ );
12940
+ return c.json({ message: VISITOR_MESSAGE }, 200);
12941
+ }
12942
+ console.log(
12943
+ `${TAG25} contactValue=${maskContact(contactValue)} result=ok messageId=${sendResult.messageId}`
12944
+ );
12945
+ return c.json({ message: VISITOR_MESSAGE }, 200);
12946
+ });
12947
+ var request_magic_link_default = app38;
12948
+
12949
+ // server/routes/access/index.ts
12950
+ var app39 = new Hono();
12951
+ app39.route("/verify-token", verify_token_default);
12952
+ app39.route("/request-magic-link", request_magic_link_default);
12953
+ var access_default = app39;
12954
+
12955
+ // server/routes/sites.ts
12956
+ import { existsSync as existsSync21, readFileSync as readFileSync17, realpathSync as realpathSync5, statSync as statSync7 } from "fs";
12957
+ import { resolve as resolve20 } from "path";
12139
12958
  var SAFE_SEG_RE = /^[a-z0-9_][a-z0-9_.-]{0,99}$/i;
12140
12959
  var MIME = {
12141
12960
  ".html": "text/html; charset=utf-8",
@@ -12166,8 +12985,8 @@ function getExt(p) {
12166
12985
  if (idx < p.lastIndexOf("/")) return "";
12167
12986
  return p.slice(idx).toLowerCase();
12168
12987
  }
12169
- var app35 = new Hono();
12170
- app35.get("/:rel{.*}", (c) => {
12988
+ var app40 = new Hono();
12989
+ app40.get("/:rel{.*}", (c) => {
12171
12990
  const reqPath = c.req.path;
12172
12991
  const rawRel = c.req.param("rel") ?? "";
12173
12992
  const trimmed = rawRel.replace(/^\/+/, "").replace(/\/+$/, "");
@@ -12192,8 +13011,8 @@ app35.get("/:rel{.*}", (c) => {
12192
13011
  }
12193
13012
  segments.push(seg);
12194
13013
  }
12195
- const rootDir = resolve18(account.accountDir, "sites");
12196
- let filePath = segments.length === 0 ? rootDir : resolve18(rootDir, ...segments);
13014
+ const rootDir = resolve20(account.accountDir, "sites");
13015
+ let filePath = segments.length === 0 ? rootDir : resolve20(rootDir, ...segments);
12197
13016
  if (filePath !== rootDir && !filePath.startsWith(rootDir + "/")) {
12198
13017
  console.error(`[sites] path-traversal-rejected path=${reqPath} reason=escape status=403`);
12199
13018
  return c.text("Forbidden", 403);
@@ -12213,7 +13032,7 @@ app35.get("/:rel{.*}", (c) => {
12213
13032
  return c.redirect(target, 301);
12214
13033
  }
12215
13034
  if (stat7?.isDirectory()) {
12216
- filePath = resolve18(filePath, "index.html");
13035
+ filePath = resolve20(filePath, "index.html");
12217
13036
  }
12218
13037
  if (!filePath.startsWith(rootDir + "/")) {
12219
13038
  console.error(`[sites] path-traversal-rejected path=${reqPath} reason=escape status=403`);
@@ -12238,7 +13057,7 @@ app35.get("/:rel{.*}", (c) => {
12238
13057
  }
12239
13058
  let body;
12240
13059
  try {
12241
- body = readFileSync16(realPath);
13060
+ body = readFileSync17(realPath);
12242
13061
  } catch (err) {
12243
13062
  const code = err?.code;
12244
13063
  if (code === "EISDIR") {
@@ -12270,11 +13089,11 @@ app35.get("/:rel{.*}", (c) => {
12270
13089
  "X-Content-Type-Options": "nosniff"
12271
13090
  });
12272
13091
  });
12273
- var sites_default = app35;
13092
+ var sites_default = app40;
12274
13093
 
12275
13094
  // app/lib/visitor-token.ts
12276
13095
  import { createHmac, randomBytes, timingSafeEqual } from "crypto";
12277
- import { mkdirSync as mkdirSync4, readFileSync as readFileSync17, writeFileSync as writeFileSync6 } from "fs";
13096
+ import { mkdirSync as mkdirSync4, readFileSync as readFileSync18, writeFileSync as writeFileSync6 } from "fs";
12278
13097
  import { dirname as dirname5 } from "path";
12279
13098
  var TOKEN_PREFIX = "v1.";
12280
13099
  var SECRET_BYTES = 32;
@@ -12283,7 +13102,7 @@ var cachedSecret = null;
12283
13102
  function getSecret() {
12284
13103
  if (cachedSecret) return cachedSecret;
12285
13104
  try {
12286
- const hex2 = readFileSync17(VISITOR_TOKEN_SECRET_FILE, "utf-8").trim();
13105
+ const hex2 = readFileSync18(VISITOR_TOKEN_SECRET_FILE, "utf-8").trim();
12287
13106
  if (hex2.length === SECRET_BYTES * 2) {
12288
13107
  cachedSecret = Buffer.from(hex2, "hex");
12289
13108
  return cachedSecret;
@@ -12297,7 +13116,7 @@ function getSecret() {
12297
13116
  console.log(`[visitor-token] secret minted path=${VISITOR_TOKEN_SECRET_FILE}`);
12298
13117
  } catch {
12299
13118
  }
12300
- const hex = readFileSync17(VISITOR_TOKEN_SECRET_FILE, "utf-8").trim();
13119
+ const hex = readFileSync18(VISITOR_TOKEN_SECRET_FILE, "utf-8").trim();
12301
13120
  cachedSecret = Buffer.from(hex, "hex");
12302
13121
  return cachedSecret;
12303
13122
  }
@@ -12350,7 +13169,7 @@ var VISITOR_COOKIE_NAME = "mxy_v";
12350
13169
  var VISITOR_COOKIE_MAX_AGE_SECONDS = Math.floor(DEFAULT_TTL_MS / 1e3);
12351
13170
 
12352
13171
  // app/lib/brand-config.ts
12353
- import { existsSync as existsSync22, readFileSync as readFileSync18 } from "fs";
13172
+ import { existsSync as existsSync22, readFileSync as readFileSync19 } from "fs";
12354
13173
  import { join as join14 } from "path";
12355
13174
  var cached2 = null;
12356
13175
  var cachedAttempted = false;
@@ -12362,7 +13181,7 @@ function readBrandConfig() {
12362
13181
  const brandPath = join14(platformRoot, "config", "brand.json");
12363
13182
  if (!existsSync22(brandPath)) return null;
12364
13183
  try {
12365
- cached2 = JSON.parse(readFileSync18(brandPath, "utf-8"));
13184
+ cached2 = JSON.parse(readFileSync19(brandPath, "utf-8"));
12366
13185
  return cached2;
12367
13186
  } catch {
12368
13187
  return null;
@@ -12370,7 +13189,7 @@ function readBrandConfig() {
12370
13189
  }
12371
13190
 
12372
13191
  // server/routes/visitor-consent.ts
12373
- var app36 = new Hono();
13192
+ var app41 = new Hono();
12374
13193
  var CONSENT_COOKIE_NAME = "mxy_consent";
12375
13194
  var CONSENT_COOKIE_MAX_AGE_SECONDS = 60 * 60 * 24 * 365;
12376
13195
  var DEFAULT_CONSENT_COPY = {
@@ -12415,17 +13234,17 @@ function siteSlugFromReferer(referer) {
12415
13234
  return "";
12416
13235
  }
12417
13236
  }
12418
- app36.options("/consent", (c) => {
13237
+ app41.options("/consent", (c) => {
12419
13238
  const origin = getOrigin(c);
12420
13239
  setCorsHeaders(c, origin);
12421
13240
  return c.body(null, 204);
12422
13241
  });
12423
- app36.options("/brand-config", (c) => {
13242
+ app41.options("/brand-config", (c) => {
12424
13243
  const origin = getOrigin(c);
12425
13244
  setCorsHeaders(c, origin);
12426
13245
  return c.body(null, 204);
12427
13246
  });
12428
- app36.post("/consent", async (c) => {
13247
+ app41.post("/consent", async (c) => {
12429
13248
  const origin = getOrigin(c);
12430
13249
  setCorsHeaders(c, origin);
12431
13250
  let raw;
@@ -12465,7 +13284,7 @@ app36.post("/consent", async (c) => {
12465
13284
  console.log(`[consent] ${parsed.decision} site=${site} brand=${brandName} tokenBound=${tokenBound}`);
12466
13285
  return c.body(null, 204);
12467
13286
  });
12468
- app36.get("/brand-config", (c) => {
13287
+ app41.get("/brand-config", (c) => {
12469
13288
  const origin = getOrigin(c);
12470
13289
  setCorsHeaders(c, origin);
12471
13290
  const brand = readBrandConfig();
@@ -12475,7 +13294,7 @@ app36.get("/brand-config", (c) => {
12475
13294
  c.header("Cache-Control", "public, max-age=300");
12476
13295
  return c.json({ consent: { copy, palette } });
12477
13296
  });
12478
- var visitor_consent_default = app36;
13297
+ var visitor_consent_default = app41;
12479
13298
 
12480
13299
  // server/routes/listings.ts
12481
13300
  function getCookie(headerValue, name) {
@@ -12502,8 +13321,8 @@ function appendConsentParams(pageUrl, token) {
12502
13321
  }
12503
13322
  var SAFE_SLUG_RE = /^[a-z0-9](?:[a-z0-9-]{0,118}[a-z0-9])?$/;
12504
13323
  var CHAT_SESSION_KEY_RE = /^[A-Za-z0-9][A-Za-z0-9_-]{7,127}$/;
12505
- var app37 = new Hono();
12506
- app37.get("/:slug/click", async (c) => {
13324
+ var app42 = new Hono();
13325
+ app42.get("/:slug/click", async (c) => {
12507
13326
  const slug = c.req.param("slug") ?? "";
12508
13327
  const rawSession = c.req.query("session") ?? "";
12509
13328
  const sessionKey = CHAT_SESSION_KEY_RE.test(rawSession) ? rawSession : "invalid";
@@ -12567,10 +13386,10 @@ app37.get("/:slug/click", async (c) => {
12567
13386
  console.log(`[property-card-click] sessionKey=${sessionKey} listingSlug=${slug} consent=${consentCookie ?? "absent"} ts=${(/* @__PURE__ */ new Date()).toISOString()}`);
12568
13387
  return c.redirect(redirectUrl, 302);
12569
13388
  });
12570
- var listings_default = app37;
13389
+ var listings_default = app42;
12571
13390
 
12572
13391
  // server/routes/visitor-event.ts
12573
- var app38 = new Hono();
13392
+ var app43 = new Hono();
12574
13393
  var BOT_UA_RE = /\b(bot|crawl|spider|slurp|headlesschrome|phantomjs|googlebot|bingbot|yandex|baiduspider|ahrefsbot|semrushbot|mj12bot|dotbot|petalbot)\b/i;
12575
13394
  var buckets = /* @__PURE__ */ new Map();
12576
13395
  var RATE_LIMIT = 60;
@@ -12637,12 +13456,12 @@ function originAllowed(origin, allowlist) {
12637
13456
  return false;
12638
13457
  }
12639
13458
  }
12640
- app38.options("/event", (c) => {
13459
+ app43.options("/event", (c) => {
12641
13460
  const origin = getOrigin2(c);
12642
13461
  setCorsHeaders2(c, origin);
12643
13462
  return c.body(null, 204);
12644
13463
  });
12645
- app38.post("/event", async (c) => {
13464
+ app43.post("/event", async (c) => {
12646
13465
  const origin = getOrigin2(c);
12647
13466
  setCorsHeaders2(c, origin);
12648
13467
  const ua = c.req.header("user-agent") ?? "";
@@ -12847,7 +13666,7 @@ async function writeEvent(opts) {
12847
13666
  );
12848
13667
  }
12849
13668
  }
12850
- var visitor_event_default = app38;
13669
+ var visitor_event_default = app43;
12851
13670
 
12852
13671
  // app/lib/graph-health.ts
12853
13672
  var HOUR_MS = 60 * 60 * 1e3;
@@ -12931,7 +13750,7 @@ function startGraphHealthTimer() {
12931
13750
  }
12932
13751
 
12933
13752
  // app/lib/whatsapp/inbound/claude-bridge.ts
12934
- var TAG21 = "[whatsapp-adaptor]";
13753
+ var TAG26 = "[whatsapp-adaptor]";
12935
13754
  function whatsappTurnTimeoutMs() {
12936
13755
  return Number(process.env.WHATSAPP_PTY_TURN_TIMEOUT_MS ?? String(5 * 6e4));
12937
13756
  }
@@ -12954,7 +13773,7 @@ async function dispatchToClaude(input) {
12954
13773
  await input.reply(result.turnText);
12955
13774
  } catch (err) {
12956
13775
  const m = err instanceof Error ? err.message : String(err);
12957
- console.error(`${TAG21} reject reason=reply-failed senderId=${input.senderId} message=${m}`);
13776
+ console.error(`${TAG26} reject reason=reply-failed senderId=${input.senderId} message=${m}`);
12958
13777
  }
12959
13778
  }
12960
13779
  function startReaper2() {
@@ -12991,8 +13810,8 @@ function broadcastAdminShutdown(reason) {
12991
13810
 
12992
13811
  // ../lib/entitlement/src/index.ts
12993
13812
  import { createPublicKey, createHash as createHash4, verify as cryptoVerify } from "crypto";
12994
- import { existsSync as existsSync23, readFileSync as readFileSync19, statSync as statSync8 } from "fs";
12995
- import { resolve as resolve19 } from "path";
13813
+ import { existsSync as existsSync23, readFileSync as readFileSync20, statSync as statSync8 } from "fs";
13814
+ import { resolve as resolve21 } from "path";
12996
13815
 
12997
13816
  // ../lib/entitlement/src/canonicalize.ts
12998
13817
  function canonicalize(value) {
@@ -13027,7 +13846,7 @@ var PUBKEY_SHA256 = "8eee6bcb33545fd13b16d3199a5735ca5db5062834c7b49dfe4f23801d9
13027
13846
  var GRACE_DAYS = 7;
13028
13847
  var GRACE_MS = GRACE_DAYS * 24 * 60 * 60 * 1e3;
13029
13848
  function pubkeyPath(brand) {
13030
- return resolve19(brand.platformRoot, "lib", "entitlement", "rubytech-pubkey.pem");
13849
+ return resolve21(brand.platformRoot, "lib", "entitlement", "rubytech-pubkey.pem");
13031
13850
  }
13032
13851
  var memo = null;
13033
13852
  function memoKey(mtimeMs, account) {
@@ -13039,7 +13858,7 @@ function resolveEntitlement(brand, account) {
13039
13858
  if (brand.commercialMode !== true) {
13040
13859
  return logResolved(implicitTrust(account), null);
13041
13860
  }
13042
- const entitlementPath = resolve19(brand.configDir, "entitlement.json");
13861
+ const entitlementPath = resolve21(brand.configDir, "entitlement.json");
13043
13862
  if (!existsSync23(entitlementPath)) {
13044
13863
  return logResolved(anonymousFallback("missing"), { reason: "missing" });
13045
13864
  }
@@ -13055,7 +13874,7 @@ function resolveEntitlement(brand, account) {
13055
13874
  function verifyAndResolve(brand, entitlementPath, account) {
13056
13875
  let pubkeyPem;
13057
13876
  try {
13058
- pubkeyPem = readFileSync19(pubkeyPath(brand), "utf-8");
13877
+ pubkeyPem = readFileSync20(pubkeyPath(brand), "utf-8");
13059
13878
  } catch (err) {
13060
13879
  return logResolved(anonymousFallback("pubkey-missing"), {
13061
13880
  reason: "pubkey-missing"
@@ -13069,7 +13888,7 @@ function verifyAndResolve(brand, entitlementPath, account) {
13069
13888
  }
13070
13889
  let envelope;
13071
13890
  try {
13072
- envelope = JSON.parse(readFileSync19(entitlementPath, "utf-8"));
13891
+ envelope = JSON.parse(readFileSync20(entitlementPath, "utf-8"));
13073
13892
  } catch {
13074
13893
  return logResolved(anonymousFallback("malformed"), { reason: "malformed" });
13075
13894
  }
@@ -13229,15 +14048,15 @@ function clientFrom(c) {
13229
14048
  c.req.header("x-forwarded-for")
13230
14049
  );
13231
14050
  }
13232
- var PLATFORM_ROOT7 = process.env.MAXY_PLATFORM_ROOT || "";
13233
- var BRAND_JSON_PATH = PLATFORM_ROOT7 ? join15(PLATFORM_ROOT7, "config", "brand.json") : "";
14051
+ var PLATFORM_ROOT9 = process.env.MAXY_PLATFORM_ROOT || "";
14052
+ var BRAND_JSON_PATH = PLATFORM_ROOT9 ? join15(PLATFORM_ROOT9, "config", "brand.json") : "";
13234
14053
  var BRAND = { productName: "Maxy", hostname: "maxy", configDir: ".maxy", marketingUrl: "https://getmaxy.com" };
13235
14054
  if (BRAND_JSON_PATH && !existsSync24(BRAND_JSON_PATH)) {
13236
14055
  console.error(`[brand] WARNING: brand.json not found at ${BRAND_JSON_PATH} \u2014 using Maxy defaults`);
13237
14056
  }
13238
14057
  if (BRAND_JSON_PATH && existsSync24(BRAND_JSON_PATH)) {
13239
14058
  try {
13240
- const parsed = JSON.parse(readFileSync20(BRAND_JSON_PATH, "utf-8"));
14059
+ const parsed = JSON.parse(readFileSync21(BRAND_JSON_PATH, "utf-8"));
13241
14060
  BRAND = { ...BRAND, ...parsed };
13242
14061
  } catch (err) {
13243
14062
  console.error(`[brand] Failed to parse brand.json: ${err.message}`);
@@ -13260,7 +14079,7 @@ var ALIAS_DOMAINS_PATH = join15(homedir3(), BRAND.configDir, "alias-domains.json
13260
14079
  function loadAliasDomains() {
13261
14080
  try {
13262
14081
  if (!existsSync24(ALIAS_DOMAINS_PATH)) return null;
13263
- const parsed = JSON.parse(readFileSync20(ALIAS_DOMAINS_PATH, "utf-8"));
14082
+ const parsed = JSON.parse(readFileSync21(ALIAS_DOMAINS_PATH, "utf-8"));
13264
14083
  if (!Array.isArray(parsed)) {
13265
14084
  console.error("[alias-domains] malformed alias-domains.json \u2014 expected array");
13266
14085
  return null;
@@ -13284,9 +14103,9 @@ watchFile(ALIAS_DOMAINS_PATH, { interval: 2e3 }, () => {
13284
14103
  function isPublicHost(host) {
13285
14104
  return host.startsWith("public.") || aliasDomains.has(host);
13286
14105
  }
13287
- var app39 = new Hono();
13288
- app39.use("*", clientIpMiddleware);
13289
- app39.use("*", async (c, next) => {
14106
+ var app44 = new Hono();
14107
+ app44.use("*", clientIpMiddleware);
14108
+ app44.use("*", async (c, next) => {
13290
14109
  await next();
13291
14110
  c.header("X-Content-Type-Options", "nosniff");
13292
14111
  c.header("Referrer-Policy", "strict-origin-when-cross-origin");
@@ -13296,7 +14115,7 @@ app39.use("*", async (c, next) => {
13296
14115
  );
13297
14116
  });
13298
14117
  var HTTP_LOG_PATHS = /* @__PURE__ */ new Set(["/vnc-viewer.html", "/vnc-popout.html"]);
13299
- app39.use("*", async (c, next) => {
14118
+ app44.use("*", async (c, next) => {
13300
14119
  if (!HTTP_LOG_PATHS.has(c.req.path)) {
13301
14120
  await next();
13302
14121
  return;
@@ -13329,7 +14148,7 @@ var PUBLIC_ALLOWED_PREFIXES = [
13329
14148
  "/sites/"
13330
14149
  ];
13331
14150
  var PUBLIC_ALLOWED_EXACT = ["/favicon.ico"];
13332
- app39.use("*", async (c, next) => {
14151
+ app44.use("*", async (c, next) => {
13333
14152
  const host = (c.req.header("host") ?? "").split(":")[0];
13334
14153
  if (!isPublicHost(host)) {
13335
14154
  await next();
@@ -13369,7 +14188,7 @@ function resolveRemoteAuthOpts() {
13369
14188
  return brandLoginOpts;
13370
14189
  }
13371
14190
  var MAX_LOGIN_BODY = 8 * 1024;
13372
- app39.post("/__remote-auth/login", async (c) => {
14191
+ app44.post("/__remote-auth/login", async (c) => {
13373
14192
  const client = clientFrom(c);
13374
14193
  const clientIp = client.ip || "unknown";
13375
14194
  if (!requestIsTlsTerminated(c)) {
@@ -13414,7 +14233,7 @@ app39.post("/__remote-auth/login", async (c) => {
13414
14233
  }
13415
14234
  });
13416
14235
  });
13417
- app39.get("/__remote-auth/logout", (c) => {
14236
+ app44.get("/__remote-auth/logout", (c) => {
13418
14237
  const client = clientFrom(c);
13419
14238
  const clientIp = client.ip || "unknown";
13420
14239
  console.error(`[remote-auth] logout ip=${clientIp}`);
@@ -13427,7 +14246,7 @@ app39.get("/__remote-auth/logout", (c) => {
13427
14246
  }
13428
14247
  });
13429
14248
  });
13430
- app39.post("/__remote-auth/change-password", async (c) => {
14249
+ app44.post("/__remote-auth/change-password", async (c) => {
13431
14250
  const client = clientFrom(c);
13432
14251
  const clientIp = client.ip || "unknown";
13433
14252
  const rateLimited = checkRateLimit(client);
@@ -13478,13 +14297,13 @@ app39.post("/__remote-auth/change-password", async (c) => {
13478
14297
  return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(), mode: "change", changeError: "Failed to save password", redirect }), 200);
13479
14298
  }
13480
14299
  });
13481
- app39.get("/__remote-auth/setup", (c) => {
14300
+ app44.get("/__remote-auth/setup", (c) => {
13482
14301
  if (isRemoteAuthConfigured()) {
13483
14302
  return c.redirect("/");
13484
14303
  }
13485
14304
  return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(), mode: "setup" }), 200);
13486
14305
  });
13487
- app39.post("/__remote-auth/set-initial-password", async (c) => {
14306
+ app44.post("/__remote-auth/set-initial-password", async (c) => {
13488
14307
  if (isRemoteAuthConfigured()) {
13489
14308
  return c.redirect("/");
13490
14309
  }
@@ -13522,10 +14341,10 @@ app39.post("/__remote-auth/set-initial-password", async (c) => {
13522
14341
  return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(), mode: "setup", setupError: "Failed to save password. Please try again." }), 200);
13523
14342
  }
13524
14343
  });
13525
- app39.get("/api/remote-auth/status", (c) => {
14344
+ app44.get("/api/remote-auth/status", (c) => {
13526
14345
  return c.json({ configured: isRemoteAuthConfigured() });
13527
14346
  });
13528
- app39.post("/api/remote-auth/set-password", async (c) => {
14347
+ app44.post("/api/remote-auth/set-password", async (c) => {
13529
14348
  let body;
13530
14349
  try {
13531
14350
  body = await c.req.json();
@@ -13556,9 +14375,9 @@ app39.post("/api/remote-auth/set-password", async (c) => {
13556
14375
  return c.json({ error: "Failed to save password" }, 500);
13557
14376
  }
13558
14377
  });
13559
- app39.route("/api/_client-error", client_error_default);
14378
+ app44.route("/api/_client-error", client_error_default);
13560
14379
  console.log("[client-error-route] mounted");
13561
- app39.use("*", async (c, next) => {
14380
+ app44.use("*", async (c, next) => {
13562
14381
  const host = (c.req.header("host") ?? "").split(":")[0];
13563
14382
  const path2 = c.req.path;
13564
14383
  if (path2 === "/favicon.ico" || path2.startsWith("/assets/") || path2.startsWith("/brand/")) {
@@ -13591,11 +14410,12 @@ app39.use("*", async (c, next) => {
13591
14410
  console.error(`[remote-auth] login required ip=${clientIp} path=${path2} ${disambig}`);
13592
14411
  return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(), redirect: path2 }), 200);
13593
14412
  });
13594
- app39.route("/api/health", health_default);
13595
- app39.route("/api/chat", chat_default);
13596
- app39.route("/api/whatsapp", whatsapp_default);
13597
- app39.route("/api/onboarding", onboarding_default);
13598
- app39.route("/api/admin", admin_default);
14413
+ app44.route("/api/health", health_default);
14414
+ app44.route("/api/chat", chat_default);
14415
+ app44.route("/api/whatsapp", whatsapp_default);
14416
+ app44.route("/api/onboarding", onboarding_default);
14417
+ app44.route("/api/admin", admin_default);
14418
+ app44.route("/api/access", access_default);
13599
14419
  var SAFE_SLUG_RE2 = /^[a-z][a-z0-9-]{2,49}$/;
13600
14420
  var SAFE_FILENAME_RE = /^[a-z0-9_][a-z0-9_.-]{0,99}$/i;
13601
14421
  var IMAGE_MIME = {
@@ -13607,7 +14427,7 @@ var IMAGE_MIME = {
13607
14427
  ".svg": "image/svg+xml",
13608
14428
  ".ico": "image/x-icon"
13609
14429
  };
13610
- app39.get("/agent-assets/:slug/:filename", (c) => {
14430
+ app44.get("/agent-assets/:slug/:filename", (c) => {
13611
14431
  const slug = c.req.param("slug");
13612
14432
  const filename = c.req.param("filename");
13613
14433
  if (!SAFE_SLUG_RE2.test(slug)) {
@@ -13623,8 +14443,8 @@ app39.get("/agent-assets/:slug/:filename", (c) => {
13623
14443
  console.error(`[agent-assets] no-account slug=${slug} file=${filename}`);
13624
14444
  return c.text("Not found", 404);
13625
14445
  }
13626
- const filePath = resolve20(account.accountDir, "agents", slug, "assets", filename);
13627
- const expectedDir = resolve20(account.accountDir, "agents", slug, "assets");
14446
+ const filePath = resolve22(account.accountDir, "agents", slug, "assets", filename);
14447
+ const expectedDir = resolve22(account.accountDir, "agents", slug, "assets");
13628
14448
  if (!filePath.startsWith(expectedDir + "/")) {
13629
14449
  console.error(`[agent-assets] path-traversal-rejected slug=${slug} file=${filename}`);
13630
14450
  return c.text("Forbidden", 403);
@@ -13636,13 +14456,13 @@ app39.get("/agent-assets/:slug/:filename", (c) => {
13636
14456
  const ext = "." + filename.split(".").pop()?.toLowerCase();
13637
14457
  const contentType = IMAGE_MIME[ext] || "application/octet-stream";
13638
14458
  console.log(`[agent-assets] serve slug=${slug} file=${filename} status=200`);
13639
- const body = readFileSync20(filePath);
14459
+ const body = readFileSync21(filePath);
13640
14460
  return c.body(body, 200, {
13641
14461
  "Content-Type": contentType,
13642
14462
  "Cache-Control": "public, max-age=3600"
13643
14463
  });
13644
14464
  });
13645
- app39.get("/generated/:filename", (c) => {
14465
+ app44.get("/generated/:filename", (c) => {
13646
14466
  const filename = c.req.param("filename");
13647
14467
  if (!SAFE_FILENAME_RE.test(filename) || filename.includes("..")) {
13648
14468
  console.error(`[generated] serve file=${filename} status=403`);
@@ -13653,8 +14473,8 @@ app39.get("/generated/:filename", (c) => {
13653
14473
  console.error(`[generated] serve file=${filename} status=404`);
13654
14474
  return c.text("Not found", 404);
13655
14475
  }
13656
- const filePath = resolve20(account.accountDir, "generated", filename);
13657
- const expectedDir = resolve20(account.accountDir, "generated");
14476
+ const filePath = resolve22(account.accountDir, "generated", filename);
14477
+ const expectedDir = resolve22(account.accountDir, "generated");
13658
14478
  if (!filePath.startsWith(expectedDir + "/")) {
13659
14479
  console.error(`[generated] serve file=${filename} status=403`);
13660
14480
  return c.text("Forbidden", 403);
@@ -13666,22 +14486,22 @@ app39.get("/generated/:filename", (c) => {
13666
14486
  const ext = "." + filename.split(".").pop()?.toLowerCase();
13667
14487
  const contentType = IMAGE_MIME[ext] || "application/octet-stream";
13668
14488
  console.log(`[generated] serve file=${filename} status=200`);
13669
- const body = readFileSync20(filePath);
14489
+ const body = readFileSync21(filePath);
13670
14490
  return c.body(body, 200, {
13671
14491
  "Content-Type": contentType,
13672
14492
  "Cache-Control": "public, max-age=86400"
13673
14493
  });
13674
14494
  });
13675
- app39.route("/sites", sites_default);
13676
- app39.route("/listings", listings_default);
13677
- app39.route("/v", visitor_event_default);
13678
- app39.route("/v", visitor_consent_default);
14495
+ app44.route("/sites", sites_default);
14496
+ app44.route("/listings", listings_default);
14497
+ app44.route("/v", visitor_event_default);
14498
+ app44.route("/v", visitor_consent_default);
13679
14499
  var htmlCache = /* @__PURE__ */ new Map();
13680
14500
  var brandLogoPath = "/brand/maxy-monochrome.png";
13681
14501
  var brandIconPath = "/brand/maxy-monochrome.png";
13682
14502
  if (BRAND_JSON_PATH && existsSync24(BRAND_JSON_PATH)) {
13683
14503
  try {
13684
- const fullBrand = JSON.parse(readFileSync20(BRAND_JSON_PATH, "utf-8"));
14504
+ const fullBrand = JSON.parse(readFileSync21(BRAND_JSON_PATH, "utf-8"));
13685
14505
  if (fullBrand.assets?.logo) brandLogoPath = `/brand/${fullBrand.assets.logo}`;
13686
14506
  brandIconPath = fullBrand.assets?.icon ? `/brand/${fullBrand.assets.icon}` : brandLogoPath;
13687
14507
  } catch {
@@ -13697,10 +14517,10 @@ var brandScript = `<script>window.__BRAND__=${JSON.stringify({
13697
14517
  })}</script>`;
13698
14518
  function readInstalledVersion() {
13699
14519
  try {
13700
- if (!PLATFORM_ROOT7) return "unknown";
13701
- const versionFile = join15(PLATFORM_ROOT7, "config", `.${BRAND.hostname}-version`);
14520
+ if (!PLATFORM_ROOT9) return "unknown";
14521
+ const versionFile = join15(PLATFORM_ROOT9, "config", `.${BRAND.hostname}-version`);
13702
14522
  if (!existsSync24(versionFile)) return "unknown";
13703
- const content = readFileSync20(versionFile, "utf-8").trim();
14523
+ const content = readFileSync21(versionFile, "utf-8").trim();
13704
14524
  return content || "unknown";
13705
14525
  } catch {
13706
14526
  return "unknown";
@@ -13741,7 +14561,7 @@ var clientErrorReporterScript = `<script>
13741
14561
  function cachedHtml(file) {
13742
14562
  let html = htmlCache.get(file);
13743
14563
  if (!html) {
13744
- html = readFileSync20(resolve20(process.cwd(), "public", file), "utf-8");
14564
+ html = readFileSync21(resolve22(process.cwd(), "public", file), "utf-8");
13745
14565
  const productNameEsc = escapeHtml(BRAND.productName);
13746
14566
  html = html.replace(/<title>([^<]*)<\/title>/, (_match, inner) => `<title>${escapeHtml(inner).replace(/Maxy/g, productNameEsc)}</title>`);
13747
14567
  html = html.replace('href="/favicon.ico"', `href="${escapeHtml(brandFaviconPath)}"`);
@@ -13761,12 +14581,12 @@ function loadBrandingCache(agentSlug) {
13761
14581
  try {
13762
14582
  const accountJsonPath = join15(configDir2, "account.json");
13763
14583
  if (!existsSync24(accountJsonPath)) return null;
13764
- const account = JSON.parse(readFileSync20(accountJsonPath, "utf-8"));
14584
+ const account = JSON.parse(readFileSync21(accountJsonPath, "utf-8"));
13765
14585
  const accountId = account.accountId;
13766
14586
  if (!accountId) return null;
13767
14587
  const cachePath = join15(configDir2, "branding-cache", accountId, `${agentSlug}.json`);
13768
14588
  if (!existsSync24(cachePath)) return null;
13769
- return JSON.parse(readFileSync20(cachePath, "utf-8"));
14589
+ return JSON.parse(readFileSync21(cachePath, "utf-8"));
13770
14590
  } catch {
13771
14591
  return null;
13772
14592
  }
@@ -13776,7 +14596,7 @@ function resolveDefaultSlug() {
13776
14596
  const configDir2 = join15(homedir3(), BRAND.configDir);
13777
14597
  const accountJsonPath = join15(configDir2, "account.json");
13778
14598
  if (!existsSync24(accountJsonPath)) return null;
13779
- const account = JSON.parse(readFileSync20(accountJsonPath, "utf-8"));
14599
+ const account = JSON.parse(readFileSync21(accountJsonPath, "utf-8"));
13780
14600
  return account.defaultAgent || null;
13781
14601
  } catch {
13782
14602
  return null;
@@ -13812,7 +14632,7 @@ function brandedPublicHtml(agentSlug) {
13812
14632
  function escapeHtml(s) {
13813
14633
  return s.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
13814
14634
  }
13815
- app39.get("/", (c) => {
14635
+ app44.get("/", (c) => {
13816
14636
  const host = (c.req.header("host") ?? "").split(":")[0];
13817
14637
  if (isPublicHost(host)) {
13818
14638
  const defaultSlug = resolveDefaultSlug();
@@ -13820,12 +14640,12 @@ app39.get("/", (c) => {
13820
14640
  }
13821
14641
  return c.html(cachedHtml("index.html"));
13822
14642
  });
13823
- app39.get("/public", (c) => {
14643
+ app44.get("/public", (c) => {
13824
14644
  const host = (c.req.header("host") ?? "").split(":")[0];
13825
14645
  if (isPublicHost(host)) return c.text("Not found", 404);
13826
14646
  return c.html(cachedHtml("public.html"));
13827
14647
  });
13828
- app39.get("/chat", (c) => {
14648
+ app44.get("/chat", (c) => {
13829
14649
  const host = (c.req.header("host") ?? "").split(":")[0];
13830
14650
  if (isPublicHost(host)) return c.text("Not found", 404);
13831
14651
  return c.html(cachedHtml("public.html"));
@@ -13844,12 +14664,12 @@ async function logViewerFetch(c, next) {
13844
14664
  duration_ms: Date.now() - start
13845
14665
  });
13846
14666
  }
13847
- app39.use("/vnc-viewer.html", logViewerFetch);
13848
- app39.use("/vnc-popout.html", logViewerFetch);
13849
- app39.get("/vnc-popout.html", (c) => {
14667
+ app44.use("/vnc-viewer.html", logViewerFetch);
14668
+ app44.use("/vnc-popout.html", logViewerFetch);
14669
+ app44.get("/vnc-popout.html", (c) => {
13850
14670
  let html = htmlCache.get("vnc-popout.html");
13851
14671
  if (!html) {
13852
- html = readFileSync20(resolve20(process.cwd(), "public", "vnc-popout.html"), "utf-8");
14672
+ html = readFileSync21(resolve22(process.cwd(), "public", "vnc-popout.html"), "utf-8");
13853
14673
  const name = escapeHtml(BRAND.productName);
13854
14674
  html = html.replace("<title>Browser \u2014 Maxy</title>", `<title>${name}</title>`);
13855
14675
  html = html.replace("</head>", ` ${brandScript}
@@ -13859,7 +14679,7 @@ app39.get("/vnc-popout.html", (c) => {
13859
14679
  }
13860
14680
  return c.html(html);
13861
14681
  });
13862
- app39.post("/api/vnc/client-event", async (c) => {
14682
+ app44.post("/api/vnc/client-event", async (c) => {
13863
14683
  let body;
13864
14684
  try {
13865
14685
  body = await c.req.json();
@@ -13880,25 +14700,25 @@ app39.post("/api/vnc/client-event", async (c) => {
13880
14700
  });
13881
14701
  return c.json({ ok: true });
13882
14702
  });
13883
- app39.get("/g/:slug", (c) => {
14703
+ app44.get("/g/:slug", (c) => {
13884
14704
  return c.html(brandedPublicHtml());
13885
14705
  });
13886
- app39.get("/graph", (c) => {
14706
+ app44.get("/graph", (c) => {
13887
14707
  const host = (c.req.header("host") ?? "").split(":")[0];
13888
14708
  if (isPublicHost(host)) return c.text("Not found", 404);
13889
14709
  return c.html(cachedHtml("graph.html"));
13890
14710
  });
13891
- app39.get("/sessions", (c) => {
14711
+ app44.get("/sessions", (c) => {
13892
14712
  const host = (c.req.header("host") ?? "").split(":")[0];
13893
14713
  if (isPublicHost(host)) return c.text("Not found", 404);
13894
14714
  return c.html(cachedHtml("sessions.html"));
13895
14715
  });
13896
- app39.get("/data", (c) => {
14716
+ app44.get("/data", (c) => {
13897
14717
  const host = (c.req.header("host") ?? "").split(":")[0];
13898
14718
  if (isPublicHost(host)) return c.text("Not found", 404);
13899
14719
  return c.html(cachedHtml("data.html"));
13900
14720
  });
13901
- app39.get("/:slug", async (c, next) => {
14721
+ app44.get("/:slug", async (c, next) => {
13902
14722
  const slug = c.req.param("slug");
13903
14723
  if (AGENT_SLUG_PATTERN.test(`/${slug}`)) {
13904
14724
  const branding = loadBrandingCache(slug);
@@ -13908,15 +14728,15 @@ app39.get("/:slug", async (c, next) => {
13908
14728
  await next();
13909
14729
  });
13910
14730
  if (brandFaviconPath !== "/favicon.ico") {
13911
- app39.get("/favicon.ico", (c) => {
14731
+ app44.get("/favicon.ico", (c) => {
13912
14732
  c.header("Cache-Control", "public, max-age=300");
13913
14733
  return c.redirect(brandFaviconPath, 302);
13914
14734
  });
13915
14735
  }
13916
- app39.use("/*", serveStatic({ root: "./public" }));
14736
+ app44.use("/*", serveStatic({ root: "./public" }));
13917
14737
  var port = requirePortEnv("MAXY_UI_INTERNAL_PORT", { tag: "ui-server" });
13918
14738
  var hostname = process.env.HOSTNAME ?? "127.0.0.1";
13919
- var httpServer = serve({ fetch: app39.fetch, port, hostname });
14739
+ var httpServer = serve({ fetch: app44.fetch, port, hostname });
13920
14740
  console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
13921
14741
  {
13922
14742
  const loopHist = monitorEventLoopDelay({ resolution: 50 });
@@ -13940,6 +14760,7 @@ var SUBAPP_MANIFEST = [
13940
14760
  { prefix: "/api/whatsapp", file: "server/routes/whatsapp.ts", subapp: whatsapp_default },
13941
14761
  { prefix: "/api/onboarding", file: "server/routes/onboarding.ts", subapp: onboarding_default },
13942
14762
  { prefix: "/api/admin", file: "server/routes/admin/index.ts", subapp: admin_default },
14763
+ { prefix: "/api/access", file: "server/routes/access/index.ts", subapp: access_default },
13943
14764
  { prefix: "/api/_client-error", file: "server/routes/client-error.ts", subapp: client_error_default },
13944
14765
  { prefix: "/listings", file: "server/routes/listings.ts", subapp: listings_default },
13945
14766
  { prefix: "/v", file: "server/routes/visitor-event.ts", subapp: visitor_event_default },
@@ -13951,7 +14772,7 @@ for (const m of SUBAPP_MANIFEST) {
13951
14772
  }
13952
14773
  try {
13953
14774
  const registered = [];
13954
- for (const r of app39.routes ?? []) {
14775
+ for (const r of app44.routes ?? []) {
13955
14776
  if (typeof r.path !== "string" || r.path.includes(":") || r.path.includes("*")) continue;
13956
14777
  if (AGENT_SLUG_PATTERN.test(r.path)) {
13957
14778
  registered.push({ method: (r.method ?? "ALL").toUpperCase(), path: r.path });
@@ -13974,7 +14795,7 @@ try {
13974
14795
  (async () => {
13975
14796
  try {
13976
14797
  if (!existsSync24(USERS_FILE)) return;
13977
- const usersRaw = readFileSync20(USERS_FILE, "utf-8").trim();
14798
+ const usersRaw = readFileSync21(USERS_FILE, "utf-8").trim();
13978
14799
  if (!usersRaw) return;
13979
14800
  const users = JSON.parse(usersRaw);
13980
14801
  const userId = users[0]?.userId;
@@ -14066,7 +14887,7 @@ if (bootAccountConfig?.whatsapp) {
14066
14887
  }
14067
14888
  init({
14068
14889
  configDir: configDirForWhatsApp,
14069
- platformRoot: resolve20(process.env.MAXY_PLATFORM_ROOT ?? join15(__dirname, "..")),
14890
+ platformRoot: resolve22(process.env.MAXY_PLATFORM_ROOT ?? join15(__dirname, "..")),
14070
14891
  accountConfig: bootAccountConfig,
14071
14892
  onMessage: async (msg) => {
14072
14893
  if (process.env.WHATSAPP_PTY_BRIDGE_ENABLED === "true" && msg.text && !msg.isOwnerMirror) {