@rubytech/create-maxy-code 0.1.342 → 0.1.343

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 (188) hide show
  1. package/package.json +1 -1
  2. package/payload/platform/config/brand.json +4 -1
  3. package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +88 -1
  4. package/payload/platform/plugins/cloudflare/skills/calendar-site/SKILL.md +82 -0
  5. package/payload/platform/plugins/cloudflare/skills/calendar-site/template/functions/api/book.ts +112 -0
  6. package/payload/platform/plugins/cloudflare/skills/calendar-site/template/public/booking.css +100 -0
  7. package/payload/platform/plugins/cloudflare/skills/calendar-site/template/public/booking.js +155 -0
  8. package/payload/platform/plugins/cloudflare/skills/calendar-site/template/public/index.html +47 -0
  9. package/payload/platform/plugins/cloudflare/skills/calendar-site/template/schema.sql +18 -0
  10. package/payload/platform/plugins/cloudflare/skills/calendar-site/template/wrangler.toml +14 -0
  11. package/payload/platform/plugins/docs/PLUGIN.md +1 -0
  12. package/payload/platform/plugins/docs/references/admin-ui.md +26 -0
  13. package/payload/platform/plugins/docs/references/calendar-booking.md +56 -0
  14. package/payload/platform/plugins/scheduling/PLUGIN.md +10 -0
  15. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/booking-notify.test.d.ts +2 -0
  16. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/booking-notify.test.d.ts.map +1 -0
  17. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/booking-notify.test.js +45 -0
  18. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/booking-notify.test.js.map +1 -0
  19. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/booking-reconcile.test.d.ts +2 -0
  20. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/booking-reconcile.test.d.ts.map +1 -0
  21. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/booking-reconcile.test.js +89 -0
  22. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/booking-reconcile.test.js.map +1 -0
  23. package/payload/platform/plugins/scheduling/mcp/dist/lib/booking-notify.d.ts +22 -0
  24. package/payload/platform/plugins/scheduling/mcp/dist/lib/booking-notify.d.ts.map +1 -0
  25. package/payload/platform/plugins/scheduling/mcp/dist/lib/booking-notify.js +31 -0
  26. package/payload/platform/plugins/scheduling/mcp/dist/lib/booking-notify.js.map +1 -0
  27. package/payload/platform/plugins/scheduling/mcp/dist/lib/booking-reconcile.d.ts +28 -0
  28. package/payload/platform/plugins/scheduling/mcp/dist/lib/booking-reconcile.d.ts.map +1 -0
  29. package/payload/platform/plugins/scheduling/mcp/dist/lib/booking-reconcile.js +70 -0
  30. package/payload/platform/plugins/scheduling/mcp/dist/lib/booking-reconcile.js.map +1 -0
  31. package/payload/platform/plugins/scheduling/mcp/dist/scripts/reconcile-bookings.d.ts +21 -0
  32. package/payload/platform/plugins/scheduling/mcp/dist/scripts/reconcile-bookings.d.ts.map +1 -0
  33. package/payload/platform/plugins/scheduling/mcp/dist/scripts/reconcile-bookings.js +280 -0
  34. package/payload/platform/plugins/scheduling/mcp/dist/scripts/reconcile-bookings.js.map +1 -0
  35. package/payload/server/{chunk-5BG6CHGH.js → chunk-QAKVFSEJ.js} +15 -0
  36. package/payload/server/maxy-edge.js +1 -1
  37. package/payload/server/public/assets/AdminLoginScreens-KVvngpT2.js +1 -0
  38. package/payload/server/public/assets/AdminShell-DDKZhJRo.js +1 -0
  39. package/payload/server/public/assets/Checkbox-BJlS0tR2.js +1 -0
  40. package/payload/server/public/assets/OperatorConversations-_P3_KmAW.js +1 -0
  41. package/payload/server/public/assets/admin-BTQWgciK.js +1 -0
  42. package/payload/server/public/assets/{OperatorConversations-BMIZQR9t.css → admin-auth-fetch-9JCQ2FFm.css} +1 -1
  43. package/payload/server/public/assets/admin-auth-fetch-BMOxrEnl.js +9 -0
  44. package/payload/server/public/assets/{arc-CxLB9WCE.js → arc-Crkz45LN.js} +1 -1
  45. package/payload/server/public/assets/architecture-YZFGNWBL-Dma4Bgha.js +1 -0
  46. package/payload/server/public/assets/{architectureDiagram-Q4EWVU46-D-EdJPSY.js → architectureDiagram-Q4EWVU46-C9JThHgP.js} +1 -1
  47. package/payload/server/public/assets/{blockDiagram-DXYQGD6D-CXx9RgCm.js → blockDiagram-DXYQGD6D-DpsV_HXT.js} +1 -1
  48. package/payload/server/public/assets/browser-CpOL9dN0.js +1 -0
  49. package/payload/server/public/assets/{c4Diagram-AHTNJAMY-RFBXEe0B.js → c4Diagram-AHTNJAMY-CTm_6aKi.js} +1 -1
  50. package/payload/server/public/assets/calendar-dRLyhghx.js +1 -0
  51. package/payload/server/public/assets/channel-B_-5bau1.js +1 -0
  52. package/payload/server/public/assets/chat-prZCVWvK.js +1 -0
  53. package/payload/server/public/assets/{chunk-2KRD3SAO-DBqAFwK9.js → chunk-2KRD3SAO-1KSJW1q6.js} +1 -1
  54. package/payload/server/public/assets/{chunk-336JU56O-DdnFEz6R.js → chunk-336JU56O-C3t08Ip-.js} +2 -2
  55. package/payload/server/public/assets/chunk-426QAEUC-Dirxdwjh.js +1 -0
  56. package/payload/server/public/assets/{chunk-4BX2VUAB-cnw-3Gbs.js → chunk-4BX2VUAB-kl1ze33n.js} +1 -1
  57. package/payload/server/public/assets/{chunk-4TB4RGXK-BnqJ0bDc.js → chunk-4TB4RGXK-DxpoDoHg.js} +1 -1
  58. package/payload/server/public/assets/{chunk-55IACEB6-BwyHvvzo.js → chunk-55IACEB6-WM9VJzha.js} +1 -1
  59. package/payload/server/public/assets/{chunk-5FUZZQ4R-DXA9U02I.js → chunk-5FUZZQ4R-CLH63ZH3.js} +1 -1
  60. package/payload/server/public/assets/{chunk-5PVQY5BW-D78bZ_8D.js → chunk-5PVQY5BW-DCTb8IAT.js} +1 -1
  61. package/payload/server/public/assets/{chunk-67CJDMHE-CtE_Mgv7.js → chunk-67CJDMHE-BSYzyaoH.js} +1 -1
  62. package/payload/server/public/assets/{chunk-7N4EOEYR-CUZDaySl.js → chunk-7N4EOEYR-CarJ7O2P.js} +1 -1
  63. package/payload/server/public/assets/{chunk-AA7GKIK3-CYQ3jO4X.js → chunk-AA7GKIK3-DTNLyBpH.js} +1 -1
  64. package/payload/server/public/assets/{chunk-BSJP7CBP-BbA_HyTR.js → chunk-BSJP7CBP-BFmHflHY.js} +1 -1
  65. package/payload/server/public/assets/{chunk-CIAEETIT-D7_YqRhG.js → chunk-CIAEETIT-BpjG0IzU.js} +1 -1
  66. package/payload/server/public/assets/{chunk-EDXVE4YY-B23LrtSw.js → chunk-EDXVE4YY-2fSowhlW.js} +1 -1
  67. package/payload/server/public/assets/{chunk-ENJZ2VHE-D3fTHYKN.js → chunk-ENJZ2VHE-ClcranGV.js} +1 -1
  68. package/payload/server/public/assets/{chunk-FMBD7UC4-DFAA2tyh.js → chunk-FMBD7UC4-DLq4BItQ.js} +1 -1
  69. package/payload/server/public/assets/{chunk-FOC6F5B3-BF4IHvlI.js → chunk-FOC6F5B3-B9QI3Guz.js} +1 -1
  70. package/payload/server/public/assets/{chunk-ICPOFSXX-DBUGemHy.js → chunk-ICPOFSXX-BAr-Mv1Z.js} +2 -2
  71. package/payload/server/public/assets/{chunk-K5T4RW27-rMMs08Eh.js → chunk-K5T4RW27-8PvAHasY.js} +1 -1
  72. package/payload/server/public/assets/{chunk-KGLVRYIC-BD2r8R2G.js → chunk-KGLVRYIC--aGaDRH5.js} +1 -1
  73. package/payload/server/public/assets/{chunk-LIHQZDEY-D4sYTTwM.js → chunk-LIHQZDEY-BrWj7mw2.js} +1 -1
  74. package/payload/server/public/assets/{chunk-ORNJ4GCN-CAs_uFzb.js → chunk-ORNJ4GCN-kYQmkMTi.js} +1 -1
  75. package/payload/server/public/assets/{chunk-OYMX7WX6-Fkp-2ORc.js → chunk-OYMX7WX6-CIsryCWH.js} +1 -1
  76. package/payload/server/public/assets/chunk-QZHKN3VN-Duipo1kI.js +1 -0
  77. package/payload/server/public/assets/{chunk-U2HBQHQK-D_pxZh25.js → chunk-U2HBQHQK-68X653e3.js} +1 -1
  78. package/payload/server/public/assets/{chunk-X2U36JSP-CeVmvvRf.js → chunk-X2U36JSP-v3v9soX3.js} +1 -1
  79. package/payload/server/public/assets/{chunk-XPW4576I-iWP54fKp.js → chunk-XPW4576I-CW5DPpop.js} +1 -1
  80. package/payload/server/public/assets/{chunk-YZCP3GAM-CNtbWJNN.js → chunk-YZCP3GAM-B1VP7Rl2.js} +1 -1
  81. package/payload/server/public/assets/{chunk-ZZ45TVLE-BmlFjU_9.js → chunk-ZZ45TVLE-DiJrujNY.js} +1 -1
  82. package/payload/server/public/assets/classDiagram-6PBFFD2Q-B3kX5SuB.js +1 -0
  83. package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-D761c7Lr.js +1 -0
  84. package/payload/server/public/assets/clone-BrhFv6Tb.js +1 -0
  85. package/payload/server/public/assets/{cose-bilkent-S5V4N54A-C7YmF1Pg.js → cose-bilkent-S5V4N54A-BscXxLMc.js} +1 -1
  86. package/payload/server/public/assets/{dagre-KV5264BT-Ca3ESJhc.js → dagre-KV5264BT-DVgGlTQ1.js} +1 -1
  87. package/payload/server/public/assets/{dagre-BAmiDDtR.js → dagre-MBv6WlCS.js} +1 -1
  88. package/payload/server/public/assets/data-CeUT45cU.js +1 -0
  89. package/payload/server/public/assets/{diagram-5BDNPKRD-BgT1ORf2.js → diagram-5BDNPKRD-BmhCaO0a.js} +1 -1
  90. package/payload/server/public/assets/{diagram-G4DWMVQ6-BTaUIiem.js → diagram-G4DWMVQ6-ChU9L8vz.js} +1 -1
  91. package/payload/server/public/assets/{diagram-MMDJMWI5-Bdb2g6Uo.js → diagram-MMDJMWI5-Oy409Zc_.js} +1 -1
  92. package/payload/server/public/assets/{diagram-TYMM5635-CkLGC0zZ.js → diagram-TYMM5635-Cn1YByqk.js} +1 -1
  93. package/payload/server/public/assets/{dist-Bkbhs3jw.js → dist-B0CkUodR.js} +1 -1
  94. package/payload/server/public/assets/{erDiagram-SMLLAGMA-D_sj4DMg.js → erDiagram-SMLLAGMA-e17yfLvr.js} +1 -1
  95. package/payload/server/public/assets/{flatten-E2Sr0FeD.js → flatten-Cl1Bc3dR.js} +1 -1
  96. package/payload/server/public/assets/{flowDiagram-DWJPFMVM-iLERoZTe.js → flowDiagram-DWJPFMVM-Dqt7sCVZ.js} +1 -1
  97. package/payload/server/public/assets/{ganttDiagram-T4ZO3ILL-D60hjMBy.js → ganttDiagram-T4ZO3ILL-B4IhwS2r.js} +1 -1
  98. package/payload/server/public/assets/gitGraph-7Q5UKJZL-BCQAqtLy.js +1 -0
  99. package/payload/server/public/assets/{gitGraphDiagram-UUTBAWPF-Di0XM6fi.js → gitGraphDiagram-UUTBAWPF-BDw-PKcI.js} +1 -1
  100. package/payload/server/public/assets/{graph-Bnsvbnkf.js → graph-B8h8YAJu.js} +3 -3
  101. package/payload/server/public/assets/graph-labels-BFrQ5kyH.js +1 -0
  102. package/payload/server/public/assets/{graphlib-C8fUP7uV.js → graphlib-DPIYk_sg.js} +1 -1
  103. package/payload/server/public/assets/info-OMHHGYJF-qU7hK50T.js +1 -0
  104. package/payload/server/public/assets/infoDiagram-42DDH7IO-KGdcJeTs.js +2 -0
  105. package/payload/server/public/assets/{isEmpty-CLz-UprQ.js → isEmpty-D1pGOD1J.js} +1 -1
  106. package/payload/server/public/assets/{ishikawaDiagram-UXIWVN3A-CwgI4B5Z.js → ishikawaDiagram-UXIWVN3A-BRIHsB3G.js} +1 -1
  107. package/payload/server/public/assets/{journeyDiagram-VCZTEJTY-CxBu-tze.js → journeyDiagram-VCZTEJTY-aKNgmoHd.js} +1 -1
  108. package/payload/server/public/assets/{kanban-definition-6JOO6SKY-D2qlheA5.js → kanban-definition-6JOO6SKY-cwBwT-nm.js} +1 -1
  109. package/payload/server/public/assets/{line-Bdod7uzQ.js → line-DVtVRzUA.js} +1 -1
  110. package/payload/server/public/assets/{linear-BiVe6QzC.js → linear-DtoOFPO2.js} +1 -1
  111. package/payload/server/public/assets/{mermaid-parser.core-xZN7hC6M.js → mermaid-parser.core-G7YD_zO3.js} +2 -2
  112. package/payload/server/public/assets/{mermaid.core-D0IylAac.js → mermaid.core-DpFyndHm.js} +3 -3
  113. package/payload/server/public/assets/{mindmap-definition-QFDTVHPH-J_Szhwf1.js → mindmap-definition-QFDTVHPH-CYXK_Pvz.js} +1 -1
  114. package/payload/server/public/assets/operator-D3fRwMfE.js +1 -0
  115. package/payload/server/public/assets/{ordinal-5RtyPJVt.js → ordinal-CBZeH4Yp.js} +1 -1
  116. package/payload/server/public/assets/packet-4T2RLAQJ-DiU2yS9m.js +1 -0
  117. package/payload/server/public/assets/page-D0hQWIIV.js +30 -0
  118. package/payload/server/public/assets/pie-ZZUOXDRM-zHUKKlVv.js +1 -0
  119. package/payload/server/public/assets/{pieDiagram-DEJITSTG-CV5DxUVx.js → pieDiagram-DEJITSTG-DFo4N6WH.js} +1 -1
  120. package/payload/server/public/assets/public-DX4z0sCJ.js +1 -0
  121. package/payload/server/public/assets/{quadrantDiagram-34T5L4WZ-BsFMVizF.js → quadrantDiagram-34T5L4WZ-BlH5iIhl.js} +1 -1
  122. package/payload/server/public/assets/radar-PYXPWWZC-BUnVoaPg.js +1 -0
  123. package/payload/server/public/assets/{reduce-B3Mw_0s1.js → reduce-PuPlFPRX.js} +1 -1
  124. package/payload/server/public/assets/{requirementDiagram-MS252O5E-C5eRRvGc.js → requirementDiagram-MS252O5E-D5WLty8A.js} +1 -1
  125. package/payload/server/public/assets/{sankeyDiagram-XADWPNL6-CJYv5gpw.js → sankeyDiagram-XADWPNL6-vB85IxHG.js} +1 -1
  126. package/payload/server/public/assets/{sequenceDiagram-FGHM5R23-B25563_2.js → sequenceDiagram-FGHM5R23-Cxraa0Ee.js} +1 -1
  127. package/payload/server/public/assets/{src-BKOAoFQc.js → src-CR_MU8uy.js} +1 -1
  128. package/payload/server/public/assets/{stateDiagram-FHFEXIEX-BLyNJYOA.js → stateDiagram-FHFEXIEX-BBPssYBx.js} +1 -1
  129. package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-BWmmUwDt.js +1 -0
  130. package/payload/server/public/assets/{timeline-definition-GMOUNBTQ-D-HgOM6Q.js → timeline-definition-GMOUNBTQ-Bgk6PT62.js} +1 -1
  131. package/payload/server/public/assets/treeView-SZITEDCU-C3Y-Q52h.js +1 -0
  132. package/payload/server/public/assets/treemap-W4RFUUIX-Dk0qZpAg.js +1 -0
  133. package/payload/server/public/assets/{vennDiagram-DHZGUBPP-Cw1JElYp.js → vennDiagram-DHZGUBPP-B-APTs3L.js} +1 -1
  134. package/payload/server/public/assets/wardley-RL74JXVD-BAWI02OA.js +1 -0
  135. package/payload/server/public/assets/{wardleyDiagram-NUSXRM2D-BqMWzEko.js → wardleyDiagram-NUSXRM2D-CkXJSs_i.js} +1 -1
  136. package/payload/server/public/assets/{xychartDiagram-5P7HB3ND-DBp71TLT.js → xychartDiagram-5P7HB3ND-BkyshPMV.js} +1 -1
  137. package/payload/server/public/brand/maxy-app-icon-192.png +0 -0
  138. package/payload/server/public/brand/maxy-app-icon-512.png +0 -0
  139. package/payload/server/public/brand/maxy-app-icon-maskable-512.png +0 -0
  140. package/payload/server/public/browser.html +6 -5
  141. package/payload/server/public/calendar.html +16 -0
  142. package/payload/server/public/chat.html +9 -8
  143. package/payload/server/public/data.html +6 -5
  144. package/payload/server/public/graph.html +8 -7
  145. package/payload/server/public/index.html +9 -8
  146. package/payload/server/public/operator.html +11 -10
  147. package/payload/server/public/public.html +9 -8
  148. package/payload/server/server.js +598 -190
  149. package/payload/server/public/assets/AdminLoginScreens-Brx8CmXN.js +0 -1
  150. package/payload/server/public/assets/AdminShell-CHZMDX2u.js +0 -1
  151. package/payload/server/public/assets/Checkbox-aePjWzRH.js +0 -1
  152. package/payload/server/public/assets/OperatorConversations-DpjPPIOp.js +0 -9
  153. package/payload/server/public/assets/admin-DIDvfti6.js +0 -1
  154. package/payload/server/public/assets/architecture-YZFGNWBL-DN-MYxff.js +0 -1
  155. package/payload/server/public/assets/browser-Bp5kGgyr.js +0 -1
  156. package/payload/server/public/assets/channel-y27xl53N.js +0 -1
  157. package/payload/server/public/assets/chat-C0IWx7FL.js +0 -1
  158. package/payload/server/public/assets/chunk-426QAEUC-D8Iwaegm.js +0 -1
  159. package/payload/server/public/assets/chunk-QZHKN3VN-qHqDpwz3.js +0 -1
  160. package/payload/server/public/assets/classDiagram-6PBFFD2Q-DgVoYWhO.js +0 -1
  161. package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-C0WNP3uN.js +0 -1
  162. package/payload/server/public/assets/clone-D50F_pvg.js +0 -1
  163. package/payload/server/public/assets/data-RsMye_06.js +0 -1
  164. package/payload/server/public/assets/gitGraph-7Q5UKJZL-D-L2yzYf.js +0 -1
  165. package/payload/server/public/assets/graph-labels-jduMtwXb.js +0 -1
  166. package/payload/server/public/assets/info-OMHHGYJF-BQn6jndO.js +0 -1
  167. package/payload/server/public/assets/infoDiagram-42DDH7IO-BwMdbiXg.js +0 -2
  168. package/payload/server/public/assets/operator-9K-TElDd.js +0 -1
  169. package/payload/server/public/assets/packet-4T2RLAQJ-DmkQBx4h.js +0 -1
  170. package/payload/server/public/assets/page-BT9hkXHm.js +0 -30
  171. package/payload/server/public/assets/pie-ZZUOXDRM-DDnxYUBm.js +0 -1
  172. package/payload/server/public/assets/public-DvL1Zov1.js +0 -1
  173. package/payload/server/public/assets/radar-PYXPWWZC-7H4k0ChM.js +0 -1
  174. package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-BSVuxZxz.js +0 -1
  175. package/payload/server/public/assets/treeView-SZITEDCU-BLXWTQtW.js +0 -1
  176. package/payload/server/public/assets/treemap-W4RFUUIX-DiePDF5d.js +0 -1
  177. package/payload/server/public/assets/wardley-RL74JXVD-OSUWK5LN.js +0 -1
  178. /package/payload/server/public/assets/{_baseFor-DVaNaTfF.js → _baseFor-brRjpUOX.js} +0 -0
  179. /package/payload/server/public/assets/{admin-types-CJrGd46U.js → admin-types-Dg11L4MQ.js} +0 -0
  180. /package/payload/server/public/assets/{array-8_H1156C.js → array-BGFCBI0e.js} +0 -0
  181. /package/payload/server/public/assets/{chunk-Pqm5yXtL.js → chunk-CAM3fms7.js} +0 -0
  182. /package/payload/server/public/assets/{cytoscape.esm-h10p_6j-.js → cytoscape.esm-TvRJ2tGX.js} +0 -0
  183. /package/payload/server/public/assets/{defaultLocale-pD7tFa1r.js → defaultLocale-CRZydyG6.js} +0 -0
  184. /package/payload/server/public/assets/{init-CwJ4b81e.js → init-B8gtcn7T.js} +0 -0
  185. /package/payload/server/public/assets/{katex-DJPjIBAI.js → katex-RxgSu_dy.js} +0 -0
  186. /package/payload/server/public/assets/{path-CK8wrAxY.js → path-DZF-JdEe.js} +0 -0
  187. /package/payload/server/public/assets/{preload-helper-Bf_JiD2A.js → preload-helper-CQ36nxok.js} +0 -0
  188. /package/payload/server/public/assets/{rough.esm-Dwml_la6.js → rough.esm-6CnTHTkH.js} +0 -0
@@ -105,7 +105,7 @@ import {
105
105
  vncLog,
106
106
  walkPremiumBundles,
107
107
  writeAdminUserAndPerson
108
- } from "./chunk-5BG6CHGH.js";
108
+ } from "./chunk-QAKVFSEJ.js";
109
109
  import {
110
110
  __commonJS,
111
111
  __toESM
@@ -1292,8 +1292,9 @@ var serveStatic = (options = { root: "" }) => {
1292
1292
  };
1293
1293
 
1294
1294
  // server/index.ts
1295
- import { readFileSync as readFileSync32, existsSync as existsSync31, watchFile } from "fs";
1296
- import { resolve as resolve33, join as join29, basename as basename10 } from "path";
1295
+ import { readFileSync as readFileSync33, existsSync as existsSync31, watchFile } from "fs";
1296
+ import { spawn as spawn3 } from "child_process";
1297
+ import { resolve as resolve33, join as join30, basename as basename10 } from "path";
1297
1298
  import { homedir as homedir3 } from "os";
1298
1299
  import { monitorEventLoopDelay } from "perf_hooks";
1299
1300
 
@@ -1343,7 +1344,15 @@ function buildManifest(s, b) {
1343
1344
  display: "standalone",
1344
1345
  theme_color: b.themeColor,
1345
1346
  background_color: b.backgroundColor,
1346
- icons: [{ src: b.iconPath, sizes: "any", purpose: "any" }]
1347
+ // Explicit 192/512 `any` icons (iOS/macOS composite transparency onto black,
1348
+ // so these are opaque) plus a wider-safe-zone `maskable` entry for Android's
1349
+ // circular crop. background_color sets only the splash; the tile background
1350
+ // is baked into the PNGs.
1351
+ icons: [
1352
+ { src: b.appIcon192, sizes: "192x192", purpose: "any" },
1353
+ { src: b.appIcon512, sizes: "512x512", purpose: "any" },
1354
+ { src: b.maskableIcon, sizes: "512x512", purpose: "maskable" }
1355
+ ]
1347
1356
  };
1348
1357
  }
1349
1358
  function escAttr(s) {
@@ -1358,18 +1367,20 @@ function swRegisterScript(s) {
1358
1367
  "</script>"
1359
1368
  ].join("\n");
1360
1369
  }
1361
- function pwaHeadTags(s, b) {
1370
+ function installHeadLines(manifestPath, b) {
1362
1371
  return [
1363
- `<link rel="manifest" href="${escAttr(s.manifestPath)}">`,
1364
- `<link rel="apple-touch-icon" href="${escAttr(b.iconPath)}">`,
1372
+ `<link rel="manifest" href="${escAttr(manifestPath)}">`,
1373
+ `<link rel="apple-touch-icon" href="${escAttr(b.appleTouchIcon)}">`,
1365
1374
  // Both the standards-based and the legacy Apple capable meta: Chromium
1366
1375
  // deprecates `apple-mobile-web-app-capable` in favour of
1367
1376
  // `mobile-web-app-capable`, while iOS Safari still reads the apple one.
1368
1377
  '<meta name="mobile-web-app-capable" content="yes">',
1369
1378
  '<meta name="apple-mobile-web-app-capable" content="yes">',
1370
- `<meta name="theme-color" content="${escAttr(b.themeColor)}">`,
1371
- swRegisterScript(s)
1372
- ].join("\n");
1379
+ `<meta name="theme-color" content="${escAttr(b.themeColor)}">`
1380
+ ];
1381
+ }
1382
+ function pwaHeadTags(s, b) {
1383
+ return [...installHeadLines(s.manifestPath, b), swRegisterScript(s)].join("\n");
1373
1384
  }
1374
1385
  function pwaCensus(d) {
1375
1386
  const manifestLinked = PWA_SURFACES.filter((s) => {
@@ -5328,8 +5339,8 @@ function webchatTurnTimeoutMs() {
5328
5339
  return Number(process.env.WEBCHAT_TURN_TIMEOUT_MS ?? String(2 * 6e4));
5329
5340
  }
5330
5341
  function createChatRoutes(deps) {
5331
- const app53 = new Hono();
5332
- app53.post("/", async (c) => {
5342
+ const app55 = new Hono();
5343
+ app55.post("/", async (c) => {
5333
5344
  console.log(`[chat-route] entered route=public method=POST`);
5334
5345
  const contentType = c.req.header("content-type") ?? "";
5335
5346
  const account = resolveAccount();
@@ -5592,7 +5603,7 @@ ${result.result.text}` : result.result.text;
5592
5603
  }
5593
5604
  });
5594
5605
  });
5595
- return app53;
5606
+ return app55;
5596
5607
  }
5597
5608
 
5598
5609
  // server/routes/whatsapp.ts
@@ -8634,8 +8645,8 @@ async function reapplyLeversViaManager() {
8634
8645
  var WEBCHAT_SEND_TURN_WINDOW_MS = Number(process.env.WEBCHAT_SEND_TURN_WINDOW_MS ?? String(15e3));
8635
8646
  var sendSeq = 0;
8636
8647
  function createWebchatRoutes(deps) {
8637
- const app53 = new Hono();
8638
- app53.post("/send", requireAdminSession, async (c) => {
8648
+ const app55 = new Hono();
8649
+ app55.post("/send", requireAdminSession, async (c) => {
8639
8650
  let accountId;
8640
8651
  try {
8641
8652
  accountId = resolvePlatformAccountId();
@@ -8805,7 +8816,7 @@ ${note}` : note;
8805
8816
  if (turnTimer.unref) turnTimer.unref();
8806
8817
  return c.json({ ok: true });
8807
8818
  });
8808
- app53.post("/settings", requireAdminSession, async (c) => {
8819
+ app55.post("/settings", requireAdminSession, async (c) => {
8809
8820
  const body = await c.req.json().catch(() => null);
8810
8821
  const op = body?.op;
8811
8822
  const value = body?.value;
@@ -8845,7 +8856,7 @@ ${note}` : note;
8845
8856
  console.log(`[webchat:settings] op=${op} outcome=accepted value=${value} settingsApplied=${settingsApplied}`);
8846
8857
  return c.json({ ok: true, settingsApplied });
8847
8858
  });
8848
- app53.get("/session", requireAdminSession, async (c) => {
8859
+ app55.get("/session", requireAdminSession, async (c) => {
8849
8860
  let accountId;
8850
8861
  try {
8851
8862
  accountId = resolvePlatformAccountId();
@@ -8921,7 +8932,7 @@ ${note}` : note;
8921
8932
  const indicators = await fetchComposerIndicators(sessionId);
8922
8933
  return c.json({ sessionId, projectDir, sizeBytes: jsonlSizeBytes(projectDir, sessionId), pendingPermissionPrompt: deps.pendingPromptFor?.(`session:${sessionId}`) ?? null, deliveryFailure: deps.deliveryFailureFor?.(`session:${sessionId}`) ?? null, ...indicators, ...readLevers(account) });
8923
8934
  });
8924
- app53.post("/permission-verdict", requireAdminSession, async (c) => {
8935
+ app55.post("/permission-verdict", requireAdminSession, async (c) => {
8925
8936
  const body = await c.req.json().catch(() => null);
8926
8937
  const sessionId = body?.sessionId;
8927
8938
  const requestId = body?.request_id;
@@ -8938,7 +8949,7 @@ ${note}` : note;
8938
8949
  const ok = deps.resolvePermissionVerdict?.(`session:${sessionId}`, requestId, behavior) ?? false;
8939
8950
  return c.json({ ok });
8940
8951
  });
8941
- return app53;
8952
+ return app55;
8942
8953
  }
8943
8954
 
8944
8955
  // server/routes/webchat-greeting.ts
@@ -13449,6 +13460,89 @@ async function dropFileIndex(accountId, relativePath, opts) {
13449
13460
  });
13450
13461
  }
13451
13462
 
13463
+ // server/lib/zip.ts
13464
+ import { deflateRawSync } from "zlib";
13465
+ var LOCAL_SIG = 67324752;
13466
+ var CENTRAL_SIG = 33639248;
13467
+ var EOCD_SIG = 101010256;
13468
+ var VERSION = 20;
13469
+ var FLAG_UTF8 = 2048;
13470
+ var crcTable = null;
13471
+ function crc32(buf) {
13472
+ if (!crcTable) {
13473
+ crcTable = new Uint32Array(256);
13474
+ for (let n = 0; n < 256; n++) {
13475
+ let c = n;
13476
+ for (let k = 0; k < 8; k++) c = c & 1 ? 3988292384 ^ c >>> 1 : c >>> 1;
13477
+ crcTable[n] = c >>> 0;
13478
+ }
13479
+ }
13480
+ let crc = 4294967295;
13481
+ for (let i = 0; i < buf.length; i++) crc = crcTable[(crc ^ buf[i]) & 255] ^ crc >>> 8;
13482
+ return (crc ^ 4294967295) >>> 0;
13483
+ }
13484
+ function buildZip(entries) {
13485
+ const localChunks = [];
13486
+ const centralChunks = [];
13487
+ let offset = 0;
13488
+ for (const entry of entries) {
13489
+ const nameBytes = Buffer.from(entry.name, "utf8");
13490
+ const crc = crc32(entry.data);
13491
+ const uncompressedSize = entry.data.length;
13492
+ const deflated = deflateRawSync(entry.data);
13493
+ const useDeflate = deflated.length < uncompressedSize;
13494
+ const method = useDeflate ? 8 : 0;
13495
+ const payload = useDeflate ? deflated : entry.data;
13496
+ const compressedSize = payload.length;
13497
+ const localHeader = Buffer.alloc(30);
13498
+ localHeader.writeUInt32LE(LOCAL_SIG, 0);
13499
+ localHeader.writeUInt16LE(VERSION, 4);
13500
+ localHeader.writeUInt16LE(FLAG_UTF8, 6);
13501
+ localHeader.writeUInt16LE(method, 8);
13502
+ localHeader.writeUInt16LE(0, 10);
13503
+ localHeader.writeUInt16LE(0, 12);
13504
+ localHeader.writeUInt32LE(crc, 14);
13505
+ localHeader.writeUInt32LE(compressedSize, 18);
13506
+ localHeader.writeUInt32LE(uncompressedSize, 22);
13507
+ localHeader.writeUInt16LE(nameBytes.length, 26);
13508
+ localHeader.writeUInt16LE(0, 28);
13509
+ localChunks.push(localHeader, nameBytes, payload);
13510
+ const centralHeader = Buffer.alloc(46);
13511
+ centralHeader.writeUInt32LE(CENTRAL_SIG, 0);
13512
+ centralHeader.writeUInt16LE(VERSION, 4);
13513
+ centralHeader.writeUInt16LE(VERSION, 6);
13514
+ centralHeader.writeUInt16LE(FLAG_UTF8, 8);
13515
+ centralHeader.writeUInt16LE(method, 10);
13516
+ centralHeader.writeUInt16LE(0, 12);
13517
+ centralHeader.writeUInt16LE(0, 14);
13518
+ centralHeader.writeUInt32LE(crc, 16);
13519
+ centralHeader.writeUInt32LE(compressedSize, 20);
13520
+ centralHeader.writeUInt32LE(uncompressedSize, 24);
13521
+ centralHeader.writeUInt16LE(nameBytes.length, 28);
13522
+ centralHeader.writeUInt16LE(0, 30);
13523
+ centralHeader.writeUInt16LE(0, 32);
13524
+ centralHeader.writeUInt16LE(0, 34);
13525
+ centralHeader.writeUInt16LE(0, 36);
13526
+ centralHeader.writeUInt32LE(0, 38);
13527
+ centralHeader.writeUInt32LE(offset, 42);
13528
+ centralChunks.push(centralHeader, nameBytes);
13529
+ offset += localHeader.length + nameBytes.length + payload.length;
13530
+ }
13531
+ const centralDir = Buffer.concat(centralChunks);
13532
+ const centralSize = centralDir.length;
13533
+ const centralOffset = offset;
13534
+ const eocd = Buffer.alloc(22);
13535
+ eocd.writeUInt32LE(EOCD_SIG, 0);
13536
+ eocd.writeUInt16LE(0, 4);
13537
+ eocd.writeUInt16LE(0, 6);
13538
+ eocd.writeUInt16LE(entries.length, 8);
13539
+ eocd.writeUInt16LE(entries.length, 10);
13540
+ eocd.writeUInt32LE(centralSize, 12);
13541
+ eocd.writeUInt32LE(centralOffset, 16);
13542
+ eocd.writeUInt16LE(0, 20);
13543
+ return Buffer.concat([...localChunks, centralDir, eocd]);
13544
+ }
13545
+
13452
13546
  // server/routes/admin/files.ts
13453
13547
  var UPLOAD_TMP_DIR = ".uploads-tmp";
13454
13548
  var UUID_RE3 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
@@ -13766,6 +13860,61 @@ app20.get("/download", requireAdminSession, async (c) => {
13766
13860
  return c.json({ error: message }, 500);
13767
13861
  }
13768
13862
  });
13863
+ var ZIP_MAX_FILES = 200;
13864
+ var ZIP_MAX_TOTAL_BYTES = 256 * 1024 * 1024;
13865
+ app20.get("/download-zip", requireAdminSession, async (c) => {
13866
+ const cacheKey = c.var.cacheKey;
13867
+ const accountId = getAccountIdForSession(cacheKey);
13868
+ if (!accountId) {
13869
+ console.error(`[data] auth-rejected endpoint="/api/admin/files/download-zip" reason="no account for session"`);
13870
+ return c.json({ error: "Account not found for session" }, 401);
13871
+ }
13872
+ const rawPaths = c.req.queries("path") ?? [];
13873
+ if (rawPaths.length === 0) return c.json({ error: "path required" }, 400);
13874
+ if (rawPaths.length > ZIP_MAX_FILES) {
13875
+ return c.json({ error: `Too many files (max ${ZIP_MAX_FILES})` }, 413);
13876
+ }
13877
+ const entries = [];
13878
+ let total = 0;
13879
+ for (const rawPath of rawPaths) {
13880
+ const resolution = resolveDataPath(rawPath);
13881
+ if (!resolution.ok) {
13882
+ if (resolution.status === 403) {
13883
+ console.error(`[data] path-traversal-blocked endpoint="/api/admin/files/download-zip" requested="${rawPath}" resolved="${resolution.resolved ?? "n/a"}"`);
13884
+ }
13885
+ return c.json({ error: resolution.error }, resolution.status);
13886
+ }
13887
+ const { absolute, relative: relPath } = resolution;
13888
+ if (crossesForeignAccountPartition(relPath, accountId)) {
13889
+ console.error(`[data] account-scope-blocked endpoint="/api/admin/files/download-zip" path="${relPath}" account=${accountId.slice(0, 8)}\u2026`);
13890
+ return c.json({ error: "Not found" }, 404);
13891
+ }
13892
+ try {
13893
+ const info = await stat5(absolute);
13894
+ if (!info.isFile()) return c.json({ error: "Path is not a file" }, 400);
13895
+ total += info.size;
13896
+ if (total > ZIP_MAX_TOTAL_BYTES) {
13897
+ return c.json({ error: "Selection too large to zip" }, 413);
13898
+ }
13899
+ entries.push({ name: basename8(absolute), data: await readFile5(absolute) });
13900
+ } catch (err) {
13901
+ const code = err.code;
13902
+ if (code === "ENOENT") return c.json({ error: "Not found" }, 404);
13903
+ throw err;
13904
+ }
13905
+ }
13906
+ const archive = buildZip(entries);
13907
+ console.error(`[data] file-download-zip files=${entries.length} bytes=${archive.length}`);
13908
+ return new Response(new Uint8Array(archive), {
13909
+ status: 200,
13910
+ headers: {
13911
+ "Content-Type": "application/zip",
13912
+ "Content-Length": String(archive.length),
13913
+ "Content-Disposition": `attachment; filename="files.zip"`,
13914
+ "Cache-Control": "no-store"
13915
+ }
13916
+ });
13917
+ });
13769
13918
  function dataUploadCeiling() {
13770
13919
  const override = Number(process.env.MAXY_DATA_UPLOAD_MAX_BYTES);
13771
13920
  return Number.isFinite(override) && override > 0 ? override : MAX_DATA_UPLOAD_BYTES;
@@ -17431,49 +17580,93 @@ app42.post("/launch", requireAdminSession, async (c) => {
17431
17580
  });
17432
17581
  var browser_default = app42;
17433
17582
 
17434
- // server/routes/admin/index.ts
17583
+ // server/routes/admin/calendar.ts
17435
17584
  var app43 = new Hono();
17436
- app43.route("/session", session_default);
17437
- app43.route("/logs", logs_default);
17438
- app43.route("/claude-info", claude_info_default);
17439
- app43.route("/attachment", attachment_default);
17440
- app43.route("/agents", agents_default);
17441
- app43.route("/sessions", sessions_default);
17442
- app43.route("/claude-sessions", claude_sessions_default);
17443
- app43.route("/log-ingest", log_ingest_default);
17444
- app43.route("/events", events_default);
17445
- app43.route("/files", files_default);
17446
- app43.route("/graph-search", graph_search_default);
17447
- app43.route("/graph-subgraph", graph_subgraph_default);
17448
- app43.route("/graph-delete", graph_delete_default);
17449
- app43.route("/graph-restore", graph_restore_default);
17450
- app43.route("/graph-labels-in-graph", graph_labels_in_graph_default);
17451
- app43.route("/graph-default-view", graph_default_view_default);
17452
- app43.route("/sidebar-artefacts", sidebar_artefacts_default);
17453
- app43.route("/sidebar-sessions", sidebar_sessions_default);
17454
- app43.route("/session-delete", session_delete_default);
17455
- app43.route("/session-stop", session_stop_default);
17456
- app43.route("/session-archive", session_archive_default);
17457
- app43.route("/session-rename", session_rename_default);
17458
- app43.route("/browser", browser_default);
17459
- app43.route("/session-rc-spawn", session_rc_spawn_default);
17460
- app43.route("/session-reseat", session_reseat_default);
17461
- app43.route("/system-stats", system_stats_default);
17462
- app43.route("/health-brand", health_default2);
17463
- app43.route("/linkedin-ingest", linkedin_ingest_default);
17464
- app43.route("/post-turn-context", post_turn_context_default);
17465
- app43.route("/public-session-context", public_session_context_default);
17466
- app43.route("/public-session-exit", public_session_exit_default);
17467
- app43.route("/access-session-evict", access_session_evict_default);
17468
- app43.route("/enrol-person", enrol_person_default);
17469
- var admin_default = app43;
17585
+ app43.get("/meetings", requireAdminSession, async (c) => {
17586
+ const cacheKey = c.var.cacheKey;
17587
+ const accountId = getAccountIdForSession(cacheKey);
17588
+ if (!accountId) {
17589
+ console.error('[calendar] op=admin-read auth-rejected reason="no account for session"');
17590
+ return c.json({ error: "Account not found for session" }, 401);
17591
+ }
17592
+ const from = c.req.query("from");
17593
+ const to = c.req.query("to");
17594
+ if (!from || !to) return c.json({ error: "from and to (ISO) required" }, 400);
17595
+ const session = getSession();
17596
+ try {
17597
+ const res = await session.run(
17598
+ `MATCH (m:Meeting {accountId: $accountId})
17599
+ WHERE NOT (m.endsAt IS NOT NULL AND m.endsAt <= datetime($from))
17600
+ AND m.startsAt < datetime($to)
17601
+ RETURN elementId(m) AS meetingId, m.title AS title,
17602
+ toString(m.startsAt) AS startsAt, toString(m.endsAt) AS endsAt,
17603
+ m.location AS location, coalesce(m.isAllDay, false) AS isAllDay
17604
+ ORDER BY m.startsAt`,
17605
+ { accountId, from, to }
17606
+ );
17607
+ const meetings = res.records.map((r) => ({
17608
+ meetingId: r.get("meetingId"),
17609
+ title: r.get("title"),
17610
+ startsAt: r.get("startsAt"),
17611
+ endsAt: r.get("endsAt"),
17612
+ location: r.get("location"),
17613
+ isAllDay: r.get("isAllDay")
17614
+ }));
17615
+ return c.json({ meetings });
17616
+ } catch (err) {
17617
+ const message = err instanceof Error ? err.message : String(err);
17618
+ console.error(`[calendar] op=admin-read-fail err="${message}"`);
17619
+ return c.json({ error: `Calendar unavailable: ${message}` }, 503);
17620
+ } finally {
17621
+ await session.close();
17622
+ }
17623
+ });
17624
+ var calendar_default = app43;
17625
+
17626
+ // server/routes/admin/index.ts
17627
+ var app44 = new Hono();
17628
+ app44.route("/session", session_default);
17629
+ app44.route("/logs", logs_default);
17630
+ app44.route("/claude-info", claude_info_default);
17631
+ app44.route("/attachment", attachment_default);
17632
+ app44.route("/agents", agents_default);
17633
+ app44.route("/sessions", sessions_default);
17634
+ app44.route("/claude-sessions", claude_sessions_default);
17635
+ app44.route("/log-ingest", log_ingest_default);
17636
+ app44.route("/events", events_default);
17637
+ app44.route("/files", files_default);
17638
+ app44.route("/graph-search", graph_search_default);
17639
+ app44.route("/graph-subgraph", graph_subgraph_default);
17640
+ app44.route("/graph-delete", graph_delete_default);
17641
+ app44.route("/graph-restore", graph_restore_default);
17642
+ app44.route("/graph-labels-in-graph", graph_labels_in_graph_default);
17643
+ app44.route("/graph-default-view", graph_default_view_default);
17644
+ app44.route("/sidebar-artefacts", sidebar_artefacts_default);
17645
+ app44.route("/sidebar-sessions", sidebar_sessions_default);
17646
+ app44.route("/session-delete", session_delete_default);
17647
+ app44.route("/session-stop", session_stop_default);
17648
+ app44.route("/session-archive", session_archive_default);
17649
+ app44.route("/session-rename", session_rename_default);
17650
+ app44.route("/browser", browser_default);
17651
+ app44.route("/session-rc-spawn", session_rc_spawn_default);
17652
+ app44.route("/session-reseat", session_reseat_default);
17653
+ app44.route("/system-stats", system_stats_default);
17654
+ app44.route("/health-brand", health_default2);
17655
+ app44.route("/linkedin-ingest", linkedin_ingest_default);
17656
+ app44.route("/post-turn-context", post_turn_context_default);
17657
+ app44.route("/public-session-context", public_session_context_default);
17658
+ app44.route("/public-session-exit", public_session_exit_default);
17659
+ app44.route("/access-session-evict", access_session_evict_default);
17660
+ app44.route("/enrol-person", enrol_person_default);
17661
+ app44.route("/calendar", calendar_default);
17662
+ var admin_default = app44;
17470
17663
 
17471
17664
  // server/routes/access/verify-token.ts
17472
17665
  var TAG37 = "[access-verify]";
17473
17666
  var MINT_TAG = "[access-session-mint]";
17474
17667
  var COOKIE_NAME = "__access_session";
17475
- var app44 = new Hono();
17476
- app44.post("/", async (c) => {
17668
+ var app45 = new Hono();
17669
+ app45.post("/", async (c) => {
17477
17670
  const ip = c.var.clientIp || "unknown";
17478
17671
  let body;
17479
17672
  try {
@@ -17555,7 +17748,7 @@ app44.post("/", async (c) => {
17555
17748
  displayName: grant.displayName
17556
17749
  });
17557
17750
  });
17558
- var verify_token_default = app44;
17751
+ var verify_token_default = app45;
17559
17752
 
17560
17753
  // app/lib/access-email.ts
17561
17754
  import { spawn as spawn2 } from "child_process";
@@ -17618,9 +17811,9 @@ async function sendMagicLinkEmail(payload) {
17618
17811
 
17619
17812
  // server/routes/access/request-magic-link.ts
17620
17813
  var TAG38 = "[access-request-link]";
17621
- var app45 = new Hono();
17814
+ var app46 = new Hono();
17622
17815
  var VISITOR_MESSAGE = "If that email is on the invite list, a fresh link is on the way.";
17623
- app45.post("/", async (c) => {
17816
+ app46.post("/", async (c) => {
17624
17817
  let body;
17625
17818
  try {
17626
17819
  body = await c.req.json();
@@ -17694,13 +17887,13 @@ app45.post("/", async (c) => {
17694
17887
  );
17695
17888
  return c.json({ message: VISITOR_MESSAGE }, 200);
17696
17889
  });
17697
- var request_magic_link_default = app45;
17890
+ var request_magic_link_default = app46;
17698
17891
 
17699
17892
  // server/routes/access/index.ts
17700
- var app46 = new Hono();
17701
- app46.route("/verify-token", verify_token_default);
17702
- app46.route("/request-magic-link", request_magic_link_default);
17703
- var access_default = app46;
17893
+ var app47 = new Hono();
17894
+ app47.route("/verify-token", verify_token_default);
17895
+ app47.route("/request-magic-link", request_magic_link_default);
17896
+ var access_default = app47;
17704
17897
 
17705
17898
  // server/routes/sites.ts
17706
17899
  import { existsSync as existsSync26, readFileSync as readFileSync28, realpathSync as realpathSync7, statSync as statSync11 } from "fs";
@@ -17735,8 +17928,8 @@ function getExt(p) {
17735
17928
  if (idx < p.lastIndexOf("/")) return "";
17736
17929
  return p.slice(idx).toLowerCase();
17737
17930
  }
17738
- var app47 = new Hono();
17739
- app47.get("/:rel{.*}", (c) => {
17931
+ var app48 = new Hono();
17932
+ app48.get("/:rel{.*}", (c) => {
17740
17933
  const reqPath = c.req.path;
17741
17934
  const rawRel = c.req.param("rel") ?? "";
17742
17935
  const trimmed = rawRel.replace(/^\/+/, "").replace(/\/+$/, "");
@@ -17839,7 +18032,7 @@ app47.get("/:rel{.*}", (c) => {
17839
18032
  "X-Content-Type-Options": "nosniff"
17840
18033
  });
17841
18034
  });
17842
- var sites_default = app47;
18035
+ var sites_default = app48;
17843
18036
 
17844
18037
  // app/lib/visitor-token.ts
17845
18038
  import { createHmac, randomBytes, timingSafeEqual } from "crypto";
@@ -17939,7 +18132,7 @@ function readBrandConfig() {
17939
18132
  }
17940
18133
 
17941
18134
  // server/routes/visitor-consent.ts
17942
- var app48 = new Hono();
18135
+ var app49 = new Hono();
17943
18136
  var CONSENT_COOKIE_NAME = "mxy_consent";
17944
18137
  var CONSENT_COOKIE_MAX_AGE_SECONDS = 60 * 60 * 24 * 365;
17945
18138
  var DEFAULT_CONSENT_COPY = {
@@ -17984,17 +18177,17 @@ function siteSlugFromReferer(referer) {
17984
18177
  return "";
17985
18178
  }
17986
18179
  }
17987
- app48.options("/consent", (c) => {
18180
+ app49.options("/consent", (c) => {
17988
18181
  const origin = getOrigin(c);
17989
18182
  setCorsHeaders(c, origin);
17990
18183
  return c.body(null, 204);
17991
18184
  });
17992
- app48.options("/brand-config", (c) => {
18185
+ app49.options("/brand-config", (c) => {
17993
18186
  const origin = getOrigin(c);
17994
18187
  setCorsHeaders(c, origin);
17995
18188
  return c.body(null, 204);
17996
18189
  });
17997
- app48.post("/consent", async (c) => {
18190
+ app49.post("/consent", async (c) => {
17998
18191
  const origin = getOrigin(c);
17999
18192
  setCorsHeaders(c, origin);
18000
18193
  let raw;
@@ -18034,7 +18227,7 @@ app48.post("/consent", async (c) => {
18034
18227
  console.log(`[consent] ${parsed.decision} site=${site} brand=${brandName} tokenBound=${tokenBound}`);
18035
18228
  return c.body(null, 204);
18036
18229
  });
18037
- app48.get("/brand-config", (c) => {
18230
+ app49.get("/brand-config", (c) => {
18038
18231
  const origin = getOrigin(c);
18039
18232
  setCorsHeaders(c, origin);
18040
18233
  const brand = readBrandConfig();
@@ -18044,7 +18237,7 @@ app48.get("/brand-config", (c) => {
18044
18237
  c.header("Cache-Control", "public, max-age=300");
18045
18238
  return c.json({ consent: { copy, palette } });
18046
18239
  });
18047
- var visitor_consent_default = app48;
18240
+ var visitor_consent_default = app49;
18048
18241
 
18049
18242
  // server/routes/listings.ts
18050
18243
  function getCookie(headerValue, name) {
@@ -18071,8 +18264,8 @@ function appendConsentParams(pageUrl, token) {
18071
18264
  }
18072
18265
  var SAFE_SLUG_RE = /^[a-z0-9](?:[a-z0-9-]{0,118}[a-z0-9])?$/;
18073
18266
  var CHAT_SESSION_KEY_RE = /^[A-Za-z0-9][A-Za-z0-9_-]{7,127}$/;
18074
- var app49 = new Hono();
18075
- app49.get("/:slug/click", async (c) => {
18267
+ var app50 = new Hono();
18268
+ app50.get("/:slug/click", async (c) => {
18076
18269
  const slug = c.req.param("slug") ?? "";
18077
18270
  const rawSession = c.req.query("session") ?? "";
18078
18271
  const sessionKey = CHAT_SESSION_KEY_RE.test(rawSession) ? rawSession : "invalid";
@@ -18136,10 +18329,10 @@ app49.get("/:slug/click", async (c) => {
18136
18329
  console.log(`[property-card-click] sessionKey=${sessionKey} listingSlug=${slug} consent=${consentCookie ?? "absent"} ts=${(/* @__PURE__ */ new Date()).toISOString()}`);
18137
18330
  return c.redirect(redirectUrl, 302);
18138
18331
  });
18139
- var listings_default = app49;
18332
+ var listings_default = app50;
18140
18333
 
18141
18334
  // server/routes/visitor-event.ts
18142
- var app50 = new Hono();
18335
+ var app51 = new Hono();
18143
18336
  var BOT_UA_RE = /\b(bot|crawl|spider|slurp|headlesschrome|phantomjs|googlebot|bingbot|yandex|baiduspider|ahrefsbot|semrushbot|mj12bot|dotbot|petalbot)\b/i;
18144
18337
  var buckets = /* @__PURE__ */ new Map();
18145
18338
  var RATE_LIMIT = 60;
@@ -18206,12 +18399,12 @@ function originAllowed(origin, allowlist) {
18206
18399
  return false;
18207
18400
  }
18208
18401
  }
18209
- app50.options("/event", (c) => {
18402
+ app51.options("/event", (c) => {
18210
18403
  const origin = getOrigin2(c);
18211
18404
  setCorsHeaders2(c, origin);
18212
18405
  return c.body(null, 204);
18213
18406
  });
18214
- app50.post("/event", async (c) => {
18407
+ app51.post("/event", async (c) => {
18215
18408
  const origin = getOrigin2(c);
18216
18409
  setCorsHeaders2(c, origin);
18217
18410
  const ua = c.req.header("user-agent") ?? "";
@@ -18416,7 +18609,7 @@ async function writeEvent(opts) {
18416
18609
  );
18417
18610
  }
18418
18611
  }
18419
- var visitor_event_default = app50;
18612
+ var visitor_event_default = app51;
18420
18613
 
18421
18614
  // server/routes/session.ts
18422
18615
  import { resolve as resolve29 } from "path";
@@ -18462,8 +18655,8 @@ function withVisitorCookie(response, visitorId) {
18462
18655
  headers
18463
18656
  });
18464
18657
  }
18465
- var app51 = new Hono();
18466
- app51.post("/", async (c) => {
18658
+ var app52 = new Hono();
18659
+ app52.post("/", async (c) => {
18467
18660
  let body;
18468
18661
  try {
18469
18662
  body = await c.req.json();
@@ -18610,7 +18803,175 @@ app51.post("/", async (c) => {
18610
18803
  newVisitorId
18611
18804
  );
18612
18805
  });
18613
- var session_default2 = app51;
18806
+ var session_default2 = app52;
18807
+
18808
+ // server/lib/calendar-availability.ts
18809
+ import { readFileSync as readFileSync31 } from "fs";
18810
+ import { join as join28 } from "path";
18811
+ var AVAILABILITY_FILENAME = "calendar-availability.json";
18812
+ var REQUIRED_KEYS = [
18813
+ "timezone",
18814
+ "durationMins",
18815
+ "bufferMins",
18816
+ "weekly"
18817
+ ];
18818
+ function readAvailabilityConfig(accountDir) {
18819
+ const path2 = join28(accountDir, AVAILABILITY_FILENAME);
18820
+ let raw;
18821
+ try {
18822
+ raw = readFileSync31(path2, "utf-8");
18823
+ } catch {
18824
+ throw new Error(`availability not configured: ${path2} not found`);
18825
+ }
18826
+ let parsed;
18827
+ try {
18828
+ parsed = JSON.parse(raw);
18829
+ } catch (err) {
18830
+ throw new Error(`invalid JSON in ${path2}: ${err.message}`);
18831
+ }
18832
+ const cfg = parsed;
18833
+ for (const key of REQUIRED_KEYS) {
18834
+ if (cfg[key] === void 0 || cfg[key] === null) {
18835
+ throw new Error(`availability config ${path2} missing required key: ${key}`);
18836
+ }
18837
+ }
18838
+ return cfg;
18839
+ }
18840
+
18841
+ // server/lib/calendar-slots.ts
18842
+ var WEEKDAYS = ["sun", "mon", "tue", "wed", "thu", "fri", "sat"];
18843
+ function tzOffsetMs(instant, timeZone) {
18844
+ const dtf = new Intl.DateTimeFormat("en-US", {
18845
+ timeZone,
18846
+ hour12: false,
18847
+ year: "numeric",
18848
+ month: "2-digit",
18849
+ day: "2-digit",
18850
+ hour: "2-digit",
18851
+ minute: "2-digit",
18852
+ second: "2-digit"
18853
+ });
18854
+ const parts = {};
18855
+ for (const p of dtf.formatToParts(instant)) parts[p.type] = p.value;
18856
+ let hour = Number(parts.hour);
18857
+ if (hour === 24) hour = 0;
18858
+ const asUTC = Date.UTC(
18859
+ Number(parts.year),
18860
+ Number(parts.month) - 1,
18861
+ Number(parts.day),
18862
+ hour,
18863
+ Number(parts.minute),
18864
+ Number(parts.second)
18865
+ );
18866
+ return asUTC - instant.getTime();
18867
+ }
18868
+ function zonedWallClockToUtc(y, m, d, hh, mm, timeZone) {
18869
+ const naiveUtc = Date.UTC(y, m - 1, d, hh, mm, 0, 0);
18870
+ const offset = tzOffsetMs(new Date(naiveUtc), timeZone);
18871
+ return new Date(naiveUtc - offset);
18872
+ }
18873
+ function tzCalendarDate(instant, timeZone) {
18874
+ const dtf = new Intl.DateTimeFormat("en-US", {
18875
+ timeZone,
18876
+ year: "numeric",
18877
+ month: "2-digit",
18878
+ day: "2-digit"
18879
+ });
18880
+ const parts = {};
18881
+ for (const p of dtf.formatToParts(instant)) parts[p.type] = p.value;
18882
+ return { y: Number(parts.year), m: Number(parts.month), d: Number(parts.day) };
18883
+ }
18884
+ function parseHHMM(s) {
18885
+ const [hh, mm] = s.split(":").map(Number);
18886
+ return { hh, mm };
18887
+ }
18888
+ function computeOpenSlots(config, busy, fromISO, toISO) {
18889
+ const tz = config.timezone;
18890
+ const durMs = config.durationMins * 6e4;
18891
+ const bufMs = config.bufferMins * 6e4;
18892
+ const from = new Date(fromISO).getTime();
18893
+ const to = new Date(toISO).getTime();
18894
+ const busyMs = busy.map((b) => ({ start: new Date(b.start).getTime(), end: new Date(b.end).getTime() }));
18895
+ const slots = [];
18896
+ const firstDate = tzCalendarDate(new Date(from), tz);
18897
+ let dayMidnight = zonedWallClockToUtc(firstDate.y, firstDate.m, firstDate.d, 0, 0, tz);
18898
+ for (let guard = 0; guard < 400 && dayMidnight.getTime() < to; guard++) {
18899
+ const { y, m, d } = tzCalendarDate(dayMidnight, tz);
18900
+ const weekday = WEEKDAYS[new Date(Date.UTC(y, m - 1, d)).getUTCDay()];
18901
+ const windows = config.weekly[weekday] ?? [];
18902
+ for (const [openStr, closeStr] of windows) {
18903
+ const open = parseHHMM(openStr);
18904
+ const close = parseHHMM(closeStr);
18905
+ const openUtc = zonedWallClockToUtc(y, m, d, open.hh, open.mm, tz).getTime();
18906
+ const closeUtc = zonedWallClockToUtc(y, m, d, close.hh, close.mm, tz).getTime();
18907
+ for (let s = openUtc; s + durMs <= closeUtc; s += durMs) {
18908
+ const slotEnd = s + durMs;
18909
+ if (s < from || s >= to) continue;
18910
+ const blocked = busyMs.some((b) => b.start < slotEnd + bufMs && b.end > s - bufMs);
18911
+ if (blocked) continue;
18912
+ slots.push({ start: new Date(s).toISOString(), end: new Date(slotEnd).toISOString() });
18913
+ }
18914
+ }
18915
+ const next = tzCalendarDate(new Date(dayMidnight.getTime() + 26 * 36e5), tz);
18916
+ dayMidnight = zonedWallClockToUtc(next.y, next.m, next.d, 0, 0, tz);
18917
+ }
18918
+ return slots;
18919
+ }
18920
+
18921
+ // server/routes/calendar-public.ts
18922
+ var app53 = new Hono();
18923
+ function setCors(c) {
18924
+ c.header("Access-Control-Allow-Origin", "*");
18925
+ c.header("Access-Control-Allow-Methods", "GET, OPTIONS");
18926
+ c.header("Access-Control-Allow-Headers", "content-type");
18927
+ }
18928
+ app53.options("/free-busy", (c) => {
18929
+ setCors(c);
18930
+ return c.body(null, 204);
18931
+ });
18932
+ app53.get("/free-busy", async (c) => {
18933
+ setCors(c);
18934
+ const from = c.req.query("from");
18935
+ const to = c.req.query("to");
18936
+ if (!from || !to) return c.json({ error: "from and to (ISO) required" }, 400);
18937
+ if (Number.isNaN(Date.parse(from)) || Number.isNaN(Date.parse(to))) {
18938
+ return c.json({ error: "from and to must be parseable ISO timestamps" }, 400);
18939
+ }
18940
+ const account = resolveAccount();
18941
+ if (!account) return c.json({ error: "No account configured" }, 500);
18942
+ let config;
18943
+ try {
18944
+ config = readAvailabilityConfig(account.accountDir);
18945
+ } catch (err) {
18946
+ console.error(`[calendar] op=free-busy-unconfigured err="${err.message}"`);
18947
+ return c.json({ error: "Availability not configured" }, 404);
18948
+ }
18949
+ const session = getSession();
18950
+ try {
18951
+ const res = await session.run(
18952
+ `MATCH (m:Meeting {accountId: $accountId})
18953
+ WHERE NOT (m.endsAt IS NOT NULL AND m.endsAt <= datetime($from))
18954
+ AND m.startsAt < datetime($to)
18955
+ RETURN toString(m.startsAt) AS start, toString(m.endsAt) AS end`,
18956
+ { accountId: account.accountId, from, to }
18957
+ );
18958
+ const busy = res.records.map((r) => {
18959
+ const start = r.get("start");
18960
+ const rawEnd = r.get("end");
18961
+ const end = rawEnd ?? new Date(new Date(start).getTime() + config.durationMins * 6e4).toISOString();
18962
+ return { start, end };
18963
+ });
18964
+ const slots = computeOpenSlots(config, busy, from, to);
18965
+ return c.json({ durationMins: config.durationMins, timezone: config.timezone, slots });
18966
+ } catch (err) {
18967
+ const message = err instanceof Error ? err.message : String(err);
18968
+ console.error(`[calendar] op=free-busy-fail err="${message}"`);
18969
+ return c.json({ error: `Availability unavailable: ${message}` }, 503);
18970
+ } finally {
18971
+ await session.close();
18972
+ }
18973
+ });
18974
+ var calendar_public_default = app53;
18614
18975
 
18615
18976
  // app/lib/graph-health.ts
18616
18977
  var import_dist4 = __toESM(require_dist3(), 1);
@@ -18958,7 +19319,7 @@ async function migrateUploads(opts = {}) {
18958
19319
  // app/lib/migrate-admin-webchat-sidecars.ts
18959
19320
  import { readdir as readdir6, readFile as readFile7, rename as rename3, writeFile as writeFile4, unlink as unlink3 } from "fs/promises";
18960
19321
  import { existsSync as existsSync29 } from "fs";
18961
- import { join as join28 } from "path";
19322
+ import { join as join29 } from "path";
18962
19323
  var ADMIN_ROLE2 = "admin";
18963
19324
  var WEBCHAT_CHANNEL2 = "webchat";
18964
19325
  var SESSION_META_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\.meta\.json$/i;
@@ -19003,7 +19364,7 @@ async function collectLiveSidecars(projectsRoot) {
19003
19364
  }
19004
19365
  for (const slug of slugs) {
19005
19366
  if (!slug.isDirectory()) continue;
19006
- const slugDir = join28(projectsRoot, slug.name);
19367
+ const slugDir = join29(projectsRoot, slug.name);
19007
19368
  let entries;
19008
19369
  try {
19009
19370
  entries = await readdir6(slugDir, { withFileTypes: true });
@@ -19012,9 +19373,9 @@ async function collectLiveSidecars(projectsRoot) {
19012
19373
  }
19013
19374
  for (const entry of entries) {
19014
19375
  if (entry.isFile() && SESSION_META_RE.test(entry.name)) {
19015
- out.push(join28(slugDir, entry.name));
19376
+ out.push(join29(slugDir, entry.name));
19016
19377
  } else if (entry.isDirectory() && entry.name === "subagents") {
19017
- const subDir = join28(slugDir, entry.name);
19378
+ const subDir = join29(slugDir, entry.name);
19018
19379
  let subs;
19019
19380
  try {
19020
19381
  subs = await readdir6(subDir, { withFileTypes: true });
@@ -19022,7 +19383,7 @@ async function collectLiveSidecars(projectsRoot) {
19022
19383
  continue;
19023
19384
  }
19024
19385
  for (const s of subs) {
19025
- if (s.isFile() && SESSION_META_RE.test(s.name)) out.push(join28(subDir, s.name));
19386
+ if (s.isFile() && SESSION_META_RE.test(s.name)) out.push(join29(subDir, s.name));
19026
19387
  }
19027
19388
  }
19028
19389
  }
@@ -19034,7 +19395,7 @@ function shortId(path2) {
19034
19395
  return base.slice(0, 8);
19035
19396
  }
19036
19397
  async function migrateAdminWebchatSidecars(opts = {}) {
19037
- const projectsRoot = opts.projectsRoot ?? (process.env.CLAUDE_CONFIG_DIR ? join28(process.env.CLAUDE_CONFIG_DIR, "projects") : null) ?? "";
19398
+ const projectsRoot = opts.projectsRoot ?? (process.env.CLAUDE_CONFIG_DIR ? join29(process.env.CLAUDE_CONFIG_DIR, "projects") : null) ?? "";
19038
19399
  const usersFile = opts.usersFile ?? USERS_FILE;
19039
19400
  const accountId = opts.accountId ?? resolveAccount()?.accountId ?? null;
19040
19401
  const resolveName = opts.resolveName ?? defaultResolveName;
@@ -19376,8 +19737,8 @@ var streamSSE = (c, cb, onError) => {
19376
19737
 
19377
19738
  // app/lib/whatsapp/gateway/routes.ts
19378
19739
  function createWaChannelRoutes(deps) {
19379
- const app53 = new Hono();
19380
- app53.get("/wa-channel/inbound", (c) => {
19740
+ const app55 = new Hono();
19741
+ app55.get("/wa-channel/inbound", (c) => {
19381
19742
  const senderId = c.req.query("senderId");
19382
19743
  if (!senderId) return c.json({ error: "senderId required" }, 400);
19383
19744
  return streamSSE(c, async (stream2) => {
@@ -19395,7 +19756,7 @@ function createWaChannelRoutes(deps) {
19395
19756
  }
19396
19757
  });
19397
19758
  });
19398
- app53.post("/wa-channel/reply", async (c) => {
19759
+ app55.post("/wa-channel/reply", async (c) => {
19399
19760
  const body = await c.req.json().catch(() => null);
19400
19761
  const senderId = body?.senderId;
19401
19762
  const text = body?.text;
@@ -19416,7 +19777,7 @@ function createWaChannelRoutes(deps) {
19416
19777
  console.error(`[whatsapp-native] op=reply-dispatch senderId=${senderId} bytes=${bytes}`);
19417
19778
  return c.json({ ok: true });
19418
19779
  });
19419
- app53.post("/wa-channel/reply-document", async (c) => {
19780
+ app55.post("/wa-channel/reply-document", async (c) => {
19420
19781
  const body = await c.req.json().catch(() => null);
19421
19782
  const senderId = body?.senderId;
19422
19783
  const files = body?.files;
@@ -19455,7 +19816,7 @@ function createWaChannelRoutes(deps) {
19455
19816
  }
19456
19817
  return c.json({ ok: true, results });
19457
19818
  });
19458
- app53.post("/wa-channel/ready", async (c) => {
19819
+ app55.post("/wa-channel/ready", async (c) => {
19459
19820
  const body = await c.req.json().catch(() => null);
19460
19821
  const senderId = body?.senderId;
19461
19822
  if (typeof senderId !== "string") {
@@ -19464,7 +19825,7 @@ function createWaChannelRoutes(deps) {
19464
19825
  deps.onReady?.(senderId);
19465
19826
  return c.json({ ok: true });
19466
19827
  });
19467
- app53.post("/wa-channel/received", async (c) => {
19828
+ app55.post("/wa-channel/received", async (c) => {
19468
19829
  const body = await c.req.json().catch(() => null);
19469
19830
  const senderId = body?.senderId;
19470
19831
  const waMessageId = body?.waMessageId;
@@ -19474,7 +19835,7 @@ function createWaChannelRoutes(deps) {
19474
19835
  deps.onReceived?.(senderId, waMessageId);
19475
19836
  return c.json({ ok: true });
19476
19837
  });
19477
- app53.post("/wa-channel/turn-end", async (c) => {
19838
+ app55.post("/wa-channel/turn-end", async (c) => {
19478
19839
  const body = await c.req.json().catch(() => null);
19479
19840
  const senderId = body?.senderId;
19480
19841
  const sessionId = body?.sessionId;
@@ -19505,7 +19866,7 @@ function createWaChannelRoutes(deps) {
19505
19866
  console.error(`[whatsapp-native] op=turn-end sessionId=${sid} replied=no delivered=fallback bytes=${bytes}`);
19506
19867
  return c.json({ ok: true, delivered: "fallback" });
19507
19868
  });
19508
- return app53;
19869
+ return app55;
19509
19870
  }
19510
19871
 
19511
19872
  // app/lib/whatsapp/gateway/wa-gateway.ts
@@ -19798,8 +20159,8 @@ var InboundHub2 = class {
19798
20159
 
19799
20160
  // app/lib/webchat/gateway/routes.ts
19800
20161
  function createWebchatChannelRoutes(deps) {
19801
- const app53 = new Hono();
19802
- app53.get("/webchat-channel/inbound", (c) => {
20162
+ const app55 = new Hono();
20163
+ app55.get("/webchat-channel/inbound", (c) => {
19803
20164
  const key = c.req.query("key");
19804
20165
  if (!key) return c.json({ error: "key required" }, 400);
19805
20166
  return streamSSE(c, async (stream2) => {
@@ -19817,7 +20178,7 @@ function createWebchatChannelRoutes(deps) {
19817
20178
  }
19818
20179
  });
19819
20180
  });
19820
- app53.post("/webchat-channel/reply", async (c) => {
20181
+ app55.post("/webchat-channel/reply", async (c) => {
19821
20182
  const body = await c.req.json().catch(() => null);
19822
20183
  const key = body?.key;
19823
20184
  const text = body?.text;
@@ -19827,7 +20188,7 @@ function createWebchatChannelRoutes(deps) {
19827
20188
  deps.onReply?.(key, text);
19828
20189
  return c.json({ ok: true });
19829
20190
  });
19830
- app53.post("/webchat-channel/ready", async (c) => {
20191
+ app55.post("/webchat-channel/ready", async (c) => {
19831
20192
  const body = await c.req.json().catch(() => null);
19832
20193
  const key = body?.key;
19833
20194
  if (typeof key !== "string") {
@@ -19836,7 +20197,7 @@ function createWebchatChannelRoutes(deps) {
19836
20197
  deps.onReady?.(key);
19837
20198
  return c.json({ ok: true });
19838
20199
  });
19839
- app53.post("/webchat-channel/permission-request", async (c) => {
20200
+ app55.post("/webchat-channel/permission-request", async (c) => {
19840
20201
  const body = await c.req.json().catch(() => null);
19841
20202
  const key = body?.key;
19842
20203
  const requestId = body?.request_id;
@@ -19850,7 +20211,7 @@ function createWebchatChannelRoutes(deps) {
19850
20211
  const verdict = await deps.awaitPermissionVerdict({ key, requestId, toolName, description, inputPreview });
19851
20212
  return c.json(verdict);
19852
20213
  });
19853
- app53.post("/webchat-channel/received", async (c) => {
20214
+ app55.post("/webchat-channel/received", async (c) => {
19854
20215
  const body = await c.req.json().catch(() => null);
19855
20216
  const key = body?.key;
19856
20217
  const messageId = body?.messageId;
@@ -19860,7 +20221,7 @@ function createWebchatChannelRoutes(deps) {
19860
20221
  deps.onReceived?.(key, messageId);
19861
20222
  return c.json({ ok: true });
19862
20223
  });
19863
- app53.post("/webchat-channel/turn-end", async (c) => {
20224
+ app55.post("/webchat-channel/turn-end", async (c) => {
19864
20225
  const body = await c.req.json().catch(() => null);
19865
20226
  const key = body?.key;
19866
20227
  const sessionId = body?.sessionId;
@@ -19880,7 +20241,7 @@ function createWebchatChannelRoutes(deps) {
19880
20241
  console.error(`[webchat-native] op=turn-undelivered channel=webchat key=${k} sessionId=${sid} bytes=${finalBytes}`);
19881
20242
  return c.json({ ok: true, delivered: "undelivered" });
19882
20243
  });
19883
- return app53;
20244
+ return app55;
19884
20245
  }
19885
20246
 
19886
20247
  // app/lib/webchat/gateway/webchat-gateway.ts
@@ -20372,7 +20733,7 @@ function broadcastAdminShutdown(reason) {
20372
20733
 
20373
20734
  // ../lib/entitlement/src/index.ts
20374
20735
  import { createPublicKey, createHash as createHash5, verify as cryptoVerify } from "crypto";
20375
- import { existsSync as existsSync30, readFileSync as readFileSync31, statSync as statSync12 } from "fs";
20736
+ import { existsSync as existsSync30, readFileSync as readFileSync32, statSync as statSync12 } from "fs";
20376
20737
  import { resolve as resolve32 } from "path";
20377
20738
 
20378
20739
  // ../lib/entitlement/src/canonicalize.ts
@@ -20436,7 +20797,7 @@ function resolveEntitlement(brand, account) {
20436
20797
  function verifyAndResolve(brand, entitlementPath, account) {
20437
20798
  let pubkeyPem;
20438
20799
  try {
20439
- pubkeyPem = readFileSync31(pubkeyPath(brand), "utf-8");
20800
+ pubkeyPem = readFileSync32(pubkeyPath(brand), "utf-8");
20440
20801
  } catch (err) {
20441
20802
  return logResolved(anonymousFallback("pubkey-missing"), {
20442
20803
  reason: "pubkey-missing"
@@ -20450,7 +20811,7 @@ function verifyAndResolve(brand, entitlementPath, account) {
20450
20811
  }
20451
20812
  let envelope;
20452
20813
  try {
20453
- envelope = JSON.parse(readFileSync31(entitlementPath, "utf-8"));
20814
+ envelope = JSON.parse(readFileSync32(entitlementPath, "utf-8"));
20454
20815
  } catch {
20455
20816
  return logResolved(anonymousFallback("malformed"), { reason: "malformed" });
20456
20817
  }
@@ -20611,14 +20972,14 @@ function clientFrom(c) {
20611
20972
  );
20612
20973
  }
20613
20974
  var PLATFORM_ROOT8 = process.env.MAXY_PLATFORM_ROOT || "";
20614
- var BRAND_JSON_PATH = PLATFORM_ROOT8 ? join29(PLATFORM_ROOT8, "config", "brand.json") : "";
20975
+ var BRAND_JSON_PATH = PLATFORM_ROOT8 ? join30(PLATFORM_ROOT8, "config", "brand.json") : "";
20615
20976
  var BRAND = { productName: "Maxy", hostname: "maxy", configDir: ".maxy", marketingUrl: "https://getmaxy.com" };
20616
20977
  if (BRAND_JSON_PATH && !existsSync31(BRAND_JSON_PATH)) {
20617
20978
  console.error(`[brand] WARNING: brand.json not found at ${BRAND_JSON_PATH} \u2014 using Maxy defaults`);
20618
20979
  }
20619
20980
  if (BRAND_JSON_PATH && existsSync31(BRAND_JSON_PATH)) {
20620
20981
  try {
20621
- const parsed = JSON.parse(readFileSync32(BRAND_JSON_PATH, "utf-8"));
20982
+ const parsed = JSON.parse(readFileSync33(BRAND_JSON_PATH, "utf-8"));
20622
20983
  BRAND = { ...BRAND, ...parsed };
20623
20984
  } catch (err) {
20624
20985
  console.error(`[brand] Failed to parse brand.json: ${err.message}`);
@@ -20626,6 +20987,12 @@ if (BRAND_JSON_PATH && existsSync31(BRAND_JSON_PATH)) {
20626
20987
  }
20627
20988
  }
20628
20989
  var brandFaviconPath = BRAND.assets?.favicon ? `/brand/${BRAND.assets.favicon}` : "/favicon.ico";
20990
+ var brandAssetsBoot = BRAND.assets ?? {};
20991
+ var brandIconPathBoot = brandAssetsBoot.icon ? `/brand/${brandAssetsBoot.icon}` : "/brand/maxy-monochrome.png";
20992
+ var brandAppIcon512Path = brandAssetsBoot.appIcon ? `/brand/${brandAssetsBoot.appIcon}` : brandIconPathBoot;
20993
+ var brandAppIcon192Path = brandAssetsBoot.appIcon192 ? `/brand/${brandAssetsBoot.appIcon192}` : brandAppIcon512Path;
20994
+ var brandMaskableIconPath = brandAssetsBoot.maskableIcon ? `/brand/${brandAssetsBoot.maskableIcon}` : brandAppIcon512Path;
20995
+ var ADMIN_MANIFEST_PATH = PWA_SURFACES.find((s) => s.surface === "admin").manifestPath;
20629
20996
  var brandLoginOpts = {
20630
20997
  primaryColor: BRAND.defaultColors?.primary,
20631
20998
  primaryHoverColor: BRAND.defaultColors?.primaryHover,
@@ -20637,13 +21004,18 @@ var brandLoginOpts = {
20637
21004
  bodyFont: BRAND.defaultFonts?.body,
20638
21005
  logoContainsName: !!BRAND.logoContainsName,
20639
21006
  tagline: BRAND.tagline,
20640
- ogCardPath: BRAND.assets?.ogCard ? `/brand/${BRAND.assets.ogCard}` : void 0
21007
+ ogCardPath: BRAND.assets?.ogCard ? `/brand/${BRAND.assets.ogCard}` : void 0,
21008
+ // Make the sign-in page installable: link the admin manifest + opaque
21009
+ // apple-touch-icon so an install from there adopts the brand name + icon.
21010
+ manifestPath: ADMIN_MANIFEST_PATH,
21011
+ appleTouchIconPath: brandAppIcon512Path,
21012
+ themeColor: BRAND.defaultColors?.primary
20641
21013
  };
20642
- var ALIAS_DOMAINS_PATH = join29(homedir3(), BRAND.configDir, "alias-domains.json");
21014
+ var ALIAS_DOMAINS_PATH = join30(homedir3(), BRAND.configDir, "alias-domains.json");
20643
21015
  function loadAliasDomains() {
20644
21016
  try {
20645
21017
  if (!existsSync31(ALIAS_DOMAINS_PATH)) return null;
20646
- const parsed = JSON.parse(readFileSync32(ALIAS_DOMAINS_PATH, "utf-8"));
21018
+ const parsed = JSON.parse(readFileSync33(ALIAS_DOMAINS_PATH, "utf-8"));
20647
21019
  if (!Array.isArray(parsed)) {
20648
21020
  console.error("[alias-domains] malformed alias-domains.json \u2014 expected array");
20649
21021
  return null;
@@ -20667,11 +21039,11 @@ watchFile(ALIAS_DOMAINS_PATH, { interval: 2e3 }, () => {
20667
21039
  function isPublicHost(host) {
20668
21040
  return host.startsWith("public.") || aliasDomains.has(host);
20669
21041
  }
20670
- var app52 = new Hono();
21042
+ var app54 = new Hono();
20671
21043
  var nativeFileFollowers = /* @__PURE__ */ new Map();
20672
21044
  var waGateway = new WaGateway({
20673
21045
  gatewayUrl: `http://127.0.0.1:${process.env.MAXY_UI_INTERNAL_PORT ?? ""}`,
20674
- serverPath: process.env.MAXY_WA_CHANNEL_SERVER_PATH ?? resolve33(process.env.MAXY_PLATFORM_ROOT ?? join29(__dirname, ".."), "services/whatsapp-channel/dist/server.js"),
21046
+ serverPath: process.env.MAXY_WA_CHANNEL_SERVER_PATH ?? resolve33(process.env.MAXY_PLATFORM_ROOT ?? join30(__dirname, ".."), "services/whatsapp-channel/dist/server.js"),
20675
21047
  // Task 751 — file delivery on the native channel: resolve the platform
20676
21048
  // account + path validation here and funnel through the shared send core.
20677
21049
  sendDocument: async ({ senderId, accountId, filePath, caption }) => {
@@ -20687,7 +21059,7 @@ var waGateway = new WaGateway({
20687
21059
  caption,
20688
21060
  accountId,
20689
21061
  maxyAccountId,
20690
- platformRoot: resolve33(process.env.MAXY_PLATFORM_ROOT ?? join29(__dirname, ".."))
21062
+ platformRoot: resolve33(process.env.MAXY_PLATFORM_ROOT ?? join30(__dirname, ".."))
20691
21063
  });
20692
21064
  return result.ok ? { ok: true, messageId: result.messageId } : { ok: false, error: result.error };
20693
21065
  },
@@ -20717,7 +21089,7 @@ var waGateway = new WaGateway({
20717
21089
  nativeFileFollowers.set(senderId, ac);
20718
21090
  }
20719
21091
  });
20720
- app52.route("/", waGateway.routes());
21092
+ app54.route("/", waGateway.routes());
20721
21093
  waGateway.startSweeper();
20722
21094
  var webchatGateway = new WebchatGateway({
20723
21095
  gatewayUrl: webchatGatewayUrl(),
@@ -20790,15 +21162,15 @@ var webchatGateway = new WebchatGateway({
20790
21162
  deleteSession: (sessionId) => managerDelete(sessionId),
20791
21163
  firePublicSessionEndReview: (input) => firePublicSessionEndReview(input)
20792
21164
  });
20793
- app52.route("/", webchatGateway.routes());
21165
+ app54.route("/", webchatGateway.routes());
20794
21166
  webchatGateway.startSweeper();
20795
21167
  webchatGateway.startPublicReaper();
20796
21168
  var chatRoutes = createChatRoutes({
20797
21169
  handleInbound: (input) => webchatGateway.handleInbound(input),
20798
21170
  awaitReply: (key, timeoutMs) => webchatGateway.awaitReply(key, timeoutMs)
20799
21171
  });
20800
- app52.use("*", clientIpMiddleware);
20801
- app52.use("*", async (c, next) => {
21172
+ app54.use("*", clientIpMiddleware);
21173
+ app54.use("*", async (c, next) => {
20802
21174
  await next();
20803
21175
  c.header("X-Content-Type-Options", "nosniff");
20804
21176
  c.header("Referrer-Policy", "strict-origin-when-cross-origin");
@@ -20808,7 +21180,7 @@ app52.use("*", async (c, next) => {
20808
21180
  );
20809
21181
  });
20810
21182
  var HTTP_LOG_PATHS = /* @__PURE__ */ new Set(["/vnc-viewer.html", "/vnc-popout.html"]);
20811
- app52.use("*", async (c, next) => {
21183
+ app54.use("*", async (c, next) => {
20812
21184
  if (!HTTP_LOG_PATHS.has(c.req.path)) {
20813
21185
  await next();
20814
21186
  return;
@@ -20826,7 +21198,7 @@ app52.use("*", async (c, next) => {
20826
21198
  });
20827
21199
  }
20828
21200
  });
20829
- app52.use("*", async (c, next) => {
21201
+ app54.use("*", async (c, next) => {
20830
21202
  const host = (c.req.header("host") ?? "").split(":")[0];
20831
21203
  if (isOperatorHost(host, getOperatorDomains()) || !isPublicHost(host)) {
20832
21204
  await next();
@@ -20864,7 +21236,7 @@ function resolveRemoteAuthOpts(c) {
20864
21236
  return { ...brandLoginOpts, origin };
20865
21237
  }
20866
21238
  var MAX_LOGIN_BODY = 8 * 1024;
20867
- app52.post("/__remote-auth/login", async (c) => {
21239
+ app54.post("/__remote-auth/login", async (c) => {
20868
21240
  const client = clientFrom(c);
20869
21241
  const clientIp = client.ip || "unknown";
20870
21242
  if (!requestIsTlsTerminated(c)) {
@@ -20909,7 +21281,7 @@ app52.post("/__remote-auth/login", async (c) => {
20909
21281
  }
20910
21282
  });
20911
21283
  });
20912
- app52.get("/__remote-auth/logout", (c) => {
21284
+ app54.get("/__remote-auth/logout", (c) => {
20913
21285
  const client = clientFrom(c);
20914
21286
  const clientIp = client.ip || "unknown";
20915
21287
  console.error(`[remote-auth] logout ip=${clientIp}`);
@@ -20922,7 +21294,7 @@ app52.get("/__remote-auth/logout", (c) => {
20922
21294
  }
20923
21295
  });
20924
21296
  });
20925
- app52.post("/__remote-auth/change-password", async (c) => {
21297
+ app54.post("/__remote-auth/change-password", async (c) => {
20926
21298
  const client = clientFrom(c);
20927
21299
  const clientIp = client.ip || "unknown";
20928
21300
  const rateLimited = checkRateLimit(client);
@@ -20981,13 +21353,13 @@ app52.post("/__remote-auth/change-password", async (c) => {
20981
21353
  return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(c), mode: "change", changeError: "Failed to save password", redirect }), 200);
20982
21354
  }
20983
21355
  });
20984
- app52.get("/__remote-auth/setup", (c) => {
21356
+ app54.get("/__remote-auth/setup", (c) => {
20985
21357
  if (isRemoteAuthConfigured()) {
20986
21358
  return c.redirect("/");
20987
21359
  }
20988
21360
  return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(c), mode: "setup" }), 200);
20989
21361
  });
20990
- app52.post("/__remote-auth/set-initial-password", async (c) => {
21362
+ app54.post("/__remote-auth/set-initial-password", async (c) => {
20991
21363
  if (isRemoteAuthConfigured()) {
20992
21364
  return c.redirect("/");
20993
21365
  }
@@ -21025,10 +21397,10 @@ app52.post("/__remote-auth/set-initial-password", async (c) => {
21025
21397
  return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(c), mode: "setup", setupError: "Failed to save password. Please try again." }), 200);
21026
21398
  }
21027
21399
  });
21028
- app52.get("/api/remote-auth/status", (c) => {
21400
+ app54.get("/api/remote-auth/status", (c) => {
21029
21401
  return c.json({ configured: isRemoteAuthConfigured() });
21030
21402
  });
21031
- app52.post("/api/remote-auth/set-password", async (c) => {
21403
+ app54.post("/api/remote-auth/set-password", async (c) => {
21032
21404
  let body;
21033
21405
  try {
21034
21406
  body = await c.req.json();
@@ -21067,13 +21439,17 @@ app52.post("/api/remote-auth/set-password", async (c) => {
21067
21439
  return c.json({ error: "Failed to save password" }, 500);
21068
21440
  }
21069
21441
  });
21070
- app52.route("/api/_client-error", client_error_default);
21442
+ app54.route("/api/_client-error", client_error_default);
21071
21443
  console.log("[client-error-route] mounted");
21072
21444
  var PWA_PUBLIC_PATHS = /* @__PURE__ */ new Set(["/sw.js", ...PWA_SURFACES.map((s) => s.manifestPath)]);
21073
- app52.use("*", async (c, next) => {
21445
+ app54.use("*", async (c, next) => {
21074
21446
  const host = (c.req.header("host") ?? "").split(":")[0];
21075
21447
  const path2 = c.req.path;
21076
- if (path2 === "/favicon.ico" || path2.startsWith("/assets/") || path2.startsWith("/brand/") || PWA_PUBLIC_PATHS.has(path2)) {
21448
+ if (path2 === "/favicon.ico" || path2.startsWith("/assets/") || path2.startsWith("/brand/") || // Public free/busy is read by the booking page (a separate Cloudflare Pages
21449
+ // origin) against the brand host with no admin cookie. It is public by
21450
+ // design — open slots only, no :Meeting detail — so it bypasses the gate
21451
+ // exactly like /brand/. The detail-leak guard lives in the route + its test.
21452
+ path2 === "/api/calendar/free-busy" || PWA_PUBLIC_PATHS.has(path2)) {
21077
21453
  await next();
21078
21454
  return;
21079
21455
  }
@@ -21110,25 +21486,26 @@ app52.use("*", async (c, next) => {
21110
21486
  }
21111
21487
  return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(c), redirect: path2 }), 200);
21112
21488
  });
21113
- app52.route("/api/health", health_default);
21114
- app52.route("/api/chat", chatRoutes);
21115
- app52.route("/api/whatsapp", whatsapp_default);
21116
- app52.route("/api/whatsapp-reader", whatsapp_reader_default);
21117
- app52.route("/api/public-reader", public_reader_default);
21118
- app52.route("/api/webchat", createWebchatRoutes({
21489
+ app54.route("/api/health", health_default);
21490
+ app54.route("/api/chat", chatRoutes);
21491
+ app54.route("/api/whatsapp", whatsapp_default);
21492
+ app54.route("/api/whatsapp-reader", whatsapp_reader_default);
21493
+ app54.route("/api/public-reader", public_reader_default);
21494
+ app54.route("/api/webchat", createWebchatRoutes({
21119
21495
  handleInbound: (input) => webchatGateway.handleInbound(input),
21120
21496
  // Task 940 — the permission relay's pointer read + verdict write.
21121
21497
  pendingPromptFor: (key) => webchatGateway.pendingPromptFor(key),
21122
21498
  deliveryFailureFor: (key) => webchatGateway.deliveryFailureFor(key),
21123
21499
  resolvePermissionVerdict: (key, requestId, behavior) => webchatGateway.resolvePermissionVerdict(key, requestId, behavior)
21124
21500
  }));
21125
- app52.route("/api/webchat/greeting", webchat_greeting_default);
21126
- app52.route("/api/telegram", telegram_default);
21127
- app52.route("/api/quickbooks", quickbooks_default);
21128
- app52.route("/api/onboarding", onboarding_default);
21129
- app52.route("/api/admin", admin_default);
21130
- app52.route("/api/access", access_default);
21131
- app52.route("/api/session", session_default2);
21501
+ app54.route("/api/webchat/greeting", webchat_greeting_default);
21502
+ app54.route("/api/telegram", telegram_default);
21503
+ app54.route("/api/quickbooks", quickbooks_default);
21504
+ app54.route("/api/onboarding", onboarding_default);
21505
+ app54.route("/api/admin", admin_default);
21506
+ app54.route("/api/access", access_default);
21507
+ app54.route("/api/session", session_default2);
21508
+ app54.route("/api/calendar", calendar_public_default);
21132
21509
  var SAFE_SLUG_RE2 = /^[a-z][a-z0-9-]{2,49}$/;
21133
21510
  var SAFE_FILENAME_RE = /^[a-z0-9_][a-z0-9_.-]{0,99}$/i;
21134
21511
  var IMAGE_MIME = {
@@ -21140,7 +21517,7 @@ var IMAGE_MIME = {
21140
21517
  ".svg": "image/svg+xml",
21141
21518
  ".ico": "image/x-icon"
21142
21519
  };
21143
- app52.get("/agent-assets/:slug/:filename", (c) => {
21520
+ app54.get("/agent-assets/:slug/:filename", (c) => {
21144
21521
  const slug = c.req.param("slug");
21145
21522
  const filename = c.req.param("filename");
21146
21523
  if (!SAFE_SLUG_RE2.test(slug)) {
@@ -21169,13 +21546,13 @@ app52.get("/agent-assets/:slug/:filename", (c) => {
21169
21546
  const ext = "." + filename.split(".").pop()?.toLowerCase();
21170
21547
  const contentType = IMAGE_MIME[ext] || "application/octet-stream";
21171
21548
  console.log(`[agent-assets] serve slug=${slug} file=${filename} status=200`);
21172
- const body = readFileSync32(filePath);
21549
+ const body = readFileSync33(filePath);
21173
21550
  return c.body(body, 200, {
21174
21551
  "Content-Type": contentType,
21175
21552
  "Cache-Control": "public, max-age=3600"
21176
21553
  });
21177
21554
  });
21178
- app52.get("/generated/:filename", (c) => {
21555
+ app54.get("/generated/:filename", (c) => {
21179
21556
  const filename = c.req.param("filename");
21180
21557
  if (!SAFE_FILENAME_RE.test(filename) || filename.includes("..")) {
21181
21558
  console.error(`[generated] serve file=${filename} status=403`);
@@ -21199,22 +21576,22 @@ app52.get("/generated/:filename", (c) => {
21199
21576
  const ext = "." + filename.split(".").pop()?.toLowerCase();
21200
21577
  const contentType = IMAGE_MIME[ext] || "application/octet-stream";
21201
21578
  console.log(`[generated] serve file=${filename} status=200`);
21202
- const body = readFileSync32(filePath);
21579
+ const body = readFileSync33(filePath);
21203
21580
  return c.body(body, 200, {
21204
21581
  "Content-Type": contentType,
21205
21582
  "Cache-Control": "public, max-age=86400"
21206
21583
  });
21207
21584
  });
21208
- app52.route("/sites", sites_default);
21209
- app52.route("/listings", listings_default);
21210
- app52.route("/v", visitor_event_default);
21211
- app52.route("/v", visitor_consent_default);
21585
+ app54.route("/sites", sites_default);
21586
+ app54.route("/listings", listings_default);
21587
+ app54.route("/v", visitor_event_default);
21588
+ app54.route("/v", visitor_consent_default);
21212
21589
  var htmlCache = /* @__PURE__ */ new Map();
21213
21590
  var brandLogoPath = "/brand/maxy-monochrome.png";
21214
21591
  var brandIconPath = "/brand/maxy-monochrome.png";
21215
21592
  if (BRAND_JSON_PATH && existsSync31(BRAND_JSON_PATH)) {
21216
21593
  try {
21217
- const fullBrand = JSON.parse(readFileSync32(BRAND_JSON_PATH, "utf-8"));
21594
+ const fullBrand = JSON.parse(readFileSync33(BRAND_JSON_PATH, "utf-8"));
21218
21595
  if (fullBrand.assets?.logo) brandLogoPath = `/brand/${fullBrand.assets.logo}`;
21219
21596
  brandIconPath = fullBrand.assets?.icon ? `/brand/${fullBrand.assets.icon}` : brandLogoPath;
21220
21597
  } catch {
@@ -21240,11 +21617,12 @@ var brandScript = `<script>window.__BRAND__=${JSON.stringify({
21240
21617
  })}</script>`;
21241
21618
  var brandThemeColor = BRAND.defaultColors?.primary ?? "#000000";
21242
21619
  var brandBackgroundColor = BRAND.defaultColors?.background ?? "#ffffff";
21243
- var brandIconFsPath = resolve33(process.cwd(), "public", brandIconPath.replace(/^\//, ""));
21244
- var brandIconExists = existsSync31(brandIconFsPath);
21620
+ var brandAppIconsExist = [brandAppIcon192Path, brandAppIcon512Path, brandMaskableIconPath].every(
21621
+ (p) => existsSync31(resolve33(process.cwd(), "public", p.replace(/^\//, "")))
21622
+ );
21245
21623
  var SW_SOURCE = (() => {
21246
21624
  try {
21247
- return readFileSync32(resolve33(process.cwd(), "public", "sw.js"), "utf-8");
21625
+ return readFileSync33(resolve33(process.cwd(), "public", "sw.js"), "utf-8");
21248
21626
  } catch {
21249
21627
  return null;
21250
21628
  }
@@ -21252,9 +21630,9 @@ var SW_SOURCE = (() => {
21252
21630
  function readInstalledVersion() {
21253
21631
  try {
21254
21632
  if (!PLATFORM_ROOT8) return "unknown";
21255
- const versionFile = join29(PLATFORM_ROOT8, "config", `.${BRAND.hostname}-version`);
21633
+ const versionFile = join30(PLATFORM_ROOT8, "config", `.${BRAND.hostname}-version`);
21256
21634
  if (!existsSync31(versionFile)) return "unknown";
21257
- const content = readFileSync32(versionFile, "utf-8").trim();
21635
+ const content = readFileSync33(versionFile, "utf-8").trim();
21258
21636
  return content || "unknown";
21259
21637
  } catch {
21260
21638
  return "unknown";
@@ -21295,12 +21673,12 @@ var clientErrorReporterScript = `<script>
21295
21673
  function cachedHtml(file) {
21296
21674
  let html = htmlCache.get(file);
21297
21675
  if (!html) {
21298
- html = readFileSync32(resolve33(process.cwd(), "public", file), "utf-8");
21676
+ html = readFileSync33(resolve33(process.cwd(), "public", file), "utf-8");
21299
21677
  const productNameEsc = escapeHtml(BRAND.productName);
21300
21678
  html = html.replace(/<title>([^<]*)<\/title>/, (_match, inner) => `<title>${escapeHtml(inner).replace(/Maxy/g, productNameEsc)}</title>`);
21301
21679
  html = html.replace('href="/favicon.ico"', `href="${escapeHtml(brandFaviconPath)}"`);
21302
21680
  const pwaSurface = pwaSurfaceForShell(file);
21303
- const pwaHead = pwaSurface ? "\n" + pwaHeadTags(pwaSurface, { iconPath: brandIconPath, themeColor: brandThemeColor }) + "\n" : "";
21681
+ const pwaHead = pwaSurface ? "\n" + pwaHeadTags(pwaSurface, { appleTouchIcon: brandAppIcon512Path, themeColor: brandThemeColor }) + "\n" : "";
21304
21682
  const headInjection = (file === "index.html" ? `${brandScript}
21305
21683
  ${versionScript}
21306
21684
  ${clientErrorReporterScript}
@@ -21312,13 +21690,13 @@ ${clientErrorReporterScript}
21312
21690
  return html;
21313
21691
  }
21314
21692
  function loadBrandingCache(agentSlug) {
21315
- const configDir2 = join29(homedir3(), BRAND.configDir);
21693
+ const configDir2 = join30(homedir3(), BRAND.configDir);
21316
21694
  try {
21317
21695
  const accountId = getDefaultAccountId();
21318
21696
  if (!accountId) return null;
21319
- const cachePath = join29(configDir2, "branding-cache", accountId, `${agentSlug}.json`);
21697
+ const cachePath = join30(configDir2, "branding-cache", accountId, `${agentSlug}.json`);
21320
21698
  if (!existsSync31(cachePath)) return null;
21321
- return JSON.parse(readFileSync32(cachePath, "utf-8"));
21699
+ return JSON.parse(readFileSync33(cachePath, "utf-8"));
21322
21700
  } catch {
21323
21701
  return null;
21324
21702
  }
@@ -21362,7 +21740,7 @@ function brandedPublicHtml(agentSlug) {
21362
21740
  function agentUnavailableHtml() {
21363
21741
  return `<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>${escapeHtml(BRAND.productName)}</title></head><body style="font-family:system-ui,sans-serif;max-width:32rem;margin:4rem auto;padding:0 1.5rem;color:#222"><h1 style="font-size:1.25rem">Agent unavailable</h1><p>This agent isn't available right now. If you reached this page from a saved link, the agent may have been turned off.</p></body></html>`;
21364
21742
  }
21365
- app52.get("/", (c) => {
21743
+ app54.get("/", (c) => {
21366
21744
  const host = (c.req.header("host") ?? "").split(":")[0];
21367
21745
  const klass = classifyHost(host, getOperatorDomains(), isPublicHost);
21368
21746
  if (klass === "operator") {
@@ -21384,12 +21762,12 @@ app52.get("/", (c) => {
21384
21762
  console.log(`[host-class] host=${host} class=admin served=index.html`);
21385
21763
  return c.html(cachedHtml("index.html"));
21386
21764
  });
21387
- app52.get("/public", (c) => {
21765
+ app54.get("/public", (c) => {
21388
21766
  const host = (c.req.header("host") ?? "").split(":")[0];
21389
21767
  if (isPublicHost(host)) return c.text("Not found", 404);
21390
21768
  return c.html(cachedHtml("public.html"));
21391
21769
  });
21392
- app52.get("/public-chat", (c) => {
21770
+ app54.get("/public-chat", (c) => {
21393
21771
  const host = (c.req.header("host") ?? "").split(":")[0];
21394
21772
  if (isPublicHost(host)) return c.text("Not found", 404);
21395
21773
  return c.html(cachedHtml("public.html"));
@@ -21408,12 +21786,12 @@ async function logViewerFetch(c, next) {
21408
21786
  duration_ms: Date.now() - start
21409
21787
  });
21410
21788
  }
21411
- app52.use("/vnc-viewer.html", logViewerFetch);
21412
- app52.use("/vnc-popout.html", logViewerFetch);
21413
- app52.get("/vnc-popout.html", (c) => {
21789
+ app54.use("/vnc-viewer.html", logViewerFetch);
21790
+ app54.use("/vnc-popout.html", logViewerFetch);
21791
+ app54.get("/vnc-popout.html", (c) => {
21414
21792
  let html = htmlCache.get("vnc-popout.html");
21415
21793
  if (!html) {
21416
- html = readFileSync32(resolve33(process.cwd(), "public", "vnc-popout.html"), "utf-8");
21794
+ html = readFileSync33(resolve33(process.cwd(), "public", "vnc-popout.html"), "utf-8");
21417
21795
  const name = escapeHtml(BRAND.productName);
21418
21796
  html = html.replace("<title>Browser \u2014 Maxy</title>", `<title>${name}</title>`);
21419
21797
  html = html.replace("</head>", ` ${brandScript}
@@ -21423,7 +21801,7 @@ app52.get("/vnc-popout.html", (c) => {
21423
21801
  }
21424
21802
  return c.html(html);
21425
21803
  });
21426
- app52.post("/api/vnc/client-event", async (c) => {
21804
+ app54.post("/api/vnc/client-event", async (c) => {
21427
21805
  let body;
21428
21806
  try {
21429
21807
  body = await c.req.json();
@@ -21444,25 +21822,27 @@ app52.post("/api/vnc/client-event", async (c) => {
21444
21822
  });
21445
21823
  return c.json({ ok: true });
21446
21824
  });
21447
- app52.get("/g/:slug", (c) => {
21825
+ app54.get("/g/:slug", (c) => {
21448
21826
  return c.html(brandedPublicHtml(resolveDefaultSlug() ?? void 0));
21449
21827
  });
21450
21828
  for (const pwa of PWA_SURFACES) {
21451
- app52.get(pwa.manifestPath, (c) => {
21829
+ app54.get(pwa.manifestPath, (c) => {
21452
21830
  const manifest = buildManifest(pwa, {
21453
21831
  productName: BRAND.productName,
21454
- iconPath: brandIconPath,
21832
+ appIcon192: brandAppIcon192Path,
21833
+ appIcon512: brandAppIcon512Path,
21834
+ maskableIcon: brandMaskableIconPath,
21455
21835
  themeColor: brandThemeColor,
21456
21836
  backgroundColor: brandBackgroundColor
21457
21837
  });
21458
21838
  c.header("Content-Type", MANIFEST_CONTENT_TYPE);
21459
21839
  console.log(
21460
- `[pwa] op=manifest surface=${pwa.surface} brand=${BRAND.productName} status=200 ct=${MANIFEST_CONTENT_TYPE} icon=${brandIconPath} iconExists=${brandIconExists}`
21840
+ `[pwa] op=manifest surface=${pwa.surface} brand=${BRAND.productName} status=200 ct=${MANIFEST_CONTENT_TYPE} icon=${brandAppIcon512Path} iconExists=${brandAppIconsExist}`
21461
21841
  );
21462
21842
  return c.body(JSON.stringify(manifest));
21463
21843
  });
21464
21844
  }
21465
- app52.get("/sw.js", (c) => {
21845
+ app54.get("/sw.js", (c) => {
21466
21846
  if (SW_SOURCE == null) {
21467
21847
  console.error("[pwa] op=sw status=500 reason=sw-source-missing");
21468
21848
  return c.text("Service worker unavailable", 500);
@@ -21471,27 +21851,32 @@ app52.get("/sw.js", (c) => {
21471
21851
  console.log(`[pwa] op=sw status=200 ct=${SW_CONTENT_TYPE}`);
21472
21852
  return c.body(SW_SOURCE);
21473
21853
  });
21474
- app52.get("/graph", (c) => {
21854
+ app54.get("/graph", (c) => {
21475
21855
  const host = (c.req.header("host") ?? "").split(":")[0];
21476
21856
  if (isPublicHost(host) || isOperatorHost(host, getOperatorDomains())) return c.text("Not found", 404);
21477
21857
  return c.html(cachedHtml("graph.html"));
21478
21858
  });
21479
- app52.get("/chat", (c) => {
21859
+ app54.get("/chat", (c) => {
21480
21860
  const host = (c.req.header("host") ?? "").split(":")[0];
21481
21861
  if (isPublicHost(host)) return c.text("Not found", 404);
21482
21862
  return c.html(cachedHtml("chat.html"));
21483
21863
  });
21484
- app52.get("/data", (c) => {
21864
+ app54.get("/data", (c) => {
21485
21865
  const host = (c.req.header("host") ?? "").split(":")[0];
21486
21866
  if (isPublicHost(host)) return c.text("Not found", 404);
21487
21867
  return c.html(cachedHtml("data.html"));
21488
21868
  });
21489
- app52.get("/browser", (c) => {
21869
+ app54.get("/calendar", (c) => {
21870
+ const host = (c.req.header("host") ?? "").split(":")[0];
21871
+ if (isPublicHost(host)) return c.text("Not found", 404);
21872
+ return c.html(cachedHtml("calendar.html"));
21873
+ });
21874
+ app54.get("/browser", (c) => {
21490
21875
  const host = (c.req.header("host") ?? "").split(":")[0];
21491
21876
  if (isPublicHost(host) || isOperatorHost(host, getOperatorDomains())) return c.text("Not found", 404);
21492
21877
  return c.html(cachedHtml("browser.html"));
21493
21878
  });
21494
- app52.get("/:slug", async (c, next) => {
21879
+ app54.get("/:slug", async (c, next) => {
21495
21880
  const slug = c.req.param("slug");
21496
21881
  if (AGENT_SLUG_PATTERN.test(`/${slug}`)) {
21497
21882
  const account = resolveAccount();
@@ -21506,13 +21891,13 @@ app52.get("/:slug", async (c, next) => {
21506
21891
  await next();
21507
21892
  });
21508
21893
  if (brandFaviconPath !== "/favicon.ico") {
21509
- app52.get("/favicon.ico", (c) => {
21894
+ app54.get("/favicon.ico", (c) => {
21510
21895
  c.header("Cache-Control", "public, max-age=300");
21511
21896
  return c.redirect(brandFaviconPath, 302);
21512
21897
  });
21513
21898
  }
21514
- app52.use("/*", serveStatic({ root: "./public" }));
21515
- app52.all("*", (c) => {
21899
+ app54.use("/*", serveStatic({ root: "./public" }));
21900
+ app54.all("*", (c) => {
21516
21901
  const host = (c.req.header("host") ?? "").split(":")[0];
21517
21902
  const path2 = c.req.path;
21518
21903
  if (isPublicHost(host)) {
@@ -21526,8 +21911,30 @@ app52.all("*", (c) => {
21526
21911
  });
21527
21912
  var port = requirePortEnv("MAXY_UI_INTERNAL_PORT", { tag: "ui-server" });
21528
21913
  var hostname = process.env.HOSTNAME ?? "127.0.0.1";
21529
- var httpServer = serve({ fetch: app52.fetch, port, hostname });
21914
+ var httpServer = serve({ fetch: app54.fetch, port, hostname });
21530
21915
  console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
21916
+ {
21917
+ const reconcilePlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join30(__dirname, "..");
21918
+ const reconcileScript = resolve33(reconcilePlatformRoot, "plugins/scheduling/mcp/dist/scripts/reconcile-bookings.js");
21919
+ const RECONCILE_INTERVAL_MS = 12e4;
21920
+ const runReconcile = () => {
21921
+ if (!existsSync31(reconcileScript)) return;
21922
+ try {
21923
+ const child = spawn3(process.execPath, [reconcileScript], {
21924
+ env: { ...process.env, PLATFORM_ROOT: reconcilePlatformRoot },
21925
+ stdio: "inherit"
21926
+ });
21927
+ child.unref();
21928
+ child.on("error", (err) => console.error(`[calendar-reconcile] spawn-failed err="${err.message}"`));
21929
+ } catch (err) {
21930
+ console.error(`[calendar-reconcile] spawn-failed err="${err.message}"`);
21931
+ }
21932
+ };
21933
+ const firstRun = setTimeout(runReconcile, 15e3);
21934
+ firstRun.unref();
21935
+ const loop = setInterval(runReconcile, RECONCILE_INTERVAL_MS);
21936
+ loop.unref();
21937
+ }
21531
21938
  {
21532
21939
  const census = pwaCensus({
21533
21940
  shellHtml: (file) => {
@@ -21537,7 +21944,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
21537
21944
  return null;
21538
21945
  }
21539
21946
  },
21540
- iconExists: () => brandIconExists
21947
+ iconExists: () => brandAppIconsExist
21541
21948
  });
21542
21949
  console.log(
21543
21950
  `[pwa-census] surfaces=${census.surfaces} manifest-linked=${census.manifestLinked} icons-present=${census.iconsPresent}`
@@ -21569,6 +21976,7 @@ var SUBAPP_MANIFEST = [
21569
21976
  { prefix: "/api/admin", file: "server/routes/admin/index.ts", subapp: admin_default },
21570
21977
  { prefix: "/api/access", file: "server/routes/access/index.ts", subapp: access_default },
21571
21978
  { prefix: "/api/session", file: "server/routes/session.ts", subapp: session_default2 },
21979
+ { prefix: "/api/calendar", file: "server/routes/calendar-public.ts", subapp: calendar_public_default },
21572
21980
  { prefix: "/api/_client-error", file: "server/routes/client-error.ts", subapp: client_error_default },
21573
21981
  { prefix: "/listings", file: "server/routes/listings.ts", subapp: listings_default },
21574
21982
  { prefix: "/v", file: "server/routes/visitor-event.ts", subapp: visitor_event_default },
@@ -21580,7 +21988,7 @@ for (const m of SUBAPP_MANIFEST) {
21580
21988
  }
21581
21989
  try {
21582
21990
  const registered = [];
21583
- for (const r of app52.routes ?? []) {
21991
+ for (const r of app54.routes ?? []) {
21584
21992
  if (typeof r.path !== "string" || r.path.includes(":") || r.path.includes("*")) continue;
21585
21993
  if (AGENT_SLUG_PATTERN.test(r.path)) {
21586
21994
  registered.push({ method: (r.method ?? "ALL").toUpperCase(), path: r.path });
@@ -21624,7 +22032,7 @@ async function runAdminUserReconcileTick() {
21624
22032
  console.error("[adminuser-self-heal] skip reason=no-users-file");
21625
22033
  return;
21626
22034
  }
21627
- const usersRaw = readFileSync32(USERS_FILE, "utf-8").trim();
22035
+ const usersRaw = readFileSync33(USERS_FILE, "utf-8").trim();
21628
22036
  if (!usersRaw) {
21629
22037
  console.error("[adminuser-self-heal] skip reason=empty-users-file");
21630
22038
  return;
@@ -21738,7 +22146,7 @@ if (bootAccountConfig?.whatsapp) {
21738
22146
  }
21739
22147
  init({
21740
22148
  configDir: configDirForWhatsApp,
21741
- platformRoot: resolve33(process.env.MAXY_PLATFORM_ROOT ?? join29(__dirname, "..")),
22149
+ platformRoot: resolve33(process.env.MAXY_PLATFORM_ROOT ?? join30(__dirname, "..")),
21742
22150
  accountConfig: bootAccountConfig,
21743
22151
  onMessage: async (msg) => {
21744
22152
  if (msg.isOwnerMirror) {