@rubytech/create-maxy-code 0.1.478 → 0.1.480
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/docs/superpowers/plans/2026-07-20-task-1789-reseat-channel-row-fork.md +1244 -0
- package/payload/platform/docs/superpowers/plans/2026-07-20-task-1818-loop-gate-app-routes.md +462 -0
- package/payload/platform/docs/superpowers/plans/2026-07-20-task-1819-top-level-label-allowlist.md +321 -0
- package/payload/platform/docs/superpowers/plans/2026-07-20-task-1829-platform-ui-typecheck.md +830 -0
- package/payload/platform/docs/superpowers/plans/2026-07-20-task-1831-data-portal-class-derived-allowlist.md +2177 -0
- package/payload/platform/docs/superpowers/specs/2026-07-20-task-1789-reseat-channel-row-fork-design.md +201 -0
- package/payload/platform/docs/superpowers/specs/2026-07-20-task-1818-loop-gate-app-routes-design.md +217 -0
- package/payload/platform/docs/superpowers/specs/2026-07-20-task-1819-top-level-label-allowlist-design.md +80 -0
- package/payload/platform/docs/superpowers/specs/2026-07-20-task-1829-platform-ui-typecheck-design.md +163 -0
- package/payload/platform/docs/superpowers/specs/2026-07-20-task-1831-data-portal-class-derived-allowlist-design.md +163 -0
- package/payload/platform/lib/graph-style/dist/index.d.ts.map +1 -1
- package/payload/platform/lib/graph-style/dist/index.js +22 -0
- package/payload/platform/lib/graph-style/dist/index.js.map +1 -1
- package/payload/platform/lib/graph-style/src/__tests__/parity.test.ts +6 -0
- package/payload/platform/lib/graph-style/src/index.ts +20 -0
- package/payload/platform/lib/storage-broker/dist/__tests__/cf-exec.test.js +155 -12
- package/payload/platform/lib/storage-broker/dist/__tests__/cf-exec.test.js.map +1 -1
- package/payload/platform/lib/storage-broker/dist/cf-exec.d.ts +3 -0
- package/payload/platform/lib/storage-broker/dist/cf-exec.d.ts.map +1 -1
- package/payload/platform/lib/storage-broker/dist/cf-exec.js +68 -28
- package/payload/platform/lib/storage-broker/dist/cf-exec.js.map +1 -1
- package/payload/platform/lib/storage-broker/src/__tests__/cf-exec.test.ts +194 -16
- package/payload/platform/lib/storage-broker/src/cf-exec.ts +74 -29
- package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +13 -1
- package/payload/platform/plugins/cloudflare/PLUGIN.md +26 -1
- package/payload/platform/plugins/cloudflare/bin/d1-http.mjs +80 -0
- package/payload/platform/plugins/cloudflare/bin/portal-enrol.mjs +17 -2
- package/payload/platform/plugins/cloudflare/bin/portal-index-push.mjs +357 -0
- package/payload/platform/plugins/cloudflare/bin/schema-exposed-dirs.mjs +103 -0
- package/payload/platform/plugins/cloudflare/mcp/__tests__/authorize.test.ts +2 -2
- package/payload/platform/plugins/cloudflare/mcp/__tests__/d1-http.test.ts +96 -0
- package/payload/platform/plugins/cloudflare/mcp/__tests__/portal-directory-listing.test.ts +120 -0
- package/payload/platform/plugins/cloudflare/mcp/__tests__/portal-enrol.test.ts +59 -4
- package/payload/platform/plugins/cloudflare/mcp/__tests__/portal-index-push.test.ts +176 -0
- package/payload/platform/plugins/cloudflare/mcp/__tests__/portal-index-state-contract.test.ts +75 -0
- package/payload/platform/plugins/cloudflare/mcp/__tests__/portal-indexed-download.test.ts +131 -0
- package/payload/platform/plugins/cloudflare/mcp/__tests__/portal-sign.test.ts +68 -0
- package/payload/platform/plugins/cloudflare/mcp/__tests__/schema-exposed-dirs.test.ts +88 -0
- package/payload/platform/plugins/cloudflare/mcp/__tests__/template-config.test.ts +39 -0
- package/payload/platform/plugins/cloudflare/skills/data-portal/SKILL.md +68 -1
- package/payload/platform/plugins/cloudflare/skills/data-portal/schema.sql +30 -1
- package/payload/platform/plugins/cloudflare/skills/data-portal/template/functions/api/_lib/portal-sign.mjs +87 -0
- package/payload/platform/plugins/cloudflare/skills/data-portal/template/functions/api/_lib/session.ts +8 -4
- package/payload/platform/plugins/cloudflare/skills/data-portal/template/functions/api/_lib/types.ts +10 -0
- package/payload/platform/plugins/cloudflare/skills/data-portal/template/functions/api/download.ts +107 -1
- package/payload/platform/plugins/cloudflare/skills/data-portal/template/functions/api/files.ts +82 -2
- package/payload/platform/plugins/cloudflare/skills/data-portal/template/index.html +9 -0
- package/payload/platform/plugins/cloudflare/skills/data-portal/template/portal.css +56 -0
- package/payload/platform/plugins/cloudflare/skills/data-portal/template/portal.js +98 -2
- package/payload/platform/plugins/cloudflare/skills/data-portal/template/wrangler.toml +12 -0
- package/payload/platform/plugins/docs/references/cloudflare.md +12 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/device-flow.test.js +41 -1
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/device-flow.test.js.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/graph-client.test.js +249 -2
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/graph-client.test.js.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/token-store.test.js +127 -3
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/token-store.test.js.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/auth/device-flow.d.ts +17 -0
- package/payload/platform/plugins/outlook/mcp/dist/auth/device-flow.d.ts.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/auth/device-flow.js +26 -1
- package/payload/platform/plugins/outlook/mcp/dist/auth/device-flow.js.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/auth/token-store.d.ts +37 -1
- package/payload/platform/plugins/outlook/mcp/dist/auth/token-store.d.ts.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/auth/token-store.js +126 -5
- package/payload/platform/plugins/outlook/mcp/dist/auth/token-store.js.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/lib/graph-client.d.ts +18 -0
- package/payload/platform/plugins/outlook/mcp/dist/lib/graph-client.d.ts.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/lib/graph-client.js +123 -18
- package/payload/platform/plugins/outlook/mcp/dist/lib/graph-client.js.map +1 -1
- package/payload/platform/plugins/outlook/references/auth.md +45 -2
- package/payload/platform/plugins/scheduling/mcp/dist/__tests__/portal-index-audit.test.d.ts +2 -0
- package/payload/platform/plugins/scheduling/mcp/dist/__tests__/portal-index-audit.test.d.ts.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/__tests__/portal-index-audit.test.js +66 -0
- package/payload/platform/plugins/scheduling/mcp/dist/__tests__/portal-index-audit.test.js.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/portal-index-audit.d.ts +41 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/portal-index-audit.d.ts.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/portal-index-audit.js +75 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/portal-index-audit.js.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/check-due-events.js +122 -0
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/check-due-events.js.map +1 -1
- package/payload/server/{chunk-3XLLTG6R.js → chunk-REWXLAPC.js} +30 -10
- package/payload/server/package.json +1 -1
- package/payload/server/public/activity.html +7 -6
- package/payload/server/public/assets/{AdminLoginScreens-DR5eLF-a.js → AdminLoginScreens-DXYJWXaX.js} +1 -1
- package/payload/server/public/assets/AdminShell-UDgNN4CA.js +2 -0
- package/payload/server/public/assets/Checkbox-C08Ulc_a.js +1 -0
- package/payload/server/public/assets/activity-06gsRDVv.js +1 -0
- package/payload/server/public/assets/admin-BhiHyORH.js +1 -0
- package/payload/server/public/assets/{arc-BrHWN2Kr.js → arc-BHXuzTwg.js} +1 -1
- package/payload/server/public/assets/architecture-YZFGNWBL-Ct5q6ks1.js +1 -0
- package/payload/server/public/assets/{architectureDiagram-Q4EWVU46-Clt9NOr0.js → architectureDiagram-Q4EWVU46-DsN_vxau.js} +1 -1
- package/payload/server/public/assets/{blockDiagram-DXYQGD6D-BOe_jmXi.js → blockDiagram-DXYQGD6D-CvNat1uB.js} +1 -1
- package/payload/server/public/assets/browser-Cz4IKS47.js +1 -0
- package/payload/server/public/assets/{c4Diagram-AHTNJAMY-B64FaKMV.js → c4Diagram-AHTNJAMY-E9yqTPTW.js} +1 -1
- package/payload/server/public/assets/calendar-D0xtNqH8.js +1 -0
- package/payload/server/public/assets/channel-PE9vBKEQ.js +1 -0
- package/payload/server/public/assets/chat-DwLJ830n.js +1 -0
- package/payload/server/public/assets/chevron-left-Cv-TFuEf.js +1 -0
- package/payload/server/public/assets/{chunk-2KRD3SAO-BGAUfWvM.js → chunk-2KRD3SAO-DpzI_0EY.js} +1 -1
- package/payload/server/public/assets/{chunk-336JU56O-PFi6uQZ0.js → chunk-336JU56O-eR_I9Eu4.js} +2 -2
- package/payload/server/public/assets/chunk-426QAEUC-BDNZtJsz.js +1 -0
- package/payload/server/public/assets/{chunk-4BX2VUAB-CkxXXCiF.js → chunk-4BX2VUAB-D-6xk1t8.js} +1 -1
- package/payload/server/public/assets/{chunk-4TB4RGXK-B-7tN4IA.js → chunk-4TB4RGXK-BkqUcFpL.js} +1 -1
- package/payload/server/public/assets/{chunk-55IACEB6-CBkNmqrt.js → chunk-55IACEB6-ChCl2pSP.js} +1 -1
- package/payload/server/public/assets/{chunk-5FUZZQ4R-wa9ujn7i.js → chunk-5FUZZQ4R-DasGsg54.js} +1 -1
- package/payload/server/public/assets/{chunk-5PVQY5BW-DIk-xBaE.js → chunk-5PVQY5BW-DXlHUX1h.js} +1 -1
- package/payload/server/public/assets/{chunk-67CJDMHE-BbMV1lgW.js → chunk-67CJDMHE-BZfVpTsE.js} +1 -1
- package/payload/server/public/assets/{chunk-7N4EOEYR-C49Mv-nQ.js → chunk-7N4EOEYR-C6U5TIgT.js} +1 -1
- package/payload/server/public/assets/{chunk-AA7GKIK3-Dy_pJOp_.js → chunk-AA7GKIK3-CA7cVDmw.js} +1 -1
- package/payload/server/public/assets/{chunk-BSJP7CBP-Ce-OmPqn.js → chunk-BSJP7CBP-DxN2GC3p.js} +1 -1
- package/payload/server/public/assets/{chunk-CIAEETIT-tnjq-APC.js → chunk-CIAEETIT-C0-4wA8y.js} +1 -1
- package/payload/server/public/assets/{chunk-EDXVE4YY-Bvv_deZ2.js → chunk-EDXVE4YY--kyb1rLc.js} +1 -1
- package/payload/server/public/assets/{chunk-ENJZ2VHE-CmxCPKyU.js → chunk-ENJZ2VHE-FBN6hR7V.js} +1 -1
- package/payload/server/public/assets/{chunk-FMBD7UC4-Top6y08S.js → chunk-FMBD7UC4-Lpge4M5g.js} +1 -1
- package/payload/server/public/assets/{chunk-FOC6F5B3-B9LPNCUS.js → chunk-FOC6F5B3-CM1rwOou.js} +1 -1
- package/payload/server/public/assets/{chunk-ICPOFSXX-Bi-_c351.js → chunk-ICPOFSXX-JdYLY3yu.js} +2 -2
- package/payload/server/public/assets/{chunk-K5T4RW27-DAnRM04h.js → chunk-K5T4RW27-DCUblGBr.js} +1 -1
- package/payload/server/public/assets/{chunk-KGLVRYIC-C0QYcbLe.js → chunk-KGLVRYIC-BP_cide2.js} +1 -1
- package/payload/server/public/assets/{chunk-LIHQZDEY-BjvA5ykt.js → chunk-LIHQZDEY-DjMa1lOl.js} +1 -1
- package/payload/server/public/assets/{chunk-ORNJ4GCN-Cs46Gj7z.js → chunk-ORNJ4GCN-D51eJAoV.js} +1 -1
- package/payload/server/public/assets/{chunk-OYMX7WX6-BBpimab5.js → chunk-OYMX7WX6-4hWIO544.js} +1 -1
- package/payload/server/public/assets/chunk-QZHKN3VN-B_Hris8x.js +1 -0
- package/payload/server/public/assets/{chunk-U2HBQHQK-CQUSBHoK.js → chunk-U2HBQHQK-BwdQf9cS.js} +1 -1
- package/payload/server/public/assets/{chunk-X2U36JSP-Dm8TevG0.js → chunk-X2U36JSP-DmDxwLOg.js} +1 -1
- package/payload/server/public/assets/{chunk-XPW4576I-B10QuxYb.js → chunk-XPW4576I-JQyAB7ND.js} +1 -1
- package/payload/server/public/assets/{chunk-YZCP3GAM-vaGsu4Wk.js → chunk-YZCP3GAM-BonLdCzI.js} +1 -1
- package/payload/server/public/assets/{chunk-ZZ45TVLE-D1Qi22bL.js → chunk-ZZ45TVLE-BXKB2wSk.js} +1 -1
- package/payload/server/public/assets/classDiagram-6PBFFD2Q-D8Q05iMI.js +1 -0
- package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-Dm3hAxfm.js +1 -0
- package/payload/server/public/assets/clone-C-wOwvpY.js +1 -0
- package/payload/server/public/assets/{cose-bilkent-S5V4N54A-CRDvLRXs.js → cose-bilkent-S5V4N54A-BDnd4Blw.js} +1 -1
- package/payload/server/public/assets/{dagre-CEmqJTt3.js → dagre-BmexzuER.js} +1 -1
- package/payload/server/public/assets/{dagre-KV5264BT-LXMTo7Fp.js → dagre-KV5264BT-D4WVuwPj.js} +1 -1
- package/payload/server/public/assets/data-DUDJfnjI.js +1 -0
- package/payload/server/public/assets/{diagram-5BDNPKRD-zyis6nPt.js → diagram-5BDNPKRD-Bbf4mT8a.js} +1 -1
- package/payload/server/public/assets/{diagram-G4DWMVQ6-BiU0XAAD.js → diagram-G4DWMVQ6-BpqVTUmx.js} +1 -1
- package/payload/server/public/assets/{diagram-MMDJMWI5-rGFiz1SX.js → diagram-MMDJMWI5-DY-k5k6r.js} +1 -1
- package/payload/server/public/assets/{diagram-TYMM5635-B0wak1ZJ.js → diagram-TYMM5635-CXHxwacS.js} +1 -1
- package/payload/server/public/assets/{erDiagram-SMLLAGMA-D8AUxLyX.js → erDiagram-SMLLAGMA-BNKEc3l8.js} +1 -1
- package/payload/server/public/assets/{flatten-naJ5PJ4f.js → flatten-jn8bZgcI.js} +1 -1
- package/payload/server/public/assets/{flowDiagram-DWJPFMVM-BtdUf68b.js → flowDiagram-DWJPFMVM-CbAeuJsi.js} +1 -1
- package/payload/server/public/assets/{ganttDiagram-T4ZO3ILL-C-lkzDmf.js → ganttDiagram-T4ZO3ILL-CeeNnL4l.js} +1 -1
- package/payload/server/public/assets/gitGraph-7Q5UKJZL-BBHsY16R.js +1 -0
- package/payload/server/public/assets/{gitGraphDiagram-UUTBAWPF-DRUKumq2.js → gitGraphDiagram-UUTBAWPF-DG8RCtfg.js} +1 -1
- package/payload/server/public/assets/{graph-DRloNic4.js → graph-BAoktBcx.js} +19 -19
- package/payload/server/public/assets/graph-labels-LeIhl7ZG.js +1 -0
- package/payload/server/public/assets/{graphlib-zRsaoFEC.js → graphlib-BkDExPQi.js} +1 -1
- package/payload/server/public/assets/info-OMHHGYJF-C1j5I_zx.js +1 -0
- package/payload/server/public/assets/infoDiagram-42DDH7IO-ZWkwHWEu.js +2 -0
- package/payload/server/public/assets/{isEmpty-Bnpg7b8f.js → isEmpty-Dkk6_L-B.js} +1 -1
- package/payload/server/public/assets/{ishikawaDiagram-UXIWVN3A-BguMgoR8.js → ishikawaDiagram-UXIWVN3A-D_iMHV64.js} +1 -1
- package/payload/server/public/assets/{journeyDiagram-VCZTEJTY-CXhyK1n-.js → journeyDiagram-VCZTEJTY-BvPRpulx.js} +1 -1
- package/payload/server/public/assets/{kanban-definition-6JOO6SKY-C0UCWVx-.js → kanban-definition-6JOO6SKY-CkWaXWo2.js} +1 -1
- package/payload/server/public/assets/{line--uhS9YDE.js → line-BGQT9unx.js} +1 -1
- package/payload/server/public/assets/{linear-CAOWPodQ.js → linear-DzrYI8-8.js} +1 -1
- package/payload/server/public/assets/{maximize-2-CzDaBugs.js → maximize-2-2MgPSwlx.js} +1 -1
- package/payload/server/public/assets/{mermaid-parser.core-D6kPx7vc.js → mermaid-parser.core-DrMY7FMe.js} +2 -2
- package/payload/server/public/assets/{mermaid.core-CTd4DS6O.js → mermaid.core-BWdf6fZ7.js} +3 -3
- package/payload/server/public/assets/{mindmap-definition-QFDTVHPH-_AyvyG8C.js → mindmap-definition-QFDTVHPH-CgqM9h97.js} +1 -1
- package/payload/server/public/assets/operator-CZifmNhp.js +1 -0
- package/payload/server/public/assets/{ordinal-Wx4m9zHk.js → ordinal-DuJYM7p1.js} +1 -1
- package/payload/server/public/assets/packet-4T2RLAQJ-CGiTbe0N.js +1 -0
- package/payload/server/public/assets/{page-ef-6rRPL.js → page-B4kllGd_.js} +6 -6
- package/payload/server/public/assets/page-DbfE7QwJ.js +1 -0
- package/payload/server/public/assets/{pdf-render-CeX6d2v7.js → pdf-render-riHufgrn.js} +1 -1
- package/payload/server/public/assets/pie-ZZUOXDRM-BrK023dE.js +1 -0
- package/payload/server/public/assets/{pieDiagram-DEJITSTG-6TsUDkkQ.js → pieDiagram-DEJITSTG-BeUGoYS7.js} +1 -1
- package/payload/server/public/assets/public-VtmyYz8E.js +1 -0
- package/payload/server/public/assets/{quadrantDiagram-34T5L4WZ-CWw9gOI4.js → quadrantDiagram-34T5L4WZ-CX82WgKy.js} +1 -1
- package/payload/server/public/assets/radar-PYXPWWZC-p7NRamXX.js +1 -0
- package/payload/server/public/assets/{reduce-BUmc8RDR.js → reduce-COyotL2V.js} +1 -1
- package/payload/server/public/assets/{requirementDiagram-MS252O5E-DCOlxNvx.js → requirementDiagram-MS252O5E-CzqebQXj.js} +1 -1
- package/payload/server/public/assets/{rotate-ccw-DXEKUhXP.js → rotate-ccw-DaZdB2cz.js} +1 -1
- package/payload/server/public/assets/{sankeyDiagram-XADWPNL6-CW4Xbir3.js → sankeyDiagram-XADWPNL6-CaHi1yzU.js} +1 -1
- package/payload/server/public/assets/{sequenceDiagram-FGHM5R23-vhb_pBLG.js → sequenceDiagram-FGHM5R23-BF2AeiKW.js} +1 -1
- package/payload/server/public/assets/{stateDiagram-FHFEXIEX-Cb1WExEX.js → stateDiagram-FHFEXIEX-DtUCA8Ih.js} +1 -1
- package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-a4FvB3L8.js +1 -0
- package/payload/server/public/assets/tasks-CvqVDbfO.js +1 -0
- package/payload/server/public/assets/{time-entry-format-CsYAjplS.js → time-entry-format-TD-PPEIu.js} +1 -1
- package/payload/server/public/assets/{timeline-definition-GMOUNBTQ-BAHTf_AF.js → timeline-definition-GMOUNBTQ-D3Q1YrKY.js} +1 -1
- package/payload/server/public/assets/treeView-SZITEDCU-CoWk8VoG.js +1 -0
- package/payload/server/public/assets/treemap-W4RFUUIX-BhWeGj8Y.js +1 -0
- package/payload/server/public/assets/{triangle-alert-0lSBIUg-.js → triangle-alert-BaLhVNnn.js} +1 -1
- package/payload/server/public/assets/useCopyFeedback-BhjPzRa3.js +1 -0
- package/payload/server/public/assets/useSubAccountSwitcher-CWRpVt1-.css +1 -0
- package/payload/server/public/assets/{useSubAccountSwitcher-BZnzc5C3.js → useSubAccountSwitcher-HEVLb6Z9.js} +2 -2
- package/payload/server/public/assets/useVoiceRecorder-CfGgj3vm.js +2 -0
- package/payload/server/public/assets/{vennDiagram-DHZGUBPP-DtEttvW4.js → vennDiagram-DHZGUBPP-DnjzQDGf.js} +1 -1
- package/payload/server/public/assets/wardley-RL74JXVD-C0q3Hqng.js +1 -0
- package/payload/server/public/assets/{wardleyDiagram-NUSXRM2D-K06y6CVJ.js → wardleyDiagram-NUSXRM2D-T3WHrUvi.js} +1 -1
- package/payload/server/public/assets/{xychartDiagram-5P7HB3ND-D66c40lp.js → xychartDiagram-5P7HB3ND-2Efyv4h2.js} +1 -1
- package/payload/server/public/brand-defaults.css +1 -0
- package/payload/server/public/browser.html +4 -5
- package/payload/server/public/calendar.html +5 -6
- package/payload/server/public/chat.html +12 -13
- package/payload/server/public/data.html +11 -12
- package/payload/server/public/graph.html +10 -11
- package/payload/server/public/index.html +14 -15
- package/payload/server/public/operator.html +14 -15
- package/payload/server/public/privacy.html +1 -0
- package/payload/server/public/public.html +12 -13
- package/payload/server/public/tasks.html +7 -5
- package/payload/server/public/vnc-popout.html +1 -0
- package/payload/server/server.js +833 -389
- package/payload/server/{src-XQ63FPRE.js → src-JJ6PHO5I.js} +1 -1
- package/payload/server/public/assets/AdminShell-D9zaUxPd.js +0 -2
- package/payload/server/public/assets/Checkbox-DcbbAIe5.js +0 -1
- package/payload/server/public/assets/activity-C9P5NjEZ.js +0 -1
- package/payload/server/public/assets/admin-D2KLZnQW.js +0 -1
- package/payload/server/public/assets/architecture-YZFGNWBL-CZQzGRSM.js +0 -1
- package/payload/server/public/assets/browser-CDWqEiCD.js +0 -1
- package/payload/server/public/assets/calendar-BG7fOpbB.js +0 -1
- package/payload/server/public/assets/channel-zDUAN9Ks.js +0 -1
- package/payload/server/public/assets/chat-RyH-WYQC.js +0 -1
- package/payload/server/public/assets/chevron-left-DmXCr6nB.js +0 -1
- package/payload/server/public/assets/chunk-426QAEUC-iJnqNRug.js +0 -1
- package/payload/server/public/assets/chunk-QZHKN3VN-Qlc2PFAj.js +0 -1
- package/payload/server/public/assets/classDiagram-6PBFFD2Q-CFsKL52p.js +0 -1
- package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-BWBUUCiO.js +0 -1
- package/payload/server/public/assets/clone-h5XhkDUL.js +0 -1
- package/payload/server/public/assets/data-BaY_LkLR.js +0 -1
- package/payload/server/public/assets/gitGraph-7Q5UKJZL-CBZPQJLj.js +0 -1
- package/payload/server/public/assets/graph-labels-DD-GqS4v.js +0 -1
- package/payload/server/public/assets/info-OMHHGYJF-1vIoX-kP.js +0 -1
- package/payload/server/public/assets/infoDiagram-42DDH7IO-KHcXRaan.js +0 -2
- package/payload/server/public/assets/operator-BZeeyait.js +0 -1
- package/payload/server/public/assets/packet-4T2RLAQJ-CTU0gCGb.js +0 -1
- package/payload/server/public/assets/page-B0obcg3b.js +0 -1
- package/payload/server/public/assets/pie-ZZUOXDRM-D-EEHxhC.js +0 -1
- package/payload/server/public/assets/public-9D3MMOkC.js +0 -1
- package/payload/server/public/assets/radar-PYXPWWZC-DHXATZGB.js +0 -1
- package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-CxOIKvXh.js +0 -1
- package/payload/server/public/assets/tasks-BB0L7e5a.js +0 -1
- package/payload/server/public/assets/treeView-SZITEDCU-CC7ouMFd.js +0 -1
- package/payload/server/public/assets/treemap-W4RFUUIX-CaCg27im.js +0 -1
- package/payload/server/public/assets/useCopyFeedback-jTBQ2h0V.js +0 -1
- package/payload/server/public/assets/useSelectionMode-BooArySZ.js +0 -1
- package/payload/server/public/assets/useSubAccountSwitcher-D9knHOKq.css +0 -1
- package/payload/server/public/assets/useVoiceRecorder-PUT8q_sm.js +0 -2
- package/payload/server/public/assets/wardley-RL74JXVD-bMPsqQZ7.js +0 -1
- /package/payload/server/public/assets/{_baseFor-k8hYPe-U.js → _baseFor-Bjj9It3Q.js} +0 -0
- /package/payload/server/public/assets/{array-BFDiaBgf.js → array-BrrxEo12.js} +0 -0
- /package/payload/server/public/assets/{cytoscape.esm-Ch0xptA4.js → cytoscape.esm-CTWJxUxo.js} +0 -0
- /package/payload/server/public/assets/{defaultLocale-ZeknFqNe.js → defaultLocale-CFDbRV4k.js} +0 -0
- /package/payload/server/public/assets/{dist-B2N0v8hZ.js → dist-BATfy7Sn.js} +0 -0
- /package/payload/server/public/assets/{init-0VJVrkRJ.js → init-dXCzGgLP.js} +0 -0
- /package/payload/server/public/assets/{katex-Br7bH10N.js → katex-CHA1JFYJ.js} +0 -0
- /package/payload/server/public/assets/{path-INs8XTPH.js → path-CCRpdXhK.js} +0 -0
- /package/payload/server/public/assets/{preload-helper-D_sPnjeL.js → preload-helper-CH6UZRzu.js} +0 -0
- /package/payload/server/public/assets/{rough.esm-DU9bZLvZ.js → rough.esm-Bud4g4fh.js} +0 -0
- /package/payload/server/public/assets/{src-DHSQDYx-.js → src-DNdBRXXx.js} +0 -0
package/payload/server/server.js
CHANGED
|
@@ -133,7 +133,7 @@ import {
|
|
|
133
133
|
registerResource,
|
|
134
134
|
resolveOwner,
|
|
135
135
|
tooLargeMessage
|
|
136
|
-
} from "./chunk-
|
|
136
|
+
} from "./chunk-REWXLAPC.js";
|
|
137
137
|
import {
|
|
138
138
|
__commonJS,
|
|
139
139
|
__toESM
|
|
@@ -652,7 +652,17 @@ var require_dist3 = __commonJS({
|
|
|
652
652
|
Risk: "#C15B57",
|
|
653
653
|
Source: "#2F8FA6",
|
|
654
654
|
Finding: "#4FA389",
|
|
655
|
-
Hypothesis: "#9A8C3E"
|
|
655
|
+
Hypothesis: "#9A8C3E",
|
|
656
|
+
// Ledger cash movement (schema-base.md, added by 7b225f814). One movement
|
|
657
|
+
// of money attached to no invoice — operator-entry with no parent to drill
|
|
658
|
+
// down from, so it is a top-level chip. Burnished olive-gold sits in the
|
|
659
|
+
// money register beside Invoice's brown without colliding with it.
|
|
660
|
+
//
|
|
661
|
+
// Deliberately appended at the END rather than beside the ledger family:
|
|
662
|
+
// SHAPE_BY_LABEL assigns shapes by index position in ALL_GRAPH_LABELS
|
|
663
|
+
// (this object's key order), so a mid-table insert would silently change
|
|
664
|
+
// the rendered shape of every label after it.
|
|
665
|
+
CashEntry: "#7D5A2B"
|
|
656
666
|
};
|
|
657
667
|
exports.FALLBACK_LABEL_COLOUR = "#94A3B8";
|
|
658
668
|
exports.ALL_GRAPH_LABELS = Object.freeze(Object.keys(exports.GRAPH_LABEL_COLOURS));
|
|
@@ -749,7 +759,8 @@ var require_dist3 = __commonJS({
|
|
|
749
759
|
Risk: '<path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3"></path><path d="M12 9v4"></path><path d="M12 17h.01"></path>',
|
|
750
760
|
Source: '<path d="M12 7v14"></path><path d="M3 18a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h5a4 4 0 0 1 4 4 4 4 0 0 1 4-4h5a1 1 0 0 1 1 1v13a1 1 0 0 1-1 1h-6a3 3 0 0 0-3 3 3 3 0 0 0-3-3z"></path>',
|
|
751
761
|
Finding: '<path d="M15 14c.2-1 .7-1.7 1.5-2.5 1-.9 1.5-2.2 1.5-3.5A6 6 0 0 0 6 8c0 1 .2 2.2 1.5 3.5.7.7 1.3 1.5 1.5 2.5"></path><path d="M9 18h6"></path><path d="M10 22h4"></path>',
|
|
752
|
-
Hypothesis: '<path d="M14 2v6a2 2 0 0 0 .245.96l5.51 10.08A2 2 0 0 1 18 22H6a2 2 0 0 1-1.755-2.96l5.51-10.08A2 2 0 0 0 10 8V2"></path><path d="M6.453 15h11.094"></path><path d="M8.5 2h7"></path>'
|
|
762
|
+
Hypothesis: '<path d="M14 2v6a2 2 0 0 0 .245.96l5.51 10.08A2 2 0 0 1 18 22H6a2 2 0 0 1-1.755-2.96l5.51-10.08A2 2 0 0 0 10 8V2"></path><path d="M6.453 15h11.094"></path><path d="M8.5 2h7"></path>',
|
|
763
|
+
CashEntry: '<rect width="20" height="12" x="2" y="6" rx="2"></rect><circle cx="12" cy="12" r="2"></circle><path d="M6 12h.01"></path><path d="M18 12h.01"></path>'
|
|
753
764
|
};
|
|
754
765
|
function iconForLabel2(label) {
|
|
755
766
|
return Object.prototype.hasOwnProperty.call(LABEL_ICONS, label) ? LABEL_ICONS[label] : null;
|
|
@@ -845,6 +856,11 @@ var require_dist3 = __commonJS({
|
|
|
845
856
|
if (clientName.length > 0) {
|
|
846
857
|
return clientName.length > 24 ? clientName.slice(0, 24) + "\u2026" : clientName;
|
|
847
858
|
}
|
|
859
|
+
} else if (primaryLabel2 === "CashEntry") {
|
|
860
|
+
const desc = typeof props.description === "string" ? props.description : "";
|
|
861
|
+
if (desc.length > 0) {
|
|
862
|
+
return desc.length > 24 ? desc.slice(0, 24) + "\u2026" : desc;
|
|
863
|
+
}
|
|
848
864
|
} else if (primaryLabel2 === "Message") {
|
|
849
865
|
const role = typeof props.role === "string" ? props.role : "";
|
|
850
866
|
const roleShort = role === "user" ? "user" : role === "assistant" ? "asst" : role === "system" ? "sys" : role === "tool" ? "tool" : null;
|
|
@@ -918,6 +934,10 @@ var require_dist3 = __commonJS({
|
|
|
918
934
|
const clientName = typeof props.clientName === "string" ? props.clientName : "";
|
|
919
935
|
if (clientName.length > 0)
|
|
920
936
|
return clientName;
|
|
937
|
+
} else if (primaryLabel2 === "CashEntry") {
|
|
938
|
+
const desc = typeof props.description === "string" ? props.description : "";
|
|
939
|
+
if (desc.length > 0)
|
|
940
|
+
return desc;
|
|
921
941
|
}
|
|
922
942
|
for (const k of ["name", "title", "summary", "givenName", "subject", "text"]) {
|
|
923
943
|
const v = props[k];
|
|
@@ -1291,8 +1311,8 @@ var require_dist4 = __commonJS({
|
|
|
1291
1311
|
let edgesCreated = 0;
|
|
1292
1312
|
for (const rel of relationships) {
|
|
1293
1313
|
const type = rel.type.replace(/`/g, "");
|
|
1294
|
-
const
|
|
1295
|
-
const r = await tx.run(
|
|
1314
|
+
const q2 = rel.direction === "outgoing" ? `MATCH (a), (b) WHERE elementId(a) = $from AND elementId(b) = $to CREATE (a)-[:\`${type}\`]->(b)` : `MATCH (a), (b) WHERE elementId(a) = $from AND elementId(b) = $to CREATE (b)-[:\`${type}\`]->(a)`;
|
|
1315
|
+
const r = await tx.run(q2, { from: nodeId, to: rel.targetNodeId });
|
|
1296
1316
|
const created = r.summary.counters.updates().relationshipsCreated;
|
|
1297
1317
|
if (created === 0) {
|
|
1298
1318
|
process.stderr.write(`[graph-write] reject reason=unresolved-target-on-create labels=${labelCsv} agent=${agentLabel} relType=${rel.type} targetId=${rel.targetNodeId}
|
|
@@ -1540,10 +1560,10 @@ var serveStatic = (options = { root: "" }) => {
|
|
|
1540
1560
|
};
|
|
1541
1561
|
|
|
1542
1562
|
// server/index.ts
|
|
1543
|
-
import { readFileSync as
|
|
1563
|
+
import { readFileSync as readFileSync42, existsSync as existsSync38, watchFile, readdirSync as readdirSync26, statSync as statSync17 } from "fs";
|
|
1544
1564
|
import { spawn as spawn3 } from "child_process";
|
|
1545
1565
|
import { createHash as createHash7 } from "crypto";
|
|
1546
|
-
import { resolve as
|
|
1566
|
+
import { resolve as resolve37, join as join44, basename as basename12 } from "path";
|
|
1547
1567
|
import { homedir as homedir4 } from "os";
|
|
1548
1568
|
import { monitorEventLoopDelay } from "perf_hooks";
|
|
1549
1569
|
|
|
@@ -1958,14 +1978,14 @@ function readAllConversationsCached(accountId) {
|
|
|
1958
1978
|
for (const file of dir) {
|
|
1959
1979
|
if (!file.endsWith(".jsonl")) continue;
|
|
1960
1980
|
const path3 = join4(STORE_ROOT(), accountId, file);
|
|
1961
|
-
let
|
|
1981
|
+
let stat9;
|
|
1962
1982
|
try {
|
|
1963
|
-
|
|
1983
|
+
stat9 = statSync3(path3);
|
|
1964
1984
|
} catch {
|
|
1965
1985
|
continue;
|
|
1966
1986
|
}
|
|
1967
1987
|
const hit = idCache.get(path3);
|
|
1968
|
-
if (hit && hit.size ===
|
|
1988
|
+
if (hit && hit.size === stat9.size && hit.mtimeMs === stat9.mtimeMs) {
|
|
1969
1989
|
out.set(file, hit.ids);
|
|
1970
1990
|
continue;
|
|
1971
1991
|
}
|
|
@@ -1982,7 +2002,7 @@ function readAllConversationsCached(accountId) {
|
|
|
1982
2002
|
} catch {
|
|
1983
2003
|
continue;
|
|
1984
2004
|
}
|
|
1985
|
-
idCache.set(path3, { size:
|
|
2005
|
+
idCache.set(path3, { size: stat9.size, mtimeMs: stat9.mtimeMs, ids: parsed });
|
|
1986
2006
|
out.set(file, parsed);
|
|
1987
2007
|
}
|
|
1988
2008
|
return out;
|
|
@@ -2816,7 +2836,7 @@ var credsSaveQueue = Promise.resolve();
|
|
|
2816
2836
|
async function drainCredsSaveQueue(timeoutMs = 5e3) {
|
|
2817
2837
|
console.error(`${TAG6} draining credential save queue\u2026`);
|
|
2818
2838
|
const timer = new Promise(
|
|
2819
|
-
(
|
|
2839
|
+
(resolve38) => setTimeout(() => resolve38("timeout"), timeoutMs)
|
|
2820
2840
|
);
|
|
2821
2841
|
const result = await Promise.race([
|
|
2822
2842
|
credsSaveQueue.then(() => "drained"),
|
|
@@ -2993,11 +3013,11 @@ async function createWaSocket(opts) {
|
|
|
2993
3013
|
return sock;
|
|
2994
3014
|
}
|
|
2995
3015
|
async function waitForConnection(sock) {
|
|
2996
|
-
return new Promise((
|
|
3016
|
+
return new Promise((resolve38, reject) => {
|
|
2997
3017
|
const handler = (update) => {
|
|
2998
3018
|
if (update.connection === "open") {
|
|
2999
3019
|
sock.ev.off("connection.update", handler);
|
|
3000
|
-
|
|
3020
|
+
resolve38();
|
|
3001
3021
|
}
|
|
3002
3022
|
if (update.connection === "close") {
|
|
3003
3023
|
sock.ev.off("connection.update", handler);
|
|
@@ -3105,7 +3125,7 @@ function isTimeoutError(err) {
|
|
|
3105
3125
|
return Boolean(err?.__maxyTimeout);
|
|
3106
3126
|
}
|
|
3107
3127
|
function withTimeout(label, promise, timeoutMs) {
|
|
3108
|
-
return new Promise((
|
|
3128
|
+
return new Promise((resolve38, reject) => {
|
|
3109
3129
|
const timer = setTimeout(() => {
|
|
3110
3130
|
const err = new Error(`${label} timed out after ${timeoutMs}ms`);
|
|
3111
3131
|
err.__maxyTimeout = true;
|
|
@@ -3115,7 +3135,7 @@ function withTimeout(label, promise, timeoutMs) {
|
|
|
3115
3135
|
promise.then(
|
|
3116
3136
|
(value) => {
|
|
3117
3137
|
clearTimeout(timer);
|
|
3118
|
-
|
|
3138
|
+
resolve38(value);
|
|
3119
3139
|
},
|
|
3120
3140
|
(err) => {
|
|
3121
3141
|
clearTimeout(timer);
|
|
@@ -4029,15 +4049,15 @@ function getChannelActivity(accountId) {
|
|
|
4029
4049
|
|
|
4030
4050
|
// app/lib/whatsapp/outbound/send.ts
|
|
4031
4051
|
var TAG10 = "[whatsapp:outbound]";
|
|
4032
|
-
function buildQuoted(
|
|
4052
|
+
function buildQuoted(q2) {
|
|
4033
4053
|
return {
|
|
4034
4054
|
key: {
|
|
4035
|
-
remoteJid:
|
|
4036
|
-
id:
|
|
4037
|
-
fromMe:
|
|
4038
|
-
...
|
|
4055
|
+
remoteJid: q2.remoteJid,
|
|
4056
|
+
id: q2.id,
|
|
4057
|
+
fromMe: q2.fromMe,
|
|
4058
|
+
...q2.participant ? { participant: q2.participant } : {}
|
|
4039
4059
|
},
|
|
4040
|
-
message: { conversation:
|
|
4060
|
+
message: { conversation: q2.body ?? "" }
|
|
4041
4061
|
};
|
|
4042
4062
|
}
|
|
4043
4063
|
async function sendTextMessage(sock, to, text, opts) {
|
|
@@ -4155,8 +4175,8 @@ async function persistWhatsAppMessage(input) {
|
|
|
4155
4175
|
const { givenName, familyName } = splitName(input.pushName);
|
|
4156
4176
|
const prev = sessionWriteLocks.get(input.cacheKey);
|
|
4157
4177
|
let release;
|
|
4158
|
-
const mine = new Promise((
|
|
4159
|
-
release =
|
|
4178
|
+
const mine = new Promise((resolve38) => {
|
|
4179
|
+
release = resolve38;
|
|
4160
4180
|
});
|
|
4161
4181
|
const chained = (prev ?? Promise.resolve()).then(() => mine);
|
|
4162
4182
|
sessionWriteLocks.set(input.cacheKey, chained);
|
|
@@ -4280,7 +4300,7 @@ async function ensureWhatsAppConversation(input) {
|
|
|
4280
4300
|
return null;
|
|
4281
4301
|
}
|
|
4282
4302
|
console.error(
|
|
4283
|
-
`${TAG12} conversation-merged cacheKey=${input.cacheKey} agentType=${input.agentType} channel=whatsapp created=${result.created} ms=${ms}`
|
|
4303
|
+
`${TAG12} conversation-merged cacheKey=${input.cacheKey} agentType=${input.agentType} channel=whatsapp accountId=${input.platformAccountId} connectionAccountId=${input.connectionAccountId ?? input.platformAccountId} created=${result.created} ms=${ms}`
|
|
4284
4304
|
);
|
|
4285
4305
|
return { sessionId: result.sessionId, created: result.created };
|
|
4286
4306
|
} catch (err) {
|
|
@@ -5642,6 +5662,29 @@ function selectStoreAccountKey(conns, accountId) {
|
|
|
5642
5662
|
function resolveReadStoreKey(accountId) {
|
|
5643
5663
|
return selectStoreAccountKey(Array.from(connections.values()), accountId);
|
|
5644
5664
|
}
|
|
5665
|
+
function resolveInboundPersistAccount(args) {
|
|
5666
|
+
const result = args.accessResult;
|
|
5667
|
+
if (result?.allowed && result.reason === "account-manager" && result.effectiveAccountId) {
|
|
5668
|
+
return result.effectiveAccountId;
|
|
5669
|
+
}
|
|
5670
|
+
return args.connPlatformAccountId;
|
|
5671
|
+
}
|
|
5672
|
+
function resolveConversationPersistAccount(sendAccountId, counterpartyPhone) {
|
|
5673
|
+
const conn = findConnectionByKey(Array.from(connections.values()), sendAccountId);
|
|
5674
|
+
if (!conn) return sendAccountId;
|
|
5675
|
+
const access = checkDmAccess({
|
|
5676
|
+
senderPhone: counterpartyPhone,
|
|
5677
|
+
selfPhone: conn.selfPhone ?? "",
|
|
5678
|
+
config: whatsAppConfig,
|
|
5679
|
+
accountConfig: whatsAppConfig.accounts?.[conn.accountId],
|
|
5680
|
+
accountId: conn.accountId,
|
|
5681
|
+
isValidAccount: (id) => isValidAccountId(id)
|
|
5682
|
+
});
|
|
5683
|
+
return resolveInboundPersistAccount({
|
|
5684
|
+
accessResult: access,
|
|
5685
|
+
connPlatformAccountId: conn.platformAccountId
|
|
5686
|
+
});
|
|
5687
|
+
}
|
|
5645
5688
|
function listConnectionPlatformAccountIds() {
|
|
5646
5689
|
return [...connections.values()].map((conn) => conn.platformAccountId);
|
|
5647
5690
|
}
|
|
@@ -5860,11 +5903,11 @@ async function connectWithReconnect(conn) {
|
|
|
5860
5903
|
console.error(
|
|
5861
5904
|
`${TAG17} reconnecting account=${conn.accountId} in ${delay}ms (attempt ${decision.nextAttempts}/${maxAttempts})`
|
|
5862
5905
|
);
|
|
5863
|
-
await new Promise((
|
|
5864
|
-
const timer = setTimeout(
|
|
5906
|
+
await new Promise((resolve38) => {
|
|
5907
|
+
const timer = setTimeout(resolve38, delay);
|
|
5865
5908
|
conn.abortController.signal.addEventListener("abort", () => {
|
|
5866
5909
|
clearTimeout(timer);
|
|
5867
|
-
|
|
5910
|
+
resolve38();
|
|
5868
5911
|
}, { once: true });
|
|
5869
5912
|
});
|
|
5870
5913
|
}
|
|
@@ -5872,16 +5915,16 @@ async function connectWithReconnect(conn) {
|
|
|
5872
5915
|
}
|
|
5873
5916
|
}
|
|
5874
5917
|
function waitForDisconnectEvent(conn) {
|
|
5875
|
-
return new Promise((
|
|
5918
|
+
return new Promise((resolve38) => {
|
|
5876
5919
|
if (!conn.sock) {
|
|
5877
|
-
|
|
5920
|
+
resolve38();
|
|
5878
5921
|
return;
|
|
5879
5922
|
}
|
|
5880
5923
|
const sock = conn.sock;
|
|
5881
5924
|
const handler = (update) => {
|
|
5882
5925
|
if (update.connection === "close") {
|
|
5883
5926
|
sock.ev.off("connection.update", handler);
|
|
5884
|
-
|
|
5927
|
+
resolve38();
|
|
5885
5928
|
}
|
|
5886
5929
|
};
|
|
5887
5930
|
sock.ev.on("connection.update", handler);
|
|
@@ -5948,6 +5991,7 @@ function monitorInbound(conn) {
|
|
|
5948
5991
|
if (!remoteJid || remoteJid === "status@broadcast") continue;
|
|
5949
5992
|
if (!msg.message) continue;
|
|
5950
5993
|
let receiptDeferredToWorker = false;
|
|
5994
|
+
let dmAccess = null;
|
|
5951
5995
|
const extracted = extractMessage(msg);
|
|
5952
5996
|
const mediaServable = !!(extracted.mediaType && extracted.mimetype);
|
|
5953
5997
|
if (!extracted.text && !mediaServable && msg.key.id) {
|
|
@@ -5962,7 +6006,21 @@ function monitorInbound(conn) {
|
|
|
5962
6006
|
const senderJid = isGroup ? msg.key.participant ?? remoteJid : remoteJid;
|
|
5963
6007
|
const senderPhone = await resolveJidToE164(senderJid, conn.lidMapping) ?? senderJid;
|
|
5964
6008
|
const ts = msg.messageTimestamp ? Number(msg.messageTimestamp) : Math.floor(Date.now() / 1e3);
|
|
5965
|
-
const
|
|
6009
|
+
const fromMe = Boolean(msg.key.fromMe);
|
|
6010
|
+
dmAccess = isGroup ? null : checkDmAccess({
|
|
6011
|
+
senderPhone,
|
|
6012
|
+
selfPhone: conn.selfPhone ?? "",
|
|
6013
|
+
config: whatsAppConfig,
|
|
6014
|
+
accountConfig: whatsAppConfig.accounts?.[conn.accountId],
|
|
6015
|
+
accountId: conn.accountId,
|
|
6016
|
+
isValidAccount: (id) => isValidAccountId(id)
|
|
6017
|
+
});
|
|
6018
|
+
const persistAccountId = resolveInboundPersistAccount({
|
|
6019
|
+
accessResult: dmAccess,
|
|
6020
|
+
connPlatformAccountId: conn.platformAccountId
|
|
6021
|
+
});
|
|
6022
|
+
const managerScoped = dmAccess?.allowed === true && dmAccess.reason === "account-manager";
|
|
6023
|
+
const storeKey = messageStoreKey(persistAccountId, remoteJid);
|
|
5966
6024
|
if (extracted.text) {
|
|
5967
6025
|
storeMessage(storeKey, {
|
|
5968
6026
|
id: msg.key.id ?? `${remoteJid}-${ts}`,
|
|
@@ -5983,16 +6041,16 @@ function monitorInbound(conn) {
|
|
|
5983
6041
|
direction: "inbound",
|
|
5984
6042
|
jid: remoteJid
|
|
5985
6043
|
});
|
|
5986
|
-
const
|
|
5987
|
-
const cacheKeyAgentType = isGroup ? "public" : fromMe ? "admin" : checkDmAccess({
|
|
5988
|
-
senderPhone,
|
|
5989
|
-
selfPhone: conn.selfPhone ?? "",
|
|
5990
|
-
config: whatsAppConfig,
|
|
5991
|
-
accountConfig: whatsAppConfig.accounts?.[conn.accountId]
|
|
5992
|
-
}).agentType;
|
|
6044
|
+
const cacheKeyAgentType = isGroup ? "public" : fromMe ? "admin" : dmAccess?.agentType ?? "public";
|
|
5993
6045
|
const cacheKey = deriveCacheKey({
|
|
5994
6046
|
agentType: cacheKeyAgentType,
|
|
5995
|
-
|
|
6047
|
+
// Task 1786 — an account-manager keys on its bound sub-account, so a
|
|
6048
|
+
// manager gets its own `:Conversation` instead of merging into the
|
|
6049
|
+
// owner's bare `whatsapp:<dirname>` owner-mirror row. `deriveCacheKey`
|
|
6050
|
+
// returns `whatsapp:<accountId>` for admin; a UUID contains no colon,
|
|
6051
|
+
// so the key stays 2 segments and `deriveScope` still reads "admin".
|
|
6052
|
+
// Every non-manager case passes conn.accountId, byte-identical.
|
|
6053
|
+
accountId: managerScoped ? persistAccountId : conn.accountId,
|
|
5996
6054
|
senderPhone,
|
|
5997
6055
|
isGroup,
|
|
5998
6056
|
groupJid: isGroup ? remoteJid : void 0,
|
|
@@ -6001,7 +6059,10 @@ function monitorInbound(conn) {
|
|
|
6001
6059
|
if (msg.key.id) {
|
|
6002
6060
|
const merged = await ensureWhatsAppConversation({
|
|
6003
6061
|
accountId: conn.accountId,
|
|
6004
|
-
platformAccountId:
|
|
6062
|
+
platformAccountId: persistAccountId,
|
|
6063
|
+
// Task 1786 — the socket owner, logged beside the stamped account
|
|
6064
|
+
// so a divergence between the two is one greppable line.
|
|
6065
|
+
connectionAccountId: conn.platformAccountId,
|
|
6005
6066
|
cacheKey,
|
|
6006
6067
|
agentType: cacheKeyAgentType,
|
|
6007
6068
|
groupJid: isGroup ? remoteJid : void 0
|
|
@@ -6013,7 +6074,12 @@ function monitorInbound(conn) {
|
|
|
6013
6074
|
);
|
|
6014
6075
|
const alreadyRecorded = fromMe && echoAlreadyRecorded({
|
|
6015
6076
|
readConversation,
|
|
6016
|
-
|
|
6077
|
+
// Task 1786 — read the account /reply now APPENDS under. The
|
|
6078
|
+
// echo gate asks "did /reply already store this?", so it must
|
|
6079
|
+
// look in the same store file the reply landed in; reading the
|
|
6080
|
+
// socket owner's file would report every manager echo as
|
|
6081
|
+
// unrecorded and store a duplicate copy.
|
|
6082
|
+
platformAccountId: persistAccountId,
|
|
6017
6083
|
canonicalRemoteJid,
|
|
6018
6084
|
accountId: conn.accountId,
|
|
6019
6085
|
remoteJid,
|
|
@@ -6032,7 +6098,7 @@ function monitorInbound(conn) {
|
|
|
6032
6098
|
});
|
|
6033
6099
|
const basePersist = {
|
|
6034
6100
|
accountId: conn.accountId,
|
|
6035
|
-
platformAccountId:
|
|
6101
|
+
platformAccountId: persistAccountId,
|
|
6036
6102
|
remoteJid,
|
|
6037
6103
|
canonicalRemoteJid,
|
|
6038
6104
|
cacheKey,
|
|
@@ -6051,7 +6117,7 @@ function monitorInbound(conn) {
|
|
|
6051
6117
|
};
|
|
6052
6118
|
if (wantsServableMedia && upsert.type === "append") {
|
|
6053
6119
|
const sock2 = conn.sock;
|
|
6054
|
-
const platformAccountId =
|
|
6120
|
+
const platformAccountId = persistAccountId;
|
|
6055
6121
|
const caption = extracted.caption ?? "";
|
|
6056
6122
|
const fallbackBody = mediaFallbackBody(extracted);
|
|
6057
6123
|
const msgKeyId = msg.key.id;
|
|
@@ -6089,7 +6155,8 @@ function monitorInbound(conn) {
|
|
|
6089
6155
|
mediaAttachmentId = await storeInboundMediaServable(
|
|
6090
6156
|
msg,
|
|
6091
6157
|
conn.sock,
|
|
6092
|
-
|
|
6158
|
+
// Task 1786 — attachment bytes follow the record's account.
|
|
6159
|
+
persistAccountId,
|
|
6093
6160
|
mediaSeed,
|
|
6094
6161
|
{ maxBytes }
|
|
6095
6162
|
) ?? void 0;
|
|
@@ -6291,8 +6358,8 @@ async function handleInboundMessage(conn, msg) {
|
|
|
6291
6358
|
const conversationKey = isGroup ? remoteJid : senderPhone;
|
|
6292
6359
|
const debounceKey = `${conn.accountId}:${conversationKey}:${senderPhone}`;
|
|
6293
6360
|
let resolvePending;
|
|
6294
|
-
const sttPending = new Promise((
|
|
6295
|
-
resolvePending =
|
|
6361
|
+
const sttPending = new Promise((resolve38) => {
|
|
6362
|
+
resolvePending = resolve38;
|
|
6296
6363
|
});
|
|
6297
6364
|
if (conn.debouncer) conn.debouncer.registerPending(debounceKey, sttPending);
|
|
6298
6365
|
try {
|
|
@@ -6720,20 +6787,20 @@ function buildX11Env(chromiumWrapperPath, transport = "vnc") {
|
|
|
6720
6787
|
|
|
6721
6788
|
// server/routes/health.ts
|
|
6722
6789
|
function checkPort(port2, timeoutMs = 500) {
|
|
6723
|
-
return new Promise((
|
|
6790
|
+
return new Promise((resolve38) => {
|
|
6724
6791
|
const socket = createConnection2(port2, "127.0.0.1");
|
|
6725
6792
|
socket.setTimeout(timeoutMs);
|
|
6726
6793
|
socket.once("connect", () => {
|
|
6727
6794
|
socket.destroy();
|
|
6728
|
-
|
|
6795
|
+
resolve38(true);
|
|
6729
6796
|
});
|
|
6730
6797
|
socket.once("error", () => {
|
|
6731
6798
|
socket.destroy();
|
|
6732
|
-
|
|
6799
|
+
resolve38(false);
|
|
6733
6800
|
});
|
|
6734
6801
|
socket.once("timeout", () => {
|
|
6735
6802
|
socket.destroy();
|
|
6736
|
-
|
|
6803
|
+
resolve38(false);
|
|
6737
6804
|
});
|
|
6738
6805
|
});
|
|
6739
6806
|
}
|
|
@@ -6988,8 +7055,8 @@ function webchatTurnTimeoutMs() {
|
|
|
6988
7055
|
return Number(process.env.WEBCHAT_TURN_TIMEOUT_MS ?? String(2 * 6e4));
|
|
6989
7056
|
}
|
|
6990
7057
|
function createChatRoutes(deps) {
|
|
6991
|
-
const
|
|
6992
|
-
|
|
7058
|
+
const app65 = new Hono();
|
|
7059
|
+
app65.post("/", async (c) => {
|
|
6993
7060
|
console.log(`[chat-route] entered route=public method=POST`);
|
|
6994
7061
|
const contentType = c.req.header("content-type") ?? "";
|
|
6995
7062
|
const account = resolveAccount();
|
|
@@ -7253,7 +7320,7 @@ ${result.result.text}` : result.result.text;
|
|
|
7253
7320
|
}
|
|
7254
7321
|
});
|
|
7255
7322
|
});
|
|
7256
|
-
return
|
|
7323
|
+
return app65;
|
|
7257
7324
|
}
|
|
7258
7325
|
|
|
7259
7326
|
// app/lib/channel-pty-bridge/admin-session-id.ts
|
|
@@ -7287,8 +7354,8 @@ function isLoopbackAddr(addr) {
|
|
|
7287
7354
|
return addr === "127.0.0.1" || addr === "::1" || addr === "::ffff:127.0.0.1";
|
|
7288
7355
|
}
|
|
7289
7356
|
function createScheduleInjectRoutes(deps) {
|
|
7290
|
-
const
|
|
7291
|
-
|
|
7357
|
+
const app65 = new Hono();
|
|
7358
|
+
app65.post("/", async (c) => {
|
|
7292
7359
|
const env = c.env;
|
|
7293
7360
|
const remoteAddr = env?.incoming?.socket?.remoteAddress ?? "";
|
|
7294
7361
|
if (!isLoopbackAddr(remoteAddr)) {
|
|
@@ -7389,7 +7456,7 @@ function createScheduleInjectRoutes(deps) {
|
|
|
7389
7456
|
console.error(`${TAG20} op=inject-spawn eventId=${eventId} sessionId=${sessionId} result=ok status=-`);
|
|
7390
7457
|
return c.json({ ok: true }, 200);
|
|
7391
7458
|
});
|
|
7392
|
-
return
|
|
7459
|
+
return app65;
|
|
7393
7460
|
}
|
|
7394
7461
|
|
|
7395
7462
|
// server/index.ts
|
|
@@ -8022,8 +8089,8 @@ async function startLogin(opts) {
|
|
|
8022
8089
|
await clearAuth(authDir);
|
|
8023
8090
|
let resolveCode = null;
|
|
8024
8091
|
let rejectCode = null;
|
|
8025
|
-
const codePromise = new Promise((
|
|
8026
|
-
resolveCode =
|
|
8092
|
+
const codePromise = new Promise((resolve38, reject) => {
|
|
8093
|
+
resolveCode = resolve38;
|
|
8027
8094
|
rejectCode = reject;
|
|
8028
8095
|
});
|
|
8029
8096
|
const codeTimer = setTimeout(
|
|
@@ -9321,7 +9388,10 @@ app3.post("/pages/deploy", pagesDeployBodyLimit, async (c) => {
|
|
|
9321
9388
|
`[pages-broker] op=project-ensure pub=${pub} project=${project} existed=${existed} created=${!existed}`
|
|
9322
9389
|
);
|
|
9323
9390
|
const started = Date.now();
|
|
9324
|
-
const { url } = await exec.pagesDeploy(dir, project, branch);
|
|
9391
|
+
const { url, excluded } = await exec.pagesDeploy(dir, project, branch);
|
|
9392
|
+
console.error(
|
|
9393
|
+
`[pages-broker] op=stage pub=${pub} excluded=${excluded.length > 0 ? excluded.join(",") : "none"}`
|
|
9394
|
+
);
|
|
9325
9395
|
console.error(`[pages-broker] op=deploy pub=${pub} ranMs=${Date.now() - started} url=${url}`);
|
|
9326
9396
|
const httpStatus = await verifyDeployed(url);
|
|
9327
9397
|
const verified = httpStatus === 200;
|
|
@@ -9638,7 +9708,7 @@ app3.post("/r2/object/delete", envelopeBodyLimit("write"), async (c) => {
|
|
|
9638
9708
|
});
|
|
9639
9709
|
var storage_broker_default = app3;
|
|
9640
9710
|
async function runStorageAudit(root) {
|
|
9641
|
-
const { reconcileStorage } = await import("./src-
|
|
9711
|
+
const { reconcileStorage } = await import("./src-JJ6PHO5I.js");
|
|
9642
9712
|
const cf = await makeHouseCfExec(root);
|
|
9643
9713
|
const accountsDir = resolve11(root, "..", "data", "accounts");
|
|
9644
9714
|
const strays = (await reconcileStorage({
|
|
@@ -9678,7 +9748,7 @@ async function runStorageAudit(root) {
|
|
|
9678
9748
|
);
|
|
9679
9749
|
}
|
|
9680
9750
|
async function runPagesAudit(root) {
|
|
9681
|
-
const { reconcilePages } = await import("./src-
|
|
9751
|
+
const { reconcilePages } = await import("./src-JJ6PHO5I.js");
|
|
9682
9752
|
const cf = await makeHousePagesExec(root);
|
|
9683
9753
|
const r = await reconcilePages({
|
|
9684
9754
|
cfProjects: async () => (await cf.pagesProjectList()).map((p) => p.name),
|
|
@@ -10961,6 +11031,9 @@ function readFrom(path3, from) {
|
|
|
10961
11031
|
closeSync(fd);
|
|
10962
11032
|
}
|
|
10963
11033
|
}
|
|
11034
|
+
function resolveReplyAppendAccount(args) {
|
|
11035
|
+
return args.resolve(args.sendAccountId, args.counterpartyPhone) || args.sendAccountId;
|
|
11036
|
+
}
|
|
10964
11037
|
function resumeOffset(lastEventId, fileEnd) {
|
|
10965
11038
|
if (!lastEventId) return 0;
|
|
10966
11039
|
const n = Number(lastEventId);
|
|
@@ -11337,12 +11410,12 @@ app5.get("/store-stream", requireAdminSession, (c) => {
|
|
|
11337
11410
|
...reactions && reactions.length > 0 ? { reactions } : {}
|
|
11338
11411
|
};
|
|
11339
11412
|
if (r.quotedId) {
|
|
11340
|
-
const
|
|
11413
|
+
const q2 = byMsgKeyId.get(r.quotedId);
|
|
11341
11414
|
turn = {
|
|
11342
11415
|
...turn,
|
|
11343
|
-
quoted:
|
|
11416
|
+
quoted: q2 ? { body: q2.body, sender: q2.senderName ?? (q2.fromMe ? null : q2.senderTelephone || null), fromMe: q2.fromMe } : { body: "", sender: r.quotedSender, fromMe: false }
|
|
11344
11417
|
};
|
|
11345
|
-
console.log(`[wa-reader] op=store-quote msgId=${r.msgKeyId} quoted=${!!
|
|
11418
|
+
console.log(`[wa-reader] op=store-quote msgId=${r.msgKeyId} quoted=${!!q2}`);
|
|
11346
11419
|
}
|
|
11347
11420
|
if (r.attachmentId) {
|
|
11348
11421
|
const meta = readAttachmentMeta(join19(uploadsBase, r.attachmentId));
|
|
@@ -11650,9 +11723,14 @@ app5.post("/reply", requireAdminSession, async (c) => {
|
|
|
11650
11723
|
// so the store reader renders it inline instead of a `[sent …]` text line.
|
|
11651
11724
|
...attachmentId ? { attachmentId } : {}
|
|
11652
11725
|
};
|
|
11653
|
-
|
|
11654
|
-
|
|
11655
|
-
|
|
11726
|
+
const appendAccountId = resolveReplyAppendAccount({
|
|
11727
|
+
sendAccountId: accountId,
|
|
11728
|
+
counterpartyPhone: remoteJid.split("@")[0],
|
|
11729
|
+
resolve: resolveConversationPersistAccount
|
|
11730
|
+
});
|
|
11731
|
+
appendMessage(appendAccountId, rec);
|
|
11732
|
+
const present = readConversation(appendAccountId, remoteJid).some((r) => r.messageId === rec.messageId);
|
|
11733
|
+
console.log(`${MANUAL_TAG} op=recorded sendId=${sendId} waMessageId=${waMessageId} present=${present} appendAccount=${appendAccountId.slice(0, 8)} sendAccount=${acct8} attachment=${attachmentId ? "yes" : "no"}`);
|
|
11656
11734
|
};
|
|
11657
11735
|
let sent = 0;
|
|
11658
11736
|
const sendStagedFile = async (file, label) => {
|
|
@@ -12252,10 +12330,7 @@ function locateSidecar(sessionId) {
|
|
|
12252
12330
|
}
|
|
12253
12331
|
for (const slug of slugs) {
|
|
12254
12332
|
const metaPath = join23(projectsRoot, slug, `${sessionId}.meta.json`);
|
|
12255
|
-
if (existsSync15(metaPath))
|
|
12256
|
-
const meta = readSidecarMeta(metaPath);
|
|
12257
|
-
return { channel: meta.channel, adminUserId: meta.adminUserId, accountId: meta.accountId };
|
|
12258
|
-
}
|
|
12333
|
+
if (existsSync15(metaPath)) return readSidecarMeta(metaPath);
|
|
12259
12334
|
}
|
|
12260
12335
|
return null;
|
|
12261
12336
|
}
|
|
@@ -12268,6 +12343,13 @@ function readTargetOwner(sessionId) {
|
|
|
12268
12343
|
}
|
|
12269
12344
|
return { found: false, owner: null };
|
|
12270
12345
|
}
|
|
12346
|
+
function readSourceRowMeta(sessionId) {
|
|
12347
|
+
const sidecar = locateSidecar(sessionId);
|
|
12348
|
+
if (sidecar !== null) return sidecar;
|
|
12349
|
+
const { projectDir } = locateSession(sessionId);
|
|
12350
|
+
if (projectDir !== null) return readSidecarMeta(join23(projectDir, `${sessionId}.meta.json`));
|
|
12351
|
+
return null;
|
|
12352
|
+
}
|
|
12271
12353
|
function ownedByRequester(owner, requesterUserId, primaryUserId) {
|
|
12272
12354
|
if (requesterUserId === null) return false;
|
|
12273
12355
|
return owner === requesterUserId || owner === null && requesterUserId === primaryUserId;
|
|
@@ -12476,8 +12558,8 @@ async function reapplyLeversViaManager() {
|
|
|
12476
12558
|
var WEBCHAT_SEND_TURN_WINDOW_MS = Number(process.env.WEBCHAT_SEND_TURN_WINDOW_MS ?? String(15e3));
|
|
12477
12559
|
var sendSeq = 0;
|
|
12478
12560
|
function createWebchatRoutes(deps) {
|
|
12479
|
-
const
|
|
12480
|
-
|
|
12561
|
+
const app65 = new Hono();
|
|
12562
|
+
app65.post("/send", requireAdminSession, async (c) => {
|
|
12481
12563
|
let accountId;
|
|
12482
12564
|
try {
|
|
12483
12565
|
accountId = resolvePlatformAccountId();
|
|
@@ -12689,7 +12771,7 @@ ${note}` : note;
|
|
|
12689
12771
|
if (turnTimer.unref) turnTimer.unref();
|
|
12690
12772
|
return c.json({ ok: true });
|
|
12691
12773
|
});
|
|
12692
|
-
|
|
12774
|
+
app65.post("/interrupt", requireAdminSession, async (c) => {
|
|
12693
12775
|
const cacheKey = c.get("cacheKey");
|
|
12694
12776
|
const requesterUserId = (cacheKey ? getUserIdForSession(cacheKey) : void 0) ?? null;
|
|
12695
12777
|
const primaryUserId = resolvePrimaryAdminUserId();
|
|
@@ -12717,7 +12799,7 @@ ${note}` : note;
|
|
|
12717
12799
|
}
|
|
12718
12800
|
return c.json({ ok: true, stopped: outcome.stopped, intId: outcome.intId, deadChild: outcome.deadChild }, 200);
|
|
12719
12801
|
});
|
|
12720
|
-
|
|
12802
|
+
app65.post("/settings", requireAdminSession, async (c) => {
|
|
12721
12803
|
const body = await c.req.json().catch(() => null);
|
|
12722
12804
|
const op = body?.op;
|
|
12723
12805
|
const value = body?.value;
|
|
@@ -12757,7 +12839,7 @@ ${note}` : note;
|
|
|
12757
12839
|
console.log(`[webchat:settings] op=${op} outcome=accepted value=${value} settingsApplied=${settingsApplied}`);
|
|
12758
12840
|
return c.json({ ok: true, settingsApplied });
|
|
12759
12841
|
});
|
|
12760
|
-
|
|
12842
|
+
app65.get("/session", requireAdminSession, async (c) => {
|
|
12761
12843
|
let accountId;
|
|
12762
12844
|
try {
|
|
12763
12845
|
accountId = resolvePlatformAccountId();
|
|
@@ -12835,7 +12917,7 @@ ${note}` : note;
|
|
|
12835
12917
|
const indicators = await fetchComposerIndicators(sessionId);
|
|
12836
12918
|
return c.json({ sessionId, projectDir, source, sizeBytes: jsonlSizeBytes(projectDir, sessionId), pendingPermissionPrompt: deps.pendingPromptFor?.(`session:${sessionId}`) ?? null, deliveryFailure: deps.deliveryFailureFor?.(`session:${sessionId}`) ?? null, ...indicators, ...readLevers(account) });
|
|
12837
12919
|
});
|
|
12838
|
-
|
|
12920
|
+
app65.post("/permission-verdict", requireAdminSession, async (c) => {
|
|
12839
12921
|
const body = await c.req.json().catch(() => null);
|
|
12840
12922
|
const sessionId = body?.sessionId;
|
|
12841
12923
|
const requestId = body?.request_id;
|
|
@@ -12852,7 +12934,7 @@ ${note}` : note;
|
|
|
12852
12934
|
const ok = deps.resolvePermissionVerdict?.(`session:${sessionId}`, requestId, behavior) ?? false;
|
|
12853
12935
|
return c.json({ ok });
|
|
12854
12936
|
});
|
|
12855
|
-
return
|
|
12937
|
+
return app65;
|
|
12856
12938
|
}
|
|
12857
12939
|
|
|
12858
12940
|
// server/routes/webchat-greeting.ts
|
|
@@ -14208,15 +14290,15 @@ function operatorRoleFor(config, userId) {
|
|
|
14208
14290
|
async function buildAccountOptionList(accounts, userId) {
|
|
14209
14291
|
const houseAccount = accounts.find((a) => a.config.role === "house");
|
|
14210
14292
|
console.log(`[admin-accounts] op=list count=${accounts.length} house=${houseAccount?.accountId ?? "none"}`);
|
|
14211
|
-
const
|
|
14293
|
+
const resolve38 = async (a) => {
|
|
14212
14294
|
const businessName = await fetchAccountName(a.accountId) || void 0;
|
|
14213
14295
|
return { accountId: a.accountId, businessName, role: operatorRoleFor(a.config, userId), isHouse: a.config.role === "house" };
|
|
14214
14296
|
};
|
|
14215
14297
|
const houseOptions = await Promise.all(
|
|
14216
|
-
accounts.filter((a) => a.config.role === "house").map(
|
|
14298
|
+
accounts.filter((a) => a.config.role === "house").map(resolve38)
|
|
14217
14299
|
);
|
|
14218
14300
|
const nonHouseOptions = await Promise.all(
|
|
14219
|
-
accounts.filter((a) => a.config.role !== "house").map(
|
|
14301
|
+
accounts.filter((a) => a.config.role !== "house").map(resolve38)
|
|
14220
14302
|
);
|
|
14221
14303
|
const sortKey = (o) => (o.businessName || o.accountId).toLowerCase();
|
|
14222
14304
|
nonHouseOptions.sort((x, y) => {
|
|
@@ -15223,9 +15305,9 @@ app18.post("/:id/resume", async (c) => {
|
|
|
15223
15305
|
if (healQueue.length > 0) {
|
|
15224
15306
|
void (async () => {
|
|
15225
15307
|
for (let i = 0; i < healQueue.length; i++) {
|
|
15226
|
-
const
|
|
15308
|
+
const q2 = healQueue[i];
|
|
15227
15309
|
try {
|
|
15228
|
-
const healComponents = await Promise.all(
|
|
15310
|
+
const healComponents = await Promise.all(q2.components.map(async (c2) => {
|
|
15229
15311
|
const base = {
|
|
15230
15312
|
componentId: c2.componentId,
|
|
15231
15313
|
name: c2.name,
|
|
@@ -15263,25 +15345,25 @@ app18.post("/:id/resume", async (c) => {
|
|
|
15263
15345
|
}));
|
|
15264
15346
|
const messageId = await persistMessage(
|
|
15265
15347
|
sessionId,
|
|
15266
|
-
|
|
15267
|
-
|
|
15348
|
+
q2.role,
|
|
15349
|
+
q2.content,
|
|
15268
15350
|
accountId,
|
|
15269
15351
|
void 0,
|
|
15270
|
-
|
|
15352
|
+
q2.createdAt,
|
|
15271
15353
|
void 0,
|
|
15272
15354
|
healComponents,
|
|
15273
15355
|
void 0
|
|
15274
15356
|
);
|
|
15275
15357
|
jsonlHealOk += 1;
|
|
15276
15358
|
try {
|
|
15277
|
-
streamLogPath.write(`[${(/* @__PURE__ */ new Date()).toISOString()}] [admin-persist-heal] convId=${sessionId.slice(0, 8)} turnIndex=${i} role=${
|
|
15359
|
+
streamLogPath.write(`[${(/* @__PURE__ */ new Date()).toISOString()}] [admin-persist-heal] convId=${sessionId.slice(0, 8)} turnIndex=${i} role=${q2.role} outcome=ok messageId=${(messageId ?? "").slice(0, 8)}
|
|
15278
15360
|
`);
|
|
15279
15361
|
} catch {
|
|
15280
15362
|
}
|
|
15281
15363
|
} catch (err) {
|
|
15282
15364
|
jsonlHealFail += 1;
|
|
15283
15365
|
try {
|
|
15284
|
-
streamLogPath.write(`[${(/* @__PURE__ */ new Date()).toISOString()}] [admin-persist-heal] convId=${sessionId.slice(0, 8)} turnIndex=${i} role=${
|
|
15366
|
+
streamLogPath.write(`[${(/* @__PURE__ */ new Date()).toISOString()}] [admin-persist-heal] convId=${sessionId.slice(0, 8)} turnIndex=${i} role=${q2.role} outcome=fail reason=${JSON.stringify((err instanceof Error ? err.message : String(err)).slice(0, 200))}
|
|
15285
15367
|
`);
|
|
15286
15368
|
} catch {
|
|
15287
15369
|
}
|
|
@@ -15606,10 +15688,10 @@ function computeSpecialistDomains(accountId) {
|
|
|
15606
15688
|
const resolveDesc = (qualified) => {
|
|
15607
15689
|
if (!qualified.startsWith("mcp__")) return void 0;
|
|
15608
15690
|
const rest = qualified.slice(5);
|
|
15609
|
-
const
|
|
15610
|
-
if (
|
|
15611
|
-
const plugin = rest.slice(0,
|
|
15612
|
-
const tool = rest.slice(
|
|
15691
|
+
const sep10 = rest.indexOf("__");
|
|
15692
|
+
if (sep10 < 0) return void 0;
|
|
15693
|
+
const plugin = rest.slice(0, sep10);
|
|
15694
|
+
const tool = rest.slice(sep10 + 2);
|
|
15613
15695
|
let map = descByPlugin.get(plugin);
|
|
15614
15696
|
if (!map) {
|
|
15615
15697
|
const dir = pluginDirs.get(plugin);
|
|
@@ -15912,18 +15994,18 @@ var ALLOWED_EVENTS = /* @__PURE__ */ new Set([
|
|
|
15912
15994
|
]);
|
|
15913
15995
|
var app21 = new Hono();
|
|
15914
15996
|
app21.post("/", async (c) => {
|
|
15915
|
-
const
|
|
15997
|
+
const TAG54 = "[admin:events]";
|
|
15916
15998
|
let body;
|
|
15917
15999
|
try {
|
|
15918
16000
|
body = await c.req.json();
|
|
15919
16001
|
} catch (err) {
|
|
15920
16002
|
const detail = err instanceof Error ? err.message : String(err);
|
|
15921
|
-
console.error(`${
|
|
16003
|
+
console.error(`${TAG54} reject reason=body-not-json detail=${detail}`);
|
|
15922
16004
|
return c.json({ ok: false, detail: "Request body was not valid JSON" }, 400);
|
|
15923
16005
|
}
|
|
15924
16006
|
const event = typeof body.event === "string" ? body.event : "";
|
|
15925
16007
|
if (!ALLOWED_EVENTS.has(event)) {
|
|
15926
|
-
console.error(`${
|
|
16008
|
+
console.error(`${TAG54} reject reason=event-not-allowed event=${JSON.stringify(event)}`);
|
|
15927
16009
|
return c.json({ ok: false, detail: `Event "${event}" is not allowed` }, 400);
|
|
15928
16010
|
}
|
|
15929
16011
|
const rawFields = body.fields && typeof body.fields === "object" ? body.fields : {};
|
|
@@ -18564,7 +18646,7 @@ var CONVERSATION_PARENT_LABELS = /* @__PURE__ */ new Set(["AdminConversation", "
|
|
|
18564
18646
|
var app23 = new Hono();
|
|
18565
18647
|
app23.get("/", requireAdminSession, async (c) => {
|
|
18566
18648
|
const cacheKey = c.var.cacheKey;
|
|
18567
|
-
const
|
|
18649
|
+
const q2 = (c.req.query("q") ?? "").trim();
|
|
18568
18650
|
const rawLimit = c.req.query("limit");
|
|
18569
18651
|
const rawLabels = c.req.query("labels");
|
|
18570
18652
|
const rawThreshold = c.req.query("threshold");
|
|
@@ -18573,13 +18655,13 @@ app23.get("/", requireAdminSession, async (c) => {
|
|
|
18573
18655
|
console.error(`[graph-search] auth-rejected endpoint="/api/admin/graph-search" reason="no account for session"`);
|
|
18574
18656
|
return c.json({ error: "Account not found for session" }, 401);
|
|
18575
18657
|
}
|
|
18576
|
-
if (!
|
|
18658
|
+
if (!q2) return c.json({ error: "q (search query) required" }, 400);
|
|
18577
18659
|
const labels = rawLabels ? rawLabels.split(",").map((s) => s.trim()).filter((s) => s.length > 0) : [];
|
|
18578
18660
|
const wildcard = labels.includes("*");
|
|
18579
18661
|
const noChips = labels.length === 0;
|
|
18580
18662
|
const allLabels = wildcard || noChips;
|
|
18581
18663
|
if (noChips && !wildcard) {
|
|
18582
|
-
console.error(`[graph-search] op=labels-default applied=all query="${
|
|
18664
|
+
console.error(`[graph-search] op=labels-default applied=all query="${q2}"`);
|
|
18583
18665
|
}
|
|
18584
18666
|
const parsedLimit = rawLimit ? parseInt(rawLimit, 10) : DEFAULT_LIMIT;
|
|
18585
18667
|
const limit = Number.isFinite(parsedLimit) && parsedLimit > 0 ? Math.min(parsedLimit, MAX_LIMIT) : DEFAULT_LIMIT;
|
|
@@ -18599,7 +18681,7 @@ app23.get("/", requireAdminSession, async (c) => {
|
|
|
18599
18681
|
const session = getSession();
|
|
18600
18682
|
try {
|
|
18601
18683
|
const res = await hybrid(session, embed, {
|
|
18602
|
-
query:
|
|
18684
|
+
query: q2,
|
|
18603
18685
|
accountId,
|
|
18604
18686
|
labels: forwardedLabels,
|
|
18605
18687
|
limit,
|
|
@@ -18609,7 +18691,7 @@ app23.get("/", requireAdminSession, async (c) => {
|
|
|
18609
18691
|
const total = Date.now() - started;
|
|
18610
18692
|
if (res.embedError) {
|
|
18611
18693
|
console.error(`[graph-search] embed-unavailable err="${res.embedError}" \u2014 bm25-only`);
|
|
18612
|
-
console.error(`[graph-search] embed-degraded query="${
|
|
18694
|
+
console.error(`[graph-search] embed-degraded query="${q2}" results=${res.results.length}`);
|
|
18613
18695
|
}
|
|
18614
18696
|
let labelMatchCount = 0;
|
|
18615
18697
|
let bodyFulltextCount = 0;
|
|
@@ -18660,16 +18742,16 @@ app23.get("/", requireAdminSession, async (c) => {
|
|
|
18660
18742
|
}
|
|
18661
18743
|
resolvedResults = Array.from(seen.values()).sort((a, b) => b.score - a.score);
|
|
18662
18744
|
console.error(
|
|
18663
|
-
`[graph-search] label-match query="${
|
|
18745
|
+
`[graph-search] label-match query="${q2}" hits=${labelMatchCount}`
|
|
18664
18746
|
);
|
|
18665
18747
|
console.error(
|
|
18666
|
-
`[graph-search] body-fulltext query="${
|
|
18748
|
+
`[graph-search] body-fulltext query="${q2}" hits=${bodyFulltextCount} ms=${total}`
|
|
18667
18749
|
);
|
|
18668
18750
|
}
|
|
18669
18751
|
const labelsToken = wildcard ? "*" : noChips ? "all" : labels.join(",");
|
|
18670
18752
|
const expandedFlag = !wildcard && wantsBodyFulltext ? 1 : 0;
|
|
18671
18753
|
console.error(
|
|
18672
|
-
`[graph-search] query="${
|
|
18754
|
+
`[graph-search] query="${q2}" labels=${labelsToken} expanded=${expandedFlag} limit=${limit} mode=${res.mode} raw-results=${res.results.length} resolved-results=${resolvedResults.length} expand-ms=${res.expandMs} total-ms=${total}`
|
|
18673
18755
|
);
|
|
18674
18756
|
console.error(
|
|
18675
18757
|
`[graph-search] threshold=${res.threshold ?? "off"} raw-merged=${res.rawMerged} suppressed=${res.suppressed} rendered=${res.results.length} bm25-bypass=${res.bm25Bypass}`
|
|
@@ -18692,7 +18774,7 @@ app23.get("/", requireAdminSession, async (c) => {
|
|
|
18692
18774
|
} catch (err) {
|
|
18693
18775
|
const elapsed = Date.now() - started;
|
|
18694
18776
|
const message = err instanceof Error ? err.message : String(err);
|
|
18695
|
-
console.error(`[graph-search] neo4j-unreachable query="${
|
|
18777
|
+
console.error(`[graph-search] neo4j-unreachable query="${q2}" ms=${elapsed} err="${message}"`);
|
|
18696
18778
|
return c.json({ error: `Graph search unavailable: ${message}` }, 503);
|
|
18697
18779
|
} finally {
|
|
18698
18780
|
await session.close();
|
|
@@ -18759,7 +18841,11 @@ var FILTER_TOP_LEVEL_LABELS = Object.freeze(
|
|
|
18759
18841
|
"Risk",
|
|
18760
18842
|
"Source",
|
|
18761
18843
|
"Finding",
|
|
18762
|
-
"Hypothesis"
|
|
18844
|
+
"Hypothesis",
|
|
18845
|
+
// Base-schema ledger (schema-base.md, Task 1819). A cash movement
|
|
18846
|
+
// attached to no invoice — its only edges are outbound, so nothing
|
|
18847
|
+
// drills down to it and a chip is the sole way to reach it.
|
|
18848
|
+
"CashEntry"
|
|
18763
18849
|
])
|
|
18764
18850
|
);
|
|
18765
18851
|
var AGENT_ACTION_LABELS = Object.freeze(
|
|
@@ -18810,6 +18896,17 @@ var STATIC_TOP_LEVEL_LABELS = Object.freeze(
|
|
|
18810
18896
|
"Review",
|
|
18811
18897
|
"ImageObject",
|
|
18812
18898
|
"Invoice",
|
|
18899
|
+
// Supplier invoice. Declared in schema-base.md's `## Node Types` table
|
|
18900
|
+
// (promoted there from schema-construction.md's top-level table by
|
|
18901
|
+
// 7b225f814), which this module never parses — so it is static, not
|
|
18902
|
+
// derived. Top-level because it is operator-entry with a natural key
|
|
18903
|
+
// (accountId, supplier, confirmationNumber), unlike its InvoiceLine /
|
|
18904
|
+
// InvoicePayment / Credit children.
|
|
18905
|
+
"InboundInvoice",
|
|
18906
|
+
// Ledger cash movement, declared in schema-base.md. Operator-entry with
|
|
18907
|
+
// a natural key (accountId, entryId) and no parent label, so it is
|
|
18908
|
+
// reachable only as a chip.
|
|
18909
|
+
"CashEntry",
|
|
18813
18910
|
"Task",
|
|
18814
18911
|
"Project",
|
|
18815
18912
|
"Event",
|
|
@@ -18860,8 +18957,8 @@ function parseTopLevelLabels(content) {
|
|
|
18860
18957
|
const line = lines[i];
|
|
18861
18958
|
if (line.startsWith("## ")) return [];
|
|
18862
18959
|
if (!line.includes("|")) continue;
|
|
18863
|
-
const
|
|
18864
|
-
if (!/^\s*\|?[\s:|-]+\|?\s*$/.test(
|
|
18960
|
+
const sep10 = lines[i + 1];
|
|
18961
|
+
if (!/^\s*\|?[\s:|-]+\|?\s*$/.test(sep10) || !sep10.includes("-")) continue;
|
|
18865
18962
|
const headers = splitPipeRow(line).map((h) => stripBackticks(h).toLowerCase());
|
|
18866
18963
|
const labelCol = headers.findIndex((h) => h === "neo4j label");
|
|
18867
18964
|
if (labelCol === -1) return [];
|
|
@@ -19112,7 +19209,7 @@ async function handleNeighbourhood(c, accountId) {
|
|
|
19112
19209
|
}
|
|
19113
19210
|
const includeAgentActions = c.req.query("includeAgentActions") === "1";
|
|
19114
19211
|
const agentActionLabels = includeAgentActions ? [] : [...AGENT_ACTION_LABELS];
|
|
19115
|
-
const
|
|
19212
|
+
const q2 = (c.req.query("q") ?? "").trim();
|
|
19116
19213
|
const rawLimit = c.req.query("limit");
|
|
19117
19214
|
const parsedLimit = rawLimit ? parseInt(rawLimit, 10) : NEIGHBOURHOOD_SEARCH_DEFAULT_LIMIT;
|
|
19118
19215
|
const searchLimit = Number.isFinite(parsedLimit) && parsedLimit > 0 ? Math.min(parsedLimit, NEIGHBOURHOOD_SEARCH_MAX_LIMIT) : NEIGHBOURHOOD_SEARCH_DEFAULT_LIMIT;
|
|
@@ -19120,9 +19217,9 @@ async function handleNeighbourhood(c, accountId) {
|
|
|
19120
19217
|
const session = getSession();
|
|
19121
19218
|
try {
|
|
19122
19219
|
let allowedIds = null;
|
|
19123
|
-
if (
|
|
19220
|
+
if (q2) {
|
|
19124
19221
|
const searchRes = await hybrid(session, embed, {
|
|
19125
|
-
query:
|
|
19222
|
+
query: q2,
|
|
19126
19223
|
accountId,
|
|
19127
19224
|
labels: [],
|
|
19128
19225
|
limit: searchLimit,
|
|
@@ -19205,7 +19302,7 @@ async function handleNeighbourhood(c, accountId) {
|
|
|
19205
19302
|
}
|
|
19206
19303
|
if (allowedIds !== null) {
|
|
19207
19304
|
console.error(
|
|
19208
|
-
`[graph-page] neighbourhood-search-intersect q="${
|
|
19305
|
+
`[graph-page] neighbourhood-search-intersect q="${q2}" allowed=${allowedIds.length} root=${elementId} rendered=${nodes.length}`
|
|
19209
19306
|
);
|
|
19210
19307
|
}
|
|
19211
19308
|
return c.json({ nodes, edges });
|
|
@@ -20546,18 +20643,68 @@ var session_rc_spawn_default = app35;
|
|
|
20546
20643
|
|
|
20547
20644
|
// server/routes/admin/session-reseat.ts
|
|
20548
20645
|
import { randomUUID as randomUUID12 } from "crypto";
|
|
20549
|
-
|
|
20646
|
+
|
|
20647
|
+
// server/channel-session-override.ts
|
|
20648
|
+
import { readFileSync as readFileSync34, writeFileSync as writeFileSync12, renameSync as renameSync9 } from "fs";
|
|
20649
|
+
import { join as join35 } from "path";
|
|
20650
|
+
var FILE2 = "channel-session-override.json";
|
|
20651
|
+
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}$/;
|
|
20652
|
+
function channelOverridePath(accountDir) {
|
|
20653
|
+
return join35(accountDir, FILE2);
|
|
20654
|
+
}
|
|
20655
|
+
function senderKey(channel, senderId) {
|
|
20656
|
+
return `${channel}:${senderId}`;
|
|
20657
|
+
}
|
|
20658
|
+
function readRaw2(accountDir) {
|
|
20659
|
+
try {
|
|
20660
|
+
return JSON.parse(readFileSync34(channelOverridePath(accountDir), "utf8"));
|
|
20661
|
+
} catch {
|
|
20662
|
+
return null;
|
|
20663
|
+
}
|
|
20664
|
+
}
|
|
20665
|
+
function readChannelOverrideId(accountDir, channel, senderId) {
|
|
20666
|
+
const raw = readRaw2(accountDir);
|
|
20667
|
+
if (!raw || !raw.bySender || typeof raw.bySender !== "object") return null;
|
|
20668
|
+
const v = raw.bySender[senderKey(channel, senderId)];
|
|
20669
|
+
return typeof v === "string" && UUID2.test(v) ? v : null;
|
|
20670
|
+
}
|
|
20671
|
+
function writeChannelOverrideId(accountDir, channel, senderId, sessionId) {
|
|
20672
|
+
const raw = readRaw2(accountDir);
|
|
20673
|
+
const bySender = {};
|
|
20674
|
+
if (raw?.bySender && typeof raw.bySender === "object") {
|
|
20675
|
+
for (const [k, v] of Object.entries(raw.bySender)) {
|
|
20676
|
+
if (typeof v === "string") bySender[k] = v;
|
|
20677
|
+
}
|
|
20678
|
+
}
|
|
20679
|
+
bySender[senderKey(channel, senderId)] = sessionId;
|
|
20680
|
+
const path3 = channelOverridePath(accountDir);
|
|
20681
|
+
const tmp = `${path3}.${process.pid}.tmp`;
|
|
20682
|
+
writeFileSync12(tmp, JSON.stringify({ bySender }, null, 2), "utf8");
|
|
20683
|
+
renameSync9(tmp, path3);
|
|
20684
|
+
}
|
|
20685
|
+
|
|
20686
|
+
// server/routes/admin/session-reseat.ts
|
|
20687
|
+
function isAdminChannelSource(source) {
|
|
20688
|
+
return !!source && source.role === "admin" && (source.channel === "whatsapp" || source.channel === "telegram") && typeof source.senderId === "string" && source.senderId.length > 0;
|
|
20689
|
+
}
|
|
20690
|
+
function resolveReseatPlan(body, mintId = randomUUID12, adminUserId, authenticatedName, source) {
|
|
20550
20691
|
const sessionId = mintId();
|
|
20551
20692
|
const key = `session:${sessionId}`;
|
|
20552
20693
|
const payload = {
|
|
20553
20694
|
sessionId,
|
|
20554
20695
|
forkFromSessionId: body.fromSessionId,
|
|
20555
20696
|
model: body.model,
|
|
20556
|
-
role: "admin"
|
|
20557
|
-
channel: "webchat",
|
|
20558
|
-
senderId: key,
|
|
20559
|
-
webchatChannel: webchatChannelDescriptor(key)
|
|
20697
|
+
role: "admin"
|
|
20560
20698
|
};
|
|
20699
|
+
if (isAdminChannelSource(source)) {
|
|
20700
|
+
payload.channel = source.channel;
|
|
20701
|
+
payload.senderId = source.senderId;
|
|
20702
|
+
if (source.accountId) payload.targetAccountId = source.accountId;
|
|
20703
|
+
} else {
|
|
20704
|
+
payload.channel = "webchat";
|
|
20705
|
+
payload.senderId = key;
|
|
20706
|
+
payload.webchatChannel = webchatChannelDescriptor(key);
|
|
20707
|
+
}
|
|
20561
20708
|
if (body.name) payload.name = body.name;
|
|
20562
20709
|
if (body.permissionMode) payload.permissionMode = body.permissionMode;
|
|
20563
20710
|
if (body.effort) payload.effort = body.effort;
|
|
@@ -20598,14 +20745,18 @@ app36.post("/", requireAdminSession, async (c) => {
|
|
|
20598
20745
|
}
|
|
20599
20746
|
const accountId = cacheKey ? getAccountIdForSession(cacheKey) : void 0;
|
|
20600
20747
|
const authenticatedName = accountId ? await resolveAuthenticatedName(accountId, adminUserId) : void 0;
|
|
20748
|
+
const sourceMeta = readSourceRowMeta(fromSessionId);
|
|
20749
|
+
const source = sourceMeta ? { channel: sourceMeta.channel, senderId: sourceMeta.senderId, role: sourceMeta.role, accountId: sourceMeta.accountId } : null;
|
|
20601
20750
|
const plan = resolveReseatPlan(
|
|
20602
20751
|
{ fromSessionId, model, ...permissionMode ? { permissionMode } : {}, ...effort ? { effort } : {} },
|
|
20603
20752
|
randomUUID12,
|
|
20604
20753
|
adminUserId,
|
|
20605
|
-
authenticatedName
|
|
20754
|
+
authenticatedName,
|
|
20755
|
+
source
|
|
20606
20756
|
);
|
|
20757
|
+
const isChannelFork = isAdminChannelSource(source);
|
|
20607
20758
|
const reseatReqId = randomUUID12();
|
|
20608
|
-
console.log(`[chat-reseat] op=request reseatReqId=${reseatReqId} from=${fromSessionId} to-model=${model} to-mode=${permissionMode ?? "none"} to-effort=${effort ?? "none"} new=${plan.sessionId}`);
|
|
20759
|
+
console.log(`[chat-reseat] op=request reseatReqId=${reseatReqId} from=${fromSessionId} from-channel=${source?.channel ?? "none"} from-sender=${source?.senderId ?? "none"} to-model=${model} to-mode=${permissionMode ?? "none"} to-effort=${effort ?? "none"} new=${plan.sessionId}`);
|
|
20609
20760
|
let res;
|
|
20610
20761
|
try {
|
|
20611
20762
|
res = await fetch(`${managerBase("session-reseat:wrapper")}/rc-spawn`, {
|
|
@@ -20645,6 +20796,23 @@ app36.post("/", requireAdminSession, async (c) => {
|
|
|
20645
20796
|
} catch (err) {
|
|
20646
20797
|
console.error(`[session-reseat] canonical-forward-failed err=${err instanceof Error ? err.message : String(err)}`);
|
|
20647
20798
|
}
|
|
20799
|
+
if (isChannelFork) {
|
|
20800
|
+
const channel = plan.payload.channel;
|
|
20801
|
+
const senderId = plan.payload.senderId;
|
|
20802
|
+
try {
|
|
20803
|
+
const provenance = source?.accountId ? "source-account" : "boot-account-fallback";
|
|
20804
|
+
const dir = source?.accountId ? listValidAccounts().find((a) => a.accountId === source.accountId)?.accountDir ?? null : resolveAccount()?.accountDir ?? null;
|
|
20805
|
+
if (dir) {
|
|
20806
|
+
writeChannelOverrideId(dir, channel, senderId, plan.sessionId);
|
|
20807
|
+
console.log(`[chat-reseat] op=channel-forward reseatReqId=${reseatReqId} senderId=${senderId} from=${fromSessionId} to=${plan.sessionId} keyedOn=${provenance}`);
|
|
20808
|
+
} else {
|
|
20809
|
+
console.error(`[session-reseat] channel-forward-failed reason=no-account-dir accountId=${source?.accountId ?? "none"} senderId=${senderId}`);
|
|
20810
|
+
}
|
|
20811
|
+
} catch (err) {
|
|
20812
|
+
console.error(`[session-reseat] channel-forward-failed err=${err instanceof Error ? err.message : String(err)} senderId=${senderId}`);
|
|
20813
|
+
}
|
|
20814
|
+
console.log(`[chat-reseat] op=channel-fork-unbound reseatReqId=${reseatReqId} senderId=${senderId} session=${plan.sessionId} note=binds-on-next-inbound`);
|
|
20815
|
+
}
|
|
20648
20816
|
const target = `/chat?session=${plan.sessionId}`;
|
|
20649
20817
|
console.log(`[chat-reseat] op=acquired reseatReqId=${reseatReqId} pid=${parsed?.spawnedPid ?? "none"} new=${plan.sessionId}`);
|
|
20650
20818
|
console.log(`[chat-reseat] op=navigate reseatReqId=${reseatReqId} target=${target}`);
|
|
@@ -20779,14 +20947,14 @@ app37.get("/", async (c) => {
|
|
|
20779
20947
|
var system_stats_default = app37;
|
|
20780
20948
|
|
|
20781
20949
|
// server/routes/admin/health.ts
|
|
20782
|
-
import { existsSync as existsSync31, readFileSync as
|
|
20783
|
-
import { resolve as resolve26, join as
|
|
20950
|
+
import { existsSync as existsSync31, readFileSync as readFileSync35 } from "fs";
|
|
20951
|
+
import { resolve as resolve26, join as join36 } from "path";
|
|
20784
20952
|
var PLATFORM_ROOT6 = process.env.MAXY_PLATFORM_ROOT ?? resolve26(process.cwd(), "..");
|
|
20785
20953
|
var brandHostname = "maxy";
|
|
20786
|
-
var brandJsonPath =
|
|
20954
|
+
var brandJsonPath = join36(PLATFORM_ROOT6, "config", "brand.json");
|
|
20787
20955
|
if (existsSync31(brandJsonPath)) {
|
|
20788
20956
|
try {
|
|
20789
|
-
const brand = JSON.parse(
|
|
20957
|
+
const brand = JSON.parse(readFileSync35(brandJsonPath, "utf-8"));
|
|
20790
20958
|
if (brand.hostname) brandHostname = brand.hostname;
|
|
20791
20959
|
} catch {
|
|
20792
20960
|
}
|
|
@@ -20796,7 +20964,7 @@ var PROCESS_STARTED_AT = (/* @__PURE__ */ new Date()).toISOString();
|
|
|
20796
20964
|
var PROBE_TIMEOUT_MS = 1e3;
|
|
20797
20965
|
function readVersion() {
|
|
20798
20966
|
if (!existsSync31(VERSION_FILE)) return "unknown";
|
|
20799
|
-
return
|
|
20967
|
+
return readFileSync35(VERSION_FILE, "utf-8").trim() || "unknown";
|
|
20800
20968
|
}
|
|
20801
20969
|
async function probeConversationDb() {
|
|
20802
20970
|
let session;
|
|
@@ -20976,7 +21144,7 @@ function managerLogFollowUrl(sessionId, opts) {
|
|
|
20976
21144
|
|
|
20977
21145
|
// server/routes/admin/linkedin-ingest.ts
|
|
20978
21146
|
var TAG31 = "[linkedin-ingest-route]";
|
|
20979
|
-
var
|
|
21147
|
+
var UUID3 = /^[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}$/;
|
|
20980
21148
|
var ISO = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,3})?(?:Z|[+-]\d{2}:\d{2})$/;
|
|
20981
21149
|
var LINKEDIN_URL = /^https:\/\/www\.linkedin\.com\//;
|
|
20982
21150
|
function rejectSchema(reason, missing = []) {
|
|
@@ -20990,7 +21158,7 @@ function validate(body) {
|
|
|
20990
21158
|
if (b.kind !== "profile" && b.kind !== "thread") missing.push("kind");
|
|
20991
21159
|
if (typeof b.capturedAt !== "string" || !ISO.test(b.capturedAt)) missing.push("capturedAt");
|
|
20992
21160
|
if (typeof b.pageUrl !== "string" || !LINKEDIN_URL.test(b.pageUrl)) missing.push("pageUrl");
|
|
20993
|
-
if (typeof b.dispatchId !== "string" || !
|
|
21161
|
+
if (typeof b.dispatchId !== "string" || !UUID3.test(b.dispatchId)) missing.push("dispatchId");
|
|
20994
21162
|
if (missing.length) return rejectSchema("envelope", missing);
|
|
20995
21163
|
if (b.kind === "profile") {
|
|
20996
21164
|
const p = b.profile;
|
|
@@ -21573,7 +21741,7 @@ var browser_default = app45;
|
|
|
21573
21741
|
|
|
21574
21742
|
// server/routes/admin/calendar.ts
|
|
21575
21743
|
import { existsSync as existsSync32 } from "fs";
|
|
21576
|
-
import { join as
|
|
21744
|
+
import { join as join38 } from "path";
|
|
21577
21745
|
|
|
21578
21746
|
// server/lib/calendar-ics.ts
|
|
21579
21747
|
var CRLF = "\r\n";
|
|
@@ -21672,8 +21840,8 @@ function countVEvents(ics) {
|
|
|
21672
21840
|
}
|
|
21673
21841
|
|
|
21674
21842
|
// server/lib/calendar-availability.ts
|
|
21675
|
-
import { readFileSync as
|
|
21676
|
-
import { join as
|
|
21843
|
+
import { readFileSync as readFileSync36 } from "fs";
|
|
21844
|
+
import { join as join37 } from "path";
|
|
21677
21845
|
var AVAILABILITY_FILENAME = "calendar-availability.json";
|
|
21678
21846
|
var REQUIRED_KEYS = [
|
|
21679
21847
|
"timezone",
|
|
@@ -21682,10 +21850,10 @@ var REQUIRED_KEYS = [
|
|
|
21682
21850
|
"weekly"
|
|
21683
21851
|
];
|
|
21684
21852
|
function readAvailabilityConfig(accountDir) {
|
|
21685
|
-
const path3 =
|
|
21853
|
+
const path3 = join37(accountDir, AVAILABILITY_FILENAME);
|
|
21686
21854
|
let raw;
|
|
21687
21855
|
try {
|
|
21688
|
-
raw =
|
|
21856
|
+
raw = readFileSync36(path3, "utf-8");
|
|
21689
21857
|
} catch {
|
|
21690
21858
|
throw new Error(`availability not configured: ${path3} not found`);
|
|
21691
21859
|
}
|
|
@@ -22044,7 +22212,7 @@ app46.get("/booking-link", requireAdminSession, (c) => {
|
|
|
22044
22212
|
console.error('[calendar] op=booking-link-fail err="no account configured"');
|
|
22045
22213
|
return c.json({ bookingDomain: null });
|
|
22046
22214
|
}
|
|
22047
|
-
if (!existsSync32(
|
|
22215
|
+
if (!existsSync32(join38(account.accountDir, AVAILABILITY_FILENAME))) {
|
|
22048
22216
|
console.log("[calendar] op=booking-link bookingDomain=none source=availability-config");
|
|
22049
22217
|
return c.json({ bookingDomain: null });
|
|
22050
22218
|
}
|
|
@@ -22847,7 +23015,7 @@ app56.route("/request-magic-link", request_magic_link_default);
|
|
|
22847
23015
|
var access_default = app56;
|
|
22848
23016
|
|
|
22849
23017
|
// server/routes/sites.ts
|
|
22850
|
-
import { existsSync as existsSync33, readFileSync as
|
|
23018
|
+
import { existsSync as existsSync33, readFileSync as readFileSync37, realpathSync as realpathSync7, statSync as statSync14 } from "fs";
|
|
22851
23019
|
import { resolve as resolve28 } from "path";
|
|
22852
23020
|
var SAFE_SEG_RE = /^[a-z0-9_][a-z0-9_.-]{0,99}$/i;
|
|
22853
23021
|
var MIME = {
|
|
@@ -22911,13 +23079,13 @@ app57.get("/:rel{.*}", (c) => {
|
|
|
22911
23079
|
console.error(`[sites] path-traversal-rejected path=${reqPath} reason=escape status=403`);
|
|
22912
23080
|
return c.text("Forbidden", 403);
|
|
22913
23081
|
}
|
|
22914
|
-
let
|
|
23082
|
+
let stat9;
|
|
22915
23083
|
try {
|
|
22916
|
-
|
|
23084
|
+
stat9 = existsSync33(filePath) ? statSync14(filePath) : null;
|
|
22917
23085
|
} catch {
|
|
22918
|
-
|
|
23086
|
+
stat9 = null;
|
|
22919
23087
|
}
|
|
22920
|
-
if (
|
|
23088
|
+
if (stat9?.isDirectory() && !reqPath.endsWith("/")) {
|
|
22921
23089
|
const search = new URL(c.req.url).search;
|
|
22922
23090
|
const target = `${reqPath}/${search}`;
|
|
22923
23091
|
console.log(
|
|
@@ -22925,7 +23093,7 @@ app57.get("/:rel{.*}", (c) => {
|
|
|
22925
23093
|
);
|
|
22926
23094
|
return c.redirect(target, 301);
|
|
22927
23095
|
}
|
|
22928
|
-
if (
|
|
23096
|
+
if (stat9?.isDirectory()) {
|
|
22929
23097
|
filePath = resolve28(filePath, "index.html");
|
|
22930
23098
|
}
|
|
22931
23099
|
if (!filePath.startsWith(rootDir + "/")) {
|
|
@@ -22951,7 +23119,7 @@ app57.get("/:rel{.*}", (c) => {
|
|
|
22951
23119
|
}
|
|
22952
23120
|
let body;
|
|
22953
23121
|
try {
|
|
22954
|
-
body =
|
|
23122
|
+
body = readFileSync37(realPath);
|
|
22955
23123
|
} catch (err) {
|
|
22956
23124
|
const code = err?.code;
|
|
22957
23125
|
if (code === "EISDIR") {
|
|
@@ -22987,7 +23155,7 @@ var sites_default = app57;
|
|
|
22987
23155
|
|
|
22988
23156
|
// app/lib/visitor-token.ts
|
|
22989
23157
|
import { createHmac, randomBytes, timingSafeEqual } from "crypto";
|
|
22990
|
-
import { mkdirSync as mkdirSync8, readFileSync as
|
|
23158
|
+
import { mkdirSync as mkdirSync8, readFileSync as readFileSync38, writeFileSync as writeFileSync13 } from "fs";
|
|
22991
23159
|
import { dirname as dirname12 } from "path";
|
|
22992
23160
|
var TOKEN_PREFIX = "v1.";
|
|
22993
23161
|
var SECRET_BYTES = 32;
|
|
@@ -22996,7 +23164,7 @@ var cachedSecret = null;
|
|
|
22996
23164
|
function getSecret() {
|
|
22997
23165
|
if (cachedSecret) return cachedSecret;
|
|
22998
23166
|
try {
|
|
22999
|
-
const hex2 =
|
|
23167
|
+
const hex2 = readFileSync38(VISITOR_TOKEN_SECRET_FILE, "utf-8").trim();
|
|
23000
23168
|
if (hex2.length === SECRET_BYTES * 2) {
|
|
23001
23169
|
cachedSecret = Buffer.from(hex2, "hex");
|
|
23002
23170
|
return cachedSecret;
|
|
@@ -23006,11 +23174,11 @@ function getSecret() {
|
|
|
23006
23174
|
const fresh = randomBytes(SECRET_BYTES).toString("hex");
|
|
23007
23175
|
try {
|
|
23008
23176
|
mkdirSync8(dirname12(VISITOR_TOKEN_SECRET_FILE), { recursive: true, mode: 448 });
|
|
23009
|
-
|
|
23177
|
+
writeFileSync13(VISITOR_TOKEN_SECRET_FILE, fresh, { mode: 384, flag: "wx" });
|
|
23010
23178
|
console.log(`[visitor-token] secret minted path=${VISITOR_TOKEN_SECRET_FILE}`);
|
|
23011
23179
|
} catch {
|
|
23012
23180
|
}
|
|
23013
|
-
const hex =
|
|
23181
|
+
const hex = readFileSync38(VISITOR_TOKEN_SECRET_FILE, "utf-8").trim();
|
|
23014
23182
|
cachedSecret = Buffer.from(hex, "hex");
|
|
23015
23183
|
return cachedSecret;
|
|
23016
23184
|
}
|
|
@@ -23063,8 +23231,8 @@ var VISITOR_COOKIE_NAME = "mxy_v";
|
|
|
23063
23231
|
var VISITOR_COOKIE_MAX_AGE_SECONDS = Math.floor(DEFAULT_TTL_MS / 1e3);
|
|
23064
23232
|
|
|
23065
23233
|
// app/lib/brand-config.ts
|
|
23066
|
-
import { existsSync as existsSync34, readFileSync as
|
|
23067
|
-
import { join as
|
|
23234
|
+
import { existsSync as existsSync34, readFileSync as readFileSync39 } from "fs";
|
|
23235
|
+
import { join as join39 } from "path";
|
|
23068
23236
|
var cached2 = null;
|
|
23069
23237
|
var cachedAttempted = false;
|
|
23070
23238
|
function readBrandConfig() {
|
|
@@ -23072,10 +23240,10 @@ function readBrandConfig() {
|
|
|
23072
23240
|
cachedAttempted = true;
|
|
23073
23241
|
const platformRoot5 = process.env.MAXY_PLATFORM_ROOT;
|
|
23074
23242
|
if (!platformRoot5) return null;
|
|
23075
|
-
const brandPath =
|
|
23243
|
+
const brandPath = join39(platformRoot5, "config", "brand.json");
|
|
23076
23244
|
if (!existsSync34(brandPath)) return null;
|
|
23077
23245
|
try {
|
|
23078
|
-
cached2 = JSON.parse(
|
|
23246
|
+
cached2 = JSON.parse(readFileSync39(brandPath, "utf-8"));
|
|
23079
23247
|
return cached2;
|
|
23080
23248
|
} catch {
|
|
23081
23249
|
return null;
|
|
@@ -23209,8 +23377,8 @@ function appendConsentParams(pageUrl, token) {
|
|
|
23209
23377
|
const hashIdx = pageUrl.indexOf("#");
|
|
23210
23378
|
const hash = hashIdx >= 0 ? pageUrl.slice(hashIdx) : "";
|
|
23211
23379
|
const base = hashIdx >= 0 ? pageUrl.slice(0, hashIdx) : pageUrl;
|
|
23212
|
-
const
|
|
23213
|
-
return base +
|
|
23380
|
+
const sep10 = base.indexOf("?") >= 0 ? "&" : "?";
|
|
23381
|
+
return base + sep10 + `consent=needed&v=${encodeURIComponent(token)}` + hash;
|
|
23214
23382
|
}
|
|
23215
23383
|
}
|
|
23216
23384
|
var SAFE_SLUG_RE = /^[a-z0-9](?:[a-z0-9-]{0,118}[a-z0-9])?$/;
|
|
@@ -23573,13 +23741,13 @@ var visitor_event_default = app60;
|
|
|
23573
23741
|
|
|
23574
23742
|
// server/routes/session.ts
|
|
23575
23743
|
import { resolve as resolve29 } from "path";
|
|
23576
|
-
import { existsSync as existsSync35, writeFileSync as
|
|
23744
|
+
import { existsSync as existsSync35, writeFileSync as writeFileSync14, mkdirSync as mkdirSync9 } from "fs";
|
|
23577
23745
|
var UUID_RE5 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
23578
23746
|
function writeBrandingCache(accountId, agentSlug, branding) {
|
|
23579
23747
|
try {
|
|
23580
23748
|
const cacheDir = resolve29(MAXY_DIR, "branding-cache", accountId);
|
|
23581
23749
|
mkdirSync9(cacheDir, { recursive: true });
|
|
23582
|
-
|
|
23750
|
+
writeFileSync14(resolve29(cacheDir, `${agentSlug}.json`), JSON.stringify(branding), "utf-8");
|
|
23583
23751
|
} catch (err) {
|
|
23584
23752
|
console.error(`[branding] cache write failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
23585
23753
|
}
|
|
@@ -23904,8 +24072,205 @@ app62.get("/free-busy", async (c) => {
|
|
|
23904
24072
|
});
|
|
23905
24073
|
var calendar_public_default = app62;
|
|
23906
24074
|
|
|
24075
|
+
// server/routes/portal-fetch.ts
|
|
24076
|
+
import { createReadStream as createReadStream3 } from "fs";
|
|
24077
|
+
import { stat as stat6, readFile as readFile6, realpath } from "fs/promises";
|
|
24078
|
+
import { join as join40, resolve as resolve30, sep as sep8 } from "path";
|
|
24079
|
+
import { Readable as Readable3 } from "stream";
|
|
24080
|
+
|
|
24081
|
+
// ../plugins/cloudflare/bin/schema-exposed-dirs.mjs
|
|
24082
|
+
var ONT_START = "<!-- ontology-buckets:start -->";
|
|
24083
|
+
var ONT_END = "<!-- ontology-buckets:end -->";
|
|
24084
|
+
var OWNED_START = "<!-- plugin-owned-dirs:start -->";
|
|
24085
|
+
var OWNED_END = "<!-- plugin-owned-dirs:end -->";
|
|
24086
|
+
var ALWAYS_EXPOSED = "output";
|
|
24087
|
+
var DOMAIN_LINE = /^- `([^`/]+)\/` - one folder per /;
|
|
24088
|
+
var OWNED_LINE = /^- `([^`/]+)\/` — /;
|
|
24089
|
+
function region(text, start, end) {
|
|
24090
|
+
const i = text.indexOf(start);
|
|
24091
|
+
if (i === -1) return null;
|
|
24092
|
+
const j = text.indexOf(end, i);
|
|
24093
|
+
return text.slice(i, j === -1 ? text.length : j);
|
|
24094
|
+
}
|
|
24095
|
+
function allowedBlockOrNull(text) {
|
|
24096
|
+
const lines = text.split("\n");
|
|
24097
|
+
const start = lines.findIndex((l) => l.trim() === "```allowed-top-level");
|
|
24098
|
+
if (start === -1) return null;
|
|
24099
|
+
for (let j = start + 1; j < lines.length; j++) {
|
|
24100
|
+
if (lines[j].trim() === "```") {
|
|
24101
|
+
return lines.slice(start + 1, j).map((l) => l.trim()).filter(Boolean);
|
|
24102
|
+
}
|
|
24103
|
+
}
|
|
24104
|
+
return null;
|
|
24105
|
+
}
|
|
24106
|
+
function resolveExposedDirs(schemaText) {
|
|
24107
|
+
const empty = {
|
|
24108
|
+
schemaPresent: false,
|
|
24109
|
+
allowed: (
|
|
24110
|
+
/** @type {string[]} */
|
|
24111
|
+
[]
|
|
24112
|
+
),
|
|
24113
|
+
domainBuckets: (
|
|
24114
|
+
/** @type {string[]} */
|
|
24115
|
+
[]
|
|
24116
|
+
),
|
|
24117
|
+
pluginOwned: (
|
|
24118
|
+
/** @type {string[]} */
|
|
24119
|
+
[]
|
|
24120
|
+
),
|
|
24121
|
+
collisions: (
|
|
24122
|
+
/** @type {string[]} */
|
|
24123
|
+
[]
|
|
24124
|
+
),
|
|
24125
|
+
exposed: (
|
|
24126
|
+
/** @type {string[]} */
|
|
24127
|
+
[]
|
|
24128
|
+
)
|
|
24129
|
+
};
|
|
24130
|
+
if (typeof schemaText !== "string" || schemaText.length === 0) return empty;
|
|
24131
|
+
const allowed = allowedBlockOrNull(schemaText);
|
|
24132
|
+
if (allowed === null) return { ...empty, schemaPresent: true };
|
|
24133
|
+
const ont = region(schemaText, ONT_START, ONT_END);
|
|
24134
|
+
const owned = region(schemaText, OWNED_START, OWNED_END);
|
|
24135
|
+
const dirsIn = (text, re) => {
|
|
24136
|
+
if (!text) return [];
|
|
24137
|
+
const out = [];
|
|
24138
|
+
for (const line of text.split("\n")) {
|
|
24139
|
+
const m = re.exec(line.trim());
|
|
24140
|
+
if (m && !out.includes(m[1])) out.push(m[1]);
|
|
24141
|
+
}
|
|
24142
|
+
return out;
|
|
24143
|
+
};
|
|
24144
|
+
const domainBuckets = dirsIn(ont, DOMAIN_LINE);
|
|
24145
|
+
const pluginOwned = dirsIn(owned, OWNED_LINE);
|
|
24146
|
+
const collisions = domainBuckets.filter((d) => pluginOwned.includes(d));
|
|
24147
|
+
const candidates = [ALWAYS_EXPOSED, ...domainBuckets.filter((d) => !collisions.includes(d))];
|
|
24148
|
+
const exposed = [...new Set(candidates)].filter((d) => allowed.includes(d)).sort();
|
|
24149
|
+
return { schemaPresent: true, allowed, domainBuckets, pluginOwned, collisions, exposed };
|
|
24150
|
+
}
|
|
24151
|
+
|
|
24152
|
+
// server/routes/portal-fetch.ts
|
|
24153
|
+
var app63 = new Hono();
|
|
24154
|
+
var TAG45 = "[portal-fetch]";
|
|
24155
|
+
async function verifySignature(secret, accountId, relPath, expiresAtMs, signature, nowMs) {
|
|
24156
|
+
if (!secret || !signature) return false;
|
|
24157
|
+
if (!Number.isFinite(expiresAtMs) || expiresAtMs <= nowMs) return false;
|
|
24158
|
+
const key = await crypto.subtle.importKey(
|
|
24159
|
+
"raw",
|
|
24160
|
+
new TextEncoder().encode(secret),
|
|
24161
|
+
{ name: "HMAC", hash: "SHA-256" },
|
|
24162
|
+
false,
|
|
24163
|
+
["sign"]
|
|
24164
|
+
);
|
|
24165
|
+
const mac = await crypto.subtle.sign(
|
|
24166
|
+
"HMAC",
|
|
24167
|
+
key,
|
|
24168
|
+
new TextEncoder().encode(`${accountId}
|
|
24169
|
+
${relPath}
|
|
24170
|
+
${expiresAtMs}`)
|
|
24171
|
+
);
|
|
24172
|
+
const expected = [...new Uint8Array(mac)].map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
24173
|
+
if (expected.length !== signature.length) return false;
|
|
24174
|
+
let diff = 0;
|
|
24175
|
+
for (let i = 0; i < expected.length; i++) diff |= expected.charCodeAt(i) ^ signature.charCodeAt(i);
|
|
24176
|
+
return diff === 0;
|
|
24177
|
+
}
|
|
24178
|
+
async function readAccountSecret(accountId) {
|
|
24179
|
+
const path3 = resolve30(DATA_ROOT, "accounts", accountId, "secrets", "data-portal.env");
|
|
24180
|
+
let text;
|
|
24181
|
+
try {
|
|
24182
|
+
text = await readFile6(path3, "utf8");
|
|
24183
|
+
} catch {
|
|
24184
|
+
console.error(`${TAG45} op=no-secret account="${accountId}" path="${path3}"`);
|
|
24185
|
+
return "";
|
|
24186
|
+
}
|
|
24187
|
+
for (const line of text.split("\n")) {
|
|
24188
|
+
const m = line.match(/^PORTAL_FETCH_SECRET=(.*)$/);
|
|
24189
|
+
if (m) return m[1].trim();
|
|
24190
|
+
}
|
|
24191
|
+
console.error(`${TAG45} op=no-secret account="${accountId}" reason=key-absent path="${path3}"`);
|
|
24192
|
+
return "";
|
|
24193
|
+
}
|
|
24194
|
+
function isValidAccountId2(value) {
|
|
24195
|
+
return /^[A-Za-z0-9-]+$/.test(value);
|
|
24196
|
+
}
|
|
24197
|
+
async function resolveFetchTarget(accountDir, relPath) {
|
|
24198
|
+
let schemaText = null;
|
|
24199
|
+
try {
|
|
24200
|
+
schemaText = await readFile6(join40(accountDir, "SCHEMA.md"), "utf8");
|
|
24201
|
+
} catch {
|
|
24202
|
+
schemaText = null;
|
|
24203
|
+
}
|
|
24204
|
+
const { exposed } = resolveExposedDirs(schemaText);
|
|
24205
|
+
if (exposed.length === 0) return { ok: false, reason: "not-exposed" };
|
|
24206
|
+
const lexical = resolve30(accountDir, relPath);
|
|
24207
|
+
let root;
|
|
24208
|
+
let absolute;
|
|
24209
|
+
try {
|
|
24210
|
+
root = await realpath(accountDir);
|
|
24211
|
+
absolute = await realpath(lexical);
|
|
24212
|
+
} catch {
|
|
24213
|
+
return { ok: false, reason: "enoent" };
|
|
24214
|
+
}
|
|
24215
|
+
if (absolute === root || !absolute.startsWith(root + sep8)) return { ok: false, reason: "not-exposed" };
|
|
24216
|
+
const top = absolute.slice(root.length + 1).split(sep8)[0];
|
|
24217
|
+
if (!exposed.includes(top)) return { ok: false, reason: "not-exposed" };
|
|
24218
|
+
try {
|
|
24219
|
+
const st = await stat6(absolute);
|
|
24220
|
+
if (!st.isFile()) return { ok: false, reason: "enoent" };
|
|
24221
|
+
return { ok: true, absolute, sizeBytes: st.size };
|
|
24222
|
+
} catch {
|
|
24223
|
+
return { ok: false, reason: "enoent" };
|
|
24224
|
+
}
|
|
24225
|
+
}
|
|
24226
|
+
function q(value) {
|
|
24227
|
+
return `"${value.replace(/[\\"]/g, "\\$&").replace(/[\r\n\t]/g, " ")}"`;
|
|
24228
|
+
}
|
|
24229
|
+
async function handle(c, headOnly) {
|
|
24230
|
+
const accountId = c.req.query("accountId") ?? "";
|
|
24231
|
+
const relPath = c.req.query("path") ?? "";
|
|
24232
|
+
const exp = Number(c.req.query("exp") ?? "0");
|
|
24233
|
+
const sig = c.req.query("sig") ?? "";
|
|
24234
|
+
console.log(`${TAG45} op=request account=${q(accountId)} path=${q(relPath)} head=${headOnly}`);
|
|
24235
|
+
if (!isValidAccountId2(accountId)) {
|
|
24236
|
+
console.error(`${TAG45} op=denied account=${q(accountId)} reason=account-id`);
|
|
24237
|
+
return c.json({ ok: false, error: "denied" }, 401);
|
|
24238
|
+
}
|
|
24239
|
+
const secret = await readAccountSecret(accountId);
|
|
24240
|
+
if (!await verifySignature(secret, accountId, relPath, exp, sig, Date.now())) {
|
|
24241
|
+
console.error(`${TAG45} op=denied account=${q(accountId)} reason=secret`);
|
|
24242
|
+
return c.json({ ok: false, error: "denied" }, 401);
|
|
24243
|
+
}
|
|
24244
|
+
const target = await resolveFetchTarget(resolve30(DATA_ROOT, "accounts", accountId), relPath);
|
|
24245
|
+
if (!target.ok) {
|
|
24246
|
+
const op = target.reason === "enoent" ? "miss" : "denied";
|
|
24247
|
+
console.error(`${TAG45} op=${op} account=${accountId} reason=${target.reason}`);
|
|
24248
|
+
return c.json({ ok: false, error: "not found" }, 404);
|
|
24249
|
+
}
|
|
24250
|
+
if (headOnly) {
|
|
24251
|
+
console.log(`${TAG45} op=served account=${accountId} bytes=${target.sizeBytes} head=true`);
|
|
24252
|
+
return c.body(null, 200);
|
|
24253
|
+
}
|
|
24254
|
+
console.log(`${TAG45} op=served account=${accountId} bytes=${target.sizeBytes}`);
|
|
24255
|
+
return new Response(Readable3.toWeb(createReadStream3(target.absolute)), {
|
|
24256
|
+
status: 200,
|
|
24257
|
+
headers: {
|
|
24258
|
+
"content-type": detectMimeType(target.absolute),
|
|
24259
|
+
// Percent-encoded per RFC 6266, so a name carrying a quote cannot break
|
|
24260
|
+
// out of the form and CR/LF cannot reach the Headers constructor.
|
|
24261
|
+
"content-disposition": `attachment; filename*=UTF-8''${encodeURIComponent(relPath.split("/").pop() ?? "file")}`,
|
|
24262
|
+
"x-content-type-options": "nosniff",
|
|
24263
|
+
// Private client documents: never let a browser or proxy cache one.
|
|
24264
|
+
"cache-control": "private, no-store"
|
|
24265
|
+
}
|
|
24266
|
+
});
|
|
24267
|
+
}
|
|
24268
|
+
app63.get("/fetch", (c) => handle(c, false));
|
|
24269
|
+
app63.on("HEAD", "/fetch", (c) => handle(c, true));
|
|
24270
|
+
var portal_fetch_default = app63;
|
|
24271
|
+
|
|
23907
24272
|
// app/lib/timeentry-census.ts
|
|
23908
|
-
var
|
|
24273
|
+
var TAG46 = "[timeentry-census]";
|
|
23909
24274
|
function reconcileTimeEntries(rows, now, horizonMs) {
|
|
23910
24275
|
const openEntries = rows.openEntries.length;
|
|
23911
24276
|
let oldestOpenAgeMs = 0;
|
|
@@ -23945,12 +24310,12 @@ async function runTimeEntryCensusOnce(session, now, horizonMs) {
|
|
|
23945
24310
|
sumAdjustments: toNum(r.get("sumAdjustments"))
|
|
23946
24311
|
}));
|
|
23947
24312
|
const finding = reconcileTimeEntries({ openEntries, taskSums }, now, horizonMs);
|
|
23948
|
-
const line = `${
|
|
24313
|
+
const line = `${TAG46} openEntries=${finding.openEntries} oldestOpenAgeMin=${finding.oldestOpenAgeMin} maxOpenPerAccount=${finding.maxOpenPerAccount} driftTasks=${finding.driftTasks}`;
|
|
23949
24314
|
if (finding.alarm) console.error(`${line} alarm=true`);
|
|
23950
24315
|
else console.log(line);
|
|
23951
24316
|
return finding;
|
|
23952
24317
|
} catch (err) {
|
|
23953
|
-
console.error(`${
|
|
24318
|
+
console.error(`${TAG46} error="${err instanceof Error ? err.message : String(err)}"`);
|
|
23954
24319
|
return null;
|
|
23955
24320
|
}
|
|
23956
24321
|
}
|
|
@@ -23964,13 +24329,13 @@ function startTimeEntryCensus(openSession, opts = {}) {
|
|
|
23964
24329
|
try {
|
|
23965
24330
|
session = openSession();
|
|
23966
24331
|
} catch (err) {
|
|
23967
|
-
console.error(`${
|
|
24332
|
+
console.error(`${TAG46} open-session-failed error="${err instanceof Error ? err.message : String(err)}"`);
|
|
23968
24333
|
return;
|
|
23969
24334
|
}
|
|
23970
24335
|
try {
|
|
23971
24336
|
await runTimeEntryCensusOnce(session, Date.now(), horizonMs);
|
|
23972
24337
|
} catch (err) {
|
|
23973
|
-
console.error(`${
|
|
24338
|
+
console.error(`${TAG46} tick-failed error="${err instanceof Error ? err.message : String(err)}"`);
|
|
23974
24339
|
} finally {
|
|
23975
24340
|
try {
|
|
23976
24341
|
await session.close();
|
|
@@ -23987,8 +24352,8 @@ function startTimeEntryCensus(openSession, opts = {}) {
|
|
|
23987
24352
|
}
|
|
23988
24353
|
|
|
23989
24354
|
// app/lib/ledger-census.ts
|
|
23990
|
-
import { readdirSync as readdirSync25, readFileSync as
|
|
23991
|
-
import { join as
|
|
24355
|
+
import { readdirSync as readdirSync25, readFileSync as readFileSync40, statSync as statSync15 } from "fs";
|
|
24356
|
+
import { join as join41 } from "path";
|
|
23992
24357
|
|
|
23993
24358
|
// ../lib/ledger-core/dist/reconcile.js
|
|
23994
24359
|
var LEDGER_INDEX_NAMES = [
|
|
@@ -24031,7 +24396,7 @@ function formatCensusLine(f) {
|
|
|
24031
24396
|
}
|
|
24032
24397
|
|
|
24033
24398
|
// app/lib/ledger-census.ts
|
|
24034
|
-
var
|
|
24399
|
+
var TAG47 = "[ledger-census]";
|
|
24035
24400
|
var DAY_MS = 864e5;
|
|
24036
24401
|
function countWriteRejects24h(logDir, now) {
|
|
24037
24402
|
let entries;
|
|
@@ -24042,10 +24407,10 @@ function countWriteRejects24h(logDir, now) {
|
|
|
24042
24407
|
}
|
|
24043
24408
|
let total = 0;
|
|
24044
24409
|
for (const name of entries) {
|
|
24045
|
-
const path3 =
|
|
24410
|
+
const path3 = join41(logDir, name);
|
|
24046
24411
|
try {
|
|
24047
24412
|
if (now - statSync15(path3).mtimeMs > DAY_MS) continue;
|
|
24048
|
-
for (const line of
|
|
24413
|
+
for (const line of readFileSync40(path3, "utf8").split("\n")) {
|
|
24049
24414
|
if (line.includes("[graph-write] reject")) total += 1;
|
|
24050
24415
|
}
|
|
24051
24416
|
} catch {
|
|
@@ -24107,7 +24472,7 @@ async function runLedgerCensusOnce(session, now) {
|
|
|
24107
24472
|
else console.log(line);
|
|
24108
24473
|
return finding;
|
|
24109
24474
|
} catch (err) {
|
|
24110
|
-
console.error(`${
|
|
24475
|
+
console.error(`${TAG47} error="${err instanceof Error ? err.message : String(err)}"`);
|
|
24111
24476
|
return null;
|
|
24112
24477
|
}
|
|
24113
24478
|
}
|
|
@@ -24120,7 +24485,7 @@ function startLedgerCensus(openSession, opts = {}) {
|
|
|
24120
24485
|
session = openSession();
|
|
24121
24486
|
} catch (err) {
|
|
24122
24487
|
console.error(
|
|
24123
|
-
`${
|
|
24488
|
+
`${TAG47} open-session-failed error="${err instanceof Error ? err.message : String(err)}"`
|
|
24124
24489
|
);
|
|
24125
24490
|
return;
|
|
24126
24491
|
}
|
|
@@ -24128,7 +24493,7 @@ function startLedgerCensus(openSession, opts = {}) {
|
|
|
24128
24493
|
await runLedgerCensusOnce(session, Date.now());
|
|
24129
24494
|
} catch (err) {
|
|
24130
24495
|
console.error(
|
|
24131
|
-
`${
|
|
24496
|
+
`${TAG47} tick-failed error="${err instanceof Error ? err.message : String(err)}"`
|
|
24132
24497
|
);
|
|
24133
24498
|
} finally {
|
|
24134
24499
|
try {
|
|
@@ -24146,7 +24511,7 @@ function startLedgerCensus(openSession, opts = {}) {
|
|
|
24146
24511
|
}
|
|
24147
24512
|
|
|
24148
24513
|
// app/lib/conversation-audit.ts
|
|
24149
|
-
var
|
|
24514
|
+
var TAG48 = "[conversation-audit]";
|
|
24150
24515
|
async function runConversationAudit() {
|
|
24151
24516
|
const session = getSession();
|
|
24152
24517
|
try {
|
|
@@ -24165,19 +24530,30 @@ async function runConversationAudit() {
|
|
|
24165
24530
|
size([p IN forensic WHERE p IS NOT NULL AND NOT p IN anchored]) AS unreconciled`,
|
|
24166
24531
|
{}
|
|
24167
24532
|
);
|
|
24533
|
+
const multiRes = await session.run(
|
|
24534
|
+
`MATCH (a:Conversation {channel: 'whatsapp'})
|
|
24535
|
+
WHERE a.channelAddress =~ '[0-9]+'
|
|
24536
|
+
WITH a.channelAddress AS addr, count(DISTINCT a.accountId) AS acctCount
|
|
24537
|
+
RETURN count(addr) AS senders,
|
|
24538
|
+
sum(CASE WHEN acctCount > 1 THEN 1 ELSE 0 END) AS multiAnchored`,
|
|
24539
|
+
{}
|
|
24540
|
+
);
|
|
24168
24541
|
const rec = res.records[0];
|
|
24542
|
+
const multiRec = multiRes.records[0];
|
|
24169
24543
|
const result = {
|
|
24170
24544
|
total: Number(rec?.get("total") ?? 0),
|
|
24171
24545
|
unreconciled: Number(rec?.get("unreconciled") ?? 0),
|
|
24172
|
-
unpairable: Number(rec?.get("unpairable") ?? 0)
|
|
24546
|
+
unpairable: Number(rec?.get("unpairable") ?? 0),
|
|
24547
|
+
senders: Number(multiRec?.get("senders") ?? 0),
|
|
24548
|
+
multiAnchored: Number(multiRec?.get("multiAnchored") ?? 0)
|
|
24173
24549
|
};
|
|
24174
24550
|
console.error(
|
|
24175
|
-
`${
|
|
24551
|
+
`${TAG48} channel=whatsapp total=${result.total} senders=${result.senders} multiAnchored=${result.multiAnchored} unreconciled=${result.unreconciled} unpairable=${result.unpairable}`
|
|
24176
24552
|
);
|
|
24177
24553
|
return result;
|
|
24178
24554
|
} catch (err) {
|
|
24179
24555
|
const reason = err instanceof Error ? `${err.name}:${err.message.slice(0, 200)}` : String(err).slice(0, 200);
|
|
24180
|
-
console.error(`${
|
|
24556
|
+
console.error(`${TAG48} op=failed reason=${reason}`);
|
|
24181
24557
|
return null;
|
|
24182
24558
|
} finally {
|
|
24183
24559
|
await session.close();
|
|
@@ -24303,13 +24679,13 @@ function startGraphHealthTimer() {
|
|
|
24303
24679
|
|
|
24304
24680
|
// app/lib/file-watcher.ts
|
|
24305
24681
|
import * as fsp2 from "fs/promises";
|
|
24306
|
-
import { resolve as
|
|
24682
|
+
import { resolve as resolve31, sep as sep9 } from "path";
|
|
24307
24683
|
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;
|
|
24308
24684
|
var DEFAULT_COALESCE_MS = 500;
|
|
24309
24685
|
var ROOTS = ["accounts"];
|
|
24310
24686
|
function _routeEvent(rootName, filename) {
|
|
24311
24687
|
if (!filename) return null;
|
|
24312
|
-
const segs = filename.split(
|
|
24688
|
+
const segs = filename.split(sep9).filter(Boolean);
|
|
24313
24689
|
if (segs.length < 2) return null;
|
|
24314
24690
|
const accountId = segs[0];
|
|
24315
24691
|
if (!ACCOUNT_UUID_RE4.test(accountId)) return null;
|
|
@@ -24322,7 +24698,7 @@ async function startFileWatcher(opts = {}) {
|
|
|
24322
24698
|
const dropFn = opts.drop ?? dropFileIndex;
|
|
24323
24699
|
for (const r of ROOTS) {
|
|
24324
24700
|
try {
|
|
24325
|
-
await fsp2.mkdir(
|
|
24701
|
+
await fsp2.mkdir(resolve31(dataRoot, r), { recursive: true });
|
|
24326
24702
|
} catch (err) {
|
|
24327
24703
|
console.error(
|
|
24328
24704
|
`[file-watcher] start-failed root="${r}" err="${err.message}" \u2014 index will be maintained by the 5-min reconcile backstop only`
|
|
@@ -24343,7 +24719,7 @@ async function startFileWatcher(opts = {}) {
|
|
|
24343
24719
|
timers.set(relativePath, t);
|
|
24344
24720
|
}
|
|
24345
24721
|
async function runHook(relativePath, accountId) {
|
|
24346
|
-
const absolute =
|
|
24722
|
+
const absolute = resolve31(dataRoot, relativePath);
|
|
24347
24723
|
let exists = false;
|
|
24348
24724
|
try {
|
|
24349
24725
|
const st = await fsp2.stat(absolute);
|
|
@@ -24367,7 +24743,7 @@ async function startFileWatcher(opts = {}) {
|
|
|
24367
24743
|
}
|
|
24368
24744
|
}
|
|
24369
24745
|
async function watchRoot(rootName) {
|
|
24370
|
-
const absRoot =
|
|
24746
|
+
const absRoot = resolve31(dataRoot, rootName);
|
|
24371
24747
|
try {
|
|
24372
24748
|
const iter = fsp2.watch(absRoot, { recursive: true, signal: controller.signal });
|
|
24373
24749
|
for await (const event of iter) {
|
|
@@ -24406,7 +24782,7 @@ async function startFileWatcher(opts = {}) {
|
|
|
24406
24782
|
|
|
24407
24783
|
// app/lib/migrate-uploads.ts
|
|
24408
24784
|
import { mkdir as mkdir5, readdir as readdir5, rename as rename2, rm as rm3 } from "fs/promises";
|
|
24409
|
-
import { dirname as dirname13, relative as relative6, resolve as
|
|
24785
|
+
import { dirname as dirname13, relative as relative6, resolve as resolve32 } from "path";
|
|
24410
24786
|
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;
|
|
24411
24787
|
async function walkFiles(dir) {
|
|
24412
24788
|
const out = [];
|
|
@@ -24417,7 +24793,7 @@ async function walkFiles(dir) {
|
|
|
24417
24793
|
return out;
|
|
24418
24794
|
}
|
|
24419
24795
|
for (const e of entries) {
|
|
24420
|
-
const abs =
|
|
24796
|
+
const abs = resolve32(dir, e.name);
|
|
24421
24797
|
if (e.isDirectory()) {
|
|
24422
24798
|
out.push(...await walkFiles(abs));
|
|
24423
24799
|
} else if (e.isFile()) {
|
|
@@ -24437,7 +24813,7 @@ async function relocateTree(srcDir, destDir, dataRoot, accountId, session) {
|
|
|
24437
24813
|
let moved = 0;
|
|
24438
24814
|
for (const oldAbs of files) {
|
|
24439
24815
|
const suffix = relative6(srcDir, oldAbs);
|
|
24440
|
-
const newAbs =
|
|
24816
|
+
const newAbs = resolve32(destDir, suffix);
|
|
24441
24817
|
await mkdir5(dirname13(newAbs), { recursive: true });
|
|
24442
24818
|
await rename2(oldAbs, newAbs);
|
|
24443
24819
|
moved++;
|
|
@@ -24464,7 +24840,7 @@ async function relocateTree(srcDir, destDir, dataRoot, accountId, session) {
|
|
|
24464
24840
|
}
|
|
24465
24841
|
async function migrateUploads(opts = {}) {
|
|
24466
24842
|
const dataRoot = opts.dataRoot ?? DATA_ROOT;
|
|
24467
|
-
const oldRoot =
|
|
24843
|
+
const oldRoot = resolve32(dataRoot, "uploads");
|
|
24468
24844
|
let topEntries;
|
|
24469
24845
|
try {
|
|
24470
24846
|
topEntries = await readdir5(oldRoot, { withFileTypes: true });
|
|
@@ -24485,8 +24861,8 @@ async function migrateUploads(opts = {}) {
|
|
|
24485
24861
|
const name = entry.name;
|
|
24486
24862
|
if (ACCOUNT_UUID_RE5.test(name)) {
|
|
24487
24863
|
moved += await relocateTree(
|
|
24488
|
-
|
|
24489
|
-
|
|
24864
|
+
resolve32(oldRoot, name),
|
|
24865
|
+
resolve32(dataRoot, "accounts", name, "uploads"),
|
|
24490
24866
|
dataRoot,
|
|
24491
24867
|
name,
|
|
24492
24868
|
session
|
|
@@ -24498,8 +24874,8 @@ async function migrateUploads(opts = {}) {
|
|
|
24498
24874
|
continue;
|
|
24499
24875
|
}
|
|
24500
24876
|
moved += await relocateTree(
|
|
24501
|
-
|
|
24502
|
-
|
|
24877
|
+
resolve32(oldRoot, "public"),
|
|
24878
|
+
resolve32(dataRoot, "accounts", installAccountId, "uploads", "public"),
|
|
24503
24879
|
dataRoot,
|
|
24504
24880
|
null,
|
|
24505
24881
|
// public uploads carry no graph nodes
|
|
@@ -24528,9 +24904,9 @@ async function migrateUploads(opts = {}) {
|
|
|
24528
24904
|
}
|
|
24529
24905
|
|
|
24530
24906
|
// app/lib/migrate-admin-webchat-sidecars.ts
|
|
24531
|
-
import { readdir as readdir6, readFile as
|
|
24907
|
+
import { readdir as readdir6, readFile as readFile7, rename as rename3, writeFile as writeFile4, unlink as unlink3 } from "fs/promises";
|
|
24532
24908
|
import { existsSync as existsSync36 } from "fs";
|
|
24533
|
-
import { join as
|
|
24909
|
+
import { join as join42 } from "path";
|
|
24534
24910
|
var ADMIN_ROLE2 = "admin";
|
|
24535
24911
|
var WEBCHAT_CHANNEL2 = "webchat";
|
|
24536
24912
|
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;
|
|
@@ -24538,10 +24914,10 @@ var defaultResolveName = async (_accountId, userId) => {
|
|
|
24538
24914
|
const res = await loadAdminUserName(userId);
|
|
24539
24915
|
return res.source === "neo4j" && res.joined.length > 0 ? res.joined : null;
|
|
24540
24916
|
};
|
|
24541
|
-
async function
|
|
24917
|
+
async function readRaw3(path3) {
|
|
24542
24918
|
let raw;
|
|
24543
24919
|
try {
|
|
24544
|
-
raw = await
|
|
24920
|
+
raw = await readFile7(path3, "utf8");
|
|
24545
24921
|
} catch {
|
|
24546
24922
|
return null;
|
|
24547
24923
|
}
|
|
@@ -24575,7 +24951,7 @@ async function collectLiveSidecars(projectsRoot) {
|
|
|
24575
24951
|
}
|
|
24576
24952
|
for (const slug of slugs) {
|
|
24577
24953
|
if (!slug.isDirectory()) continue;
|
|
24578
|
-
const slugDir =
|
|
24954
|
+
const slugDir = join42(projectsRoot, slug.name);
|
|
24579
24955
|
let entries;
|
|
24580
24956
|
try {
|
|
24581
24957
|
entries = await readdir6(slugDir, { withFileTypes: true });
|
|
@@ -24584,9 +24960,9 @@ async function collectLiveSidecars(projectsRoot) {
|
|
|
24584
24960
|
}
|
|
24585
24961
|
for (const entry of entries) {
|
|
24586
24962
|
if (entry.isFile() && SESSION_META_RE.test(entry.name)) {
|
|
24587
|
-
out.push(
|
|
24963
|
+
out.push(join42(slugDir, entry.name));
|
|
24588
24964
|
} else if (entry.isDirectory() && entry.name === "subagents") {
|
|
24589
|
-
const subDir =
|
|
24965
|
+
const subDir = join42(slugDir, entry.name);
|
|
24590
24966
|
let subs;
|
|
24591
24967
|
try {
|
|
24592
24968
|
subs = await readdir6(subDir, { withFileTypes: true });
|
|
@@ -24594,7 +24970,7 @@ async function collectLiveSidecars(projectsRoot) {
|
|
|
24594
24970
|
continue;
|
|
24595
24971
|
}
|
|
24596
24972
|
for (const s of subs) {
|
|
24597
|
-
if (s.isFile() && SESSION_META_RE.test(s.name)) out.push(
|
|
24973
|
+
if (s.isFile() && SESSION_META_RE.test(s.name)) out.push(join42(subDir, s.name));
|
|
24598
24974
|
}
|
|
24599
24975
|
}
|
|
24600
24976
|
}
|
|
@@ -24606,7 +24982,7 @@ function shortId(path3) {
|
|
|
24606
24982
|
return base.slice(0, 8);
|
|
24607
24983
|
}
|
|
24608
24984
|
async function migrateAdminWebchatSidecars(opts = {}) {
|
|
24609
|
-
const projectsRoot = opts.projectsRoot ?? (process.env.CLAUDE_CONFIG_DIR ?
|
|
24985
|
+
const projectsRoot = opts.projectsRoot ?? (process.env.CLAUDE_CONFIG_DIR ? join42(process.env.CLAUDE_CONFIG_DIR, "projects") : null) ?? "";
|
|
24610
24986
|
const usersFile = opts.usersFile ?? USERS_FILE;
|
|
24611
24987
|
const accountId = opts.accountId ?? resolveAccount()?.accountId ?? null;
|
|
24612
24988
|
const resolveName = opts.resolveName ?? defaultResolveName;
|
|
@@ -24646,7 +25022,7 @@ async function migrateAdminWebchatSidecars(opts = {}) {
|
|
|
24646
25022
|
};
|
|
24647
25023
|
const sidecars = await collectLiveSidecars(projectsRoot);
|
|
24648
25024
|
for (const path3 of sidecars) {
|
|
24649
|
-
const raw = await
|
|
25025
|
+
const raw = await readRaw3(path3);
|
|
24650
25026
|
if (!raw) continue;
|
|
24651
25027
|
if (raw.role !== ADMIN_ROLE2 || raw.channel !== WEBCHAT_CHANNEL2) continue;
|
|
24652
25028
|
result.scanned++;
|
|
@@ -24664,7 +25040,7 @@ async function migrateAdminWebchatSidecars(opts = {}) {
|
|
|
24664
25040
|
await writeRaw(path3, raw);
|
|
24665
25041
|
result.backfilled++;
|
|
24666
25042
|
await resolveAdminName();
|
|
24667
|
-
const back = await
|
|
25043
|
+
const back = await readRaw3(path3);
|
|
24668
25044
|
const wrote = back && back.adminUserId === soleAdminUserId;
|
|
24669
25045
|
if (wrote && resolvedNameForAdmin) {
|
|
24670
25046
|
result.resolvedName++;
|
|
@@ -24951,8 +25327,8 @@ var streamSSE = (c, cb, onError) => {
|
|
|
24951
25327
|
|
|
24952
25328
|
// app/lib/whatsapp/gateway/routes.ts
|
|
24953
25329
|
function createWaChannelRoutes(deps) {
|
|
24954
|
-
const
|
|
24955
|
-
|
|
25330
|
+
const app65 = new Hono();
|
|
25331
|
+
app65.get("/wa-channel/inbound", (c) => {
|
|
24956
25332
|
const senderId = c.req.query("senderId");
|
|
24957
25333
|
if (!senderId) return c.json({ error: "senderId required" }, 400);
|
|
24958
25334
|
return streamSSE(c, async (stream2) => {
|
|
@@ -24960,9 +25336,11 @@ function createWaChannelRoutes(deps) {
|
|
|
24960
25336
|
void stream2.writeSSE({ data: JSON.stringify(payload) });
|
|
24961
25337
|
});
|
|
24962
25338
|
console.error(`[whatsapp-native] op=channel-attached senderId=${senderId}`);
|
|
25339
|
+
deps.onAttached?.(senderId);
|
|
24963
25340
|
stream2.onAbort(() => {
|
|
24964
25341
|
deps.hub.detach(senderId);
|
|
24965
25342
|
console.error(`[whatsapp-native] op=channel-detached senderId=${senderId}`);
|
|
25343
|
+
deps.onDetached?.(senderId);
|
|
24966
25344
|
});
|
|
24967
25345
|
while (!stream2.aborted) {
|
|
24968
25346
|
await stream2.sleep(15e3);
|
|
@@ -24970,7 +25348,7 @@ function createWaChannelRoutes(deps) {
|
|
|
24970
25348
|
}
|
|
24971
25349
|
});
|
|
24972
25350
|
});
|
|
24973
|
-
|
|
25351
|
+
app65.post("/wa-channel/reply", async (c) => {
|
|
24974
25352
|
const body = await c.req.json().catch(() => null);
|
|
24975
25353
|
const senderId = body?.senderId;
|
|
24976
25354
|
const text = body?.text;
|
|
@@ -24991,7 +25369,7 @@ function createWaChannelRoutes(deps) {
|
|
|
24991
25369
|
console.error(`[whatsapp-native] op=reply-dispatch senderId=${senderId} bytes=${bytes}`);
|
|
24992
25370
|
return c.json({ ok: true });
|
|
24993
25371
|
});
|
|
24994
|
-
|
|
25372
|
+
app65.post("/wa-channel/reply-document", async (c) => {
|
|
24995
25373
|
const body = await c.req.json().catch(() => null);
|
|
24996
25374
|
const senderId = body?.senderId;
|
|
24997
25375
|
const files = body?.files;
|
|
@@ -25030,7 +25408,7 @@ function createWaChannelRoutes(deps) {
|
|
|
25030
25408
|
}
|
|
25031
25409
|
return c.json({ ok: true, results });
|
|
25032
25410
|
});
|
|
25033
|
-
|
|
25411
|
+
app65.post("/wa-channel/ready", async (c) => {
|
|
25034
25412
|
const body = await c.req.json().catch(() => null);
|
|
25035
25413
|
const senderId = body?.senderId;
|
|
25036
25414
|
if (typeof senderId !== "string") {
|
|
@@ -25039,7 +25417,7 @@ function createWaChannelRoutes(deps) {
|
|
|
25039
25417
|
deps.onReady?.(senderId);
|
|
25040
25418
|
return c.json({ ok: true });
|
|
25041
25419
|
});
|
|
25042
|
-
|
|
25420
|
+
app65.post("/wa-channel/received", async (c) => {
|
|
25043
25421
|
const body = await c.req.json().catch(() => null);
|
|
25044
25422
|
const senderId = body?.senderId;
|
|
25045
25423
|
const waMessageId = body?.waMessageId;
|
|
@@ -25049,7 +25427,7 @@ function createWaChannelRoutes(deps) {
|
|
|
25049
25427
|
deps.onReceived?.(senderId, waMessageId);
|
|
25050
25428
|
return c.json({ ok: true });
|
|
25051
25429
|
});
|
|
25052
|
-
|
|
25430
|
+
app65.post("/wa-channel/turn-end", async (c) => {
|
|
25053
25431
|
const body = await c.req.json().catch(() => null);
|
|
25054
25432
|
const senderId = body?.senderId;
|
|
25055
25433
|
const sessionId = body?.sessionId;
|
|
@@ -25082,7 +25460,7 @@ function createWaChannelRoutes(deps) {
|
|
|
25082
25460
|
console.error(`[whatsapp-native] op=turn-end sessionId=${sid} replied=no delivered=none`);
|
|
25083
25461
|
return c.json({ ok: true, delivered: "none" });
|
|
25084
25462
|
});
|
|
25085
|
-
return
|
|
25463
|
+
return app65;
|
|
25086
25464
|
}
|
|
25087
25465
|
|
|
25088
25466
|
// app/lib/whatsapp/gateway/wa-gateway.ts
|
|
@@ -25104,9 +25482,30 @@ var WaGateway = class {
|
|
|
25104
25482
|
spawning = /* @__PURE__ */ new Set();
|
|
25105
25483
|
seq = 0;
|
|
25106
25484
|
/** Hono sub-app exposing the loopback channel routes; mount on the UI app. */
|
|
25485
|
+
/** Task 1789 — senderId -> the ms at which its channel server last detached,
|
|
25486
|
+
* cleared on attach. The standing audit reads this; nothing else does. */
|
|
25487
|
+
detachedAt = /* @__PURE__ */ new Map();
|
|
25488
|
+
/** Seat lost. `atMs` is injected so the audit's accounting is testable. */
|
|
25489
|
+
recordDetached(senderId, atMs) {
|
|
25490
|
+
this.detachedAt.set(senderId, atMs);
|
|
25491
|
+
}
|
|
25492
|
+
/** Seat regained — the sender is no longer unseated. */
|
|
25493
|
+
recordAttached(senderId) {
|
|
25494
|
+
this.detachedAt.delete(senderId);
|
|
25495
|
+
}
|
|
25496
|
+
/** Senders with no channel server for longer than `olderThanMs`. */
|
|
25497
|
+
unseatedSenders(nowMs, olderThanMs) {
|
|
25498
|
+
const out = [];
|
|
25499
|
+
for (const [senderId, sinceMs] of this.detachedAt) {
|
|
25500
|
+
if (nowMs - sinceMs > olderThanMs) out.push({ senderId, sinceMs });
|
|
25501
|
+
}
|
|
25502
|
+
return out.sort((a, b) => a.senderId.localeCompare(b.senderId));
|
|
25503
|
+
}
|
|
25107
25504
|
routes() {
|
|
25108
25505
|
return createWaChannelRoutes({
|
|
25109
25506
|
hub: this.hub,
|
|
25507
|
+
onAttached: (senderId) => this.recordAttached(senderId),
|
|
25508
|
+
onDetached: (senderId) => this.recordDetached(senderId, Date.now()),
|
|
25110
25509
|
sendOutbound: (senderId, text) => this.sendOutbound(senderId, text),
|
|
25111
25510
|
sendDocument: (senderId, filePath, caption) => this.sendDocument(senderId, filePath, caption),
|
|
25112
25511
|
onReady: (senderId) => {
|
|
@@ -25217,7 +25616,7 @@ var WaGateway = class {
|
|
|
25217
25616
|
},
|
|
25218
25617
|
Date.now()
|
|
25219
25618
|
);
|
|
25220
|
-
const resolvedSessionId = adminSessionIdFor(
|
|
25619
|
+
const resolvedSessionId = (input.role === "admin" ? this.deps.resolveSessionOverride?.(input.effectiveAccountId, senderId) : null) || adminSessionIdFor(
|
|
25221
25620
|
input.effectiveAccountId,
|
|
25222
25621
|
senderId,
|
|
25223
25622
|
input.role === "public" && input.personId ? input.personId : void 0
|
|
@@ -25285,17 +25684,28 @@ function logDuplicateSenderGroups(groups) {
|
|
|
25285
25684
|
);
|
|
25286
25685
|
}
|
|
25287
25686
|
}
|
|
25687
|
+
function logUnseatedSenders(entries, nowMs) {
|
|
25688
|
+
for (const e of entries) {
|
|
25689
|
+
const secs = Math.round((nowMs - e.sinceMs) / 1e3);
|
|
25690
|
+
console.error(`[whatsapp-native] op=sender-unseated senderId=${e.senderId} since=${new Date(e.sinceMs).toISOString()} forSeconds=${secs}`);
|
|
25691
|
+
}
|
|
25692
|
+
}
|
|
25288
25693
|
|
|
25289
25694
|
// app/lib/whatsapp/gateway/spawn-request.ts
|
|
25290
25695
|
function buildWaSpawnRequest(input) {
|
|
25291
25696
|
const personId = input.role === "public" && input.personId ? input.personId : void 0;
|
|
25292
25697
|
return {
|
|
25293
|
-
// Task 1746 — the deterministic per-sender id is the
|
|
25294
|
-
// session
|
|
25295
|
-
//
|
|
25296
|
-
//
|
|
25297
|
-
//
|
|
25298
|
-
|
|
25698
|
+
// Task 1746 — the deterministic per-sender id is the DEFAULT id a sender's
|
|
25699
|
+
// session has. Task 1526's per-firing seat override used to replace it on a
|
|
25700
|
+
// scheduled cold start, which spawned a second session per firing; that
|
|
25701
|
+
// session then attached under the sender's own hub key and swallowed their
|
|
25702
|
+
// real messages until it detached, splitting the conversation in two.
|
|
25703
|
+
//
|
|
25704
|
+
// Task 1789's override is not that. It is a stable per-sender forward
|
|
25705
|
+
// pointer written once by an operator re-seat and resolving identically for
|
|
25706
|
+
// every subsequent inbound, so it can never yield a second id for a sender.
|
|
25707
|
+
// Do not remove it without reading server/channel-session-override.ts.
|
|
25708
|
+
sessionId: input.overrideSessionId || adminSessionIdFor(input.accountId, input.senderId, personId),
|
|
25299
25709
|
role: input.role,
|
|
25300
25710
|
channel: "whatsapp",
|
|
25301
25711
|
personId,
|
|
@@ -25479,8 +25889,8 @@ var import_dist7 = __toESM(require_dist(), 1);
|
|
|
25479
25889
|
|
|
25480
25890
|
// app/lib/webchat/gateway/routes.ts
|
|
25481
25891
|
function createWebchatChannelRoutes(deps) {
|
|
25482
|
-
const
|
|
25483
|
-
|
|
25892
|
+
const app65 = new Hono();
|
|
25893
|
+
app65.get("/webchat-channel/inbound", (c) => {
|
|
25484
25894
|
const key = c.req.query("key");
|
|
25485
25895
|
if (!key) return c.json({ error: "key required" }, 400);
|
|
25486
25896
|
return streamSSE(c, async (stream2) => {
|
|
@@ -25498,7 +25908,7 @@ function createWebchatChannelRoutes(deps) {
|
|
|
25498
25908
|
}
|
|
25499
25909
|
});
|
|
25500
25910
|
});
|
|
25501
|
-
|
|
25911
|
+
app65.post("/webchat-channel/reply", async (c) => {
|
|
25502
25912
|
const body = await c.req.json().catch(() => null);
|
|
25503
25913
|
const key = body?.key;
|
|
25504
25914
|
const text = body?.text;
|
|
@@ -25508,7 +25918,7 @@ function createWebchatChannelRoutes(deps) {
|
|
|
25508
25918
|
deps.onReply?.(key, text);
|
|
25509
25919
|
return c.json({ ok: true });
|
|
25510
25920
|
});
|
|
25511
|
-
|
|
25921
|
+
app65.post("/webchat-channel/connector-auth", async (c) => {
|
|
25512
25922
|
const body = await c.req.json().catch(() => null);
|
|
25513
25923
|
const key = body?.key;
|
|
25514
25924
|
const sessionId = body?.sessionId;
|
|
@@ -25521,7 +25931,7 @@ function createWebchatChannelRoutes(deps) {
|
|
|
25521
25931
|
const result = await deps.onConnectorAuth({ key, sessionId, name, completed });
|
|
25522
25932
|
return c.json(result);
|
|
25523
25933
|
});
|
|
25524
|
-
|
|
25934
|
+
app65.post("/webchat-channel/ready", async (c) => {
|
|
25525
25935
|
const body = await c.req.json().catch(() => null);
|
|
25526
25936
|
const key = body?.key;
|
|
25527
25937
|
if (typeof key !== "string") {
|
|
@@ -25530,7 +25940,7 @@ function createWebchatChannelRoutes(deps) {
|
|
|
25530
25940
|
deps.onReady?.(key);
|
|
25531
25941
|
return c.json({ ok: true });
|
|
25532
25942
|
});
|
|
25533
|
-
|
|
25943
|
+
app65.post("/webchat-channel/permission-request", async (c) => {
|
|
25534
25944
|
const body = await c.req.json().catch(() => null);
|
|
25535
25945
|
const key = body?.key;
|
|
25536
25946
|
const requestId = body?.request_id;
|
|
@@ -25544,7 +25954,7 @@ function createWebchatChannelRoutes(deps) {
|
|
|
25544
25954
|
const verdict = await deps.awaitPermissionVerdict({ key, requestId, toolName, description, inputPreview });
|
|
25545
25955
|
return c.json(verdict);
|
|
25546
25956
|
});
|
|
25547
|
-
|
|
25957
|
+
app65.post("/webchat-channel/received", async (c) => {
|
|
25548
25958
|
const body = await c.req.json().catch(() => null);
|
|
25549
25959
|
const key = body?.key;
|
|
25550
25960
|
const messageId = body?.messageId;
|
|
@@ -25554,7 +25964,7 @@ function createWebchatChannelRoutes(deps) {
|
|
|
25554
25964
|
deps.onReceived?.(key, messageId);
|
|
25555
25965
|
return c.json({ ok: true });
|
|
25556
25966
|
});
|
|
25557
|
-
|
|
25967
|
+
app65.post("/webchat-channel/turn-end", async (c) => {
|
|
25558
25968
|
const body = await c.req.json().catch(() => null);
|
|
25559
25969
|
const key = body?.key;
|
|
25560
25970
|
const sessionId = body?.sessionId;
|
|
@@ -25577,7 +25987,7 @@ function createWebchatChannelRoutes(deps) {
|
|
|
25577
25987
|
console.error(`[webchat-native] op=turn-end channel=webchat key=${k} sessionId=${sid} replied=no delivered=none`);
|
|
25578
25988
|
return c.json({ ok: true, delivered: "none" });
|
|
25579
25989
|
});
|
|
25580
|
-
return
|
|
25990
|
+
return app65;
|
|
25581
25991
|
}
|
|
25582
25992
|
|
|
25583
25993
|
// app/lib/webchat/gateway/webchat-gateway.ts
|
|
@@ -25635,16 +26045,16 @@ var WebchatGateway = class _WebchatGateway {
|
|
|
25635
26045
|
* The public /api/chat route awaits this to return the reply on the same SSE
|
|
25636
26046
|
* response, preserving the pre-756 POST→single-blob browser contract. */
|
|
25637
26047
|
awaitReply(key, timeoutMs) {
|
|
25638
|
-
return new Promise((
|
|
26048
|
+
return new Promise((resolve38) => {
|
|
25639
26049
|
const timer = setTimeout(() => {
|
|
25640
26050
|
this.replyAwaiters.delete(key);
|
|
25641
|
-
|
|
26051
|
+
resolve38({ timeout: true });
|
|
25642
26052
|
}, timeoutMs);
|
|
25643
26053
|
if (timer.unref) timer.unref();
|
|
25644
26054
|
this.replyAwaiters.set(key, (r) => {
|
|
25645
26055
|
clearTimeout(timer);
|
|
25646
26056
|
this.replyAwaiters.delete(key);
|
|
25647
|
-
|
|
26057
|
+
resolve38(r);
|
|
25648
26058
|
});
|
|
25649
26059
|
});
|
|
25650
26060
|
}
|
|
@@ -25655,11 +26065,11 @@ var WebchatGateway = class _WebchatGateway {
|
|
|
25655
26065
|
* terminal dialog stays answerable). */
|
|
25656
26066
|
awaitPermissionVerdict(p, timeoutMs) {
|
|
25657
26067
|
const k = _WebchatGateway.promptKey(p.key, p.requestId);
|
|
25658
|
-
return new Promise((
|
|
26068
|
+
return new Promise((resolve38) => {
|
|
25659
26069
|
this.pendingPrompts.get(k)?.resolve({ timeout: true });
|
|
25660
26070
|
const timer = setTimeout(() => {
|
|
25661
26071
|
this.pendingPrompts.delete(k);
|
|
25662
|
-
|
|
26072
|
+
resolve38({ timeout: true });
|
|
25663
26073
|
}, timeoutMs);
|
|
25664
26074
|
if (timer.unref) timer.unref();
|
|
25665
26075
|
this.pendingPrompts.set(k, {
|
|
@@ -25668,7 +26078,7 @@ var WebchatGateway = class _WebchatGateway {
|
|
|
25668
26078
|
resolve: (r) => {
|
|
25669
26079
|
clearTimeout(timer);
|
|
25670
26080
|
this.pendingPrompts.delete(k);
|
|
25671
|
-
|
|
26081
|
+
resolve38(r);
|
|
25672
26082
|
}
|
|
25673
26083
|
});
|
|
25674
26084
|
console.error(`[webchat:perm] op=open key=${keyDisplay(p.key)} id=${p.requestId} tool=${p.toolName}`);
|
|
@@ -26233,7 +26643,7 @@ async function fanOut(subscribers, text, onError, tag) {
|
|
|
26233
26643
|
|
|
26234
26644
|
// app/lib/webchat/file-delivery.ts
|
|
26235
26645
|
import { realpathSync as realpathSync8 } from "fs";
|
|
26236
|
-
import { resolve as
|
|
26646
|
+
import { resolve as resolve33 } from "path";
|
|
26237
26647
|
|
|
26238
26648
|
// app/lib/channel-pty-bridge/file-delivery.ts
|
|
26239
26649
|
var SEND_USER_FILE = "SendUserFile";
|
|
@@ -26324,29 +26734,29 @@ function makeFileDelivery(opts) {
|
|
|
26324
26734
|
}
|
|
26325
26735
|
|
|
26326
26736
|
// app/lib/webchat/file-delivery.ts
|
|
26327
|
-
var
|
|
26737
|
+
var TAG49 = "[webchat-adaptor]";
|
|
26328
26738
|
function platformRoot2() {
|
|
26329
26739
|
return process.env.MAXY_PLATFORM_ROOT || "";
|
|
26330
26740
|
}
|
|
26331
26741
|
function makeWebchatSendFile(entry) {
|
|
26332
26742
|
return async (filePath) => {
|
|
26333
26743
|
if (!entry.accountId) {
|
|
26334
|
-
console.error(`${
|
|
26744
|
+
console.error(`${TAG49} file-delivery reject reason=no-account sender=${entry.senderId}`);
|
|
26335
26745
|
return { ok: false, error: "no-account" };
|
|
26336
26746
|
}
|
|
26337
|
-
const accountDir =
|
|
26747
|
+
const accountDir = resolve33(platformRoot2(), "..", "data/accounts", entry.accountId);
|
|
26338
26748
|
try {
|
|
26339
26749
|
const resolved = realpathSync8(filePath);
|
|
26340
26750
|
const accountResolved = realpathSync8(accountDir);
|
|
26341
26751
|
if (!resolved.startsWith(accountResolved + "/")) {
|
|
26342
|
-
console.error(`${
|
|
26752
|
+
console.error(`${TAG49} file-delivery reject reason=outside_account_directory sender=${entry.senderId}`);
|
|
26343
26753
|
return { ok: false, error: "outside-account" };
|
|
26344
26754
|
}
|
|
26345
26755
|
return { ok: true };
|
|
26346
26756
|
} catch (err) {
|
|
26347
26757
|
const code = err.code;
|
|
26348
26758
|
console.error(
|
|
26349
|
-
`${
|
|
26759
|
+
`${TAG49} file-delivery reject reason=${code === "ENOENT" ? "not-found" : "path-error"} sender=${entry.senderId}`
|
|
26350
26760
|
);
|
|
26351
26761
|
return { ok: false, error: code === "ENOENT" ? "not-found" : "path-error" };
|
|
26352
26762
|
}
|
|
@@ -26355,7 +26765,7 @@ function makeWebchatSendFile(entry) {
|
|
|
26355
26765
|
function makeWebchatFileDelivery(entry) {
|
|
26356
26766
|
return makeFileDelivery({
|
|
26357
26767
|
entry,
|
|
26358
|
-
tag:
|
|
26768
|
+
tag: TAG49,
|
|
26359
26769
|
channel: "webchat",
|
|
26360
26770
|
sendFile: makeWebchatSendFile(entry),
|
|
26361
26771
|
deferUntilVerdict: true
|
|
@@ -26417,7 +26827,7 @@ function buildPublicWebchatSpawnRequest(input) {
|
|
|
26417
26827
|
}
|
|
26418
26828
|
|
|
26419
26829
|
// app/lib/whatsapp/inbound/file-delivery-bridge.ts
|
|
26420
|
-
var
|
|
26830
|
+
var TAG50 = "[whatsapp-adaptor]";
|
|
26421
26831
|
var SEND_USER_FILE2 = "SendUserFile";
|
|
26422
26832
|
var WHATSAPP_SEND_DOCUMENT = "whatsapp-send-document";
|
|
26423
26833
|
function platformRoot3() {
|
|
@@ -26435,7 +26845,7 @@ function makeWhatsAppSendFile(entry, maxyAccountId) {
|
|
|
26435
26845
|
});
|
|
26436
26846
|
if (result.ok) return { ok: true };
|
|
26437
26847
|
console.error(
|
|
26438
|
-
`${
|
|
26848
|
+
`${TAG50} file-delivery reject reason=send-failed sender=${entry.senderId} status=${result.status} message=${result.error}`
|
|
26439
26849
|
);
|
|
26440
26850
|
return { ok: false, error: result.error };
|
|
26441
26851
|
};
|
|
@@ -26443,7 +26853,7 @@ function makeWhatsAppSendFile(entry, maxyAccountId) {
|
|
|
26443
26853
|
function makeWhatsAppFileDelivery(entry, maxyAccountId) {
|
|
26444
26854
|
const shared = makeFileDelivery({
|
|
26445
26855
|
entry,
|
|
26446
|
-
tag:
|
|
26856
|
+
tag: TAG50,
|
|
26447
26857
|
channel: "whatsapp",
|
|
26448
26858
|
sendFile: makeWhatsAppSendFile(entry, maxyAccountId)
|
|
26449
26859
|
});
|
|
@@ -26478,7 +26888,7 @@ function makeWhatsAppFileDelivery(entry, maxyAccountId) {
|
|
|
26478
26888
|
if (!delivered) {
|
|
26479
26889
|
const fileField = call.filePath !== void 0 ? ` file=${call.filePath}` : "";
|
|
26480
26890
|
console.error(
|
|
26481
|
-
`${
|
|
26891
|
+
`${TAG50} file-delivery-unreconciled sender=${entry.senderId} sessionId=${sid} tool=${WHATSAPP_SEND_DOCUMENT}${fileField}`
|
|
26482
26892
|
);
|
|
26483
26893
|
}
|
|
26484
26894
|
}
|
|
@@ -26664,8 +27074,8 @@ var import_dist8 = __toESM(require_dist(), 1);
|
|
|
26664
27074
|
|
|
26665
27075
|
// app/lib/telegram/gateway/routes.ts
|
|
26666
27076
|
function createTelegramChannelRoutes(deps) {
|
|
26667
|
-
const
|
|
26668
|
-
|
|
27077
|
+
const app65 = new Hono();
|
|
27078
|
+
app65.get("/tg-channel/inbound", (c) => {
|
|
26669
27079
|
const key = c.req.query("key");
|
|
26670
27080
|
if (!key) return c.json({ error: "key required" }, 400);
|
|
26671
27081
|
return streamSSE(c, async (stream2) => {
|
|
@@ -26683,7 +27093,7 @@ function createTelegramChannelRoutes(deps) {
|
|
|
26683
27093
|
}
|
|
26684
27094
|
});
|
|
26685
27095
|
});
|
|
26686
|
-
|
|
27096
|
+
app65.post("/tg-channel/reply", async (c) => {
|
|
26687
27097
|
const body = await c.req.json().catch(() => null);
|
|
26688
27098
|
const key = body?.key;
|
|
26689
27099
|
const text = body?.text;
|
|
@@ -26699,7 +27109,7 @@ function createTelegramChannelRoutes(deps) {
|
|
|
26699
27109
|
console.error(`[telegram-native] op=reply key=${key} bytes=${Buffer.byteLength(text, "utf8")}`);
|
|
26700
27110
|
return c.json({ ok: true });
|
|
26701
27111
|
});
|
|
26702
|
-
|
|
27112
|
+
app65.post("/tg-channel/ready", async (c) => {
|
|
26703
27113
|
const body = await c.req.json().catch(() => null);
|
|
26704
27114
|
const key = body?.key;
|
|
26705
27115
|
if (typeof key !== "string") {
|
|
@@ -26708,7 +27118,7 @@ function createTelegramChannelRoutes(deps) {
|
|
|
26708
27118
|
deps.onReady?.(key);
|
|
26709
27119
|
return c.json({ ok: true });
|
|
26710
27120
|
});
|
|
26711
|
-
|
|
27121
|
+
app65.post("/tg-channel/received", async (c) => {
|
|
26712
27122
|
const body = await c.req.json().catch(() => null);
|
|
26713
27123
|
const key = body?.key;
|
|
26714
27124
|
const messageId = body?.messageId;
|
|
@@ -26718,7 +27128,7 @@ function createTelegramChannelRoutes(deps) {
|
|
|
26718
27128
|
deps.onReceived?.(key, messageId);
|
|
26719
27129
|
return c.json({ ok: true });
|
|
26720
27130
|
});
|
|
26721
|
-
|
|
27131
|
+
app65.post("/tg-channel/turn-end", async (c) => {
|
|
26722
27132
|
const body = await c.req.json().catch(() => null);
|
|
26723
27133
|
const key = body?.key;
|
|
26724
27134
|
const sessionId = body?.sessionId;
|
|
@@ -26751,7 +27161,7 @@ function createTelegramChannelRoutes(deps) {
|
|
|
26751
27161
|
console.error(`[telegram-native] op=turn-end key=${key} sessionId=${sid} replied=no delivered=none`);
|
|
26752
27162
|
return c.json({ ok: true, delivered: "none" });
|
|
26753
27163
|
});
|
|
26754
|
-
return
|
|
27164
|
+
return app65;
|
|
26755
27165
|
}
|
|
26756
27166
|
|
|
26757
27167
|
// app/lib/telegram/gateway/telegram-gateway.ts
|
|
@@ -26842,7 +27252,7 @@ var TelegramGateway = class {
|
|
|
26842
27252
|
{ key: input.senderId, text: input.text, messageId: `tg-${++this.seq}`, source, standingRules, scheduleProvenance: input.scheduleProvenance },
|
|
26843
27253
|
Date.now()
|
|
26844
27254
|
);
|
|
26845
|
-
const resolvedSessionId = adminSessionIdFor(
|
|
27255
|
+
const resolvedSessionId = (input.role === "admin" ? this.deps.resolveSessionOverride?.(input.accountId, input.senderId) : null) || adminSessionIdFor(
|
|
26846
27256
|
input.accountId,
|
|
26847
27257
|
input.senderId,
|
|
26848
27258
|
input.role === "public" && input.personId ? input.personId : void 0
|
|
@@ -26879,10 +27289,11 @@ var TelegramGateway = class {
|
|
|
26879
27289
|
function buildTelegramSpawnRequest(input) {
|
|
26880
27290
|
const personId = input.role === "public" && input.personId ? input.personId : void 0;
|
|
26881
27291
|
return {
|
|
26882
|
-
// Task 1746 — the deterministic per-sender id is the
|
|
26883
|
-
//
|
|
26884
|
-
//
|
|
26885
|
-
|
|
27292
|
+
// Task 1746 / 1789 — the deterministic per-sender id is the DEFAULT; an
|
|
27293
|
+
// operator re-seat sets a stable per-sender forward pointer that wins here.
|
|
27294
|
+
// Mirror of the WhatsApp builder; see its comment for the full distinction
|
|
27295
|
+
// between this pointer and Task 1526's removed per-firing seat.
|
|
27296
|
+
sessionId: input.overrideSessionId || adminSessionIdFor(input.accountId, input.senderId, personId),
|
|
26886
27297
|
role: input.role,
|
|
26887
27298
|
channel: "telegram",
|
|
26888
27299
|
personId,
|
|
@@ -26899,9 +27310,9 @@ function buildTelegramSpawnRequest(input) {
|
|
|
26899
27310
|
|
|
26900
27311
|
// app/lib/telegram/outbound/send-document.ts
|
|
26901
27312
|
import { realpathSync as realpathSync9 } from "fs";
|
|
26902
|
-
import { readFile as
|
|
26903
|
-
import { resolve as
|
|
26904
|
-
var
|
|
27313
|
+
import { readFile as readFile8, stat as stat8 } from "fs/promises";
|
|
27314
|
+
import { resolve as resolve34, basename as basename11 } from "path";
|
|
27315
|
+
var TAG51 = "[telegram:outbound]";
|
|
26905
27316
|
var TELEGRAM_DOCUMENT_MAX_BYTES = 50 * 1024 * 1024;
|
|
26906
27317
|
async function sendTelegramDocument(input) {
|
|
26907
27318
|
const { botToken, chatId, filePath, caption, maxyAccountId, platformRoot: platformRoot5 } = input;
|
|
@@ -26911,25 +27322,25 @@ async function sendTelegramDocument(input) {
|
|
|
26911
27322
|
if (!maxyAccountId || !platformRoot5) {
|
|
26912
27323
|
return { ok: false, status: 400, error: "Cannot validate file path: missing account or platform context" };
|
|
26913
27324
|
}
|
|
26914
|
-
const accountDir =
|
|
27325
|
+
const accountDir = resolve34(platformRoot5, "..", "data/accounts", maxyAccountId);
|
|
26915
27326
|
let resolvedPath;
|
|
26916
27327
|
try {
|
|
26917
27328
|
resolvedPath = realpathSync9(filePath);
|
|
26918
27329
|
const accountResolved = realpathSync9(accountDir);
|
|
26919
27330
|
if (!resolvedPath.startsWith(accountResolved + "/")) {
|
|
26920
|
-
console.error(`${
|
|
27331
|
+
console.error(`${TAG51} document REJECTED reason=outside_account_directory maxyAccountId=${maxyAccountId}`);
|
|
26921
27332
|
return { ok: false, status: 403, error: "Access denied: file is outside the account directory" };
|
|
26922
27333
|
}
|
|
26923
27334
|
} catch (err) {
|
|
26924
27335
|
const code = err.code;
|
|
26925
27336
|
if (code === "ENOENT") {
|
|
26926
|
-
console.error(`${
|
|
27337
|
+
console.error(`${TAG51} document ENOENT path=${filePath}`);
|
|
26927
27338
|
return { ok: false, status: 404, error: `File not found: ${filePath}` };
|
|
26928
27339
|
}
|
|
26929
|
-
console.error(`${
|
|
27340
|
+
console.error(`${TAG51} document path error: ${String(err)}`);
|
|
26930
27341
|
return { ok: false, status: 500, error: String(err) };
|
|
26931
27342
|
}
|
|
26932
|
-
const fileStat = await
|
|
27343
|
+
const fileStat = await stat8(resolvedPath);
|
|
26933
27344
|
if (fileStat.size > TELEGRAM_DOCUMENT_MAX_BYTES) {
|
|
26934
27345
|
return {
|
|
26935
27346
|
ok: false,
|
|
@@ -26938,7 +27349,7 @@ async function sendTelegramDocument(input) {
|
|
|
26938
27349
|
};
|
|
26939
27350
|
}
|
|
26940
27351
|
const filename = basename11(resolvedPath);
|
|
26941
|
-
const buffer = Buffer.from(await
|
|
27352
|
+
const buffer = Buffer.from(await readFile8(resolvedPath));
|
|
26942
27353
|
const form = new FormData();
|
|
26943
27354
|
form.append("chat_id", String(chatId));
|
|
26944
27355
|
if (caption) form.append("caption", caption);
|
|
@@ -26959,13 +27370,13 @@ async function sendTelegramDocument(input) {
|
|
|
26959
27370
|
error = e instanceof Error ? e.message : String(e);
|
|
26960
27371
|
}
|
|
26961
27372
|
console.error(
|
|
26962
|
-
`${
|
|
27373
|
+
`${TAG51} sent document to=${chatId} file=${filename} bytes=${fileStat.size} ok=${ok}` + (messageId !== void 0 ? ` id=${messageId}` : "")
|
|
26963
27374
|
);
|
|
26964
27375
|
return ok ? { ok: true, messageId } : { ok: false, status: 500, error };
|
|
26965
27376
|
}
|
|
26966
27377
|
|
|
26967
27378
|
// app/lib/telegram/outbound/file-delivery.ts
|
|
26968
|
-
var
|
|
27379
|
+
var TAG52 = "[telegram:outbound]";
|
|
26969
27380
|
function platformRoot4() {
|
|
26970
27381
|
return process.env.MAXY_PLATFORM_ROOT || "";
|
|
26971
27382
|
}
|
|
@@ -26977,11 +27388,11 @@ function makeTelegramSendFile(entry) {
|
|
|
26977
27388
|
botToken = (entry.role === "admin" ? tg?.adminBotToken : tg?.publicBotToken) ?? null;
|
|
26978
27389
|
}
|
|
26979
27390
|
if (!botToken) {
|
|
26980
|
-
console.error(`${
|
|
27391
|
+
console.error(`${TAG52} file-delivery reject reason=no-bot-token sender=${entry.senderId} role=${entry.role}`);
|
|
26981
27392
|
return { ok: false, error: "no-bot-token" };
|
|
26982
27393
|
}
|
|
26983
27394
|
if (entry.replyTarget == null) {
|
|
26984
|
-
console.error(`${
|
|
27395
|
+
console.error(`${TAG52} file-delivery reject reason=no-reply-target sender=${entry.senderId} role=${entry.role}`);
|
|
26985
27396
|
return { ok: false, error: "no-reply-target" };
|
|
26986
27397
|
}
|
|
26987
27398
|
const result = await sendTelegramDocument({
|
|
@@ -26998,7 +27409,7 @@ function makeTelegramSendFile(entry) {
|
|
|
26998
27409
|
function makeTelegramFileDelivery(entry) {
|
|
26999
27410
|
return makeFileDelivery({
|
|
27000
27411
|
entry,
|
|
27001
|
-
tag:
|
|
27412
|
+
tag: TAG52,
|
|
27002
27413
|
channel: "telegram",
|
|
27003
27414
|
sendFile: makeTelegramSendFile(entry)
|
|
27004
27415
|
});
|
|
@@ -27038,17 +27449,17 @@ function startTelegramNativeFileFollower(input) {
|
|
|
27038
27449
|
}
|
|
27039
27450
|
|
|
27040
27451
|
// server/telegram-descriptor.ts
|
|
27041
|
-
import { resolve as
|
|
27452
|
+
import { resolve as resolve35, join as join43 } from "path";
|
|
27042
27453
|
function telegramGatewayUrl() {
|
|
27043
27454
|
return `http://127.0.0.1:${process.env.MAXY_UI_INTERNAL_PORT ?? ""}`;
|
|
27044
27455
|
}
|
|
27045
27456
|
function telegramServerPath() {
|
|
27046
|
-
return process.env.MAXY_TELEGRAM_CHANNEL_SERVER_PATH ??
|
|
27457
|
+
return process.env.MAXY_TELEGRAM_CHANNEL_SERVER_PATH ?? resolve35(process.env.MAXY_PLATFORM_ROOT ?? join43(__dirname, ".."), "services/telegram-channel/dist/server.js");
|
|
27047
27458
|
}
|
|
27048
27459
|
|
|
27049
27460
|
// app/lib/channel-pty-bridge/public-session-end-review.ts
|
|
27050
27461
|
import { randomUUID as randomUUID16 } from "crypto";
|
|
27051
|
-
var
|
|
27462
|
+
var TAG53 = "[public-session-review]";
|
|
27052
27463
|
var CONSUMED_CAP = 500;
|
|
27053
27464
|
var consumed = /* @__PURE__ */ new Set();
|
|
27054
27465
|
function consumeOnce(sessionId) {
|
|
@@ -27075,7 +27486,7 @@ async function fetchJsonl(sessionId) {
|
|
|
27075
27486
|
return await res.text();
|
|
27076
27487
|
} catch (err) {
|
|
27077
27488
|
console.error(
|
|
27078
|
-
`${
|
|
27489
|
+
`${TAG53} jsonl-fetch-failed sessionId=${sessionId} err="${err instanceof Error ? err.message : String(err)}"`
|
|
27079
27490
|
);
|
|
27080
27491
|
return null;
|
|
27081
27492
|
}
|
|
@@ -27099,7 +27510,7 @@ async function fetchPriorWrites(sliceToken, accountId) {
|
|
|
27099
27510
|
const res = await fetch(url);
|
|
27100
27511
|
if (!res.ok) {
|
|
27101
27512
|
console.error(
|
|
27102
|
-
`${
|
|
27513
|
+
`${TAG53} prior-writes-fetch-failed sliceToken=${sliceToken.slice(0, 8)} status=${res.status}`
|
|
27103
27514
|
);
|
|
27104
27515
|
return [];
|
|
27105
27516
|
}
|
|
@@ -27107,7 +27518,7 @@ async function fetchPriorWrites(sliceToken, accountId) {
|
|
|
27107
27518
|
return Array.isArray(body.writes) ? body.writes : [];
|
|
27108
27519
|
} catch (err) {
|
|
27109
27520
|
console.error(
|
|
27110
|
-
`${
|
|
27521
|
+
`${TAG53} prior-writes-fetch-threw sliceToken=${sliceToken.slice(0, 8)} err="${err instanceof Error ? err.message : String(err)}"`
|
|
27111
27522
|
);
|
|
27112
27523
|
return [];
|
|
27113
27524
|
}
|
|
@@ -27136,7 +27547,7 @@ function composeInitialMessage(input) {
|
|
|
27136
27547
|
}
|
|
27137
27548
|
async function dispatchReviewer(input, initialMessage) {
|
|
27138
27549
|
const sessionId = randomUUID16();
|
|
27139
|
-
console.log(`${
|
|
27550
|
+
console.log(`${TAG53} route target=rc-spawn sessionId=${sessionId.slice(0, 8)} sourceSession=${input.sessionId.slice(0, 8)}`);
|
|
27140
27551
|
const spawned = await managerRcSpawn({
|
|
27141
27552
|
sessionId,
|
|
27142
27553
|
initialMessage,
|
|
@@ -27156,21 +27567,21 @@ async function firePublicSessionEndReview(input) {
|
|
|
27156
27567
|
const dispatchedAt = Date.now();
|
|
27157
27568
|
if (consumed.has(input.sessionId)) {
|
|
27158
27569
|
console.log(
|
|
27159
|
-
`${
|
|
27570
|
+
`${TAG53} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-already-reviewed`
|
|
27160
27571
|
);
|
|
27161
27572
|
return;
|
|
27162
27573
|
}
|
|
27163
27574
|
const jsonl = await fetchJsonl(input.sessionId);
|
|
27164
27575
|
if (!jsonl) {
|
|
27165
27576
|
console.log(
|
|
27166
|
-
`${
|
|
27577
|
+
`${TAG53} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-jsonl-missing`
|
|
27167
27578
|
);
|
|
27168
27579
|
return;
|
|
27169
27580
|
}
|
|
27170
27581
|
const operatorTurns = countOperatorTurns(jsonl);
|
|
27171
27582
|
if (operatorTurns === 0) {
|
|
27172
27583
|
console.log(
|
|
27173
|
-
`${
|
|
27584
|
+
`${TAG53} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-no-turns`
|
|
27174
27585
|
);
|
|
27175
27586
|
return;
|
|
27176
27587
|
}
|
|
@@ -27184,19 +27595,19 @@ async function firePublicSessionEndReview(input) {
|
|
|
27184
27595
|
});
|
|
27185
27596
|
if (!consumeOnce(input.sessionId)) {
|
|
27186
27597
|
console.log(
|
|
27187
|
-
`${
|
|
27598
|
+
`${TAG53} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-already-reviewed`
|
|
27188
27599
|
);
|
|
27189
27600
|
return;
|
|
27190
27601
|
}
|
|
27191
27602
|
const dispatched = await dispatchReviewer(input, initialMessage);
|
|
27192
27603
|
if (!dispatched.ok) {
|
|
27193
27604
|
console.error(
|
|
27194
|
-
`${
|
|
27605
|
+
`${TAG53} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-spawn-failed reason=${dispatched.reason}`
|
|
27195
27606
|
);
|
|
27196
27607
|
return;
|
|
27197
27608
|
}
|
|
27198
27609
|
console.log(
|
|
27199
|
-
`${
|
|
27610
|
+
`${TAG53} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=dispatched managerSessionId=${dispatched.managerSessionId} operatorTurns=${operatorTurns} priorWrites=${priorWrites.length} ms=${Date.now() - dispatchedAt}`
|
|
27200
27611
|
);
|
|
27201
27612
|
}
|
|
27202
27613
|
|
|
@@ -27330,8 +27741,8 @@ function broadcastAdminShutdown(reason) {
|
|
|
27330
27741
|
|
|
27331
27742
|
// ../lib/entitlement/src/index.ts
|
|
27332
27743
|
import { createPublicKey, createHash as createHash6, verify as cryptoVerify } from "crypto";
|
|
27333
|
-
import { existsSync as existsSync37, readFileSync as
|
|
27334
|
-
import { resolve as
|
|
27744
|
+
import { existsSync as existsSync37, readFileSync as readFileSync41, statSync as statSync16 } from "fs";
|
|
27745
|
+
import { resolve as resolve36 } from "path";
|
|
27335
27746
|
|
|
27336
27747
|
// ../lib/entitlement/src/canonicalize.ts
|
|
27337
27748
|
function canonicalize(value) {
|
|
@@ -27366,7 +27777,7 @@ var PUBKEY_SHA256 = "8eee6bcb33545fd13b16d3199a5735ca5db5062834c7b49dfe4f23801d9
|
|
|
27366
27777
|
var GRACE_DAYS = 7;
|
|
27367
27778
|
var GRACE_MS = GRACE_DAYS * 24 * 60 * 60 * 1e3;
|
|
27368
27779
|
function pubkeyPath(brand) {
|
|
27369
|
-
return
|
|
27780
|
+
return resolve36(brand.platformRoot, "lib", "entitlement", "rubytech-pubkey.pem");
|
|
27370
27781
|
}
|
|
27371
27782
|
var memo = null;
|
|
27372
27783
|
function memoKey(mtimeMs, account) {
|
|
@@ -27378,12 +27789,12 @@ function resolveEntitlement(brand, account) {
|
|
|
27378
27789
|
if (brand.commercialMode !== true) {
|
|
27379
27790
|
return logResolved(implicitTrust(account), null);
|
|
27380
27791
|
}
|
|
27381
|
-
const entitlementPath =
|
|
27792
|
+
const entitlementPath = resolve36(brand.configDir, "entitlement.json");
|
|
27382
27793
|
if (!existsSync37(entitlementPath)) {
|
|
27383
27794
|
return logResolved(anonymousFallback("missing"), { reason: "missing" });
|
|
27384
27795
|
}
|
|
27385
|
-
const
|
|
27386
|
-
const key = memoKey(
|
|
27796
|
+
const stat9 = statSync16(entitlementPath);
|
|
27797
|
+
const key = memoKey(stat9.mtimeMs, account);
|
|
27387
27798
|
if (memo && memo.key === key) {
|
|
27388
27799
|
return memo.result;
|
|
27389
27800
|
}
|
|
@@ -27394,7 +27805,7 @@ function resolveEntitlement(brand, account) {
|
|
|
27394
27805
|
function verifyAndResolve(brand, entitlementPath, account) {
|
|
27395
27806
|
let pubkeyPem;
|
|
27396
27807
|
try {
|
|
27397
|
-
pubkeyPem =
|
|
27808
|
+
pubkeyPem = readFileSync41(pubkeyPath(brand), "utf-8");
|
|
27398
27809
|
} catch (err) {
|
|
27399
27810
|
return logResolved(anonymousFallback("pubkey-missing"), {
|
|
27400
27811
|
reason: "pubkey-missing"
|
|
@@ -27408,7 +27819,7 @@ function verifyAndResolve(brand, entitlementPath, account) {
|
|
|
27408
27819
|
}
|
|
27409
27820
|
let envelope;
|
|
27410
27821
|
try {
|
|
27411
|
-
envelope = JSON.parse(
|
|
27822
|
+
envelope = JSON.parse(readFileSync41(entitlementPath, "utf-8"));
|
|
27412
27823
|
} catch {
|
|
27413
27824
|
return logResolved(anonymousFallback("malformed"), { reason: "malformed" });
|
|
27414
27825
|
}
|
|
@@ -27570,14 +27981,14 @@ function clientFrom(c) {
|
|
|
27570
27981
|
}
|
|
27571
27982
|
installMediaDownloadGuard();
|
|
27572
27983
|
var PLATFORM_ROOT8 = process.env.MAXY_PLATFORM_ROOT || "";
|
|
27573
|
-
var BRAND_JSON_PATH = PLATFORM_ROOT8 ?
|
|
27984
|
+
var BRAND_JSON_PATH = PLATFORM_ROOT8 ? join44(PLATFORM_ROOT8, "config", "brand.json") : "";
|
|
27574
27985
|
var BRAND = { productName: "Maxy", hostname: "maxy", configDir: ".maxy", marketingUrl: "https://getmaxy.com" };
|
|
27575
27986
|
if (BRAND_JSON_PATH && !existsSync38(BRAND_JSON_PATH)) {
|
|
27576
27987
|
console.error(`[brand] WARNING: brand.json not found at ${BRAND_JSON_PATH} \u2014 using Maxy defaults`);
|
|
27577
27988
|
}
|
|
27578
27989
|
if (BRAND_JSON_PATH && existsSync38(BRAND_JSON_PATH)) {
|
|
27579
27990
|
try {
|
|
27580
|
-
const parsed = JSON.parse(
|
|
27991
|
+
const parsed = JSON.parse(readFileSync42(BRAND_JSON_PATH, "utf-8"));
|
|
27581
27992
|
BRAND = { ...BRAND, ...parsed };
|
|
27582
27993
|
} catch (err) {
|
|
27583
27994
|
console.error(`[brand] Failed to parse brand.json: ${err.message}`);
|
|
@@ -27609,11 +28020,11 @@ var brandLoginOpts = {
|
|
|
27609
28020
|
appleTouchIconPath: brandAppIcon512Path,
|
|
27610
28021
|
themeColor: BRAND.defaultColors?.primary
|
|
27611
28022
|
};
|
|
27612
|
-
var ALIAS_DOMAINS_PATH =
|
|
28023
|
+
var ALIAS_DOMAINS_PATH = join44(homedir4(), BRAND.configDir, "alias-domains.json");
|
|
27613
28024
|
function loadAliasDomains() {
|
|
27614
28025
|
try {
|
|
27615
28026
|
if (!existsSync38(ALIAS_DOMAINS_PATH)) return null;
|
|
27616
|
-
const parsed = JSON.parse(
|
|
28027
|
+
const parsed = JSON.parse(readFileSync42(ALIAS_DOMAINS_PATH, "utf-8"));
|
|
27617
28028
|
if (!Array.isArray(parsed)) {
|
|
27618
28029
|
console.error("[alias-domains] malformed alias-domains.json \u2014 expected array");
|
|
27619
28030
|
return null;
|
|
@@ -27637,7 +28048,15 @@ watchFile(ALIAS_DOMAINS_PATH, { interval: 2e3 }, () => {
|
|
|
27637
28048
|
function isPublicHost(host) {
|
|
27638
28049
|
return host.startsWith("public.") || aliasDomains.has(host);
|
|
27639
28050
|
}
|
|
27640
|
-
var
|
|
28051
|
+
var app64 = new Hono();
|
|
28052
|
+
function resolveChannelOverride(accountId, channel, senderId) {
|
|
28053
|
+
try {
|
|
28054
|
+
const dir = listValidAccounts().find((a) => a.accountId === accountId)?.accountDir;
|
|
28055
|
+
return dir ? readChannelOverrideId(dir, channel, senderId) : null;
|
|
28056
|
+
} catch {
|
|
28057
|
+
return null;
|
|
28058
|
+
}
|
|
28059
|
+
}
|
|
27641
28060
|
var nativeFileFollowers = /* @__PURE__ */ new Map();
|
|
27642
28061
|
var webchatFileFollowers = /* @__PURE__ */ new Map();
|
|
27643
28062
|
async function fetchAccountStandingRules(accountId) {
|
|
@@ -27676,8 +28095,9 @@ async function recordPassiveDispatch(input) {
|
|
|
27676
28095
|
}
|
|
27677
28096
|
var waGateway = new WaGateway({
|
|
27678
28097
|
fetchStandingRules: fetchAccountStandingRules,
|
|
28098
|
+
resolveSessionOverride: (accountId, senderId) => resolveChannelOverride(accountId, "whatsapp", senderId),
|
|
27679
28099
|
gatewayUrl: `http://127.0.0.1:${process.env.MAXY_UI_INTERNAL_PORT ?? ""}`,
|
|
27680
|
-
serverPath: process.env.MAXY_WA_CHANNEL_SERVER_PATH ??
|
|
28100
|
+
serverPath: process.env.MAXY_WA_CHANNEL_SERVER_PATH ?? resolve37(process.env.MAXY_PLATFORM_ROOT ?? join44(__dirname, ".."), "services/whatsapp-channel/dist/server.js"),
|
|
27681
28101
|
// Task 751 / 1390 — file delivery on the native channel. `maxyAccountId` (the
|
|
27682
28102
|
// path-validation scope) is the sender's effective SESSION account, resolved
|
|
27683
28103
|
// once at the inbound gate and threaded here via the gateway's per-sender doc
|
|
@@ -27692,7 +28112,7 @@ var waGateway = new WaGateway({
|
|
|
27692
28112
|
caption,
|
|
27693
28113
|
accountId,
|
|
27694
28114
|
maxyAccountId,
|
|
27695
|
-
platformRoot:
|
|
28115
|
+
platformRoot: resolve37(process.env.MAXY_PLATFORM_ROOT ?? join44(__dirname, ".."))
|
|
27696
28116
|
});
|
|
27697
28117
|
return result.ok ? { ok: true, messageId: result.messageId } : { ok: false, error: result.error };
|
|
27698
28118
|
},
|
|
@@ -27744,7 +28164,17 @@ var waGateway = new WaGateway({
|
|
|
27744
28164
|
const managesAccount = effectiveAccountId !== accountId ? effectiveAccountId : "none";
|
|
27745
28165
|
console.error(`[whatsapp-native] op=account-manager-route senderId=${senderId} managesAccount=${managesAccount} effectiveAccount=${effectiveAccountId} source=gate`);
|
|
27746
28166
|
}
|
|
27747
|
-
const req = buildWaSpawnRequest({
|
|
28167
|
+
const req = buildWaSpawnRequest({
|
|
28168
|
+
accountId: effectiveAccountId,
|
|
28169
|
+
senderId,
|
|
28170
|
+
role,
|
|
28171
|
+
personId,
|
|
28172
|
+
gatewayUrl,
|
|
28173
|
+
serverPath,
|
|
28174
|
+
// Task 1789 — admin rows only: a public session's id keys on personId as
|
|
28175
|
+
// well as senderId, so the per-sender pointer is not applicable to it.
|
|
28176
|
+
overrideSessionId: role === "admin" ? resolveChannelOverride(effectiveAccountId, "whatsapp", senderId) : null
|
|
28177
|
+
});
|
|
27748
28178
|
await ensureSessionConversation({
|
|
27749
28179
|
accountId: effectiveAccountId,
|
|
27750
28180
|
sessionId: req.sessionId,
|
|
@@ -27790,13 +28220,13 @@ var waGateway = new WaGateway({
|
|
|
27790
28220
|
nativeFileFollowers.set(senderId, ac);
|
|
27791
28221
|
}
|
|
27792
28222
|
});
|
|
27793
|
-
|
|
28223
|
+
app64.route("/", waGateway.routes());
|
|
27794
28224
|
waGateway.startSweeper();
|
|
27795
28225
|
function runDuplicateSenderAudit() {
|
|
27796
28226
|
try {
|
|
27797
28227
|
const configDir2 = process.env.CLAUDE_CONFIG_DIR;
|
|
27798
28228
|
if (!configDir2) return;
|
|
27799
|
-
const projectsRoot =
|
|
28229
|
+
const projectsRoot = join44(configDir2, "projects");
|
|
27800
28230
|
const records = [];
|
|
27801
28231
|
for (const { path: jsonlPath, isSubagent, archived } of enumerateJsonls(projectsRoot)) {
|
|
27802
28232
|
if (isSubagent || archived) continue;
|
|
@@ -27806,6 +28236,8 @@ function runDuplicateSenderAudit() {
|
|
|
27806
28236
|
records.push({ sessionId, channel: meta.channel, senderId: meta.senderId, accountId: meta.accountId });
|
|
27807
28237
|
}
|
|
27808
28238
|
logDuplicateSenderGroups(findDuplicateSenderGroups(records));
|
|
28239
|
+
const now = Date.now();
|
|
28240
|
+
logUnseatedSenders(waGateway.unseatedSenders(now, 15 * 6e4), now);
|
|
27809
28241
|
} catch (err) {
|
|
27810
28242
|
console.error(`[whatsapp-native] op=duplicate-sender-audit-error error=${err instanceof Error ? err.message : String(err)}`);
|
|
27811
28243
|
}
|
|
@@ -27912,16 +28344,26 @@ var webchatGateway = new WebchatGateway({
|
|
|
27912
28344
|
firePublicSessionEndReview: (input) => firePublicSessionEndReview(input)
|
|
27913
28345
|
});
|
|
27914
28346
|
setWebchatGateway(webchatGateway);
|
|
27915
|
-
|
|
28347
|
+
app64.route("/", webchatGateway.routes());
|
|
27916
28348
|
webchatGateway.startSweeper();
|
|
27917
28349
|
webchatGateway.startPublicReaper();
|
|
27918
28350
|
var telegramFileFollowers = /* @__PURE__ */ new Map();
|
|
27919
28351
|
var telegramGateway = new TelegramGateway({
|
|
27920
28352
|
fetchStandingRules: fetchAccountStandingRules,
|
|
28353
|
+
resolveSessionOverride: (accountId, senderId) => resolveChannelOverride(accountId, "telegram", senderId),
|
|
27921
28354
|
gatewayUrl: telegramGatewayUrl(),
|
|
27922
28355
|
serverPath: telegramServerPath(),
|
|
27923
28356
|
ensureChannelSession: async ({ accountId, senderId, role, personId, chatId, gatewayUrl, serverPath }) => {
|
|
27924
|
-
const req = buildTelegramSpawnRequest({
|
|
28357
|
+
const req = buildTelegramSpawnRequest({
|
|
28358
|
+
accountId,
|
|
28359
|
+
senderId,
|
|
28360
|
+
role,
|
|
28361
|
+
personId,
|
|
28362
|
+
gatewayUrl,
|
|
28363
|
+
serverPath,
|
|
28364
|
+
// Task 1789 — admin rows only; see the WhatsApp twin.
|
|
28365
|
+
overrideSessionId: role === "admin" ? resolveChannelOverride(accountId, "telegram", senderId) : null
|
|
28366
|
+
});
|
|
27925
28367
|
const result = await managerRcSpawn(req);
|
|
27926
28368
|
if ("error" in result) {
|
|
27927
28369
|
console.error(`[telegram-native] spawn-failed senderId=${senderId} role=${role} error=${result.error} status=${result.status}`);
|
|
@@ -27946,7 +28388,7 @@ var telegramGateway = new TelegramGateway({
|
|
|
27946
28388
|
}
|
|
27947
28389
|
});
|
|
27948
28390
|
setTelegramGateway(telegramGateway);
|
|
27949
|
-
|
|
28391
|
+
app64.route("/", telegramGateway.routes());
|
|
27950
28392
|
telegramGateway.startSweeper();
|
|
27951
28393
|
var chatRoutes = createChatRoutes({
|
|
27952
28394
|
handleInbound: (input) => webchatGateway.handleInbound(input),
|
|
@@ -27980,19 +28422,19 @@ var scheduleInjectRoutes = createScheduleInjectRoutes({
|
|
|
27980
28422
|
return managed.account;
|
|
27981
28423
|
}
|
|
27982
28424
|
});
|
|
27983
|
-
|
|
27984
|
-
|
|
28425
|
+
app64.route("/api/channel/schedule-inject", scheduleInjectRoutes);
|
|
28426
|
+
app64.use("*", clientIpMiddleware);
|
|
27985
28427
|
function allowsSameOriginFraming(path3) {
|
|
27986
28428
|
return path3 === "/vnc-viewer.html" || path3.startsWith("/api/admin/attachment/") || path3.startsWith("/api/public-reader/attachment/") || path3 === "/api/admin/files/download";
|
|
27987
28429
|
}
|
|
27988
|
-
|
|
28430
|
+
app64.use("*", async (c, next) => {
|
|
27989
28431
|
await next();
|
|
27990
28432
|
c.header("X-Content-Type-Options", "nosniff");
|
|
27991
28433
|
c.header("Referrer-Policy", "strict-origin-when-cross-origin");
|
|
27992
28434
|
c.header("X-Frame-Options", allowsSameOriginFraming(c.req.path) ? "SAMEORIGIN" : "DENY");
|
|
27993
28435
|
});
|
|
27994
28436
|
var HTTP_LOG_PATHS = /* @__PURE__ */ new Set(["/vnc-viewer.html", "/vnc-popout.html"]);
|
|
27995
|
-
|
|
28437
|
+
app64.use("*", async (c, next) => {
|
|
27996
28438
|
if (!HTTP_LOG_PATHS.has(c.req.path)) {
|
|
27997
28439
|
await next();
|
|
27998
28440
|
return;
|
|
@@ -28010,7 +28452,7 @@ app63.use("*", async (c, next) => {
|
|
|
28010
28452
|
});
|
|
28011
28453
|
}
|
|
28012
28454
|
});
|
|
28013
|
-
|
|
28455
|
+
app64.use("*", async (c, next) => {
|
|
28014
28456
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
28015
28457
|
if (isOperatorHost(host, getOperatorDomains()) || !isPublicHost(host)) {
|
|
28016
28458
|
await next();
|
|
@@ -28048,7 +28490,7 @@ function resolveRemoteAuthOpts(c) {
|
|
|
28048
28490
|
return { ...brandLoginOpts, origin };
|
|
28049
28491
|
}
|
|
28050
28492
|
var MAX_LOGIN_BODY = 8 * 1024;
|
|
28051
|
-
|
|
28493
|
+
app64.post("/__remote-auth/login", async (c) => {
|
|
28052
28494
|
const client = clientFrom(c);
|
|
28053
28495
|
const clientIp = client.ip || "unknown";
|
|
28054
28496
|
if (!requestIsTlsTerminated(c)) {
|
|
@@ -28093,7 +28535,7 @@ app63.post("/__remote-auth/login", async (c) => {
|
|
|
28093
28535
|
}
|
|
28094
28536
|
});
|
|
28095
28537
|
});
|
|
28096
|
-
|
|
28538
|
+
app64.get("/__remote-auth/logout", (c) => {
|
|
28097
28539
|
const client = clientFrom(c);
|
|
28098
28540
|
const clientIp = client.ip || "unknown";
|
|
28099
28541
|
console.error(`[remote-auth] logout ip=${clientIp}`);
|
|
@@ -28106,7 +28548,7 @@ app63.get("/__remote-auth/logout", (c) => {
|
|
|
28106
28548
|
}
|
|
28107
28549
|
});
|
|
28108
28550
|
});
|
|
28109
|
-
|
|
28551
|
+
app64.post("/__remote-auth/change-password", async (c) => {
|
|
28110
28552
|
const client = clientFrom(c);
|
|
28111
28553
|
const clientIp = client.ip || "unknown";
|
|
28112
28554
|
const rateLimited = checkRateLimit(client);
|
|
@@ -28165,13 +28607,13 @@ app63.post("/__remote-auth/change-password", async (c) => {
|
|
|
28165
28607
|
return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(c), mode: "change", changeError: "Failed to save password", redirect }), 200);
|
|
28166
28608
|
}
|
|
28167
28609
|
});
|
|
28168
|
-
|
|
28610
|
+
app64.get("/__remote-auth/setup", (c) => {
|
|
28169
28611
|
if (isRemoteAuthConfigured()) {
|
|
28170
28612
|
return c.redirect("/");
|
|
28171
28613
|
}
|
|
28172
28614
|
return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(c), mode: "setup" }), 200);
|
|
28173
28615
|
});
|
|
28174
|
-
|
|
28616
|
+
app64.post("/__remote-auth/set-initial-password", async (c) => {
|
|
28175
28617
|
if (isRemoteAuthConfigured()) {
|
|
28176
28618
|
return c.redirect("/");
|
|
28177
28619
|
}
|
|
@@ -28209,10 +28651,10 @@ app63.post("/__remote-auth/set-initial-password", async (c) => {
|
|
|
28209
28651
|
return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(c), mode: "setup", setupError: "Failed to save password. Please try again." }), 200);
|
|
28210
28652
|
}
|
|
28211
28653
|
});
|
|
28212
|
-
|
|
28654
|
+
app64.get("/api/remote-auth/status", (c) => {
|
|
28213
28655
|
return c.json({ configured: isRemoteAuthConfigured() });
|
|
28214
28656
|
});
|
|
28215
|
-
|
|
28657
|
+
app64.post("/api/remote-auth/set-password", async (c) => {
|
|
28216
28658
|
let body;
|
|
28217
28659
|
try {
|
|
28218
28660
|
body = await c.req.json();
|
|
@@ -28251,10 +28693,10 @@ app63.post("/api/remote-auth/set-password", async (c) => {
|
|
|
28251
28693
|
return c.json({ error: "Failed to save password" }, 500);
|
|
28252
28694
|
}
|
|
28253
28695
|
});
|
|
28254
|
-
|
|
28696
|
+
app64.route("/api/_client-error", client_error_default);
|
|
28255
28697
|
console.log("[client-error-route] mounted");
|
|
28256
28698
|
var PWA_PUBLIC_PATHS = /* @__PURE__ */ new Set(["/sw.js", ...PWA_SURFACES.map((s) => s.manifestPath)]);
|
|
28257
|
-
|
|
28699
|
+
app64.use("*", async (c, next) => {
|
|
28258
28700
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
28259
28701
|
const path3 = c.req.path;
|
|
28260
28702
|
if (path3 === "/favicon.ico" || path3.startsWith("/assets/") || path3.startsWith("/brand/") || // Public free/busy is read by the booking page (a separate Cloudflare Pages
|
|
@@ -28298,27 +28740,28 @@ app63.use("*", async (c, next) => {
|
|
|
28298
28740
|
}
|
|
28299
28741
|
return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(c), redirect: path3 }), 200);
|
|
28300
28742
|
});
|
|
28301
|
-
|
|
28302
|
-
|
|
28303
|
-
|
|
28304
|
-
|
|
28305
|
-
|
|
28306
|
-
|
|
28307
|
-
|
|
28743
|
+
app64.route("/api/health", health_default);
|
|
28744
|
+
app64.route("/api/chat", chatRoutes);
|
|
28745
|
+
app64.route("/api/whatsapp", whatsapp_default);
|
|
28746
|
+
app64.route("/api/storage", storage_broker_default);
|
|
28747
|
+
app64.route("/api/whatsapp-reader", whatsapp_reader_default);
|
|
28748
|
+
app64.route("/api/public-reader", public_reader_default);
|
|
28749
|
+
app64.route("/api/webchat", createWebchatRoutes({
|
|
28308
28750
|
handleInbound: (input) => webchatGateway.handleInbound(input),
|
|
28309
28751
|
// Task 940 — the permission relay's pointer read + verdict write.
|
|
28310
28752
|
pendingPromptFor: (key) => webchatGateway.pendingPromptFor(key),
|
|
28311
28753
|
deliveryFailureFor: (key) => webchatGateway.deliveryFailureFor(key),
|
|
28312
28754
|
resolvePermissionVerdict: (key, requestId, behavior) => webchatGateway.resolvePermissionVerdict(key, requestId, behavior)
|
|
28313
28755
|
}));
|
|
28314
|
-
|
|
28315
|
-
|
|
28316
|
-
|
|
28317
|
-
|
|
28318
|
-
|
|
28319
|
-
|
|
28320
|
-
|
|
28321
|
-
|
|
28756
|
+
app64.route("/api/webchat/greeting", webchat_greeting_default);
|
|
28757
|
+
app64.route("/api/telegram", telegram_default);
|
|
28758
|
+
app64.route("/api/quickbooks", quickbooks_default);
|
|
28759
|
+
app64.route("/api/onboarding", onboarding_default);
|
|
28760
|
+
app64.route("/api/admin", admin_default);
|
|
28761
|
+
app64.route("/api/access", access_default);
|
|
28762
|
+
app64.route("/api/session", session_default2);
|
|
28763
|
+
app64.route("/api/calendar", calendar_public_default);
|
|
28764
|
+
app64.route("/api/portal", portal_fetch_default);
|
|
28322
28765
|
var SAFE_SLUG_RE2 = /^[a-z][a-z0-9-]{2,49}$/;
|
|
28323
28766
|
var SAFE_FILENAME_RE = /^[a-z0-9_][a-z0-9_.-]{0,99}$/i;
|
|
28324
28767
|
var IMAGE_MIME = {
|
|
@@ -28330,7 +28773,7 @@ var IMAGE_MIME = {
|
|
|
28330
28773
|
".svg": "image/svg+xml",
|
|
28331
28774
|
".ico": "image/x-icon"
|
|
28332
28775
|
};
|
|
28333
|
-
|
|
28776
|
+
app64.get("/agent-assets/:slug/:filename", (c) => {
|
|
28334
28777
|
const slug = c.req.param("slug");
|
|
28335
28778
|
const filename = c.req.param("filename");
|
|
28336
28779
|
if (!SAFE_SLUG_RE2.test(slug)) {
|
|
@@ -28346,8 +28789,8 @@ app63.get("/agent-assets/:slug/:filename", (c) => {
|
|
|
28346
28789
|
console.error(`[agent-assets] no-account slug=${slug} file=${filename}`);
|
|
28347
28790
|
return c.text("Not found", 404);
|
|
28348
28791
|
}
|
|
28349
|
-
const filePath =
|
|
28350
|
-
const expectedDir =
|
|
28792
|
+
const filePath = resolve37(account.accountDir, "agents", slug, "assets", filename);
|
|
28793
|
+
const expectedDir = resolve37(account.accountDir, "agents", slug, "assets");
|
|
28351
28794
|
if (!filePath.startsWith(expectedDir + "/")) {
|
|
28352
28795
|
console.error(`[agent-assets] path-traversal-rejected slug=${slug} file=${filename}`);
|
|
28353
28796
|
return c.text("Forbidden", 403);
|
|
@@ -28359,13 +28802,13 @@ app63.get("/agent-assets/:slug/:filename", (c) => {
|
|
|
28359
28802
|
const ext = "." + filename.split(".").pop()?.toLowerCase();
|
|
28360
28803
|
const contentType = IMAGE_MIME[ext] || "application/octet-stream";
|
|
28361
28804
|
console.log(`[agent-assets] serve slug=${slug} file=${filename} status=200`);
|
|
28362
|
-
const body =
|
|
28805
|
+
const body = readFileSync42(filePath);
|
|
28363
28806
|
return c.body(body, 200, {
|
|
28364
28807
|
"Content-Type": contentType,
|
|
28365
28808
|
"Cache-Control": "public, max-age=3600"
|
|
28366
28809
|
});
|
|
28367
28810
|
});
|
|
28368
|
-
|
|
28811
|
+
app64.get("/generated/:filename", (c) => {
|
|
28369
28812
|
const filename = c.req.param("filename");
|
|
28370
28813
|
if (!SAFE_FILENAME_RE.test(filename) || filename.includes("..")) {
|
|
28371
28814
|
console.error(`[generated] serve file=${filename} status=403`);
|
|
@@ -28376,8 +28819,8 @@ app63.get("/generated/:filename", (c) => {
|
|
|
28376
28819
|
console.error(`[generated] serve file=${filename} status=404`);
|
|
28377
28820
|
return c.text("Not found", 404);
|
|
28378
28821
|
}
|
|
28379
|
-
const filePath =
|
|
28380
|
-
const expectedDir =
|
|
28822
|
+
const filePath = resolve37(account.accountDir, "generated", filename);
|
|
28823
|
+
const expectedDir = resolve37(account.accountDir, "generated");
|
|
28381
28824
|
if (!filePath.startsWith(expectedDir + "/")) {
|
|
28382
28825
|
console.error(`[generated] serve file=${filename} status=403`);
|
|
28383
28826
|
return c.text("Forbidden", 403);
|
|
@@ -28389,22 +28832,22 @@ app63.get("/generated/:filename", (c) => {
|
|
|
28389
28832
|
const ext = "." + filename.split(".").pop()?.toLowerCase();
|
|
28390
28833
|
const contentType = IMAGE_MIME[ext] || "application/octet-stream";
|
|
28391
28834
|
console.log(`[generated] serve file=${filename} status=200`);
|
|
28392
|
-
const body =
|
|
28835
|
+
const body = readFileSync42(filePath);
|
|
28393
28836
|
return c.body(body, 200, {
|
|
28394
28837
|
"Content-Type": contentType,
|
|
28395
28838
|
"Cache-Control": "public, max-age=86400"
|
|
28396
28839
|
});
|
|
28397
28840
|
});
|
|
28398
|
-
|
|
28399
|
-
|
|
28400
|
-
|
|
28401
|
-
|
|
28841
|
+
app64.route("/sites", sites_default);
|
|
28842
|
+
app64.route("/listings", listings_default);
|
|
28843
|
+
app64.route("/v", visitor_event_default);
|
|
28844
|
+
app64.route("/v", visitor_consent_default);
|
|
28402
28845
|
var htmlCache = /* @__PURE__ */ new Map();
|
|
28403
28846
|
var brandLogoPath = "/brand/maxy-monochrome.png";
|
|
28404
28847
|
var brandIconPath = "/brand/maxy-monochrome.png";
|
|
28405
28848
|
if (BRAND_JSON_PATH && existsSync38(BRAND_JSON_PATH)) {
|
|
28406
28849
|
try {
|
|
28407
|
-
const fullBrand = JSON.parse(
|
|
28850
|
+
const fullBrand = JSON.parse(readFileSync42(BRAND_JSON_PATH, "utf-8"));
|
|
28408
28851
|
if (fullBrand.assets?.logo) brandLogoPath = `/brand/${fullBrand.assets.logo}`;
|
|
28409
28852
|
brandIconPath = fullBrand.assets?.icon ? `/brand/${fullBrand.assets.icon}` : brandLogoPath;
|
|
28410
28853
|
} catch {
|
|
@@ -28431,11 +28874,11 @@ var brandScript = `<script>window.__BRAND__=${JSON.stringify({
|
|
|
28431
28874
|
var brandThemeColor = BRAND.defaultColors?.primary ?? "#000000";
|
|
28432
28875
|
var brandBackgroundColor = BRAND.defaultColors?.background ?? "#ffffff";
|
|
28433
28876
|
var brandAppIconsExist = [brandAppIcon192Path, brandAppIcon512Path, brandMaskableIconPath].every(
|
|
28434
|
-
(p) => existsSync38(
|
|
28877
|
+
(p) => existsSync38(resolve37(process.cwd(), "public", p.replace(/^\//, "")))
|
|
28435
28878
|
);
|
|
28436
28879
|
var SW_SOURCE = (() => {
|
|
28437
28880
|
try {
|
|
28438
|
-
return
|
|
28881
|
+
return readFileSync42(resolve37(process.cwd(), "public", "sw.js"), "utf-8");
|
|
28439
28882
|
} catch {
|
|
28440
28883
|
return null;
|
|
28441
28884
|
}
|
|
@@ -28443,9 +28886,9 @@ var SW_SOURCE = (() => {
|
|
|
28443
28886
|
function readInstalledVersion() {
|
|
28444
28887
|
try {
|
|
28445
28888
|
if (!PLATFORM_ROOT8) return "unknown";
|
|
28446
|
-
const versionFile =
|
|
28889
|
+
const versionFile = join44(PLATFORM_ROOT8, "config", `.${BRAND.hostname}-version`);
|
|
28447
28890
|
if (!existsSync38(versionFile)) return "unknown";
|
|
28448
|
-
const content =
|
|
28891
|
+
const content = readFileSync42(versionFile, "utf-8").trim();
|
|
28449
28892
|
return content || "unknown";
|
|
28450
28893
|
} catch {
|
|
28451
28894
|
return "unknown";
|
|
@@ -28486,7 +28929,7 @@ var clientErrorReporterScript = `<script>
|
|
|
28486
28929
|
function cachedHtml(file) {
|
|
28487
28930
|
let html = htmlCache.get(file);
|
|
28488
28931
|
if (!html) {
|
|
28489
|
-
html =
|
|
28932
|
+
html = readFileSync42(resolve37(process.cwd(), "public", file), "utf-8");
|
|
28490
28933
|
const productNameEsc = escapeHtml(BRAND.productName);
|
|
28491
28934
|
html = html.replace(/<title>([^<]*)<\/title>/, (_match, inner) => `<title>${escapeHtml(inner).replace(/Maxy/g, productNameEsc)}</title>`);
|
|
28492
28935
|
html = html.replace('href="/favicon.ico"', `href="${escapeHtml(brandFaviconPath)}"`);
|
|
@@ -28503,13 +28946,13 @@ ${clientErrorReporterScript}
|
|
|
28503
28946
|
return html;
|
|
28504
28947
|
}
|
|
28505
28948
|
function loadBrandingCache(agentSlug) {
|
|
28506
|
-
const configDir2 =
|
|
28949
|
+
const configDir2 = join44(homedir4(), BRAND.configDir);
|
|
28507
28950
|
try {
|
|
28508
28951
|
const accountId = getDefaultAccountId();
|
|
28509
28952
|
if (!accountId) return null;
|
|
28510
|
-
const cachePath =
|
|
28953
|
+
const cachePath = join44(configDir2, "branding-cache", accountId, `${agentSlug}.json`);
|
|
28511
28954
|
if (!existsSync38(cachePath)) return null;
|
|
28512
|
-
return JSON.parse(
|
|
28955
|
+
return JSON.parse(readFileSync42(cachePath, "utf-8"));
|
|
28513
28956
|
} catch {
|
|
28514
28957
|
return null;
|
|
28515
28958
|
}
|
|
@@ -28553,7 +28996,7 @@ function brandedPublicHtml(agentSlug) {
|
|
|
28553
28996
|
function agentUnavailableHtml() {
|
|
28554
28997
|
return `<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>${escapeHtml(BRAND.productName)}</title></head><body style="font-family:system-ui,sans-serif;max-width:32rem;margin:4rem auto;padding:0 1.5rem;color:#222"><h1 style="font-size:1.25rem">Agent unavailable</h1><p>This agent isn't available right now. If you reached this page from a saved link, the agent may have been turned off.</p></body></html>`;
|
|
28555
28998
|
}
|
|
28556
|
-
|
|
28999
|
+
app64.get("/", (c) => {
|
|
28557
29000
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
28558
29001
|
const klass = classifyHost(host, getOperatorDomains(), isPublicHost);
|
|
28559
29002
|
if (klass === "operator") {
|
|
@@ -28575,12 +29018,12 @@ app63.get("/", (c) => {
|
|
|
28575
29018
|
console.log(`[host-class] host=${host} class=admin served=index.html`);
|
|
28576
29019
|
return c.html(cachedHtml("index.html"));
|
|
28577
29020
|
});
|
|
28578
|
-
|
|
29021
|
+
app64.get("/public", (c) => {
|
|
28579
29022
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
28580
29023
|
if (isPublicHost(host)) return c.text("Not found", 404);
|
|
28581
29024
|
return c.html(cachedHtml("public.html"));
|
|
28582
29025
|
});
|
|
28583
|
-
|
|
29026
|
+
app64.get("/public-chat", (c) => {
|
|
28584
29027
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
28585
29028
|
if (isPublicHost(host)) return c.text("Not found", 404);
|
|
28586
29029
|
return c.html(cachedHtml("public.html"));
|
|
@@ -28599,12 +29042,12 @@ async function logViewerFetch(c, next) {
|
|
|
28599
29042
|
duration_ms: Date.now() - start
|
|
28600
29043
|
});
|
|
28601
29044
|
}
|
|
28602
|
-
|
|
28603
|
-
|
|
28604
|
-
|
|
29045
|
+
app64.use("/vnc-viewer.html", logViewerFetch);
|
|
29046
|
+
app64.use("/vnc-popout.html", logViewerFetch);
|
|
29047
|
+
app64.get("/vnc-popout.html", (c) => {
|
|
28605
29048
|
let html = htmlCache.get("vnc-popout.html");
|
|
28606
29049
|
if (!html) {
|
|
28607
|
-
html =
|
|
29050
|
+
html = readFileSync42(resolve37(process.cwd(), "public", "vnc-popout.html"), "utf-8");
|
|
28608
29051
|
const name = escapeHtml(BRAND.productName);
|
|
28609
29052
|
html = html.replace("<title>Browser \u2014 Maxy</title>", `<title>${name}</title>`);
|
|
28610
29053
|
html = html.replace("</head>", ` ${brandScript}
|
|
@@ -28614,7 +29057,7 @@ app63.get("/vnc-popout.html", (c) => {
|
|
|
28614
29057
|
}
|
|
28615
29058
|
return c.html(html);
|
|
28616
29059
|
});
|
|
28617
|
-
|
|
29060
|
+
app64.post("/api/vnc/client-event", async (c) => {
|
|
28618
29061
|
let body;
|
|
28619
29062
|
try {
|
|
28620
29063
|
body = await c.req.json();
|
|
@@ -28635,11 +29078,11 @@ app63.post("/api/vnc/client-event", async (c) => {
|
|
|
28635
29078
|
});
|
|
28636
29079
|
return c.json({ ok: true });
|
|
28637
29080
|
});
|
|
28638
|
-
|
|
29081
|
+
app64.get("/g/:slug", (c) => {
|
|
28639
29082
|
return c.html(brandedPublicHtml(resolveDefaultSlug() ?? void 0));
|
|
28640
29083
|
});
|
|
28641
29084
|
for (const pwa of PWA_SURFACES) {
|
|
28642
|
-
|
|
29085
|
+
app64.get(pwa.manifestPath, (c) => {
|
|
28643
29086
|
const manifest = buildManifest(pwa, {
|
|
28644
29087
|
productName: BRAND.productName,
|
|
28645
29088
|
appIcon192: brandAppIcon192Path,
|
|
@@ -28655,7 +29098,7 @@ for (const pwa of PWA_SURFACES) {
|
|
|
28655
29098
|
return c.body(JSON.stringify(manifest));
|
|
28656
29099
|
});
|
|
28657
29100
|
}
|
|
28658
|
-
|
|
29101
|
+
app64.get("/sw.js", (c) => {
|
|
28659
29102
|
if (SW_SOURCE == null) {
|
|
28660
29103
|
console.error("[pwa] op=sw status=500 reason=sw-source-missing");
|
|
28661
29104
|
return c.text("Service worker unavailable", 500);
|
|
@@ -28664,12 +29107,12 @@ app63.get("/sw.js", (c) => {
|
|
|
28664
29107
|
console.log(`[pwa] op=sw status=200 ct=${SW_CONTENT_TYPE}`);
|
|
28665
29108
|
return c.body(SW_SOURCE);
|
|
28666
29109
|
});
|
|
28667
|
-
|
|
29110
|
+
app64.get("/graph", (c) => {
|
|
28668
29111
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
28669
29112
|
if (isPublicHost(host) || isOperatorHost(host, getOperatorDomains())) return c.text("Not found", 404);
|
|
28670
29113
|
return c.html(cachedHtml("graph.html"));
|
|
28671
29114
|
});
|
|
28672
|
-
|
|
29115
|
+
app64.get("/chat", (c) => {
|
|
28673
29116
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
28674
29117
|
if (isOperatorHost(host, getOperatorDomains())) {
|
|
28675
29118
|
console.log(`[host-class] host=${host} class=operator served=operator.html`);
|
|
@@ -28678,32 +29121,32 @@ app63.get("/chat", (c) => {
|
|
|
28678
29121
|
if (isPublicHost(host)) return c.text("Not found", 404);
|
|
28679
29122
|
return c.html(cachedHtml("chat.html"));
|
|
28680
29123
|
});
|
|
28681
|
-
|
|
29124
|
+
app64.get("/data", (c) => {
|
|
28682
29125
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
28683
29126
|
if (isPublicHost(host)) return c.text("Not found", 404);
|
|
28684
29127
|
return c.html(cachedHtml("data.html"));
|
|
28685
29128
|
});
|
|
28686
|
-
|
|
29129
|
+
app64.get("/tasks", (c) => {
|
|
28687
29130
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
28688
29131
|
if (isPublicHost(host)) return c.text("Not found", 404);
|
|
28689
29132
|
return c.html(cachedHtml("tasks.html"));
|
|
28690
29133
|
});
|
|
28691
|
-
|
|
29134
|
+
app64.get("/activity", (c) => {
|
|
28692
29135
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
28693
29136
|
if (isPublicHost(host)) return c.text("Not found", 404);
|
|
28694
29137
|
return c.html(cachedHtml("activity.html"));
|
|
28695
29138
|
});
|
|
28696
|
-
|
|
29139
|
+
app64.get("/calendar", (c) => {
|
|
28697
29140
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
28698
29141
|
if (isPublicHost(host)) return c.text("Not found", 404);
|
|
28699
29142
|
return c.html(cachedHtml("calendar.html"));
|
|
28700
29143
|
});
|
|
28701
|
-
|
|
29144
|
+
app64.get("/browser", (c) => {
|
|
28702
29145
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
28703
29146
|
if (isPublicHost(host) || isOperatorHost(host, getOperatorDomains())) return c.text("Not found", 404);
|
|
28704
29147
|
return c.html(cachedHtml("browser.html"));
|
|
28705
29148
|
});
|
|
28706
|
-
|
|
29149
|
+
app64.get("/:slug", async (c, next) => {
|
|
28707
29150
|
const slug = c.req.param("slug");
|
|
28708
29151
|
if (AGENT_SLUG_PATTERN.test(`/${slug}`)) {
|
|
28709
29152
|
const account = resolveAccount();
|
|
@@ -28718,13 +29161,13 @@ app63.get("/:slug", async (c, next) => {
|
|
|
28718
29161
|
await next();
|
|
28719
29162
|
});
|
|
28720
29163
|
if (brandFaviconPath !== "/favicon.ico") {
|
|
28721
|
-
|
|
29164
|
+
app64.get("/favicon.ico", (c) => {
|
|
28722
29165
|
c.header("Cache-Control", "public, max-age=300");
|
|
28723
29166
|
return c.redirect(brandFaviconPath, 302);
|
|
28724
29167
|
});
|
|
28725
29168
|
}
|
|
28726
|
-
|
|
28727
|
-
|
|
29169
|
+
app64.use("/*", serveStatic({ root: "./public" }));
|
|
29170
|
+
app64.all("*", (c) => {
|
|
28728
29171
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
28729
29172
|
const path3 = c.req.path;
|
|
28730
29173
|
if (isPublicHost(host)) {
|
|
@@ -28738,11 +29181,11 @@ app63.all("*", (c) => {
|
|
|
28738
29181
|
});
|
|
28739
29182
|
var port = requirePortEnv("MAXY_UI_INTERNAL_PORT", { tag: "ui-server" });
|
|
28740
29183
|
var hostname = process.env.HOSTNAME ?? "127.0.0.1";
|
|
28741
|
-
var httpServer = serve({ fetch:
|
|
29184
|
+
var httpServer = serve({ fetch: app64.fetch, port, hostname });
|
|
28742
29185
|
console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
28743
29186
|
{
|
|
28744
|
-
const reconcilePlatformRoot = process.env.MAXY_PLATFORM_ROOT ??
|
|
28745
|
-
const reconcileScript =
|
|
29187
|
+
const reconcilePlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join44(__dirname, "..");
|
|
29188
|
+
const reconcileScript = resolve37(reconcilePlatformRoot, "plugins/scheduling/mcp/dist/scripts/reconcile-bookings.js");
|
|
28746
29189
|
const RECONCILE_INTERVAL_MS = 12e4;
|
|
28747
29190
|
const runReconcile = (ctx) => {
|
|
28748
29191
|
if (!existsSync38(reconcileScript)) return;
|
|
@@ -28767,8 +29210,8 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
28767
29210
|
});
|
|
28768
29211
|
}
|
|
28769
29212
|
{
|
|
28770
|
-
const outlookPlatformRoot = process.env.MAXY_PLATFORM_ROOT ??
|
|
28771
|
-
const outlookScript =
|
|
29213
|
+
const outlookPlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join44(__dirname, "..");
|
|
29214
|
+
const outlookScript = resolve37(outlookPlatformRoot, "plugins/outlook/mcp/dist/scripts/complete-registration.js");
|
|
28772
29215
|
const OUTLOOK_COMPLETE_INTERVAL_MS = 3e4;
|
|
28773
29216
|
const runOutlookComplete = (ctx) => {
|
|
28774
29217
|
if (!existsSync38(outlookScript)) return;
|
|
@@ -28793,8 +29236,8 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
28793
29236
|
});
|
|
28794
29237
|
}
|
|
28795
29238
|
{
|
|
28796
|
-
const auditRoot = process.env.MAXY_PLATFORM_ROOT ??
|
|
28797
|
-
const strandedAccountsDir =
|
|
29239
|
+
const auditRoot = process.env.MAXY_PLATFORM_ROOT ?? join44(__dirname, "..");
|
|
29240
|
+
const strandedAccountsDir = resolve37(auditRoot, "..", "data/accounts");
|
|
28798
29241
|
const STRANDED_AUDIT_INTERVAL_MS = 3e5;
|
|
28799
29242
|
const STRANDED_AGE_MS = 16 * 6e4;
|
|
28800
29243
|
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;
|
|
@@ -28804,7 +29247,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
28804
29247
|
const now = Date.now();
|
|
28805
29248
|
for (const name of readdirSync26(strandedAccountsDir)) {
|
|
28806
29249
|
if (!STRANDED_UUID_RE.test(name)) continue;
|
|
28807
|
-
const pendingPath2 =
|
|
29250
|
+
const pendingPath2 = resolve37(strandedAccountsDir, name, "secrets/outlook/pending-devicecode.enc");
|
|
28808
29251
|
if (!existsSync38(pendingPath2)) continue;
|
|
28809
29252
|
const ageMs = now - statSync17(pendingPath2).mtimeMs;
|
|
28810
29253
|
if (ageMs > STRANDED_AGE_MS) {
|
|
@@ -28823,8 +29266,8 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
28823
29266
|
});
|
|
28824
29267
|
}
|
|
28825
29268
|
{
|
|
28826
|
-
const googleRoot = process.env.MAXY_PLATFORM_ROOT ??
|
|
28827
|
-
const googleAccountsDir =
|
|
29269
|
+
const googleRoot = process.env.MAXY_PLATFORM_ROOT ?? join44(__dirname, "..");
|
|
29270
|
+
const googleAccountsDir = resolve37(googleRoot, "..", "data/accounts");
|
|
28828
29271
|
const GOOGLE_PENDING_AUDIT_INTERVAL_MS = 3e5;
|
|
28829
29272
|
const runGooglePendingAuditSafe = () => {
|
|
28830
29273
|
try {
|
|
@@ -28839,7 +29282,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
28839
29282
|
firstRunDelayMs: 27e3,
|
|
28840
29283
|
run: runGooglePendingAuditSafe
|
|
28841
29284
|
});
|
|
28842
|
-
const googleAuditScript =
|
|
29285
|
+
const googleAuditScript = resolve37(googleRoot, "plugins/google/mcp/dist/scripts/account-audit.js");
|
|
28843
29286
|
const GOOGLE_ACCOUNT_AUDIT_INTERVAL_MS = 36e5;
|
|
28844
29287
|
const runGoogleAccountAudit = (ctx) => {
|
|
28845
29288
|
if (!existsSync38(googleAuditScript)) return;
|
|
@@ -28867,7 +29310,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
28867
29310
|
});
|
|
28868
29311
|
}
|
|
28869
29312
|
{
|
|
28870
|
-
const auditPlatformRoot = process.env.MAXY_PLATFORM_ROOT ??
|
|
29313
|
+
const auditPlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join44(__dirname, "..");
|
|
28871
29314
|
const STORAGE_AUDIT_INTERVAL_MS = 3e5;
|
|
28872
29315
|
const runStorageAuditSafe = () => runStorageAudit(auditPlatformRoot).catch((err) => {
|
|
28873
29316
|
console.error(`[storage-audit] error="${err.message}"`);
|
|
@@ -28901,8 +29344,8 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
28901
29344
|
});
|
|
28902
29345
|
}
|
|
28903
29346
|
{
|
|
28904
|
-
const publishPlatformRoot = process.env.MAXY_PLATFORM_ROOT ??
|
|
28905
|
-
const publishScript =
|
|
29347
|
+
const publishPlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join44(__dirname, "..");
|
|
29348
|
+
const publishScript = resolve37(publishPlatformRoot, "plugins/scheduling/mcp/dist/scripts/publish-availability.js");
|
|
28906
29349
|
const PUBLISH_INTERVAL_MS = 3e5;
|
|
28907
29350
|
const currentAccount = () => {
|
|
28908
29351
|
try {
|
|
@@ -28932,21 +29375,21 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
28932
29375
|
}
|
|
28933
29376
|
};
|
|
28934
29377
|
const auditSnapshotAge = (account) => {
|
|
28935
|
-
const availPath =
|
|
29378
|
+
const availPath = resolve37(account.accountDir, "calendar-availability.json");
|
|
28936
29379
|
let hasBookingSite = false;
|
|
28937
29380
|
try {
|
|
28938
29381
|
if (existsSync38(availPath)) {
|
|
28939
|
-
const cfg = JSON.parse(
|
|
29382
|
+
const cfg = JSON.parse(readFileSync42(availPath, "utf-8"));
|
|
28940
29383
|
hasBookingSite = typeof cfg.bookingDbName === "string" && cfg.bookingDbName.length > 0;
|
|
28941
29384
|
}
|
|
28942
29385
|
} catch {
|
|
28943
29386
|
}
|
|
28944
29387
|
if (!hasBookingSite) return;
|
|
28945
|
-
const statePath =
|
|
29388
|
+
const statePath = resolve37(account.accountDir, "state", "booking-availability", "last-publish.json");
|
|
28946
29389
|
let lastSuccessAt = null;
|
|
28947
29390
|
if (existsSync38(statePath)) {
|
|
28948
29391
|
try {
|
|
28949
|
-
const rec = JSON.parse(
|
|
29392
|
+
const rec = JSON.parse(readFileSync42(statePath, "utf-8"));
|
|
28950
29393
|
lastSuccessAt = rec.lastSuccessAt ?? null;
|
|
28951
29394
|
} catch {
|
|
28952
29395
|
console.error(`[calendar-availability] op=audit accountId=${account.accountId} snapshotAgeMs=na reason=bad-state-file`);
|
|
@@ -28974,8 +29417,8 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
28974
29417
|
startTimeEntryCensus(() => getSession());
|
|
28975
29418
|
startLedgerCensus(() => getSession());
|
|
28976
29419
|
{
|
|
28977
|
-
const auditPlatformRoot = process.env.MAXY_PLATFORM_ROOT ??
|
|
28978
|
-
const auditScript =
|
|
29420
|
+
const auditPlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join44(__dirname, "..");
|
|
29421
|
+
const auditScript = resolve37(auditPlatformRoot, "plugins/connector/mcp/dist/scripts/audit-connectors.js");
|
|
28979
29422
|
const auditLogDir = process.env.LOG_DIR ?? LOG_DIR;
|
|
28980
29423
|
const CONNECTOR_AUDIT_INTERVAL_MS = 3e5;
|
|
28981
29424
|
const runConnectorAudit = (ctx) => {
|
|
@@ -29047,6 +29490,7 @@ var SUBAPP_MANIFEST = [
|
|
|
29047
29490
|
{ prefix: "/api/access", file: "server/routes/access/index.ts", subapp: access_default },
|
|
29048
29491
|
{ prefix: "/api/session", file: "server/routes/session.ts", subapp: session_default2 },
|
|
29049
29492
|
{ prefix: "/api/calendar", file: "server/routes/calendar-public.ts", subapp: calendar_public_default },
|
|
29493
|
+
{ prefix: "/api/portal", file: "server/routes/portal-fetch.ts", subapp: portal_fetch_default },
|
|
29050
29494
|
{ prefix: "/api/_client-error", file: "server/routes/client-error.ts", subapp: client_error_default },
|
|
29051
29495
|
{ prefix: "/listings", file: "server/routes/listings.ts", subapp: listings_default },
|
|
29052
29496
|
{ prefix: "/v", file: "server/routes/visitor-event.ts", subapp: visitor_event_default },
|
|
@@ -29058,7 +29502,7 @@ for (const m of SUBAPP_MANIFEST) {
|
|
|
29058
29502
|
}
|
|
29059
29503
|
try {
|
|
29060
29504
|
const registered = [];
|
|
29061
|
-
for (const r of
|
|
29505
|
+
for (const r of app64.routes ?? []) {
|
|
29062
29506
|
if (typeof r.path !== "string" || r.path.includes(":") || r.path.includes("*")) continue;
|
|
29063
29507
|
if (AGENT_SLUG_PATTERN.test(r.path)) {
|
|
29064
29508
|
registered.push({ method: (r.method ?? "ALL").toUpperCase(), path: r.path });
|
|
@@ -29102,7 +29546,7 @@ async function runAdminUserReconcileTick() {
|
|
|
29102
29546
|
console.error("[adminuser-self-heal] skip reason=no-users-file");
|
|
29103
29547
|
return;
|
|
29104
29548
|
}
|
|
29105
|
-
const usersRaw =
|
|
29549
|
+
const usersRaw = readFileSync42(USERS_FILE, "utf-8").trim();
|
|
29106
29550
|
if (!usersRaw) {
|
|
29107
29551
|
console.error("[adminuser-self-heal] skip reason=empty-users-file");
|
|
29108
29552
|
return;
|
|
@@ -29150,7 +29594,7 @@ registerLoop({
|
|
|
29150
29594
|
var USERS_RECONCILE_INTERVAL_MS = 60 * 60 * 1e3;
|
|
29151
29595
|
function countUsersRows() {
|
|
29152
29596
|
if (!existsSync38(USERS_FILE)) return 0;
|
|
29153
|
-
const raw =
|
|
29597
|
+
const raw = readFileSync42(USERS_FILE, "utf-8").trim();
|
|
29154
29598
|
if (!raw) return 0;
|
|
29155
29599
|
const users = JSON.parse(raw);
|
|
29156
29600
|
return users.filter((u) => typeof u.userId === "string").length;
|
|
@@ -29283,7 +29727,7 @@ if (bootAccountConfig?.whatsapp) {
|
|
|
29283
29727
|
}
|
|
29284
29728
|
init({
|
|
29285
29729
|
configDir: configDirForWhatsApp,
|
|
29286
|
-
platformRoot:
|
|
29730
|
+
platformRoot: resolve37(process.env.MAXY_PLATFORM_ROOT ?? join44(__dirname, "..")),
|
|
29287
29731
|
accountConfig: bootAccountConfig,
|
|
29288
29732
|
onMessage: async (msg) => {
|
|
29289
29733
|
if (msg.isOwnerMirror) {
|