@rubytech/create-maxy-code 0.1.481 → 0.1.483
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.
- package/package.json +1 -1
- package/payload/platform/plugins/admin/PLUGIN.md +1 -0
- package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +27 -1
- package/payload/platform/plugins/admin/skills/whats-new/SKILL.md +57 -0
- package/payload/platform/plugins/docs/references/admin-ui.md +25 -0
- package/payload/platform/plugins/docs/references/neo4j.md +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/typed-edge-schema.test.js +8 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/typed-edge-schema.test.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/typed-edge-schema.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/typed-edge-schema.js +5 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/typed-edge-schema.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest.test.js +10 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest.test.js.map +1 -1
- package/payload/platform/plugins/memory/references/schema-knowledge-work.md +2 -1
- package/payload/platform/plugins/voice-mirror/mcp/dist/tools/voice-distil-profile.d.ts +26 -0
- package/payload/platform/plugins/voice-mirror/mcp/dist/tools/voice-distil-profile.d.ts.map +1 -1
- package/payload/platform/plugins/voice-mirror/mcp/dist/tools/voice-distil-profile.js +46 -4
- package/payload/platform/plugins/voice-mirror/mcp/dist/tools/voice-distil-profile.js.map +1 -1
- package/payload/platform/plugins/voice-mirror/mcp/scripts/smoke.mjs +30 -0
- package/payload/platform/plugins/whatsapp/PLUGIN.md +3 -0
- package/payload/platform/plugins/whatsapp/mcp/dist/index.js +25 -3
- package/payload/platform/plugins/whatsapp/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/whatsapp/references/channels-whatsapp.md +12 -0
- package/payload/platform/plugins/whatsapp/skills/connect-whatsapp/SKILL.md +6 -0
- package/payload/platform/plugins/whatsapp/skills/manage-whatsapp-config/SKILL.md +10 -0
- package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js +1 -0
- package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js.map +1 -1
- package/payload/platform/templates/specialists/agents/personal-assistant.md +1 -1
- package/payload/server/public/activity.html +6 -6
- package/payload/server/public/assets/{AdminLoginScreens-DXYJWXaX.js → AdminLoginScreens-Jw45pvb2.js} +1 -1
- package/payload/server/public/assets/AdminShell-DR6AzflZ.js +2 -0
- package/payload/server/public/assets/{Checkbox-C08Ulc_a.js → Checkbox-D7_S2EUI.js} +1 -1
- package/payload/server/public/assets/activity-CKbv3zFM.js +1 -0
- package/payload/server/public/assets/admin-DXUspIjz.js +1 -0
- package/payload/server/public/assets/{arc-BHXuzTwg.js → arc-D-QzyndF.js} +1 -1
- package/payload/server/public/assets/architecture-YZFGNWBL-CGvD4r9F.js +1 -0
- package/payload/server/public/assets/{architectureDiagram-Q4EWVU46-DsN_vxau.js → architectureDiagram-Q4EWVU46-W7f0Us4S.js} +1 -1
- package/payload/server/public/assets/{blockDiagram-DXYQGD6D-CvNat1uB.js → blockDiagram-DXYQGD6D-CfPS4FXg.js} +1 -1
- package/payload/server/public/assets/bot-GC7iZjtW.js +1 -0
- package/payload/server/public/assets/browser-BI-wi3CM.js +1 -0
- package/payload/server/public/assets/{c4Diagram-AHTNJAMY-E9yqTPTW.js → c4Diagram-AHTNJAMY-Dq60cLVI.js} +1 -1
- package/payload/server/public/assets/calendar-DXnv4F7l.js +1 -0
- package/payload/server/public/assets/channel-Bpg-ADC0.js +1 -0
- package/payload/server/public/assets/chat-CAdgaPW1.js +1 -0
- package/payload/server/public/assets/chevron-down-BCLf1wKu.js +1 -0
- package/payload/server/public/assets/chevron-left-BNtL-CjS.js +1 -0
- package/payload/server/public/assets/chevron-right-Bu5hsjy5.js +1 -0
- package/payload/server/public/assets/{chunk-2KRD3SAO-DpzI_0EY.js → chunk-2KRD3SAO-CXDjuDN0.js} +1 -1
- package/payload/server/public/assets/{chunk-336JU56O-eR_I9Eu4.js → chunk-336JU56O-926ox0tj.js} +2 -2
- package/payload/server/public/assets/chunk-426QAEUC-1poG7Nd3.js +1 -0
- package/payload/server/public/assets/{chunk-4BX2VUAB-D-6xk1t8.js → chunk-4BX2VUAB-C2nrPCfN.js} +1 -1
- package/payload/server/public/assets/{chunk-4TB4RGXK-BkqUcFpL.js → chunk-4TB4RGXK-CuW3FOR6.js} +1 -1
- package/payload/server/public/assets/{chunk-55IACEB6-ChCl2pSP.js → chunk-55IACEB6-B4_-In90.js} +1 -1
- package/payload/server/public/assets/{chunk-5FUZZQ4R-DasGsg54.js → chunk-5FUZZQ4R-DnYGr9CP.js} +1 -1
- package/payload/server/public/assets/{chunk-5PVQY5BW-DXlHUX1h.js → chunk-5PVQY5BW-BYCOi9bf.js} +1 -1
- package/payload/server/public/assets/{chunk-67CJDMHE-BZfVpTsE.js → chunk-67CJDMHE-CBxsC4JP.js} +1 -1
- package/payload/server/public/assets/{chunk-7N4EOEYR-C6U5TIgT.js → chunk-7N4EOEYR-CAACaqFr.js} +1 -1
- package/payload/server/public/assets/{chunk-AA7GKIK3-CA7cVDmw.js → chunk-AA7GKIK3-BoamZdS6.js} +1 -1
- package/payload/server/public/assets/{chunk-BSJP7CBP-DxN2GC3p.js → chunk-BSJP7CBP-CHk82eFy.js} +1 -1
- package/payload/server/public/assets/{chunk-CIAEETIT-C0-4wA8y.js → chunk-CIAEETIT-DdeW9zxl.js} +1 -1
- package/payload/server/public/assets/{chunk-EDXVE4YY--kyb1rLc.js → chunk-EDXVE4YY-DJqSRv2w.js} +1 -1
- package/payload/server/public/assets/{chunk-ENJZ2VHE-FBN6hR7V.js → chunk-ENJZ2VHE-BRi4BwM5.js} +1 -1
- package/payload/server/public/assets/{chunk-FMBD7UC4-Lpge4M5g.js → chunk-FMBD7UC4-OJp2CtUE.js} +1 -1
- package/payload/server/public/assets/{chunk-FOC6F5B3-CM1rwOou.js → chunk-FOC6F5B3-BUAQ6spG.js} +1 -1
- package/payload/server/public/assets/{chunk-ICPOFSXX-JdYLY3yu.js → chunk-ICPOFSXX-DeRggD-Q.js} +2 -2
- package/payload/server/public/assets/{chunk-K5T4RW27-DCUblGBr.js → chunk-K5T4RW27-EHiXZ8Py.js} +1 -1
- package/payload/server/public/assets/{chunk-KGLVRYIC-BP_cide2.js → chunk-KGLVRYIC-tOajPcpn.js} +1 -1
- package/payload/server/public/assets/{chunk-LIHQZDEY-DjMa1lOl.js → chunk-LIHQZDEY-uwO6oHIU.js} +1 -1
- package/payload/server/public/assets/{chunk-ORNJ4GCN-D51eJAoV.js → chunk-ORNJ4GCN-Dpa1_BCA.js} +1 -1
- package/payload/server/public/assets/{chunk-OYMX7WX6-4hWIO544.js → chunk-OYMX7WX6-ChdDqaQL.js} +1 -1
- package/payload/server/public/assets/chunk-QZHKN3VN-GN1tIO8J.js +1 -0
- package/payload/server/public/assets/{chunk-U2HBQHQK-BwdQf9cS.js → chunk-U2HBQHQK-5JgWosPv.js} +1 -1
- package/payload/server/public/assets/{chunk-X2U36JSP-DmDxwLOg.js → chunk-X2U36JSP-BGJqysB2.js} +1 -1
- package/payload/server/public/assets/{chunk-XPW4576I-JQyAB7ND.js → chunk-XPW4576I-GERE2ekx.js} +1 -1
- package/payload/server/public/assets/{chunk-YZCP3GAM-BonLdCzI.js → chunk-YZCP3GAM-BskqX1V5.js} +1 -1
- package/payload/server/public/assets/{chunk-ZZ45TVLE-BXKB2wSk.js → chunk-ZZ45TVLE-DG1iY-L9.js} +1 -1
- package/payload/server/public/assets/classDiagram-6PBFFD2Q-DMGVq8Jv.js +1 -0
- package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-DfddT8UV.js +1 -0
- package/payload/server/public/assets/clone-Cc9vFIEk.js +1 -0
- package/payload/server/public/assets/{cose-bilkent-S5V4N54A-BDnd4Blw.js → cose-bilkent-S5V4N54A-C8Rl4ykh.js} +1 -1
- package/payload/server/public/assets/{dagre-BmexzuER.js → dagre-DTlv-FFy.js} +1 -1
- package/payload/server/public/assets/{dagre-KV5264BT-D4WVuwPj.js → dagre-KV5264BT-C0YrJPF3.js} +1 -1
- package/payload/server/public/assets/data-D7cYlorm.js +1 -0
- package/payload/server/public/assets/{diagram-5BDNPKRD-Bbf4mT8a.js → diagram-5BDNPKRD-Dfk5oepI.js} +1 -1
- package/payload/server/public/assets/{diagram-G4DWMVQ6-BpqVTUmx.js → diagram-G4DWMVQ6-QRj55DiB.js} +1 -1
- package/payload/server/public/assets/{diagram-MMDJMWI5-DY-k5k6r.js → diagram-MMDJMWI5-BAPKsohU.js} +1 -1
- package/payload/server/public/assets/{diagram-TYMM5635-CXHxwacS.js → diagram-TYMM5635-C3iZXPkC.js} +1 -1
- package/payload/server/public/assets/{erDiagram-SMLLAGMA-BNKEc3l8.js → erDiagram-SMLLAGMA-DIbI7g_5.js} +1 -1
- package/payload/server/public/assets/{flatten-jn8bZgcI.js → flatten-DlAJeN0L.js} +1 -1
- package/payload/server/public/assets/{flowDiagram-DWJPFMVM-CbAeuJsi.js → flowDiagram-DWJPFMVM-l3eJJr8R.js} +1 -1
- package/payload/server/public/assets/{ganttDiagram-T4ZO3ILL-CeeNnL4l.js → ganttDiagram-T4ZO3ILL-aaEzHxIq.js} +1 -1
- package/payload/server/public/assets/gitGraph-7Q5UKJZL-DtjXE0UI.js +1 -0
- package/payload/server/public/assets/{gitGraphDiagram-UUTBAWPF-DG8RCtfg.js → gitGraphDiagram-UUTBAWPF-Di-1eg_m.js} +1 -1
- package/payload/server/public/assets/graph-Cl72j3UC.js +51 -0
- package/payload/server/public/assets/{graph-labels-LeIhl7ZG.js → graph-labels-7p-mVi9M.js} +1 -1
- package/payload/server/public/assets/{graphlib-BkDExPQi.js → graphlib-D5awC2yp.js} +1 -1
- package/payload/server/public/assets/info-OMHHGYJF-CmArhGYf.js +1 -0
- package/payload/server/public/assets/infoDiagram-42DDH7IO-utWz7oZy.js +2 -0
- package/payload/server/public/assets/{isEmpty-Dkk6_L-B.js → isEmpty-Dh1dF-tM.js} +1 -1
- package/payload/server/public/assets/{ishikawaDiagram-UXIWVN3A-D_iMHV64.js → ishikawaDiagram-UXIWVN3A-CLOd_cNf.js} +1 -1
- package/payload/server/public/assets/{journeyDiagram-VCZTEJTY-BvPRpulx.js → journeyDiagram-VCZTEJTY-cGyfW6Tg.js} +1 -1
- package/payload/server/public/assets/{kanban-definition-6JOO6SKY-CkWaXWo2.js → kanban-definition-6JOO6SKY-CRTKWPlh.js} +1 -1
- package/payload/server/public/assets/{line-BGQT9unx.js → line-B8Brm0TD.js} +1 -1
- package/payload/server/public/assets/{linear-DzrYI8-8.js → linear-BHnSKXBe.js} +1 -1
- package/payload/server/public/assets/{maximize-2-2MgPSwlx.js → maximize-2-HcfeeOdJ.js} +1 -1
- package/payload/server/public/assets/{mermaid-parser.core-DrMY7FMe.js → mermaid-parser.core-BtNpdQct.js} +2 -2
- package/payload/server/public/assets/{mermaid.core-BWdf6fZ7.js → mermaid.core-BY87anQZ.js} +3 -3
- package/payload/server/public/assets/{mindmap-definition-QFDTVHPH-CgqM9h97.js → mindmap-definition-QFDTVHPH-rUSv9qN1.js} +1 -1
- package/payload/server/public/assets/{operator-CZifmNhp.js → operator-C0iaGPNT.js} +1 -1
- package/payload/server/public/assets/{ordinal-DuJYM7p1.js → ordinal-Bnl5GL-x.js} +1 -1
- package/payload/server/public/assets/packet-4T2RLAQJ-BTfK0Zw4.js +1 -0
- package/payload/server/public/assets/page-DPQS0X5M.js +32 -0
- package/payload/server/public/assets/page-DsV3rID5.js +1 -0
- package/payload/server/public/assets/{pdf-render-riHufgrn.js → pdf-render-CrpAi4Ba.js} +1 -1
- package/payload/server/public/assets/pie-ZZUOXDRM-BvtPeMQY.js +1 -0
- package/payload/server/public/assets/{pieDiagram-DEJITSTG-BeUGoYS7.js → pieDiagram-DEJITSTG-Cjn9IP-C.js} +1 -1
- package/payload/server/public/assets/{public-VtmyYz8E.js → public-DAn13pnw.js} +1 -1
- package/payload/server/public/assets/{quadrantDiagram-34T5L4WZ-CX82WgKy.js → quadrantDiagram-34T5L4WZ-BkSHkSsn.js} +1 -1
- package/payload/server/public/assets/radar-PYXPWWZC-D2UXvQgn.js +1 -0
- package/payload/server/public/assets/{reduce-COyotL2V.js → reduce-Bp5oNw1n.js} +1 -1
- package/payload/server/public/assets/{requirementDiagram-MS252O5E-CzqebQXj.js → requirementDiagram-MS252O5E-hLwgyB-F.js} +1 -1
- package/payload/server/public/assets/{rotate-ccw-DaZdB2cz.js → rotate-ccw-CCbbX5ef.js} +1 -1
- package/payload/server/public/assets/{sankeyDiagram-XADWPNL6-CaHi1yzU.js → sankeyDiagram-XADWPNL6-D6Dl1RIa.js} +1 -1
- package/payload/server/public/assets/{sequenceDiagram-FGHM5R23-BF2AeiKW.js → sequenceDiagram-FGHM5R23-j0QBqkbL.js} +1 -1
- package/payload/server/public/assets/{stateDiagram-FHFEXIEX-DtUCA8Ih.js → stateDiagram-FHFEXIEX-TvEBNsuV.js} +1 -1
- package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-Ur4eM6rb.js +1 -0
- package/payload/server/public/assets/tasks-_Ij9BdOE.js +1 -0
- package/payload/server/public/assets/{time-entry-format-TD-PPEIu.js → time-entry-format-C-By1MbO.js} +1 -1
- package/payload/server/public/assets/{timeline-definition-GMOUNBTQ-D3Q1YrKY.js → timeline-definition-GMOUNBTQ-BfivOP-j.js} +1 -1
- package/payload/server/public/assets/treeView-SZITEDCU-0dE7FvM2.js +1 -0
- package/payload/server/public/assets/treemap-W4RFUUIX-BrwlXpr5.js +1 -0
- package/payload/server/public/assets/{triangle-alert-BaLhVNnn.js → triangle-alert-BDtoi5R0.js} +1 -1
- package/payload/server/public/assets/useCopyFeedback-CfbJc9iN.js +1 -0
- package/payload/server/public/assets/useSubAccountSwitcher-5jQsc23X.css +1 -0
- package/payload/server/public/assets/{useSubAccountSwitcher-HEVLb6Z9.js → useSubAccountSwitcher-sPN6lobE.js} +1 -1
- package/payload/server/public/assets/useVoiceRecorder-DOFBQ2gP.js +2 -0
- package/payload/server/public/assets/{vennDiagram-DHZGUBPP-DnjzQDGf.js → vennDiagram-DHZGUBPP-Bvfw9aFJ.js} +1 -1
- package/payload/server/public/assets/wardley-RL74JXVD-CO9HdfJ-.js +1 -0
- package/payload/server/public/assets/{wardleyDiagram-NUSXRM2D-T3WHrUvi.js → wardleyDiagram-NUSXRM2D-C9h04G6b.js} +1 -1
- package/payload/server/public/assets/{xychartDiagram-5P7HB3ND-2Efyv4h2.js → xychartDiagram-5P7HB3ND-BMvi2m2q.js} +1 -1
- package/payload/server/public/brand-defaults.css +4 -0
- package/payload/server/public/browser.html +4 -4
- package/payload/server/public/calendar.html +6 -5
- package/payload/server/public/chat.html +14 -12
- package/payload/server/public/data.html +11 -11
- package/payload/server/public/graph.html +11 -10
- package/payload/server/public/index.html +17 -14
- package/payload/server/public/operator.html +16 -14
- package/payload/server/public/public.html +14 -12
- package/payload/server/public/tasks.html +6 -6
- package/payload/server/server.js +422 -219
- package/payload/server/public/assets/AdminShell-UDgNN4CA.js +0 -2
- package/payload/server/public/assets/activity-06gsRDVv.js +0 -1
- package/payload/server/public/assets/admin-BhiHyORH.js +0 -1
- package/payload/server/public/assets/architecture-YZFGNWBL-Ct5q6ks1.js +0 -1
- package/payload/server/public/assets/browser-Cz4IKS47.js +0 -1
- package/payload/server/public/assets/calendar-D0xtNqH8.js +0 -1
- package/payload/server/public/assets/channel-PE9vBKEQ.js +0 -1
- package/payload/server/public/assets/chat-DwLJ830n.js +0 -1
- package/payload/server/public/assets/chevron-left-Cv-TFuEf.js +0 -1
- package/payload/server/public/assets/chunk-426QAEUC-BDNZtJsz.js +0 -1
- package/payload/server/public/assets/chunk-QZHKN3VN-B_Hris8x.js +0 -1
- package/payload/server/public/assets/classDiagram-6PBFFD2Q-D8Q05iMI.js +0 -1
- package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-Dm3hAxfm.js +0 -1
- package/payload/server/public/assets/clone-C-wOwvpY.js +0 -1
- package/payload/server/public/assets/data-DUDJfnjI.js +0 -1
- package/payload/server/public/assets/gitGraph-7Q5UKJZL-BBHsY16R.js +0 -1
- package/payload/server/public/assets/graph-BAoktBcx.js +0 -51
- package/payload/server/public/assets/info-OMHHGYJF-C1j5I_zx.js +0 -1
- package/payload/server/public/assets/infoDiagram-42DDH7IO-ZWkwHWEu.js +0 -2
- package/payload/server/public/assets/packet-4T2RLAQJ-CGiTbe0N.js +0 -1
- package/payload/server/public/assets/page-B4kllGd_.js +0 -32
- package/payload/server/public/assets/page-DbfE7QwJ.js +0 -1
- package/payload/server/public/assets/pie-ZZUOXDRM-BrK023dE.js +0 -1
- package/payload/server/public/assets/radar-PYXPWWZC-p7NRamXX.js +0 -1
- package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-a4FvB3L8.js +0 -1
- package/payload/server/public/assets/tasks-CvqVDbfO.js +0 -1
- package/payload/server/public/assets/treeView-SZITEDCU-CoWk8VoG.js +0 -1
- package/payload/server/public/assets/treemap-W4RFUUIX-BhWeGj8Y.js +0 -1
- package/payload/server/public/assets/useCopyFeedback-BhjPzRa3.js +0 -1
- package/payload/server/public/assets/useSubAccountSwitcher-CWRpVt1-.css +0 -1
- package/payload/server/public/assets/useVoiceRecorder-CfGgj3vm.js +0 -2
- package/payload/server/public/assets/wardley-RL74JXVD-C0q3Hqng.js +0 -1
- /package/payload/server/public/assets/{_baseFor-Bjj9It3Q.js → _baseFor-BBkmqPEk.js} +0 -0
- /package/payload/server/public/assets/{array-BrrxEo12.js → array-Bjtb2C_5.js} +0 -0
- /package/payload/server/public/assets/{cytoscape.esm-CTWJxUxo.js → cytoscape.esm-B1dQ664k.js} +0 -0
- /package/payload/server/public/assets/{defaultLocale-CFDbRV4k.js → defaultLocale-iwYA7ne8.js} +0 -0
- /package/payload/server/public/assets/{dist-BATfy7Sn.js → dist-CLmrZbY7.js} +0 -0
- /package/payload/server/public/assets/{init-dXCzGgLP.js → init-CxnBAgcf.js} +0 -0
- /package/payload/server/public/assets/{katex-CHA1JFYJ.js → katex-DojHoLIr.js} +0 -0
- /package/payload/server/public/assets/{path-CCRpdXhK.js → path-JZiojYzQ.js} +0 -0
- /package/payload/server/public/assets/{preload-helper-CH6UZRzu.js → preload-helper-CT1Z6Pdu.js} +0 -0
- /package/payload/server/public/assets/{rough.esm-Bud4g4fh.js → rough.esm-D5NinLFK.js} +0 -0
- /package/payload/server/public/assets/{src-DNdBRXXx.js → src-DQ0MSD6q.js} +0 -0
package/payload/server/server.js
CHANGED
|
@@ -1563,7 +1563,7 @@ var serveStatic = (options = { root: "" }) => {
|
|
|
1563
1563
|
import { readFileSync as readFileSync42, existsSync as existsSync38, watchFile, readdirSync as readdirSync26, statSync as statSync17 } from "fs";
|
|
1564
1564
|
import { spawn as spawn3 } from "child_process";
|
|
1565
1565
|
import { createHash as createHash7 } from "crypto";
|
|
1566
|
-
import { resolve as
|
|
1566
|
+
import { resolve as resolve38, join as join45, basename as basename12 } from "path";
|
|
1567
1567
|
import { homedir as homedir4 } from "os";
|
|
1568
1568
|
import { monitorEventLoopDelay } from "perf_hooks";
|
|
1569
1569
|
|
|
@@ -2472,7 +2472,8 @@ var WhatsAppAccountSchema = z.object({
|
|
|
2472
2472
|
}).strict().optional().describe("React to incoming messages with an emoji to acknowledge receipt before the agent responds."),
|
|
2473
2473
|
debounceMs: z.number().int().nonnegative().optional().default(2e3).describe("Wait this many milliseconds after the last message before processing, to batch rapid multi-message input into a single agent turn. Default 2000ms catches most multi-message bursts. 0 means process each message immediately."),
|
|
2474
2474
|
publicAgent: z.string().optional().describe("Slug of the public agent that handles WhatsApp inbound from non-admin phones for this account. Overrides the top-level publicAgent when set."),
|
|
2475
|
-
afterHoursMessage: z.string().optional().describe("Auto-reply text sent to public senders when the business is closed (outside opening hours). If not set, messages received outside business hours are silently skipped with no reply.")
|
|
2475
|
+
afterHoursMessage: z.string().optional().describe("Auto-reply text sent to public senders when the business is closed (outside opening hours). If not set, messages received outside business hours are silently skipped with no reply."),
|
|
2476
|
+
dispatchInbound: z.boolean().optional().describe("When false, this account's WhatsApp socket only observes: inbound is stored and readable but never dispatched to an agent. Default true. Set with set-dispatch-inbound.")
|
|
2476
2477
|
}).strict().superRefine((value, ctx) => {
|
|
2477
2478
|
if (value.dmPolicy !== "open") return;
|
|
2478
2479
|
const allow = (value.allowFrom ?? []).map((v) => String(v).trim()).filter(Boolean);
|
|
@@ -2836,7 +2837,7 @@ var credsSaveQueue = Promise.resolve();
|
|
|
2836
2837
|
async function drainCredsSaveQueue(timeoutMs = 5e3) {
|
|
2837
2838
|
console.error(`${TAG6} draining credential save queue\u2026`);
|
|
2838
2839
|
const timer = new Promise(
|
|
2839
|
-
(
|
|
2840
|
+
(resolve39) => setTimeout(() => resolve39("timeout"), timeoutMs)
|
|
2840
2841
|
);
|
|
2841
2842
|
const result = await Promise.race([
|
|
2842
2843
|
credsSaveQueue.then(() => "drained"),
|
|
@@ -3013,11 +3014,11 @@ async function createWaSocket(opts) {
|
|
|
3013
3014
|
return sock;
|
|
3014
3015
|
}
|
|
3015
3016
|
async function waitForConnection(sock) {
|
|
3016
|
-
return new Promise((
|
|
3017
|
+
return new Promise((resolve39, reject) => {
|
|
3017
3018
|
const handler = (update) => {
|
|
3018
3019
|
if (update.connection === "open") {
|
|
3019
3020
|
sock.ev.off("connection.update", handler);
|
|
3020
|
-
|
|
3021
|
+
resolve39();
|
|
3021
3022
|
}
|
|
3022
3023
|
if (update.connection === "close") {
|
|
3023
3024
|
sock.ev.off("connection.update", handler);
|
|
@@ -3125,7 +3126,7 @@ function isTimeoutError(err) {
|
|
|
3125
3126
|
return Boolean(err?.__maxyTimeout);
|
|
3126
3127
|
}
|
|
3127
3128
|
function withTimeout(label, promise, timeoutMs) {
|
|
3128
|
-
return new Promise((
|
|
3129
|
+
return new Promise((resolve39, reject) => {
|
|
3129
3130
|
const timer = setTimeout(() => {
|
|
3130
3131
|
const err = new Error(`${label} timed out after ${timeoutMs}ms`);
|
|
3131
3132
|
err.__maxyTimeout = true;
|
|
@@ -3135,7 +3136,7 @@ function withTimeout(label, promise, timeoutMs) {
|
|
|
3135
3136
|
promise.then(
|
|
3136
3137
|
(value) => {
|
|
3137
3138
|
clearTimeout(timer);
|
|
3138
|
-
|
|
3139
|
+
resolve39(value);
|
|
3139
3140
|
},
|
|
3140
3141
|
(err) => {
|
|
3141
3142
|
clearTimeout(timer);
|
|
@@ -3622,6 +3623,18 @@ function checkGroupAccess(_params) {
|
|
|
3622
3623
|
return { allowed: false, reason: "group-observe-only", agentType: "public" };
|
|
3623
3624
|
}
|
|
3624
3625
|
|
|
3626
|
+
// app/lib/whatsapp/inbound/dispatch-mode.ts
|
|
3627
|
+
function shouldDispatchInbound(config, accountId) {
|
|
3628
|
+
return config.accounts?.[accountId]?.dispatchInbound ?? true;
|
|
3629
|
+
}
|
|
3630
|
+
function decideDispatch(conn, payload, deps) {
|
|
3631
|
+
if (shouldDispatchInbound(deps.config, conn.accountId)) {
|
|
3632
|
+
deps.emit(payload);
|
|
3633
|
+
return;
|
|
3634
|
+
}
|
|
3635
|
+
deps.log(`[whatsapp:observe] op=dispatch-suppressed account=${conn.accountId} sender=${payload.senderPhone} agentType=${payload.agentType}`);
|
|
3636
|
+
}
|
|
3637
|
+
|
|
3625
3638
|
// app/lib/access-gate.ts
|
|
3626
3639
|
import neo4j from "neo4j-driver";
|
|
3627
3640
|
import { readFileSync as readFileSync6 } from "fs";
|
|
@@ -4175,8 +4188,8 @@ async function persistWhatsAppMessage(input) {
|
|
|
4175
4188
|
const { givenName, familyName } = splitName(input.pushName);
|
|
4176
4189
|
const prev = sessionWriteLocks.get(input.cacheKey);
|
|
4177
4190
|
let release;
|
|
4178
|
-
const mine = new Promise((
|
|
4179
|
-
release =
|
|
4191
|
+
const mine = new Promise((resolve39) => {
|
|
4192
|
+
release = resolve39;
|
|
4180
4193
|
});
|
|
4181
4194
|
const chained = (prev ?? Promise.resolve()).then(() => mine);
|
|
4182
4195
|
sessionWriteLocks.set(input.cacheKey, chained);
|
|
@@ -5484,6 +5497,16 @@ var configDir = null;
|
|
|
5484
5497
|
var whatsAppConfig = {};
|
|
5485
5498
|
var onInboundMessage = null;
|
|
5486
5499
|
var initialized = false;
|
|
5500
|
+
function dispatchInboundPayload(conn, payload) {
|
|
5501
|
+
if (!onInboundMessage) return;
|
|
5502
|
+
decideDispatch(conn, payload, { config: whatsAppConfig, emit: onInboundMessage, log: (l) => console.error(l) });
|
|
5503
|
+
}
|
|
5504
|
+
function isObserveAccount(accountId) {
|
|
5505
|
+
return !shouldDispatchInbound(whatsAppConfig, accountId);
|
|
5506
|
+
}
|
|
5507
|
+
function getWhatsAppConfig() {
|
|
5508
|
+
return whatsAppConfig;
|
|
5509
|
+
}
|
|
5487
5510
|
var messageStore = /* @__PURE__ */ new Map();
|
|
5488
5511
|
function messageStoreKey(accountId, remoteJid) {
|
|
5489
5512
|
return `${accountId}:${remoteJid}`;
|
|
@@ -5903,11 +5926,11 @@ async function connectWithReconnect(conn) {
|
|
|
5903
5926
|
console.error(
|
|
5904
5927
|
`${TAG17} reconnecting account=${conn.accountId} in ${delay}ms (attempt ${decision.nextAttempts}/${maxAttempts})`
|
|
5905
5928
|
);
|
|
5906
|
-
await new Promise((
|
|
5907
|
-
const timer = setTimeout(
|
|
5929
|
+
await new Promise((resolve39) => {
|
|
5930
|
+
const timer = setTimeout(resolve39, delay);
|
|
5908
5931
|
conn.abortController.signal.addEventListener("abort", () => {
|
|
5909
5932
|
clearTimeout(timer);
|
|
5910
|
-
|
|
5933
|
+
resolve39();
|
|
5911
5934
|
}, { once: true });
|
|
5912
5935
|
});
|
|
5913
5936
|
}
|
|
@@ -5915,16 +5938,16 @@ async function connectWithReconnect(conn) {
|
|
|
5915
5938
|
}
|
|
5916
5939
|
}
|
|
5917
5940
|
function waitForDisconnectEvent(conn) {
|
|
5918
|
-
return new Promise((
|
|
5941
|
+
return new Promise((resolve39) => {
|
|
5919
5942
|
if (!conn.sock) {
|
|
5920
|
-
|
|
5943
|
+
resolve39();
|
|
5921
5944
|
return;
|
|
5922
5945
|
}
|
|
5923
5946
|
const sock = conn.sock;
|
|
5924
5947
|
const handler = (update) => {
|
|
5925
5948
|
if (update.connection === "close") {
|
|
5926
5949
|
sock.ev.off("connection.update", handler);
|
|
5927
|
-
|
|
5950
|
+
resolve39();
|
|
5928
5951
|
}
|
|
5929
5952
|
};
|
|
5930
5953
|
sock.ev.on("connection.update", handler);
|
|
@@ -5977,7 +6000,7 @@ function monitorInbound(conn) {
|
|
|
5977
6000
|
if (entries.length > 1) {
|
|
5978
6001
|
console.error(`${TAG17} debounce: combining ${entries.length} messages account=${conn.accountId} from=${entries[0].senderPhone}`);
|
|
5979
6002
|
}
|
|
5980
|
-
|
|
6003
|
+
dispatchInboundPayload(conn, combineInboundBatch(entries));
|
|
5981
6004
|
},
|
|
5982
6005
|
onError: (err) => {
|
|
5983
6006
|
console.error(`${TAG17} debounce flush error account=${conn.accountId}: ${String(err)}`);
|
|
@@ -6202,7 +6225,7 @@ function monitorInbound(conn) {
|
|
|
6202
6225
|
const currentSock = conn.sock;
|
|
6203
6226
|
if (currentSock) await sendComposing(currentSock, remoteJid);
|
|
6204
6227
|
};
|
|
6205
|
-
|
|
6228
|
+
dispatchInboundPayload(conn, buildSelfChatDispatch({
|
|
6206
6229
|
accountId: conn.accountId,
|
|
6207
6230
|
selfPhone: conn.selfPhone,
|
|
6208
6231
|
remoteJid,
|
|
@@ -6311,7 +6334,7 @@ async function handleInboundMessage(conn, msg) {
|
|
|
6311
6334
|
const currentSock = conn.sock;
|
|
6312
6335
|
if (currentSock) await sendComposing(currentSock, remoteJid);
|
|
6313
6336
|
};
|
|
6314
|
-
|
|
6337
|
+
dispatchInboundPayload(conn, {
|
|
6315
6338
|
accountId: conn.accountId,
|
|
6316
6339
|
agentType: "admin",
|
|
6317
6340
|
senderPhone: senderPhone2,
|
|
@@ -6358,8 +6381,8 @@ async function handleInboundMessage(conn, msg) {
|
|
|
6358
6381
|
const conversationKey = isGroup ? remoteJid : senderPhone;
|
|
6359
6382
|
const debounceKey = `${conn.accountId}:${conversationKey}:${senderPhone}`;
|
|
6360
6383
|
let resolvePending;
|
|
6361
|
-
const sttPending = new Promise((
|
|
6362
|
-
resolvePending =
|
|
6384
|
+
const sttPending = new Promise((resolve39) => {
|
|
6385
|
+
resolvePending = resolve39;
|
|
6363
6386
|
});
|
|
6364
6387
|
if (conn.debouncer) conn.debouncer.registerPending(debounceKey, sttPending);
|
|
6365
6388
|
try {
|
|
@@ -6477,7 +6500,7 @@ async function handleInboundMessage(conn, msg) {
|
|
|
6477
6500
|
if (conn.debouncer) {
|
|
6478
6501
|
conn.debouncer.enqueue(payload);
|
|
6479
6502
|
} else {
|
|
6480
|
-
|
|
6503
|
+
dispatchInboundPayload(conn, payload);
|
|
6481
6504
|
}
|
|
6482
6505
|
}
|
|
6483
6506
|
|
|
@@ -6787,20 +6810,20 @@ function buildX11Env(chromiumWrapperPath, transport = "vnc") {
|
|
|
6787
6810
|
|
|
6788
6811
|
// server/routes/health.ts
|
|
6789
6812
|
function checkPort(port2, timeoutMs = 500) {
|
|
6790
|
-
return new Promise((
|
|
6813
|
+
return new Promise((resolve39) => {
|
|
6791
6814
|
const socket = createConnection2(port2, "127.0.0.1");
|
|
6792
6815
|
socket.setTimeout(timeoutMs);
|
|
6793
6816
|
socket.once("connect", () => {
|
|
6794
6817
|
socket.destroy();
|
|
6795
|
-
|
|
6818
|
+
resolve39(true);
|
|
6796
6819
|
});
|
|
6797
6820
|
socket.once("error", () => {
|
|
6798
6821
|
socket.destroy();
|
|
6799
|
-
|
|
6822
|
+
resolve39(false);
|
|
6800
6823
|
});
|
|
6801
6824
|
socket.once("timeout", () => {
|
|
6802
6825
|
socket.destroy();
|
|
6803
|
-
|
|
6826
|
+
resolve39(false);
|
|
6804
6827
|
});
|
|
6805
6828
|
});
|
|
6806
6829
|
}
|
|
@@ -7751,6 +7774,48 @@ function setAccountManager(accountDir, phone, subAccountId, mode = "active") {
|
|
|
7751
7774
|
return { ok: false, error: msg };
|
|
7752
7775
|
}
|
|
7753
7776
|
}
|
|
7777
|
+
function setDispatchInbound(accountDir, accountId, value) {
|
|
7778
|
+
const id = accountId.trim();
|
|
7779
|
+
if (!id) return { ok: false, error: "Missing account id." };
|
|
7780
|
+
let validIds;
|
|
7781
|
+
try {
|
|
7782
|
+
validIds = listValidAccounts().map((a) => a.accountId);
|
|
7783
|
+
} catch (err) {
|
|
7784
|
+
return { ok: false, error: `Could not read the account registry to validate the account: ${err instanceof Error ? err.message : String(err)}. Try again.` };
|
|
7785
|
+
}
|
|
7786
|
+
if (!validIds.includes(id)) {
|
|
7787
|
+
return { ok: false, error: `Account "${id}" is not in this install's account registry.` };
|
|
7788
|
+
}
|
|
7789
|
+
try {
|
|
7790
|
+
const config = readConfig(accountDir);
|
|
7791
|
+
if (!config.whatsapp || typeof config.whatsapp !== "object") {
|
|
7792
|
+
config.whatsapp = {};
|
|
7793
|
+
}
|
|
7794
|
+
const wa = config.whatsapp;
|
|
7795
|
+
if (!wa.accounts || typeof wa.accounts !== "object") {
|
|
7796
|
+
wa.accounts = {};
|
|
7797
|
+
}
|
|
7798
|
+
const accounts = wa.accounts;
|
|
7799
|
+
if (!accounts[id] || typeof accounts[id] !== "object") {
|
|
7800
|
+
accounts[id] = { name: "Main" };
|
|
7801
|
+
}
|
|
7802
|
+
accounts[id].dispatchInbound = value;
|
|
7803
|
+
const parsed = WhatsAppConfigSchema.safeParse(wa);
|
|
7804
|
+
if (!parsed.success) {
|
|
7805
|
+
const msg = parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ");
|
|
7806
|
+
return { ok: false, error: `Validation failed: ${msg}` };
|
|
7807
|
+
}
|
|
7808
|
+
config.whatsapp = parsed.data;
|
|
7809
|
+
writeConfig(accountDir, config);
|
|
7810
|
+
console.error(`${TAG21} set dispatchInbound account=${id} value=${value}`);
|
|
7811
|
+
reloadManagerConfig(accountDir);
|
|
7812
|
+
return { ok: true };
|
|
7813
|
+
} catch (err) {
|
|
7814
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
7815
|
+
console.error(`${TAG21} setDispatchInbound failed: ${msg}`);
|
|
7816
|
+
return { ok: false, error: msg };
|
|
7817
|
+
}
|
|
7818
|
+
}
|
|
7754
7819
|
function clearAccountManager(accountDir, phone) {
|
|
7755
7820
|
const normalized = phone.trim();
|
|
7756
7821
|
try {
|
|
@@ -8089,8 +8154,8 @@ async function startLogin(opts) {
|
|
|
8089
8154
|
await clearAuth(authDir);
|
|
8090
8155
|
let resolveCode = null;
|
|
8091
8156
|
let rejectCode = null;
|
|
8092
|
-
const codePromise = new Promise((
|
|
8093
|
-
resolveCode =
|
|
8157
|
+
const codePromise = new Promise((resolve39, reject) => {
|
|
8158
|
+
resolveCode = resolve39;
|
|
8094
8159
|
rejectCode = reject;
|
|
8095
8160
|
});
|
|
8096
8161
|
const codeTimer = setTimeout(
|
|
@@ -8489,6 +8554,14 @@ function reconcileCredsOnDisk(opts) {
|
|
|
8489
8554
|
return entries;
|
|
8490
8555
|
}
|
|
8491
8556
|
|
|
8557
|
+
// app/lib/whatsapp/outbound/reply-guard.ts
|
|
8558
|
+
function admitReply(input) {
|
|
8559
|
+
if (isGroupJid(input.remoteJid)) return { ok: false, status: 403, reason: "group-send-blocked" };
|
|
8560
|
+
if (!isUserJid(input.remoteJid)) return { ok: false, status: 400, reason: "invalid-remote-jid" };
|
|
8561
|
+
if (!input.hasConversation) return { ok: false, status: 403, reason: "no-existing-conversation" };
|
|
8562
|
+
return { ok: true };
|
|
8563
|
+
}
|
|
8564
|
+
|
|
8492
8565
|
// app/lib/whatsapp-reader/select-sessions.ts
|
|
8493
8566
|
function isReaderChannelSession(role, channel) {
|
|
8494
8567
|
return role === "admin" && (channel === "whatsapp" || channel === "telegram") || role === "public" && (channel === "whatsapp" || channel === "webchat");
|
|
@@ -8647,6 +8720,15 @@ function houseSocketScopeDenial(c, op) {
|
|
|
8647
8720
|
console.error(`${TAG25} op=authz-deny route=${op} caller=${callerAccountId ?? "none"} houseAdmin=${houseAdminScope ? "y" : "n"}`);
|
|
8648
8721
|
return c.json({ ok: false, error: "Not authorized. The WhatsApp connection and its settings are house-scoped; this session may only act on its own account." }, 403);
|
|
8649
8722
|
}
|
|
8723
|
+
function selfOrHouseScopeDenial(c, targetAccountId, op) {
|
|
8724
|
+
const { decision, callerAccountId, houseAdminScope } = evaluateCallerScope(c, targetAccountId);
|
|
8725
|
+
if (decision.authorized) {
|
|
8726
|
+
console.error(`${TAG25} op=authz-ok route=${op} target=${targetAccountId} caller=${callerAccountId ?? "none"} houseAdmin=${houseAdminScope ? "y" : "n"}`);
|
|
8727
|
+
return null;
|
|
8728
|
+
}
|
|
8729
|
+
console.error(`${TAG25} op=authz-deny route=${op} target=${targetAccountId} caller=${callerAccountId ?? "none"} houseAdmin=${houseAdminScope ? "y" : "n"}`);
|
|
8730
|
+
return c.json({ ok: false, error: "Not authorized. This session may only pair or manage its own account." }, 403);
|
|
8731
|
+
}
|
|
8650
8732
|
var app2 = new Hono();
|
|
8651
8733
|
app2.get("/status", (c) => {
|
|
8652
8734
|
try {
|
|
@@ -8658,8 +8740,12 @@ app2.get("/status", (c) => {
|
|
|
8658
8740
|
const credsRoot = join12(MAXY_DIR, "credentials", "whatsapp");
|
|
8659
8741
|
const accountDir = resolveAccount()?.accountDir ?? null;
|
|
8660
8742
|
const reconciled = reconcileCredsOnDisk({ credsRoot, accountDir, liveAccountIds: liveIds, activeLoginAccountIds: activeLoginIds });
|
|
8661
|
-
const
|
|
8662
|
-
const
|
|
8743
|
+
const cfg = getWhatsAppConfig();
|
|
8744
|
+
const accounts = [...live, ...reconciled].map((a) => ({
|
|
8745
|
+
...a,
|
|
8746
|
+
dispatchInbound: shouldDispatchInbound(cfg, a.accountId)
|
|
8747
|
+
}));
|
|
8748
|
+
const summary = accounts.map((a) => `${a.accountId}:${a.connected ? "up" : a.linkedUnconfigured ? "linked-unconfigured" : "down"}:${a.dispatchInbound ? "dispatch" : "observe"}`).join(", ");
|
|
8663
8749
|
console.error(`${TAG25} status accounts=${accounts.length} [${summary}]`);
|
|
8664
8750
|
const reachableAccountIds = [
|
|
8665
8751
|
.../* @__PURE__ */ new Set([...listStoredAccountIds(), ...listConnectionPlatformAccountIds()])
|
|
@@ -8679,10 +8765,10 @@ app2.get("/status", (c) => {
|
|
|
8679
8765
|
});
|
|
8680
8766
|
app2.post("/login/start", async (c) => {
|
|
8681
8767
|
try {
|
|
8682
|
-
const denial = houseSocketScopeDenial(c, "login/start");
|
|
8683
|
-
if (denial) return denial;
|
|
8684
8768
|
const body = await c.req.json().catch(() => ({}));
|
|
8685
8769
|
const accountId = validateAccountId(body.accountId);
|
|
8770
|
+
const denial = selfOrHouseScopeDenial(c, accountId, "login/start");
|
|
8771
|
+
if (denial) return denial;
|
|
8686
8772
|
const force = body.force ?? false;
|
|
8687
8773
|
const phone = typeof body.phone === "string" ? body.phone.trim() : "";
|
|
8688
8774
|
if (!phone) {
|
|
@@ -8700,10 +8786,10 @@ app2.post("/login/start", async (c) => {
|
|
|
8700
8786
|
});
|
|
8701
8787
|
app2.post("/login/wait", async (c) => {
|
|
8702
8788
|
try {
|
|
8703
|
-
const denial = houseSocketScopeDenial(c, "login/wait");
|
|
8704
|
-
if (denial) return denial;
|
|
8705
8789
|
const body = await c.req.json().catch(() => ({}));
|
|
8706
8790
|
const accountId = validateAccountId(body.accountId);
|
|
8791
|
+
const denial = selfOrHouseScopeDenial(c, accountId, "login/wait");
|
|
8792
|
+
if (denial) return denial;
|
|
8707
8793
|
const timeoutMs = body.timeoutMs ?? 6e4;
|
|
8708
8794
|
const result = await waitForLogin({ accountId, timeoutMs });
|
|
8709
8795
|
console.error(`${TAG25} login/wait result account=${accountId} connected=${result.connected}${result.selfPhone ? ` phone=${result.selfPhone}` : ""} configPersisted=${result.configPersisted}`);
|
|
@@ -8720,10 +8806,10 @@ app2.post("/login/wait", async (c) => {
|
|
|
8720
8806
|
});
|
|
8721
8807
|
app2.post("/disconnect", async (c) => {
|
|
8722
8808
|
try {
|
|
8723
|
-
const denial = houseSocketScopeDenial(c, "disconnect");
|
|
8724
|
-
if (denial) return denial;
|
|
8725
8809
|
const body = await c.req.json().catch(() => ({}));
|
|
8726
8810
|
const accountId = validateAccountId(body.accountId);
|
|
8811
|
+
const denial = selfOrHouseScopeDenial(c, accountId, "disconnect");
|
|
8812
|
+
if (denial) return denial;
|
|
8727
8813
|
await stopConnection(accountId);
|
|
8728
8814
|
return c.json({ disconnected: true, accountId });
|
|
8729
8815
|
} catch (err) {
|
|
@@ -8733,10 +8819,10 @@ app2.post("/disconnect", async (c) => {
|
|
|
8733
8819
|
});
|
|
8734
8820
|
app2.post("/reconnect", async (c) => {
|
|
8735
8821
|
try {
|
|
8736
|
-
const denial = houseSocketScopeDenial(c, "reconnect");
|
|
8737
|
-
if (denial) return denial;
|
|
8738
8822
|
const body = await c.req.json().catch(() => ({}));
|
|
8739
8823
|
const accountId = validateAccountId(body.accountId);
|
|
8824
|
+
const denial = selfOrHouseScopeDenial(c, accountId, "reconnect");
|
|
8825
|
+
if (denial) return denial;
|
|
8740
8826
|
await startConnection(accountId);
|
|
8741
8827
|
return c.json({ reconnecting: true, accountId });
|
|
8742
8828
|
} catch (err) {
|
|
@@ -8751,6 +8837,22 @@ app2.post("/config", async (c) => {
|
|
|
8751
8837
|
if (!action || typeof action !== "string") {
|
|
8752
8838
|
return c.json({ ok: false, error: 'Missing required field "action".' }, 400);
|
|
8753
8839
|
}
|
|
8840
|
+
if (action === "set-dispatch-inbound") {
|
|
8841
|
+
if (!accountId || typeof accountId !== "string") {
|
|
8842
|
+
return c.json({ ok: false, error: 'Missing required field "accountId" (the account whose socket to set).' }, 400);
|
|
8843
|
+
}
|
|
8844
|
+
const target = validateAccountId(accountId);
|
|
8845
|
+
const scopeDenial = selfOrHouseScopeDenial(c, target, `config action=set-dispatch-inbound target=${target}`);
|
|
8846
|
+
if (scopeDenial) return scopeDenial;
|
|
8847
|
+
if (typeof body.dispatchInbound !== "boolean") {
|
|
8848
|
+
return c.json({ ok: false, error: 'Missing required boolean field "dispatchInbound".' }, 400);
|
|
8849
|
+
}
|
|
8850
|
+
const acct = resolveAccount();
|
|
8851
|
+
if (!acct) return c.json({ ok: false, error: "No account configured." }, 500);
|
|
8852
|
+
const result = setDispatchInbound(acct.accountDir, target, body.dispatchInbound);
|
|
8853
|
+
console.error(`${TAG25} config action=set-dispatch-inbound account=${target} dispatchInbound=${body.dispatchInbound} ok=${result.ok}`);
|
|
8854
|
+
return c.json(result, result.ok ? 200 : 400);
|
|
8855
|
+
}
|
|
8754
8856
|
const denial = houseSocketScopeDenial(c, `config action=${action}`);
|
|
8755
8857
|
if (denial) return denial;
|
|
8756
8858
|
const account = resolveAccount();
|
|
@@ -8961,6 +9063,65 @@ app2.post("/send-admin", async (c) => {
|
|
|
8961
9063
|
return c.json({ error: String(err) }, 500);
|
|
8962
9064
|
}
|
|
8963
9065
|
});
|
|
9066
|
+
app2.post("/reply", async (c) => {
|
|
9067
|
+
const sendId = process.hrtime.bigint().toString(36).slice(-8);
|
|
9068
|
+
try {
|
|
9069
|
+
const body = await c.req.json().catch(() => ({}));
|
|
9070
|
+
if (!body.accountId || typeof body.accountId !== "string") {
|
|
9071
|
+
return c.json({ error: "accountId is required" }, 400);
|
|
9072
|
+
}
|
|
9073
|
+
const accountId = validateAccountId(body.accountId);
|
|
9074
|
+
const denial = selfOrHouseScopeDenial(c, accountId, "reply");
|
|
9075
|
+
if (denial) return denial;
|
|
9076
|
+
const remoteJid = typeof body.remoteJid === "string" ? body.remoteJid : "";
|
|
9077
|
+
const text = typeof body.text === "string" && body.text.trim().length ? body.text : "";
|
|
9078
|
+
if (!remoteJid || !text) {
|
|
9079
|
+
return c.json({ error: "accountId, remoteJid and text are required" }, 400);
|
|
9080
|
+
}
|
|
9081
|
+
const storeKey = resolveReadStoreKey(accountId);
|
|
9082
|
+
const convo = readConversation(storeKey, remoteJid);
|
|
9083
|
+
const admit = admitReply({ remoteJid, hasConversation: convo.length > 0 });
|
|
9084
|
+
if (!admit.ok) {
|
|
9085
|
+
console.error(`[whatsapp:observe] op=reply-refused sendId=${sendId} account=${accountId} remoteJid=${remoteJid} reason=${admit.reason}`);
|
|
9086
|
+
return c.json({ error: admit.reason, reason: admit.reason }, admit.status);
|
|
9087
|
+
}
|
|
9088
|
+
const res = resolveSocket(accountId);
|
|
9089
|
+
if (!res.ok) {
|
|
9090
|
+
console.error(`[whatsapp:observe] op=reply-socket sendId=${sendId} account=${accountId} result=${res.reason} presentKeys=${res.presentKeys.join(",") || "none"}`);
|
|
9091
|
+
return c.json({ error: formatSocketResolutionMessage(res, accountId), reason: res.reason }, 503);
|
|
9092
|
+
}
|
|
9093
|
+
const sent = await sendTextMessage(res.sock, remoteJid, text, { accountId: res.accountId });
|
|
9094
|
+
if (!sent.success) {
|
|
9095
|
+
console.error(`[whatsapp:observe] op=reply-send-fail sendId=${sendId} account=${accountId} error=${sent.error ?? ""}`);
|
|
9096
|
+
return c.json({ error: sent.error ?? "send failed" }, 500);
|
|
9097
|
+
}
|
|
9098
|
+
if (sent.messageId) {
|
|
9099
|
+
const template = convo[convo.length - 1];
|
|
9100
|
+
const nowIso = (/* @__PURE__ */ new Date()).toISOString();
|
|
9101
|
+
const rec = {
|
|
9102
|
+
messageId: `whatsapp-live:${res.accountId}:${remoteJid}:${sent.messageId}`,
|
|
9103
|
+
sessionId: template.sessionId,
|
|
9104
|
+
dateSent: nowIso,
|
|
9105
|
+
body: text,
|
|
9106
|
+
fromMe: true,
|
|
9107
|
+
senderTelephone: template.fromMe ? template.senderTelephone : "",
|
|
9108
|
+
senderName: null,
|
|
9109
|
+
remoteJid,
|
|
9110
|
+
msgKeyId: sent.messageId,
|
|
9111
|
+
quotedId: null,
|
|
9112
|
+
quotedSender: null,
|
|
9113
|
+
scope: template.scope,
|
|
9114
|
+
createdAt: nowIso
|
|
9115
|
+
};
|
|
9116
|
+
appendMessage(storeKey, rec);
|
|
9117
|
+
}
|
|
9118
|
+
console.error(`[whatsapp:observe] op=reply-sent sendId=${sendId} account=${accountId} socket=${res.accountId} waMessageId=${sent.messageId ?? ""}`);
|
|
9119
|
+
return c.json({ success: true, messageId: sent.messageId });
|
|
9120
|
+
} catch (err) {
|
|
9121
|
+
console.error(`${TAG25} reply error: ${String(err)}`);
|
|
9122
|
+
return c.json({ error: String(err) }, 500);
|
|
9123
|
+
}
|
|
9124
|
+
});
|
|
8964
9125
|
app2.get("/activity", (c) => {
|
|
8965
9126
|
try {
|
|
8966
9127
|
const accountId = c.req.query("accountId") ?? void 0;
|
|
@@ -14342,15 +14503,15 @@ function operatorRoleFor(config, userId) {
|
|
|
14342
14503
|
async function buildAccountOptionList(accounts, userId) {
|
|
14343
14504
|
const houseAccount = accounts.find((a) => a.config.role === "house");
|
|
14344
14505
|
console.log(`[admin-accounts] op=list count=${accounts.length} house=${houseAccount?.accountId ?? "none"}`);
|
|
14345
|
-
const
|
|
14506
|
+
const resolve39 = async (a) => {
|
|
14346
14507
|
const businessName = await fetchAccountName(a.accountId) || void 0;
|
|
14347
14508
|
return { accountId: a.accountId, businessName, role: operatorRoleFor(a.config, userId), isHouse: a.config.role === "house" };
|
|
14348
14509
|
};
|
|
14349
14510
|
const houseOptions = await Promise.all(
|
|
14350
|
-
accounts.filter((a) => a.config.role === "house").map(
|
|
14511
|
+
accounts.filter((a) => a.config.role === "house").map(resolve39)
|
|
14351
14512
|
);
|
|
14352
14513
|
const nonHouseOptions = await Promise.all(
|
|
14353
|
-
accounts.filter((a) => a.config.role !== "house").map(
|
|
14514
|
+
accounts.filter((a) => a.config.role !== "house").map(resolve39)
|
|
14354
14515
|
);
|
|
14355
14516
|
const sortKey = (o) => (o.businessName || o.accountId).toLowerCase();
|
|
14356
14517
|
nonHouseOptions.sort((x, y) => {
|
|
@@ -20367,6 +20528,76 @@ function isWithin(target, root) {
|
|
|
20367
20528
|
}
|
|
20368
20529
|
var sidebar_artefacts_default = app29;
|
|
20369
20530
|
|
|
20531
|
+
// server/channel-session-override.ts
|
|
20532
|
+
import { readFileSync as readFileSync34, writeFileSync as writeFileSync12, renameSync as renameSync9 } from "fs";
|
|
20533
|
+
import { join as join35 } from "path";
|
|
20534
|
+
var FILE2 = "channel-session-override.json";
|
|
20535
|
+
var UUID2 = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;
|
|
20536
|
+
function channelOverridePath(accountDir) {
|
|
20537
|
+
return join35(accountDir, FILE2);
|
|
20538
|
+
}
|
|
20539
|
+
function senderKey(channel, senderId) {
|
|
20540
|
+
return `${channel}:${senderId}`;
|
|
20541
|
+
}
|
|
20542
|
+
function readRaw2(accountDir) {
|
|
20543
|
+
try {
|
|
20544
|
+
return JSON.parse(readFileSync34(channelOverridePath(accountDir), "utf8"));
|
|
20545
|
+
} catch {
|
|
20546
|
+
return null;
|
|
20547
|
+
}
|
|
20548
|
+
}
|
|
20549
|
+
function readChannelOverrideId(accountDir, channel, senderId) {
|
|
20550
|
+
const raw = readRaw2(accountDir);
|
|
20551
|
+
if (!raw || !raw.bySender || typeof raw.bySender !== "object") return null;
|
|
20552
|
+
const v = raw.bySender[senderKey(channel, senderId)];
|
|
20553
|
+
return typeof v === "string" && UUID2.test(v) ? v : null;
|
|
20554
|
+
}
|
|
20555
|
+
function writeChannelOverrideId(accountDir, channel, senderId, sessionId) {
|
|
20556
|
+
const raw = readRaw2(accountDir);
|
|
20557
|
+
const bySender = {};
|
|
20558
|
+
if (raw?.bySender && typeof raw.bySender === "object") {
|
|
20559
|
+
for (const [k, v] of Object.entries(raw.bySender)) {
|
|
20560
|
+
if (typeof v === "string") bySender[k] = v;
|
|
20561
|
+
}
|
|
20562
|
+
}
|
|
20563
|
+
bySender[senderKey(channel, senderId)] = sessionId;
|
|
20564
|
+
const path3 = channelOverridePath(accountDir);
|
|
20565
|
+
const tmp = `${path3}.${process.pid}.tmp`;
|
|
20566
|
+
writeFileSync12(tmp, JSON.stringify({ bySender }, null, 2), "utf8");
|
|
20567
|
+
renameSync9(tmp, path3);
|
|
20568
|
+
}
|
|
20569
|
+
function clearChannelOverrideForSession(accountDir, sessionId) {
|
|
20570
|
+
const raw = readRaw2(accountDir);
|
|
20571
|
+
if (!raw?.bySender || typeof raw.bySender !== "object") return [];
|
|
20572
|
+
const kept = {};
|
|
20573
|
+
const cleared = [];
|
|
20574
|
+
for (const [k, v] of Object.entries(raw.bySender)) {
|
|
20575
|
+
if (typeof v !== "string") continue;
|
|
20576
|
+
if (v === sessionId) cleared.push(k);
|
|
20577
|
+
else kept[k] = v;
|
|
20578
|
+
}
|
|
20579
|
+
if (cleared.length === 0) return [];
|
|
20580
|
+
const path3 = channelOverridePath(accountDir);
|
|
20581
|
+
const tmp = `${path3}.${process.pid}.tmp`;
|
|
20582
|
+
writeFileSync12(tmp, JSON.stringify({ bySender: kept }, null, 2), "utf8");
|
|
20583
|
+
renameSync9(tmp, path3);
|
|
20584
|
+
return cleared;
|
|
20585
|
+
}
|
|
20586
|
+
|
|
20587
|
+
// server/routes/admin/clear-forward-pointers.ts
|
|
20588
|
+
function clearForwardPointers(sessionId) {
|
|
20589
|
+
try {
|
|
20590
|
+
for (const a of listValidAccounts()) {
|
|
20591
|
+
const cleared = clearChannelOverrideForSession(a.accountDir, sessionId);
|
|
20592
|
+
if (cleared.length > 0) {
|
|
20593
|
+
console.log(`[session-lifecycle] op=channel-forward-cleared sessionId=${sessionId.slice(0, 8)} account=${a.accountId} senderKeys=${cleared.join(",")}`);
|
|
20594
|
+
}
|
|
20595
|
+
}
|
|
20596
|
+
} catch (err) {
|
|
20597
|
+
console.error(`[session-lifecycle] op=channel-forward-clear-failed sessionId=${sessionId.slice(0, 8)} err=${err instanceof Error ? err.message : String(err)}`);
|
|
20598
|
+
}
|
|
20599
|
+
}
|
|
20600
|
+
|
|
20370
20601
|
// server/routes/admin/session-delete.ts
|
|
20371
20602
|
var app30 = new Hono();
|
|
20372
20603
|
app30.post("/", requireAdminSession, async (c) => {
|
|
@@ -20399,6 +20630,7 @@ app30.post("/", requireAdminSession, async (c) => {
|
|
|
20399
20630
|
return c.json({ error: "manager-unreachable" }, 502);
|
|
20400
20631
|
}
|
|
20401
20632
|
if (del.status === 204) {
|
|
20633
|
+
clearForwardPointers(sessionId);
|
|
20402
20634
|
console.log(`[session-delete] sessionId=${sessionId} deleted`);
|
|
20403
20635
|
return c.json({ deleted: true });
|
|
20404
20636
|
}
|
|
@@ -20467,6 +20699,7 @@ app32.post("/", requireAdminSession, async (c) => {
|
|
|
20467
20699
|
return c.json({ error: "manager-unreachable" }, 502);
|
|
20468
20700
|
}
|
|
20469
20701
|
if (res.status === 200) {
|
|
20702
|
+
if (mode === "archive") clearForwardPointers(sessionId);
|
|
20470
20703
|
const dir = mode === "archive" ? "top\u2192archive" : "archive\u2192top";
|
|
20471
20704
|
console.log(`[session-archive] sessionId=${sessionId.slice(0, 8)} moved ${dir}`);
|
|
20472
20705
|
return c.json({ archived: mode === "archive" });
|
|
@@ -20696,49 +20929,40 @@ var session_rc_spawn_default = app35;
|
|
|
20696
20929
|
// server/routes/admin/session-reseat.ts
|
|
20697
20930
|
import { randomUUID as randomUUID12 } from "crypto";
|
|
20698
20931
|
|
|
20699
|
-
// server/
|
|
20700
|
-
import {
|
|
20701
|
-
|
|
20702
|
-
|
|
20703
|
-
var UUID2 = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;
|
|
20704
|
-
function channelOverridePath(accountDir) {
|
|
20705
|
-
return join35(accountDir, FILE2);
|
|
20932
|
+
// server/wa-descriptor.ts
|
|
20933
|
+
import { resolve as resolve26, join as join36 } from "path";
|
|
20934
|
+
function waGatewayUrl() {
|
|
20935
|
+
return `http://127.0.0.1:${process.env.MAXY_UI_INTERNAL_PORT ?? ""}`;
|
|
20706
20936
|
}
|
|
20707
|
-
function
|
|
20708
|
-
return
|
|
20937
|
+
function waServerPath() {
|
|
20938
|
+
return process.env.MAXY_WA_CHANNEL_SERVER_PATH ?? resolve26(process.env.MAXY_PLATFORM_ROOT ?? join36(__dirname, ".."), "services/whatsapp-channel/dist/server.js");
|
|
20709
20939
|
}
|
|
20710
|
-
function
|
|
20711
|
-
|
|
20712
|
-
return JSON.parse(readFileSync34(channelOverridePath(accountDir), "utf8"));
|
|
20713
|
-
} catch {
|
|
20714
|
-
return null;
|
|
20715
|
-
}
|
|
20940
|
+
function waChannelDescriptor(senderId) {
|
|
20941
|
+
return { senderId, gatewayUrl: waGatewayUrl(), serverPath: waServerPath() };
|
|
20716
20942
|
}
|
|
20717
|
-
|
|
20718
|
-
|
|
20719
|
-
|
|
20720
|
-
|
|
20721
|
-
return
|
|
20943
|
+
|
|
20944
|
+
// server/telegram-descriptor.ts
|
|
20945
|
+
import { resolve as resolve27, join as join37 } from "path";
|
|
20946
|
+
function telegramGatewayUrl() {
|
|
20947
|
+
return `http://127.0.0.1:${process.env.MAXY_UI_INTERNAL_PORT ?? ""}`;
|
|
20722
20948
|
}
|
|
20723
|
-
function
|
|
20724
|
-
|
|
20725
|
-
|
|
20726
|
-
|
|
20727
|
-
|
|
20728
|
-
if (typeof v === "string") bySender[k] = v;
|
|
20729
|
-
}
|
|
20730
|
-
}
|
|
20731
|
-
bySender[senderKey(channel, senderId)] = sessionId;
|
|
20732
|
-
const path3 = channelOverridePath(accountDir);
|
|
20733
|
-
const tmp = `${path3}.${process.pid}.tmp`;
|
|
20734
|
-
writeFileSync12(tmp, JSON.stringify({ bySender }, null, 2), "utf8");
|
|
20735
|
-
renameSync9(tmp, path3);
|
|
20949
|
+
function telegramServerPath() {
|
|
20950
|
+
return process.env.MAXY_TELEGRAM_CHANNEL_SERVER_PATH ?? resolve27(process.env.MAXY_PLATFORM_ROOT ?? join37(__dirname, ".."), "services/telegram-channel/dist/server.js");
|
|
20951
|
+
}
|
|
20952
|
+
function telegramChannelDescriptor(senderId) {
|
|
20953
|
+
return { senderId, gatewayUrl: telegramGatewayUrl(), serverPath: telegramServerPath() };
|
|
20736
20954
|
}
|
|
20737
20955
|
|
|
20738
20956
|
// server/routes/admin/session-reseat.ts
|
|
20739
20957
|
function isAdminChannelSource(source) {
|
|
20740
20958
|
return !!source && source.role === "admin" && (source.channel === "whatsapp" || source.channel === "telegram") && typeof source.senderId === "string" && source.senderId.length > 0;
|
|
20741
20959
|
}
|
|
20960
|
+
function shouldWriteChannelForward(source) {
|
|
20961
|
+
return isAdminChannelSource(source) && !!source.accountId;
|
|
20962
|
+
}
|
|
20963
|
+
function isPublicChannelSource(source) {
|
|
20964
|
+
return !!source && source.role === "public" && (source.channel === "whatsapp" || source.channel === "telegram");
|
|
20965
|
+
}
|
|
20742
20966
|
function resolveReseatPlan(body, mintId = randomUUID12, adminUserId, authenticatedName, source) {
|
|
20743
20967
|
const sessionId = mintId();
|
|
20744
20968
|
const key = `session:${sessionId}`;
|
|
@@ -20752,6 +20976,8 @@ function resolveReseatPlan(body, mintId = randomUUID12, adminUserId, authenticat
|
|
|
20752
20976
|
payload.channel = source.channel;
|
|
20753
20977
|
payload.senderId = source.senderId;
|
|
20754
20978
|
if (source.accountId) payload.targetAccountId = source.accountId;
|
|
20979
|
+
if (source.channel === "whatsapp") payload.waChannel = waChannelDescriptor(source.senderId);
|
|
20980
|
+
else payload.telegramChannel = telegramChannelDescriptor(source.senderId);
|
|
20755
20981
|
} else {
|
|
20756
20982
|
payload.channel = "webchat";
|
|
20757
20983
|
payload.senderId = key;
|
|
@@ -20799,6 +21025,10 @@ app36.post("/", requireAdminSession, async (c) => {
|
|
|
20799
21025
|
const authenticatedName = accountId ? await resolveAuthenticatedName(accountId, adminUserId) : void 0;
|
|
20800
21026
|
const sourceMeta = readSourceRowMeta(fromSessionId);
|
|
20801
21027
|
const source = sourceMeta ? { channel: sourceMeta.channel, senderId: sourceMeta.senderId, role: sourceMeta.role, accountId: sourceMeta.accountId } : null;
|
|
21028
|
+
if (isPublicChannelSource(source)) {
|
|
21029
|
+
console.error(`[chat-reseat] op=reseat-refused from=${fromSessionId} reason=public-channel channel=${source?.channel ?? "none"}`);
|
|
21030
|
+
return c.json({ error: "public channel re-seat not supported (would detach the seat)" }, 400);
|
|
21031
|
+
}
|
|
20802
21032
|
const plan = resolveReseatPlan(
|
|
20803
21033
|
{ fromSessionId, model, ...permissionMode ? { permissionMode } : {}, ...effort ? { effort } : {} },
|
|
20804
21034
|
randomUUID12,
|
|
@@ -20851,19 +21081,21 @@ app36.post("/", requireAdminSession, async (c) => {
|
|
|
20851
21081
|
if (isChannelFork) {
|
|
20852
21082
|
const channel = plan.payload.channel;
|
|
20853
21083
|
const senderId = plan.payload.senderId;
|
|
20854
|
-
|
|
20855
|
-
|
|
20856
|
-
|
|
20857
|
-
|
|
20858
|
-
|
|
20859
|
-
|
|
20860
|
-
|
|
20861
|
-
|
|
21084
|
+
if (!shouldWriteChannelForward(source)) {
|
|
21085
|
+
console.error(`[chat-reseat] op=channel-forward-skipped reseatReqId=${reseatReqId} senderId=${senderId} from=${fromSessionId} reason=no-source-account`);
|
|
21086
|
+
} else {
|
|
21087
|
+
try {
|
|
21088
|
+
const dir = listValidAccounts().find((a) => a.accountId === source.accountId)?.accountDir ?? null;
|
|
21089
|
+
if (dir) {
|
|
21090
|
+
writeChannelOverrideId(dir, channel, senderId, plan.sessionId);
|
|
21091
|
+
console.log(`[chat-reseat] op=channel-forward reseatReqId=${reseatReqId} senderId=${senderId} from=${fromSessionId} to=${plan.sessionId} account=${source.accountId}`);
|
|
21092
|
+
} else {
|
|
21093
|
+
console.error(`[session-reseat] channel-forward-failed reason=no-account-dir accountId=${source.accountId} senderId=${senderId}`);
|
|
21094
|
+
}
|
|
21095
|
+
} catch (err) {
|
|
21096
|
+
console.error(`[session-reseat] channel-forward-failed err=${err instanceof Error ? err.message : String(err)} senderId=${senderId}`);
|
|
20862
21097
|
}
|
|
20863
|
-
} catch (err) {
|
|
20864
|
-
console.error(`[session-reseat] channel-forward-failed err=${err instanceof Error ? err.message : String(err)} senderId=${senderId}`);
|
|
20865
21098
|
}
|
|
20866
|
-
console.log(`[chat-reseat] op=channel-fork-unbound reseatReqId=${reseatReqId} senderId=${senderId} session=${plan.sessionId} note=binds-on-next-inbound`);
|
|
20867
21099
|
}
|
|
20868
21100
|
const target = `/chat?session=${plan.sessionId}`;
|
|
20869
21101
|
console.log(`[chat-reseat] op=acquired reseatReqId=${reseatReqId} pid=${parsed?.spawnedPid ?? "none"} new=${plan.sessionId}`);
|
|
@@ -21000,10 +21232,10 @@ var system_stats_default = app37;
|
|
|
21000
21232
|
|
|
21001
21233
|
// server/routes/admin/health.ts
|
|
21002
21234
|
import { existsSync as existsSync31, readFileSync as readFileSync35 } from "fs";
|
|
21003
|
-
import { resolve as
|
|
21004
|
-
var PLATFORM_ROOT6 = process.env.MAXY_PLATFORM_ROOT ??
|
|
21235
|
+
import { resolve as resolve28, join as join38 } from "path";
|
|
21236
|
+
var PLATFORM_ROOT6 = process.env.MAXY_PLATFORM_ROOT ?? resolve28(process.cwd(), "..");
|
|
21005
21237
|
var brandHostname = "maxy";
|
|
21006
|
-
var brandJsonPath =
|
|
21238
|
+
var brandJsonPath = join38(PLATFORM_ROOT6, "config", "brand.json");
|
|
21007
21239
|
if (existsSync31(brandJsonPath)) {
|
|
21008
21240
|
try {
|
|
21009
21241
|
const brand = JSON.parse(readFileSync35(brandJsonPath, "utf-8"));
|
|
@@ -21011,7 +21243,7 @@ if (existsSync31(brandJsonPath)) {
|
|
|
21011
21243
|
} catch {
|
|
21012
21244
|
}
|
|
21013
21245
|
}
|
|
21014
|
-
var VERSION_FILE =
|
|
21246
|
+
var VERSION_FILE = resolve28(PLATFORM_ROOT6, `config/.${brandHostname}-version`);
|
|
21015
21247
|
var PROCESS_STARTED_AT = (/* @__PURE__ */ new Date()).toISOString();
|
|
21016
21248
|
var PROBE_TIMEOUT_MS = 1e3;
|
|
21017
21249
|
function readVersion() {
|
|
@@ -21793,7 +22025,7 @@ var browser_default = app45;
|
|
|
21793
22025
|
|
|
21794
22026
|
// server/routes/admin/calendar.ts
|
|
21795
22027
|
import { existsSync as existsSync32 } from "fs";
|
|
21796
|
-
import { join as
|
|
22028
|
+
import { join as join40 } from "path";
|
|
21797
22029
|
|
|
21798
22030
|
// server/lib/calendar-ics.ts
|
|
21799
22031
|
var CRLF = "\r\n";
|
|
@@ -21893,7 +22125,7 @@ function countVEvents(ics) {
|
|
|
21893
22125
|
|
|
21894
22126
|
// server/lib/calendar-availability.ts
|
|
21895
22127
|
import { readFileSync as readFileSync36 } from "fs";
|
|
21896
|
-
import { join as
|
|
22128
|
+
import { join as join39 } from "path";
|
|
21897
22129
|
var AVAILABILITY_FILENAME = "calendar-availability.json";
|
|
21898
22130
|
var REQUIRED_KEYS = [
|
|
21899
22131
|
"timezone",
|
|
@@ -21902,7 +22134,7 @@ var REQUIRED_KEYS = [
|
|
|
21902
22134
|
"weekly"
|
|
21903
22135
|
];
|
|
21904
22136
|
function readAvailabilityConfig(accountDir) {
|
|
21905
|
-
const path3 =
|
|
22137
|
+
const path3 = join39(accountDir, AVAILABILITY_FILENAME);
|
|
21906
22138
|
let raw;
|
|
21907
22139
|
try {
|
|
21908
22140
|
raw = readFileSync36(path3, "utf-8");
|
|
@@ -22264,7 +22496,7 @@ app46.get("/booking-link", requireAdminSession, (c) => {
|
|
|
22264
22496
|
console.error('[calendar] op=booking-link-fail err="no account configured"');
|
|
22265
22497
|
return c.json({ bookingDomain: null });
|
|
22266
22498
|
}
|
|
22267
|
-
if (!existsSync32(
|
|
22499
|
+
if (!existsSync32(join40(account.accountDir, AVAILABILITY_FILENAME))) {
|
|
22268
22500
|
console.log("[calendar] op=booking-link bookingDomain=none source=availability-config");
|
|
22269
22501
|
return c.json({ bookingDomain: null });
|
|
22270
22502
|
}
|
|
@@ -22923,9 +23155,9 @@ var verify_token_default = app54;
|
|
|
22923
23155
|
|
|
22924
23156
|
// app/lib/access-email.ts
|
|
22925
23157
|
import { spawn as spawn2 } from "child_process";
|
|
22926
|
-
import { resolve as
|
|
22927
|
-
var PLATFORM_ROOT7 = process.env.MAXY_PLATFORM_ROOT ??
|
|
22928
|
-
var SEND_SCRIPT =
|
|
23158
|
+
import { resolve as resolve29 } from "path";
|
|
23159
|
+
var PLATFORM_ROOT7 = process.env.MAXY_PLATFORM_ROOT ?? resolve29(process.cwd(), "..");
|
|
23160
|
+
var SEND_SCRIPT = resolve29(
|
|
22929
23161
|
PLATFORM_ROOT7,
|
|
22930
23162
|
"plugins",
|
|
22931
23163
|
"email",
|
|
@@ -23068,7 +23300,7 @@ var access_default = app56;
|
|
|
23068
23300
|
|
|
23069
23301
|
// server/routes/sites.ts
|
|
23070
23302
|
import { existsSync as existsSync33, readFileSync as readFileSync37, realpathSync as realpathSync7, statSync as statSync14 } from "fs";
|
|
23071
|
-
import { resolve as
|
|
23303
|
+
import { resolve as resolve30 } from "path";
|
|
23072
23304
|
var SAFE_SEG_RE = /^[a-z0-9_][a-z0-9_.-]{0,99}$/i;
|
|
23073
23305
|
var MIME = {
|
|
23074
23306
|
".html": "text/html; charset=utf-8",
|
|
@@ -23125,8 +23357,8 @@ app57.get("/:rel{.*}", (c) => {
|
|
|
23125
23357
|
}
|
|
23126
23358
|
segments.push(seg);
|
|
23127
23359
|
}
|
|
23128
|
-
const rootDir =
|
|
23129
|
-
let filePath = segments.length === 0 ? rootDir :
|
|
23360
|
+
const rootDir = resolve30(account.accountDir, "sites");
|
|
23361
|
+
let filePath = segments.length === 0 ? rootDir : resolve30(rootDir, ...segments);
|
|
23130
23362
|
if (filePath !== rootDir && !filePath.startsWith(rootDir + "/")) {
|
|
23131
23363
|
console.error(`[sites] path-traversal-rejected path=${reqPath} reason=escape status=403`);
|
|
23132
23364
|
return c.text("Forbidden", 403);
|
|
@@ -23146,7 +23378,7 @@ app57.get("/:rel{.*}", (c) => {
|
|
|
23146
23378
|
return c.redirect(target, 301);
|
|
23147
23379
|
}
|
|
23148
23380
|
if (stat9?.isDirectory()) {
|
|
23149
|
-
filePath =
|
|
23381
|
+
filePath = resolve30(filePath, "index.html");
|
|
23150
23382
|
}
|
|
23151
23383
|
if (!filePath.startsWith(rootDir + "/")) {
|
|
23152
23384
|
console.error(`[sites] path-traversal-rejected path=${reqPath} reason=escape status=403`);
|
|
@@ -23284,7 +23516,7 @@ var VISITOR_COOKIE_MAX_AGE_SECONDS = Math.floor(DEFAULT_TTL_MS / 1e3);
|
|
|
23284
23516
|
|
|
23285
23517
|
// app/lib/brand-config.ts
|
|
23286
23518
|
import { existsSync as existsSync34, readFileSync as readFileSync39 } from "fs";
|
|
23287
|
-
import { join as
|
|
23519
|
+
import { join as join41 } from "path";
|
|
23288
23520
|
var cached2 = null;
|
|
23289
23521
|
var cachedAttempted = false;
|
|
23290
23522
|
function readBrandConfig() {
|
|
@@ -23292,7 +23524,7 @@ function readBrandConfig() {
|
|
|
23292
23524
|
cachedAttempted = true;
|
|
23293
23525
|
const platformRoot5 = process.env.MAXY_PLATFORM_ROOT;
|
|
23294
23526
|
if (!platformRoot5) return null;
|
|
23295
|
-
const brandPath =
|
|
23527
|
+
const brandPath = join41(platformRoot5, "config", "brand.json");
|
|
23296
23528
|
if (!existsSync34(brandPath)) return null;
|
|
23297
23529
|
try {
|
|
23298
23530
|
cached2 = JSON.parse(readFileSync39(brandPath, "utf-8"));
|
|
@@ -23792,14 +24024,14 @@ async function writeEvent(opts) {
|
|
|
23792
24024
|
var visitor_event_default = app60;
|
|
23793
24025
|
|
|
23794
24026
|
// server/routes/session.ts
|
|
23795
|
-
import { resolve as
|
|
24027
|
+
import { resolve as resolve31 } from "path";
|
|
23796
24028
|
import { existsSync as existsSync35, writeFileSync as writeFileSync14, mkdirSync as mkdirSync9 } from "fs";
|
|
23797
24029
|
var UUID_RE5 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
23798
24030
|
function writeBrandingCache(accountId, agentSlug, branding) {
|
|
23799
24031
|
try {
|
|
23800
|
-
const cacheDir =
|
|
24032
|
+
const cacheDir = resolve31(MAXY_DIR, "branding-cache", accountId);
|
|
23801
24033
|
mkdirSync9(cacheDir, { recursive: true });
|
|
23802
|
-
writeFileSync14(
|
|
24034
|
+
writeFileSync14(resolve31(cacheDir, `${agentSlug}.json`), JSON.stringify(branding), "utf-8");
|
|
23803
24035
|
} catch (err) {
|
|
23804
24036
|
console.error(`[branding] cache write failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
23805
24037
|
}
|
|
@@ -23876,8 +24108,8 @@ app61.post("/", async (c) => {
|
|
|
23876
24108
|
}
|
|
23877
24109
|
let agentConfig = null;
|
|
23878
24110
|
if (account) {
|
|
23879
|
-
const agentDir =
|
|
23880
|
-
if (!existsSync35(agentDir) || !existsSync35(
|
|
24111
|
+
const agentDir = resolve31(account.accountDir, "agents", agentSlug);
|
|
24112
|
+
if (!existsSync35(agentDir) || !existsSync35(resolve31(agentDir, "config.json"))) {
|
|
23881
24113
|
return c.json({ error: "Agent not found" }, 404);
|
|
23882
24114
|
}
|
|
23883
24115
|
agentConfig = resolveAgentConfig(account.accountDir, agentSlug);
|
|
@@ -24127,7 +24359,7 @@ var calendar_public_default = app62;
|
|
|
24127
24359
|
// server/routes/portal-fetch.ts
|
|
24128
24360
|
import { createReadStream as createReadStream3 } from "fs";
|
|
24129
24361
|
import { stat as stat6, readFile as readFile6, realpath } from "fs/promises";
|
|
24130
|
-
import { join as
|
|
24362
|
+
import { join as join42, resolve as resolve32, sep as sep8 } from "path";
|
|
24131
24363
|
import { Readable as Readable3 } from "stream";
|
|
24132
24364
|
|
|
24133
24365
|
// ../plugins/cloudflare/bin/schema-exposed-dirs.mjs
|
|
@@ -24228,7 +24460,7 @@ ${expiresAtMs}`)
|
|
|
24228
24460
|
return diff === 0;
|
|
24229
24461
|
}
|
|
24230
24462
|
async function readAccountSecret(accountId) {
|
|
24231
|
-
const path3 =
|
|
24463
|
+
const path3 = resolve32(DATA_ROOT, "accounts", accountId, "secrets", "data-portal.env");
|
|
24232
24464
|
let text;
|
|
24233
24465
|
try {
|
|
24234
24466
|
text = await readFile6(path3, "utf8");
|
|
@@ -24249,13 +24481,13 @@ function isValidAccountId2(value) {
|
|
|
24249
24481
|
async function resolveFetchTarget(accountDir, relPath) {
|
|
24250
24482
|
let schemaText = null;
|
|
24251
24483
|
try {
|
|
24252
|
-
schemaText = await readFile6(
|
|
24484
|
+
schemaText = await readFile6(join42(accountDir, "SCHEMA.md"), "utf8");
|
|
24253
24485
|
} catch {
|
|
24254
24486
|
schemaText = null;
|
|
24255
24487
|
}
|
|
24256
24488
|
const { exposed } = resolveExposedDirs(schemaText);
|
|
24257
24489
|
if (exposed.length === 0) return { ok: false, reason: "not-exposed" };
|
|
24258
|
-
const lexical =
|
|
24490
|
+
const lexical = resolve32(accountDir, relPath);
|
|
24259
24491
|
let root;
|
|
24260
24492
|
let absolute;
|
|
24261
24493
|
try {
|
|
@@ -24293,7 +24525,7 @@ async function handle(c, headOnly) {
|
|
|
24293
24525
|
console.error(`${TAG45} op=denied account=${q(accountId)} reason=secret`);
|
|
24294
24526
|
return c.json({ ok: false, error: "denied" }, 401);
|
|
24295
24527
|
}
|
|
24296
|
-
const target = await resolveFetchTarget(
|
|
24528
|
+
const target = await resolveFetchTarget(resolve32(DATA_ROOT, "accounts", accountId), relPath);
|
|
24297
24529
|
if (!target.ok) {
|
|
24298
24530
|
const op = target.reason === "enoent" ? "miss" : "denied";
|
|
24299
24531
|
console.error(`${TAG45} op=${op} account=${accountId} reason=${target.reason}`);
|
|
@@ -24405,7 +24637,7 @@ function startTimeEntryCensus(openSession, opts = {}) {
|
|
|
24405
24637
|
|
|
24406
24638
|
// app/lib/ledger-census.ts
|
|
24407
24639
|
import { readdirSync as readdirSync25, readFileSync as readFileSync40, statSync as statSync15 } from "fs";
|
|
24408
|
-
import { join as
|
|
24640
|
+
import { join as join43 } from "path";
|
|
24409
24641
|
|
|
24410
24642
|
// ../lib/ledger-core/dist/reconcile.js
|
|
24411
24643
|
var LEDGER_INDEX_NAMES = [
|
|
@@ -24459,7 +24691,7 @@ function countWriteRejects24h(logDir, now) {
|
|
|
24459
24691
|
}
|
|
24460
24692
|
let total = 0;
|
|
24461
24693
|
for (const name of entries) {
|
|
24462
|
-
const path3 =
|
|
24694
|
+
const path3 = join43(logDir, name);
|
|
24463
24695
|
try {
|
|
24464
24696
|
if (now - statSync15(path3).mtimeMs > DAY_MS) continue;
|
|
24465
24697
|
for (const line of readFileSync40(path3, "utf8").split("\n")) {
|
|
@@ -24731,7 +24963,7 @@ function startGraphHealthTimer() {
|
|
|
24731
24963
|
|
|
24732
24964
|
// app/lib/file-watcher.ts
|
|
24733
24965
|
import * as fsp2 from "fs/promises";
|
|
24734
|
-
import { resolve as
|
|
24966
|
+
import { resolve as resolve33, sep as sep9 } from "path";
|
|
24735
24967
|
var ACCOUNT_UUID_RE4 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
24736
24968
|
var DEFAULT_COALESCE_MS = 500;
|
|
24737
24969
|
var ROOTS = ["accounts"];
|
|
@@ -24750,7 +24982,7 @@ async function startFileWatcher(opts = {}) {
|
|
|
24750
24982
|
const dropFn = opts.drop ?? dropFileIndex;
|
|
24751
24983
|
for (const r of ROOTS) {
|
|
24752
24984
|
try {
|
|
24753
|
-
await fsp2.mkdir(
|
|
24985
|
+
await fsp2.mkdir(resolve33(dataRoot, r), { recursive: true });
|
|
24754
24986
|
} catch (err) {
|
|
24755
24987
|
console.error(
|
|
24756
24988
|
`[file-watcher] start-failed root="${r}" err="${err.message}" \u2014 index will be maintained by the 5-min reconcile backstop only`
|
|
@@ -24771,7 +25003,7 @@ async function startFileWatcher(opts = {}) {
|
|
|
24771
25003
|
timers.set(relativePath, t);
|
|
24772
25004
|
}
|
|
24773
25005
|
async function runHook(relativePath, accountId) {
|
|
24774
|
-
const absolute =
|
|
25006
|
+
const absolute = resolve33(dataRoot, relativePath);
|
|
24775
25007
|
let exists = false;
|
|
24776
25008
|
try {
|
|
24777
25009
|
const st = await fsp2.stat(absolute);
|
|
@@ -24795,7 +25027,7 @@ async function startFileWatcher(opts = {}) {
|
|
|
24795
25027
|
}
|
|
24796
25028
|
}
|
|
24797
25029
|
async function watchRoot(rootName) {
|
|
24798
|
-
const absRoot =
|
|
25030
|
+
const absRoot = resolve33(dataRoot, rootName);
|
|
24799
25031
|
try {
|
|
24800
25032
|
const iter = fsp2.watch(absRoot, { recursive: true, signal: controller.signal });
|
|
24801
25033
|
for await (const event of iter) {
|
|
@@ -24834,7 +25066,7 @@ async function startFileWatcher(opts = {}) {
|
|
|
24834
25066
|
|
|
24835
25067
|
// app/lib/migrate-uploads.ts
|
|
24836
25068
|
import { mkdir as mkdir5, readdir as readdir5, rename as rename2, rm as rm3 } from "fs/promises";
|
|
24837
|
-
import { dirname as dirname13, relative as relative6, resolve as
|
|
25069
|
+
import { dirname as dirname13, relative as relative6, resolve as resolve34 } from "path";
|
|
24838
25070
|
var ACCOUNT_UUID_RE5 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
24839
25071
|
async function walkFiles(dir) {
|
|
24840
25072
|
const out = [];
|
|
@@ -24845,7 +25077,7 @@ async function walkFiles(dir) {
|
|
|
24845
25077
|
return out;
|
|
24846
25078
|
}
|
|
24847
25079
|
for (const e of entries) {
|
|
24848
|
-
const abs =
|
|
25080
|
+
const abs = resolve34(dir, e.name);
|
|
24849
25081
|
if (e.isDirectory()) {
|
|
24850
25082
|
out.push(...await walkFiles(abs));
|
|
24851
25083
|
} else if (e.isFile()) {
|
|
@@ -24865,7 +25097,7 @@ async function relocateTree(srcDir, destDir, dataRoot, accountId, session) {
|
|
|
24865
25097
|
let moved = 0;
|
|
24866
25098
|
for (const oldAbs of files) {
|
|
24867
25099
|
const suffix = relative6(srcDir, oldAbs);
|
|
24868
|
-
const newAbs =
|
|
25100
|
+
const newAbs = resolve34(destDir, suffix);
|
|
24869
25101
|
await mkdir5(dirname13(newAbs), { recursive: true });
|
|
24870
25102
|
await rename2(oldAbs, newAbs);
|
|
24871
25103
|
moved++;
|
|
@@ -24892,7 +25124,7 @@ async function relocateTree(srcDir, destDir, dataRoot, accountId, session) {
|
|
|
24892
25124
|
}
|
|
24893
25125
|
async function migrateUploads(opts = {}) {
|
|
24894
25126
|
const dataRoot = opts.dataRoot ?? DATA_ROOT;
|
|
24895
|
-
const oldRoot =
|
|
25127
|
+
const oldRoot = resolve34(dataRoot, "uploads");
|
|
24896
25128
|
let topEntries;
|
|
24897
25129
|
try {
|
|
24898
25130
|
topEntries = await readdir5(oldRoot, { withFileTypes: true });
|
|
@@ -24913,8 +25145,8 @@ async function migrateUploads(opts = {}) {
|
|
|
24913
25145
|
const name = entry.name;
|
|
24914
25146
|
if (ACCOUNT_UUID_RE5.test(name)) {
|
|
24915
25147
|
moved += await relocateTree(
|
|
24916
|
-
|
|
24917
|
-
|
|
25148
|
+
resolve34(oldRoot, name),
|
|
25149
|
+
resolve34(dataRoot, "accounts", name, "uploads"),
|
|
24918
25150
|
dataRoot,
|
|
24919
25151
|
name,
|
|
24920
25152
|
session
|
|
@@ -24926,8 +25158,8 @@ async function migrateUploads(opts = {}) {
|
|
|
24926
25158
|
continue;
|
|
24927
25159
|
}
|
|
24928
25160
|
moved += await relocateTree(
|
|
24929
|
-
|
|
24930
|
-
|
|
25161
|
+
resolve34(oldRoot, "public"),
|
|
25162
|
+
resolve34(dataRoot, "accounts", installAccountId, "uploads", "public"),
|
|
24931
25163
|
dataRoot,
|
|
24932
25164
|
null,
|
|
24933
25165
|
// public uploads carry no graph nodes
|
|
@@ -24958,7 +25190,7 @@ async function migrateUploads(opts = {}) {
|
|
|
24958
25190
|
// app/lib/migrate-admin-webchat-sidecars.ts
|
|
24959
25191
|
import { readdir as readdir6, readFile as readFile7, rename as rename3, writeFile as writeFile4, unlink as unlink3 } from "fs/promises";
|
|
24960
25192
|
import { existsSync as existsSync36 } from "fs";
|
|
24961
|
-
import { join as
|
|
25193
|
+
import { join as join44 } from "path";
|
|
24962
25194
|
var ADMIN_ROLE2 = "admin";
|
|
24963
25195
|
var WEBCHAT_CHANNEL2 = "webchat";
|
|
24964
25196
|
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;
|
|
@@ -25003,7 +25235,7 @@ async function collectLiveSidecars(projectsRoot) {
|
|
|
25003
25235
|
}
|
|
25004
25236
|
for (const slug of slugs) {
|
|
25005
25237
|
if (!slug.isDirectory()) continue;
|
|
25006
|
-
const slugDir =
|
|
25238
|
+
const slugDir = join44(projectsRoot, slug.name);
|
|
25007
25239
|
let entries;
|
|
25008
25240
|
try {
|
|
25009
25241
|
entries = await readdir6(slugDir, { withFileTypes: true });
|
|
@@ -25012,9 +25244,9 @@ async function collectLiveSidecars(projectsRoot) {
|
|
|
25012
25244
|
}
|
|
25013
25245
|
for (const entry of entries) {
|
|
25014
25246
|
if (entry.isFile() && SESSION_META_RE.test(entry.name)) {
|
|
25015
|
-
out.push(
|
|
25247
|
+
out.push(join44(slugDir, entry.name));
|
|
25016
25248
|
} else if (entry.isDirectory() && entry.name === "subagents") {
|
|
25017
|
-
const subDir =
|
|
25249
|
+
const subDir = join44(slugDir, entry.name);
|
|
25018
25250
|
let subs;
|
|
25019
25251
|
try {
|
|
25020
25252
|
subs = await readdir6(subDir, { withFileTypes: true });
|
|
@@ -25022,7 +25254,7 @@ async function collectLiveSidecars(projectsRoot) {
|
|
|
25022
25254
|
continue;
|
|
25023
25255
|
}
|
|
25024
25256
|
for (const s of subs) {
|
|
25025
|
-
if (s.isFile() && SESSION_META_RE.test(s.name)) out.push(
|
|
25257
|
+
if (s.isFile() && SESSION_META_RE.test(s.name)) out.push(join44(subDir, s.name));
|
|
25026
25258
|
}
|
|
25027
25259
|
}
|
|
25028
25260
|
}
|
|
@@ -25034,7 +25266,7 @@ function shortId(path3) {
|
|
|
25034
25266
|
return base.slice(0, 8);
|
|
25035
25267
|
}
|
|
25036
25268
|
async function migrateAdminWebchatSidecars(opts = {}) {
|
|
25037
|
-
const projectsRoot = opts.projectsRoot ?? (process.env.CLAUDE_CONFIG_DIR ?
|
|
25269
|
+
const projectsRoot = opts.projectsRoot ?? (process.env.CLAUDE_CONFIG_DIR ? join44(process.env.CLAUDE_CONFIG_DIR, "projects") : null) ?? "";
|
|
25038
25270
|
const usersFile = opts.usersFile ?? USERS_FILE;
|
|
25039
25271
|
const accountId = opts.accountId ?? resolveAccount()?.accountId ?? null;
|
|
25040
25272
|
const resolveName = opts.resolveName ?? defaultResolveName;
|
|
@@ -25388,11 +25620,9 @@ function createWaChannelRoutes(deps) {
|
|
|
25388
25620
|
void stream2.writeSSE({ data: JSON.stringify(payload) });
|
|
25389
25621
|
});
|
|
25390
25622
|
console.error(`[whatsapp-native] op=channel-attached senderId=${senderId}`);
|
|
25391
|
-
deps.onAttached?.(senderId);
|
|
25392
25623
|
stream2.onAbort(() => {
|
|
25393
25624
|
deps.hub.detach(senderId);
|
|
25394
25625
|
console.error(`[whatsapp-native] op=channel-detached senderId=${senderId}`);
|
|
25395
|
-
deps.onDetached?.(senderId);
|
|
25396
25626
|
});
|
|
25397
25627
|
while (!stream2.aborted) {
|
|
25398
25628
|
await stream2.sleep(15e3);
|
|
@@ -25533,31 +25763,16 @@ var WaGateway = class {
|
|
|
25533
25763
|
docContexts = /* @__PURE__ */ new Map();
|
|
25534
25764
|
spawning = /* @__PURE__ */ new Set();
|
|
25535
25765
|
seq = 0;
|
|
25536
|
-
/** Hono sub-app exposing the loopback channel routes; mount on the UI app.
|
|
25537
|
-
|
|
25538
|
-
*
|
|
25539
|
-
|
|
25540
|
-
|
|
25541
|
-
|
|
25542
|
-
|
|
25543
|
-
}
|
|
25544
|
-
/** Seat regained — the sender is no longer unseated. */
|
|
25545
|
-
recordAttached(senderId) {
|
|
25546
|
-
this.detachedAt.delete(senderId);
|
|
25547
|
-
}
|
|
25548
|
-
/** Senders with no channel server for longer than `olderThanMs`. */
|
|
25549
|
-
unseatedSenders(nowMs, olderThanMs) {
|
|
25550
|
-
const out = [];
|
|
25551
|
-
for (const [senderId, sinceMs] of this.detachedAt) {
|
|
25552
|
-
if (nowMs - sinceMs > olderThanMs) out.push({ senderId, sinceMs });
|
|
25553
|
-
}
|
|
25554
|
-
return out.sort((a, b) => a.senderId.localeCompare(b.senderId));
|
|
25555
|
-
}
|
|
25766
|
+
/** Hono sub-app exposing the loopback channel routes; mount on the UI app.
|
|
25767
|
+
* Task 1836 — the process-local unseated-sender census that used to live here
|
|
25768
|
+
* was retired. Its quiet state was ambiguous (a UI restart emptied it, so
|
|
25769
|
+
* silence meant "no detach seen since boot", not "every sender is seated") and
|
|
25770
|
+
* it grew unbounded. The genuine "no seat with pending work" signal is
|
|
25771
|
+
* InboundHub.sweep -> spawn-undelivered, which is driven by live queue state
|
|
25772
|
+
* and self-prunes. */
|
|
25556
25773
|
routes() {
|
|
25557
25774
|
return createWaChannelRoutes({
|
|
25558
25775
|
hub: this.hub,
|
|
25559
|
-
onAttached: (senderId) => this.recordAttached(senderId),
|
|
25560
|
-
onDetached: (senderId) => this.recordDetached(senderId, Date.now()),
|
|
25561
25776
|
sendOutbound: (senderId, text) => this.sendOutbound(senderId, text),
|
|
25562
25777
|
sendDocument: (senderId, filePath, caption) => this.sendDocument(senderId, filePath, caption),
|
|
25563
25778
|
onReady: (senderId) => {
|
|
@@ -25736,12 +25951,6 @@ function logDuplicateSenderGroups(groups) {
|
|
|
25736
25951
|
);
|
|
25737
25952
|
}
|
|
25738
25953
|
}
|
|
25739
|
-
function logUnseatedSenders(entries, nowMs) {
|
|
25740
|
-
for (const e of entries) {
|
|
25741
|
-
const secs = Math.round((nowMs - e.sinceMs) / 1e3);
|
|
25742
|
-
console.error(`[whatsapp-native] op=sender-unseated senderId=${e.senderId} since=${new Date(e.sinceMs).toISOString()} forSeconds=${secs}`);
|
|
25743
|
-
}
|
|
25744
|
-
}
|
|
25745
25954
|
|
|
25746
25955
|
// app/lib/whatsapp/gateway/spawn-request.ts
|
|
25747
25956
|
function buildWaSpawnRequest(input) {
|
|
@@ -26097,16 +26306,16 @@ var WebchatGateway = class _WebchatGateway {
|
|
|
26097
26306
|
* The public /api/chat route awaits this to return the reply on the same SSE
|
|
26098
26307
|
* response, preserving the pre-756 POST→single-blob browser contract. */
|
|
26099
26308
|
awaitReply(key, timeoutMs) {
|
|
26100
|
-
return new Promise((
|
|
26309
|
+
return new Promise((resolve39) => {
|
|
26101
26310
|
const timer = setTimeout(() => {
|
|
26102
26311
|
this.replyAwaiters.delete(key);
|
|
26103
|
-
|
|
26312
|
+
resolve39({ timeout: true });
|
|
26104
26313
|
}, timeoutMs);
|
|
26105
26314
|
if (timer.unref) timer.unref();
|
|
26106
26315
|
this.replyAwaiters.set(key, (r) => {
|
|
26107
26316
|
clearTimeout(timer);
|
|
26108
26317
|
this.replyAwaiters.delete(key);
|
|
26109
|
-
|
|
26318
|
+
resolve39(r);
|
|
26110
26319
|
});
|
|
26111
26320
|
});
|
|
26112
26321
|
}
|
|
@@ -26117,11 +26326,11 @@ var WebchatGateway = class _WebchatGateway {
|
|
|
26117
26326
|
* terminal dialog stays answerable). */
|
|
26118
26327
|
awaitPermissionVerdict(p, timeoutMs) {
|
|
26119
26328
|
const k = _WebchatGateway.promptKey(p.key, p.requestId);
|
|
26120
|
-
return new Promise((
|
|
26329
|
+
return new Promise((resolve39) => {
|
|
26121
26330
|
this.pendingPrompts.get(k)?.resolve({ timeout: true });
|
|
26122
26331
|
const timer = setTimeout(() => {
|
|
26123
26332
|
this.pendingPrompts.delete(k);
|
|
26124
|
-
|
|
26333
|
+
resolve39({ timeout: true });
|
|
26125
26334
|
}, timeoutMs);
|
|
26126
26335
|
if (timer.unref) timer.unref();
|
|
26127
26336
|
this.pendingPrompts.set(k, {
|
|
@@ -26130,7 +26339,7 @@ var WebchatGateway = class _WebchatGateway {
|
|
|
26130
26339
|
resolve: (r) => {
|
|
26131
26340
|
clearTimeout(timer);
|
|
26132
26341
|
this.pendingPrompts.delete(k);
|
|
26133
|
-
|
|
26342
|
+
resolve39(r);
|
|
26134
26343
|
}
|
|
26135
26344
|
});
|
|
26136
26345
|
console.error(`[webchat:perm] op=open key=${keyDisplay(p.key)} id=${p.requestId} tool=${p.toolName}`);
|
|
@@ -26695,7 +26904,7 @@ async function fanOut(subscribers, text, onError, tag) {
|
|
|
26695
26904
|
|
|
26696
26905
|
// app/lib/webchat/file-delivery.ts
|
|
26697
26906
|
import { realpathSync as realpathSync8 } from "fs";
|
|
26698
|
-
import { resolve as
|
|
26907
|
+
import { resolve as resolve35 } from "path";
|
|
26699
26908
|
|
|
26700
26909
|
// app/lib/channel-pty-bridge/file-delivery.ts
|
|
26701
26910
|
var SEND_USER_FILE = "SendUserFile";
|
|
@@ -26796,7 +27005,7 @@ function makeWebchatSendFile(entry) {
|
|
|
26796
27005
|
console.error(`${TAG49} file-delivery reject reason=no-account sender=${entry.senderId}`);
|
|
26797
27006
|
return { ok: false, error: "no-account" };
|
|
26798
27007
|
}
|
|
26799
|
-
const accountDir =
|
|
27008
|
+
const accountDir = resolve35(platformRoot2(), "..", "data/accounts", entry.accountId);
|
|
26800
27009
|
try {
|
|
26801
27010
|
const resolved = realpathSync8(filePath);
|
|
26802
27011
|
const accountResolved = realpathSync8(accountDir);
|
|
@@ -27363,7 +27572,7 @@ function buildTelegramSpawnRequest(input) {
|
|
|
27363
27572
|
// app/lib/telegram/outbound/send-document.ts
|
|
27364
27573
|
import { realpathSync as realpathSync9 } from "fs";
|
|
27365
27574
|
import { readFile as readFile8, stat as stat8 } from "fs/promises";
|
|
27366
|
-
import { resolve as
|
|
27575
|
+
import { resolve as resolve36, basename as basename11 } from "path";
|
|
27367
27576
|
var TAG51 = "[telegram:outbound]";
|
|
27368
27577
|
var TELEGRAM_DOCUMENT_MAX_BYTES = 50 * 1024 * 1024;
|
|
27369
27578
|
async function sendTelegramDocument(input) {
|
|
@@ -27374,7 +27583,7 @@ async function sendTelegramDocument(input) {
|
|
|
27374
27583
|
if (!maxyAccountId || !platformRoot5) {
|
|
27375
27584
|
return { ok: false, status: 400, error: "Cannot validate file path: missing account or platform context" };
|
|
27376
27585
|
}
|
|
27377
|
-
const accountDir =
|
|
27586
|
+
const accountDir = resolve36(platformRoot5, "..", "data/accounts", maxyAccountId);
|
|
27378
27587
|
let resolvedPath;
|
|
27379
27588
|
try {
|
|
27380
27589
|
resolvedPath = realpathSync9(filePath);
|
|
@@ -27500,15 +27709,6 @@ function startTelegramNativeFileFollower(input) {
|
|
|
27500
27709
|
});
|
|
27501
27710
|
}
|
|
27502
27711
|
|
|
27503
|
-
// server/telegram-descriptor.ts
|
|
27504
|
-
import { resolve as resolve35, join as join43 } from "path";
|
|
27505
|
-
function telegramGatewayUrl() {
|
|
27506
|
-
return `http://127.0.0.1:${process.env.MAXY_UI_INTERNAL_PORT ?? ""}`;
|
|
27507
|
-
}
|
|
27508
|
-
function telegramServerPath() {
|
|
27509
|
-
return process.env.MAXY_TELEGRAM_CHANNEL_SERVER_PATH ?? resolve35(process.env.MAXY_PLATFORM_ROOT ?? join43(__dirname, ".."), "services/telegram-channel/dist/server.js");
|
|
27510
|
-
}
|
|
27511
|
-
|
|
27512
27712
|
// app/lib/channel-pty-bridge/public-session-end-review.ts
|
|
27513
27713
|
import { randomUUID as randomUUID16 } from "crypto";
|
|
27514
27714
|
var TAG53 = "[public-session-review]";
|
|
@@ -27794,7 +27994,7 @@ function broadcastAdminShutdown(reason) {
|
|
|
27794
27994
|
// ../lib/entitlement/src/index.ts
|
|
27795
27995
|
import { createPublicKey, createHash as createHash6, verify as cryptoVerify } from "crypto";
|
|
27796
27996
|
import { existsSync as existsSync37, readFileSync as readFileSync41, statSync as statSync16 } from "fs";
|
|
27797
|
-
import { resolve as
|
|
27997
|
+
import { resolve as resolve37 } from "path";
|
|
27798
27998
|
|
|
27799
27999
|
// ../lib/entitlement/src/canonicalize.ts
|
|
27800
28000
|
function canonicalize(value) {
|
|
@@ -27829,7 +28029,7 @@ var PUBKEY_SHA256 = "8eee6bcb33545fd13b16d3199a5735ca5db5062834c7b49dfe4f23801d9
|
|
|
27829
28029
|
var GRACE_DAYS = 7;
|
|
27830
28030
|
var GRACE_MS = GRACE_DAYS * 24 * 60 * 60 * 1e3;
|
|
27831
28031
|
function pubkeyPath(brand) {
|
|
27832
|
-
return
|
|
28032
|
+
return resolve37(brand.platformRoot, "lib", "entitlement", "rubytech-pubkey.pem");
|
|
27833
28033
|
}
|
|
27834
28034
|
var memo = null;
|
|
27835
28035
|
function memoKey(mtimeMs, account) {
|
|
@@ -27841,7 +28041,7 @@ function resolveEntitlement(brand, account) {
|
|
|
27841
28041
|
if (brand.commercialMode !== true) {
|
|
27842
28042
|
return logResolved(implicitTrust(account), null);
|
|
27843
28043
|
}
|
|
27844
|
-
const entitlementPath =
|
|
28044
|
+
const entitlementPath = resolve37(brand.configDir, "entitlement.json");
|
|
27845
28045
|
if (!existsSync37(entitlementPath)) {
|
|
27846
28046
|
return logResolved(anonymousFallback("missing"), { reason: "missing" });
|
|
27847
28047
|
}
|
|
@@ -28033,7 +28233,7 @@ function clientFrom(c) {
|
|
|
28033
28233
|
}
|
|
28034
28234
|
installMediaDownloadGuard();
|
|
28035
28235
|
var PLATFORM_ROOT8 = process.env.MAXY_PLATFORM_ROOT || "";
|
|
28036
|
-
var BRAND_JSON_PATH = PLATFORM_ROOT8 ?
|
|
28236
|
+
var BRAND_JSON_PATH = PLATFORM_ROOT8 ? join45(PLATFORM_ROOT8, "config", "brand.json") : "";
|
|
28037
28237
|
var BRAND = { productName: "Maxy", hostname: "maxy", configDir: ".maxy", marketingUrl: "https://getmaxy.com" };
|
|
28038
28238
|
if (BRAND_JSON_PATH && !existsSync38(BRAND_JSON_PATH)) {
|
|
28039
28239
|
console.error(`[brand] WARNING: brand.json not found at ${BRAND_JSON_PATH} \u2014 using Maxy defaults`);
|
|
@@ -28072,7 +28272,7 @@ var brandLoginOpts = {
|
|
|
28072
28272
|
appleTouchIconPath: brandAppIcon512Path,
|
|
28073
28273
|
themeColor: BRAND.defaultColors?.primary
|
|
28074
28274
|
};
|
|
28075
|
-
var ALIAS_DOMAINS_PATH =
|
|
28275
|
+
var ALIAS_DOMAINS_PATH = join45(homedir4(), BRAND.configDir, "alias-domains.json");
|
|
28076
28276
|
function loadAliasDomains() {
|
|
28077
28277
|
try {
|
|
28078
28278
|
if (!existsSync38(ALIAS_DOMAINS_PATH)) return null;
|
|
@@ -28149,7 +28349,7 @@ var waGateway = new WaGateway({
|
|
|
28149
28349
|
fetchStandingRules: fetchAccountStandingRules,
|
|
28150
28350
|
resolveSessionOverride: (accountId, senderId) => resolveChannelOverride(accountId, "whatsapp", senderId),
|
|
28151
28351
|
gatewayUrl: `http://127.0.0.1:${process.env.MAXY_UI_INTERNAL_PORT ?? ""}`,
|
|
28152
|
-
serverPath: process.env.MAXY_WA_CHANNEL_SERVER_PATH ??
|
|
28352
|
+
serverPath: process.env.MAXY_WA_CHANNEL_SERVER_PATH ?? resolve38(process.env.MAXY_PLATFORM_ROOT ?? join45(__dirname, ".."), "services/whatsapp-channel/dist/server.js"),
|
|
28153
28353
|
// Task 751 / 1390 — file delivery on the native channel. `maxyAccountId` (the
|
|
28154
28354
|
// path-validation scope) is the sender's effective SESSION account, resolved
|
|
28155
28355
|
// once at the inbound gate and threaded here via the gateway's per-sender doc
|
|
@@ -28164,7 +28364,7 @@ var waGateway = new WaGateway({
|
|
|
28164
28364
|
caption,
|
|
28165
28365
|
accountId,
|
|
28166
28366
|
maxyAccountId,
|
|
28167
|
-
platformRoot:
|
|
28367
|
+
platformRoot: resolve38(process.env.MAXY_PLATFORM_ROOT ?? join45(__dirname, ".."))
|
|
28168
28368
|
});
|
|
28169
28369
|
return result.ok ? { ok: true, messageId: result.messageId } : { ok: false, error: result.error };
|
|
28170
28370
|
},
|
|
@@ -28278,7 +28478,7 @@ function runDuplicateSenderAudit() {
|
|
|
28278
28478
|
try {
|
|
28279
28479
|
const configDir2 = process.env.CLAUDE_CONFIG_DIR;
|
|
28280
28480
|
if (!configDir2) return;
|
|
28281
|
-
const projectsRoot =
|
|
28481
|
+
const projectsRoot = join45(configDir2, "projects");
|
|
28282
28482
|
const records = [];
|
|
28283
28483
|
for (const { path: jsonlPath, isSubagent, archived } of enumerateJsonls(projectsRoot)) {
|
|
28284
28484
|
if (isSubagent || archived) continue;
|
|
@@ -28288,8 +28488,6 @@ function runDuplicateSenderAudit() {
|
|
|
28288
28488
|
records.push({ sessionId, channel: meta.channel, senderId: meta.senderId, accountId: meta.accountId });
|
|
28289
28489
|
}
|
|
28290
28490
|
logDuplicateSenderGroups(findDuplicateSenderGroups(records));
|
|
28291
|
-
const now = Date.now();
|
|
28292
|
-
logUnseatedSenders(waGateway.unseatedSenders(now, 15 * 6e4), now);
|
|
28293
28491
|
} catch (err) {
|
|
28294
28492
|
console.error(`[whatsapp-native] op=duplicate-sender-audit-error error=${err instanceof Error ? err.message : String(err)}`);
|
|
28295
28493
|
}
|
|
@@ -28300,6 +28498,7 @@ registerLoop({
|
|
|
28300
28498
|
firstRunDelayMs: 0,
|
|
28301
28499
|
run: runDuplicateSenderAudit
|
|
28302
28500
|
});
|
|
28501
|
+
console.log("[1789-sequel] op=cluster-complete items=[1835,1836,1837,1839]");
|
|
28303
28502
|
var webchatGateway = new WebchatGateway({
|
|
28304
28503
|
fetchStandingRules: fetchAccountStandingRules,
|
|
28305
28504
|
gatewayUrl: webchatGatewayUrl(),
|
|
@@ -28841,8 +29040,8 @@ app64.get("/agent-assets/:slug/:filename", (c) => {
|
|
|
28841
29040
|
console.error(`[agent-assets] no-account slug=${slug} file=${filename}`);
|
|
28842
29041
|
return c.text("Not found", 404);
|
|
28843
29042
|
}
|
|
28844
|
-
const filePath =
|
|
28845
|
-
const expectedDir =
|
|
29043
|
+
const filePath = resolve38(account.accountDir, "agents", slug, "assets", filename);
|
|
29044
|
+
const expectedDir = resolve38(account.accountDir, "agents", slug, "assets");
|
|
28846
29045
|
if (!filePath.startsWith(expectedDir + "/")) {
|
|
28847
29046
|
console.error(`[agent-assets] path-traversal-rejected slug=${slug} file=${filename}`);
|
|
28848
29047
|
return c.text("Forbidden", 403);
|
|
@@ -28871,8 +29070,8 @@ app64.get("/generated/:filename", (c) => {
|
|
|
28871
29070
|
console.error(`[generated] serve file=${filename} status=404`);
|
|
28872
29071
|
return c.text("Not found", 404);
|
|
28873
29072
|
}
|
|
28874
|
-
const filePath =
|
|
28875
|
-
const expectedDir =
|
|
29073
|
+
const filePath = resolve38(account.accountDir, "generated", filename);
|
|
29074
|
+
const expectedDir = resolve38(account.accountDir, "generated");
|
|
28876
29075
|
if (!filePath.startsWith(expectedDir + "/")) {
|
|
28877
29076
|
console.error(`[generated] serve file=${filename} status=403`);
|
|
28878
29077
|
return c.text("Forbidden", 403);
|
|
@@ -28926,11 +29125,11 @@ var brandScript = `<script>window.__BRAND__=${JSON.stringify({
|
|
|
28926
29125
|
var brandThemeColor = BRAND.defaultColors?.primary ?? "#000000";
|
|
28927
29126
|
var brandBackgroundColor = BRAND.defaultColors?.background ?? "#ffffff";
|
|
28928
29127
|
var brandAppIconsExist = [brandAppIcon192Path, brandAppIcon512Path, brandMaskableIconPath].every(
|
|
28929
|
-
(p) => existsSync38(
|
|
29128
|
+
(p) => existsSync38(resolve38(process.cwd(), "public", p.replace(/^\//, "")))
|
|
28930
29129
|
);
|
|
28931
29130
|
var SW_SOURCE = (() => {
|
|
28932
29131
|
try {
|
|
28933
|
-
return readFileSync42(
|
|
29132
|
+
return readFileSync42(resolve38(process.cwd(), "public", "sw.js"), "utf-8");
|
|
28934
29133
|
} catch {
|
|
28935
29134
|
return null;
|
|
28936
29135
|
}
|
|
@@ -28938,7 +29137,7 @@ var SW_SOURCE = (() => {
|
|
|
28938
29137
|
function readInstalledVersion() {
|
|
28939
29138
|
try {
|
|
28940
29139
|
if (!PLATFORM_ROOT8) return "unknown";
|
|
28941
|
-
const versionFile =
|
|
29140
|
+
const versionFile = join45(PLATFORM_ROOT8, "config", `.${BRAND.hostname}-version`);
|
|
28942
29141
|
if (!existsSync38(versionFile)) return "unknown";
|
|
28943
29142
|
const content = readFileSync42(versionFile, "utf-8").trim();
|
|
28944
29143
|
return content || "unknown";
|
|
@@ -28981,7 +29180,7 @@ var clientErrorReporterScript = `<script>
|
|
|
28981
29180
|
function cachedHtml(file) {
|
|
28982
29181
|
let html = htmlCache.get(file);
|
|
28983
29182
|
if (!html) {
|
|
28984
|
-
html = readFileSync42(
|
|
29183
|
+
html = readFileSync42(resolve38(process.cwd(), "public", file), "utf-8");
|
|
28985
29184
|
const productNameEsc = escapeHtml(BRAND.productName);
|
|
28986
29185
|
html = html.replace(/<title>([^<]*)<\/title>/, (_match, inner) => `<title>${escapeHtml(inner).replace(/Maxy/g, productNameEsc)}</title>`);
|
|
28987
29186
|
html = html.replace('href="/favicon.ico"', `href="${escapeHtml(brandFaviconPath)}"`);
|
|
@@ -28998,11 +29197,11 @@ ${clientErrorReporterScript}
|
|
|
28998
29197
|
return html;
|
|
28999
29198
|
}
|
|
29000
29199
|
function loadBrandingCache(agentSlug) {
|
|
29001
|
-
const configDir2 =
|
|
29200
|
+
const configDir2 = join45(homedir4(), BRAND.configDir);
|
|
29002
29201
|
try {
|
|
29003
29202
|
const accountId = getDefaultAccountId();
|
|
29004
29203
|
if (!accountId) return null;
|
|
29005
|
-
const cachePath =
|
|
29204
|
+
const cachePath = join45(configDir2, "branding-cache", accountId, `${agentSlug}.json`);
|
|
29006
29205
|
if (!existsSync38(cachePath)) return null;
|
|
29007
29206
|
return JSON.parse(readFileSync42(cachePath, "utf-8"));
|
|
29008
29207
|
} catch {
|
|
@@ -29099,7 +29298,7 @@ app64.use("/vnc-popout.html", logViewerFetch);
|
|
|
29099
29298
|
app64.get("/vnc-popout.html", (c) => {
|
|
29100
29299
|
let html = htmlCache.get("vnc-popout.html");
|
|
29101
29300
|
if (!html) {
|
|
29102
|
-
html = readFileSync42(
|
|
29301
|
+
html = readFileSync42(resolve38(process.cwd(), "public", "vnc-popout.html"), "utf-8");
|
|
29103
29302
|
const name = escapeHtml(BRAND.productName);
|
|
29104
29303
|
html = html.replace("<title>Browser \u2014 Maxy</title>", `<title>${name}</title>`);
|
|
29105
29304
|
html = html.replace("</head>", ` ${brandScript}
|
|
@@ -29236,8 +29435,8 @@ var hostname = process.env.HOSTNAME ?? "127.0.0.1";
|
|
|
29236
29435
|
var httpServer = serve({ fetch: app64.fetch, port, hostname });
|
|
29237
29436
|
console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
29238
29437
|
{
|
|
29239
|
-
const reconcilePlatformRoot = process.env.MAXY_PLATFORM_ROOT ??
|
|
29240
|
-
const reconcileScript =
|
|
29438
|
+
const reconcilePlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join45(__dirname, "..");
|
|
29439
|
+
const reconcileScript = resolve38(reconcilePlatformRoot, "plugins/scheduling/mcp/dist/scripts/reconcile-bookings.js");
|
|
29241
29440
|
const RECONCILE_INTERVAL_MS = 12e4;
|
|
29242
29441
|
const runReconcile = (ctx) => {
|
|
29243
29442
|
if (!existsSync38(reconcileScript)) return;
|
|
@@ -29262,8 +29461,8 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
29262
29461
|
});
|
|
29263
29462
|
}
|
|
29264
29463
|
{
|
|
29265
|
-
const outlookPlatformRoot = process.env.MAXY_PLATFORM_ROOT ??
|
|
29266
|
-
const outlookScript =
|
|
29464
|
+
const outlookPlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join45(__dirname, "..");
|
|
29465
|
+
const outlookScript = resolve38(outlookPlatformRoot, "plugins/outlook/mcp/dist/scripts/complete-registration.js");
|
|
29267
29466
|
const OUTLOOK_COMPLETE_INTERVAL_MS = 3e4;
|
|
29268
29467
|
const runOutlookComplete = (ctx) => {
|
|
29269
29468
|
if (!existsSync38(outlookScript)) return;
|
|
@@ -29288,8 +29487,8 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
29288
29487
|
});
|
|
29289
29488
|
}
|
|
29290
29489
|
{
|
|
29291
|
-
const auditRoot = process.env.MAXY_PLATFORM_ROOT ??
|
|
29292
|
-
const strandedAccountsDir =
|
|
29490
|
+
const auditRoot = process.env.MAXY_PLATFORM_ROOT ?? join45(__dirname, "..");
|
|
29491
|
+
const strandedAccountsDir = resolve38(auditRoot, "..", "data/accounts");
|
|
29293
29492
|
const STRANDED_AUDIT_INTERVAL_MS = 3e5;
|
|
29294
29493
|
const STRANDED_AGE_MS = 16 * 6e4;
|
|
29295
29494
|
const STRANDED_UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
@@ -29299,7 +29498,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
29299
29498
|
const now = Date.now();
|
|
29300
29499
|
for (const name of readdirSync26(strandedAccountsDir)) {
|
|
29301
29500
|
if (!STRANDED_UUID_RE.test(name)) continue;
|
|
29302
|
-
const pendingPath2 =
|
|
29501
|
+
const pendingPath2 = resolve38(strandedAccountsDir, name, "secrets/outlook/pending-devicecode.enc");
|
|
29303
29502
|
if (!existsSync38(pendingPath2)) continue;
|
|
29304
29503
|
const ageMs = now - statSync17(pendingPath2).mtimeMs;
|
|
29305
29504
|
if (ageMs > STRANDED_AGE_MS) {
|
|
@@ -29318,8 +29517,8 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
29318
29517
|
});
|
|
29319
29518
|
}
|
|
29320
29519
|
{
|
|
29321
|
-
const googleRoot = process.env.MAXY_PLATFORM_ROOT ??
|
|
29322
|
-
const googleAccountsDir =
|
|
29520
|
+
const googleRoot = process.env.MAXY_PLATFORM_ROOT ?? join45(__dirname, "..");
|
|
29521
|
+
const googleAccountsDir = resolve38(googleRoot, "..", "data/accounts");
|
|
29323
29522
|
const GOOGLE_PENDING_AUDIT_INTERVAL_MS = 3e5;
|
|
29324
29523
|
const runGooglePendingAuditSafe = () => {
|
|
29325
29524
|
try {
|
|
@@ -29334,7 +29533,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
29334
29533
|
firstRunDelayMs: 27e3,
|
|
29335
29534
|
run: runGooglePendingAuditSafe
|
|
29336
29535
|
});
|
|
29337
|
-
const googleAuditScript =
|
|
29536
|
+
const googleAuditScript = resolve38(googleRoot, "plugins/google/mcp/dist/scripts/account-audit.js");
|
|
29338
29537
|
const GOOGLE_ACCOUNT_AUDIT_INTERVAL_MS = 36e5;
|
|
29339
29538
|
const runGoogleAccountAudit = (ctx) => {
|
|
29340
29539
|
if (!existsSync38(googleAuditScript)) return;
|
|
@@ -29362,7 +29561,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
29362
29561
|
});
|
|
29363
29562
|
}
|
|
29364
29563
|
{
|
|
29365
|
-
const auditPlatformRoot = process.env.MAXY_PLATFORM_ROOT ??
|
|
29564
|
+
const auditPlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join45(__dirname, "..");
|
|
29366
29565
|
const STORAGE_AUDIT_INTERVAL_MS = 3e5;
|
|
29367
29566
|
const runStorageAuditSafe = () => runStorageAudit(auditPlatformRoot).catch((err) => {
|
|
29368
29567
|
console.error(`[storage-audit] error="${err.message}"`);
|
|
@@ -29396,8 +29595,8 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
29396
29595
|
});
|
|
29397
29596
|
}
|
|
29398
29597
|
{
|
|
29399
|
-
const publishPlatformRoot = process.env.MAXY_PLATFORM_ROOT ??
|
|
29400
|
-
const publishScript =
|
|
29598
|
+
const publishPlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join45(__dirname, "..");
|
|
29599
|
+
const publishScript = resolve38(publishPlatformRoot, "plugins/scheduling/mcp/dist/scripts/publish-availability.js");
|
|
29401
29600
|
const PUBLISH_INTERVAL_MS = 3e5;
|
|
29402
29601
|
const currentAccount = () => {
|
|
29403
29602
|
try {
|
|
@@ -29427,7 +29626,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
29427
29626
|
}
|
|
29428
29627
|
};
|
|
29429
29628
|
const auditSnapshotAge = (account) => {
|
|
29430
|
-
const availPath =
|
|
29629
|
+
const availPath = resolve38(account.accountDir, "calendar-availability.json");
|
|
29431
29630
|
let hasBookingSite = false;
|
|
29432
29631
|
try {
|
|
29433
29632
|
if (existsSync38(availPath)) {
|
|
@@ -29437,7 +29636,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
29437
29636
|
} catch {
|
|
29438
29637
|
}
|
|
29439
29638
|
if (!hasBookingSite) return;
|
|
29440
|
-
const statePath =
|
|
29639
|
+
const statePath = resolve38(account.accountDir, "state", "booking-availability", "last-publish.json");
|
|
29441
29640
|
let lastSuccessAt = null;
|
|
29442
29641
|
if (existsSync38(statePath)) {
|
|
29443
29642
|
try {
|
|
@@ -29469,8 +29668,8 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
29469
29668
|
startTimeEntryCensus(() => getSession());
|
|
29470
29669
|
startLedgerCensus(() => getSession());
|
|
29471
29670
|
{
|
|
29472
|
-
const auditPlatformRoot = process.env.MAXY_PLATFORM_ROOT ??
|
|
29473
|
-
const auditScript =
|
|
29671
|
+
const auditPlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join45(__dirname, "..");
|
|
29672
|
+
const auditScript = resolve38(auditPlatformRoot, "plugins/connector/mcp/dist/scripts/audit-connectors.js");
|
|
29474
29673
|
const auditLogDir = process.env.LOG_DIR ?? LOG_DIR;
|
|
29475
29674
|
const CONNECTOR_AUDIT_INTERVAL_MS = 3e5;
|
|
29476
29675
|
const runConnectorAudit = (ctx) => {
|
|
@@ -29779,9 +29978,13 @@ if (bootAccountConfig?.whatsapp) {
|
|
|
29779
29978
|
}
|
|
29780
29979
|
init({
|
|
29781
29980
|
configDir: configDirForWhatsApp,
|
|
29782
|
-
platformRoot:
|
|
29981
|
+
platformRoot: resolve38(process.env.MAXY_PLATFORM_ROOT ?? join45(__dirname, "..")),
|
|
29783
29982
|
accountConfig: bootAccountConfig,
|
|
29784
29983
|
onMessage: async (msg) => {
|
|
29984
|
+
if (isObserveAccount(msg.accountId)) {
|
|
29985
|
+
console.error(`[whatsapp:observe] op=BREACH account=${msg.accountId} sender=${msg.senderPhone} \u2014 dispatch reached the router from an observe socket`);
|
|
29986
|
+
return;
|
|
29987
|
+
}
|
|
29785
29988
|
if (msg.isOwnerMirror) {
|
|
29786
29989
|
console.error(`[whatsapp:route] skipped reason=owner-mirror senderId=${msg.senderPhone}`);
|
|
29787
29990
|
return;
|