@rubytech/create-sitedesk-code 0.1.521 → 0.1.522
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/dist/index.js +17 -3
- package/package.json +1 -1
- package/payload/platform/lib/account-schema-regions/package-lock.json +12 -0
- package/payload/platform/lib/active-rules/package-lock.json +12 -0
- package/payload/platform/lib/mcp-lifeline/package-lock.json +12 -0
- package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +43 -6
- package/payload/platform/plugins/admin/skills/whats-new/SKILL.md +6 -0
- package/payload/platform/plugins/docs/references/admin-ui.md +10 -0
- package/payload/platform/plugins/docs/references/internals.md +4 -0
- package/payload/platform/plugins/docs/references/outlook-guide.md +27 -4
- package/payload/platform/plugins/outlook/.claude-plugin/plugin.json +1 -1
- package/payload/platform/plugins/outlook/PLUGIN.md +9 -1
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/delegated-resolve.test.d.ts +2 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/delegated-resolve.test.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/delegated-resolve.test.js +151 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/delegated-resolve.test.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/delegated-store.test.d.ts +2 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/delegated-store.test.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/delegated-store.test.js +151 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/delegated-store.test.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/device-flow.test.js +38 -4
- 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-prefix-wiring.test.d.ts +2 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/graph-client-prefix-wiring.test.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/graph-client-prefix-wiring.test.js +146 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/graph-client-prefix-wiring.test.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/mailbox-list-delegated.test.d.ts +2 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/mailbox-list-delegated.test.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/mailbox-list-delegated.test.js +120 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/mailbox-list-delegated.test.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/mailbox-list.test.js +8 -8
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/mailbox-list.test.js.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/mailbox-path-prefix.test.d.ts +2 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/mailbox-path-prefix.test.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/mailbox-path-prefix.test.js +27 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/mailbox-path-prefix.test.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/shared-mail-only-guard.test.d.ts +2 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/shared-mail-only-guard.test.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/shared-mail-only-guard.test.js +70 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/shared-mail-only-guard.test.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/shared-mailbox-add.test.d.ts +2 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/shared-mailbox-add.test.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/shared-mailbox-add.test.js +152 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/shared-mailbox-add.test.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/auth/delegated-store.d.ts +57 -0
- package/payload/platform/plugins/outlook/mcp/dist/auth/delegated-store.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/auth/delegated-store.js +163 -0
- package/payload/platform/plugins/outlook/mcp/dist/auth/delegated-store.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/auth/device-flow.d.ts +12 -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 +21 -1
- package/payload/platform/plugins/outlook/mcp/dist/auth/device-flow.js.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/auth/mailbox-registry.d.ts +35 -4
- package/payload/platform/plugins/outlook/mcp/dist/auth/mailbox-registry.d.ts.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/auth/mailbox-registry.js +92 -10
- package/payload/platform/plugins/outlook/mcp/dist/auth/mailbox-registry.js.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/index.js +106 -3
- package/payload/platform/plugins/outlook/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/lib/graph-client.d.ts +49 -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 +126 -8
- package/payload/platform/plugins/outlook/mcp/dist/lib/graph-client.js.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/tools/mailbox-info.d.ts +13 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/mailbox-info.d.ts.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/tools/mailbox-info.js +9 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/mailbox-info.js.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/tools/mailbox-list.d.ts +42 -8
- package/payload/platform/plugins/outlook/mcp/dist/tools/mailbox-list.d.ts.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/tools/mailbox-list.js +59 -19
- package/payload/platform/plugins/outlook/mcp/dist/tools/mailbox-list.js.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/tools/shared-mailbox-add.d.ts +47 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/shared-mailbox-add.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/shared-mailbox-add.js +67 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/shared-mailbox-add.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/shared-mailbox-remove.d.ts +12 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/shared-mailbox-remove.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/shared-mailbox-remove.js +18 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/shared-mailbox-remove.js.map +1 -0
- package/payload/platform/plugins/outlook/references/auth.md +15 -2
- package/payload/platform/plugins/outlook/references/graph-surfaces.md +8 -0
- package/payload/platform/plugins/outlook/skills/outlook/SKILL.md +21 -1
- package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js +2 -0
- package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js.map +1 -1
- package/payload/platform/templates/specialists/agents/personal-assistant.md +1 -1
- package/payload/server/{chunk-AZLT4LVO.js → chunk-K7CJGZPU.js} +419 -189
- package/payload/server/{manager-CQBSUJXR.js → manager-RKL7JZ4Y.js} +3 -1
- package/payload/server/public/activity.html +5 -5
- package/payload/server/public/agents.html +4 -4
- package/payload/server/public/assets/{AdminLoginScreens-Dar_MMJm.js → AdminLoginScreens-zc2_qqh-.js} +1 -1
- package/payload/server/public/assets/AdminLoginScreens-zc2_qqh-.js.br +0 -0
- package/payload/server/public/assets/AdminLoginScreens-zc2_qqh-.js.gz +0 -0
- package/payload/server/public/assets/{AdminShell-CpaI2F_p.js → AdminShell-IGUdTx5q.js} +1 -1
- package/payload/server/public/assets/AdminShell-IGUdTx5q.js.br +0 -0
- package/payload/server/public/assets/{AdminShell-CpaI2F_p.js.gz → AdminShell-IGUdTx5q.js.gz} +0 -0
- package/payload/server/public/assets/{activity-B1rt4GIy.js → activity-DVgO8YuM.js} +1 -1
- package/payload/server/public/assets/activity-DVgO8YuM.js.br +0 -0
- package/payload/server/public/assets/activity-DVgO8YuM.js.gz +0 -0
- package/payload/server/public/assets/admin-xtpM7fgl.js +1 -0
- package/payload/server/public/assets/admin-xtpM7fgl.js.br +0 -0
- package/payload/server/public/assets/admin-xtpM7fgl.js.gz +0 -0
- package/payload/server/public/assets/{agents-BXq8woVf.js → agents-P0nTIxo-.js} +1 -1
- package/payload/server/public/assets/agents-P0nTIxo-.js.br +0 -0
- package/payload/server/public/assets/agents-P0nTIxo-.js.gz +0 -0
- package/payload/server/public/assets/{browser-1QSCPl3X.js → browser-Mw7DY3ft.js} +1 -1
- package/payload/server/public/assets/browser-Mw7DY3ft.js.br +0 -0
- package/payload/server/public/assets/browser-Mw7DY3ft.js.gz +0 -0
- package/payload/server/public/assets/{calendar-D009qWVV.js → calendar-B9gvL_1_.js} +1 -1
- package/payload/server/public/assets/calendar-B9gvL_1_.js.br +0 -0
- package/payload/server/public/assets/calendar-B9gvL_1_.js.gz +0 -0
- package/payload/server/public/assets/chat-DBGqh4wg.js +1 -0
- package/payload/server/public/assets/chat-DBGqh4wg.js.br +2 -0
- package/payload/server/public/assets/chat-DBGqh4wg.js.gz +0 -0
- package/payload/server/public/assets/chevron-left-Bl4vpOM0.js +1 -0
- package/payload/server/public/assets/chevron-left-Bl4vpOM0.js.br +0 -0
- package/payload/server/public/assets/chevron-right-BExigCnp.js +1 -0
- package/payload/server/public/assets/chevron-right-BExigCnp.js.br +0 -0
- package/payload/server/public/assets/clock-D8TJvgJC.js +1 -0
- package/payload/server/public/assets/clock-D8TJvgJC.js.br +0 -0
- package/payload/server/public/assets/data-HfFSxaC2.js +1 -0
- package/payload/server/public/assets/data-HfFSxaC2.js.br +0 -0
- package/payload/server/public/assets/data-HfFSxaC2.js.gz +0 -0
- package/payload/server/public/assets/{file-text-DwGAfCsd.js → file-text-dYBnXNEX.js} +1 -1
- package/payload/server/public/assets/file-text-dYBnXNEX.js.br +0 -0
- package/payload/server/public/assets/file-text-dYBnXNEX.js.gz +0 -0
- package/payload/server/public/assets/{graph-D-DGlfhW.js → graph-CDyzd1Et.js} +1 -1
- package/payload/server/public/assets/graph-CDyzd1Et.js.br +0 -0
- package/payload/server/public/assets/graph-CDyzd1Et.js.gz +0 -0
- package/payload/server/public/assets/{graph-labels-BbX3fXzl.js → graph-labels-D1LkKiJr.js} +1 -1
- package/payload/server/public/assets/graph-labels-D1LkKiJr.js.br +0 -0
- package/payload/server/public/assets/graph-labels-D1LkKiJr.js.gz +0 -0
- package/payload/server/public/assets/{maximize-2-C_-KapxH.js → maximize-2-M-iSaznd.js} +1 -1
- package/payload/server/public/assets/maximize-2-M-iSaznd.js.br +0 -0
- package/payload/server/public/assets/maximize-2-M-iSaznd.js.gz +0 -0
- package/payload/server/public/assets/{operator-CFrZVcv6.js → operator-9jfCv1Fg.js} +1 -1
- package/payload/server/public/assets/operator-9jfCv1Fg.js.br +0 -0
- package/payload/server/public/assets/operator-9jfCv1Fg.js.gz +0 -0
- package/payload/server/public/assets/{page-E74MYB18.js → page-CZ-73FAk.js} +1 -1
- package/payload/server/public/assets/page-CZ-73FAk.js.br +0 -0
- package/payload/server/public/assets/page-CZ-73FAk.js.gz +0 -0
- package/payload/server/public/assets/{page-CHt62f1_.js → page-CjwSt7kx.js} +1 -1
- package/payload/server/public/assets/page-CjwSt7kx.js.br +0 -0
- package/payload/server/public/assets/page-CjwSt7kx.js.gz +0 -0
- package/payload/server/public/assets/{public-CI6K1sNW.js → public-CN-ltC8x.js} +1 -1
- package/payload/server/public/assets/public-CN-ltC8x.js.br +0 -0
- package/payload/server/public/assets/public-CN-ltC8x.js.gz +0 -0
- package/payload/server/public/assets/{rotate-ccw-PWu_QGrD.js → rotate-ccw-Buopsc22.js} +1 -1
- package/payload/server/public/assets/rotate-ccw-Buopsc22.js.br +0 -0
- package/payload/server/public/assets/rotate-ccw-Buopsc22.js.gz +0 -0
- package/payload/server/public/assets/{routines-Cp3_XS7S.js → routines-C-yr8ddT.js} +1 -1
- package/payload/server/public/assets/routines-C-yr8ddT.js.br +0 -0
- package/payload/server/public/assets/routines-C-yr8ddT.js.gz +0 -0
- package/payload/server/public/assets/{skills-g8HvunPA.js → skills-DiiuVzDK.js} +1 -1
- package/payload/server/public/assets/skills-DiiuVzDK.js.br +0 -0
- package/payload/server/public/assets/skills-DiiuVzDK.js.gz +0 -0
- package/payload/server/public/assets/tasks-CnHIfn2_.js +1 -0
- package/payload/server/public/assets/tasks-CnHIfn2_.js.br +0 -0
- package/payload/server/public/assets/tasks-CnHIfn2_.js.gz +0 -0
- package/payload/server/public/assets/{time-entry-format-BXWuQ6Sw.js → time-entry-format-BBokCF-D.js} +1 -1
- package/payload/server/public/assets/time-entry-format-BBokCF-D.js.br +0 -0
- package/payload/server/public/assets/time-entry-format-BBokCF-D.js.gz +0 -0
- package/payload/server/public/assets/{triangle-alert-BNshRC13.js → triangle-alert-C_AcpTW5.js} +1 -1
- package/payload/server/public/assets/triangle-alert-C_AcpTW5.js.br +0 -0
- package/payload/server/public/assets/triangle-alert-C_AcpTW5.js.gz +0 -0
- package/payload/server/public/assets/{useCopyFeedback-CwTZ9DqH.js → useCopyFeedback-TdimmPVR.js} +1 -1
- package/payload/server/public/assets/useCopyFeedback-TdimmPVR.js.br +0 -0
- package/payload/server/public/assets/useCopyFeedback-TdimmPVR.js.gz +0 -0
- package/payload/server/public/assets/{useSubAccountSwitcher-BuUYMDWl.js → useSubAccountSwitcher-BwHqV55p.js} +1 -1
- package/payload/server/public/assets/useSubAccountSwitcher-BwHqV55p.js.br +0 -0
- package/payload/server/public/assets/{useSubAccountSwitcher-BuUYMDWl.js.gz → useSubAccountSwitcher-BwHqV55p.js.gz} +0 -0
- package/payload/server/public/assets/{useSubAccountSwitcher-dzDnmWq9.css → useSubAccountSwitcher-CsrPYqtm.css} +1 -1
- package/payload/server/public/assets/useSubAccountSwitcher-CsrPYqtm.css.br +0 -0
- package/payload/server/public/assets/{useSubAccountSwitcher-dzDnmWq9.css.gz → useSubAccountSwitcher-CsrPYqtm.css.gz} +0 -0
- package/payload/server/public/assets/{useVoiceRecorder-BGqV5R6u.js → useVoiceRecorder-BT2738ZS.js} +1 -1
- package/payload/server/public/assets/useVoiceRecorder-BT2738ZS.js.br +0 -0
- package/payload/server/public/assets/useVoiceRecorder-BT2738ZS.js.gz +0 -0
- package/payload/server/public/assets/{wrench-DQ045Pyk.js → wrench-B51s5kGA.js} +1 -1
- package/payload/server/public/assets/wrench-B51s5kGA.js.br +0 -0
- package/payload/server/public/assets/wrench-B51s5kGA.js.gz +0 -0
- package/payload/server/public/browser.html +4 -4
- package/payload/server/public/calendar.html +7 -7
- package/payload/server/public/chat.html +13 -13
- package/payload/server/public/data.html +11 -11
- package/payload/server/public/graph.html +9 -9
- package/payload/server/public/index.html +14 -14
- package/payload/server/public/operator.html +14 -14
- package/payload/server/public/public.html +13 -13
- package/payload/server/public/routines.html +6 -6
- package/payload/server/public/skills.html +5 -5
- package/payload/server/public/tasks.html +6 -6
- package/payload/server/server.js +1006 -606
- package/payload/server/public/assets/AdminLoginScreens-Dar_MMJm.js.br +0 -0
- package/payload/server/public/assets/AdminLoginScreens-Dar_MMJm.js.gz +0 -0
- package/payload/server/public/assets/AdminShell-CpaI2F_p.js.br +0 -0
- package/payload/server/public/assets/activity-B1rt4GIy.js.br +0 -0
- package/payload/server/public/assets/activity-B1rt4GIy.js.gz +0 -0
- package/payload/server/public/assets/admin-DE3lLNje.js +0 -1
- package/payload/server/public/assets/admin-DE3lLNje.js.br +0 -0
- package/payload/server/public/assets/admin-DE3lLNje.js.gz +0 -0
- package/payload/server/public/assets/agents-BXq8woVf.js.br +0 -0
- package/payload/server/public/assets/agents-BXq8woVf.js.gz +0 -0
- package/payload/server/public/assets/browser-1QSCPl3X.js.br +0 -0
- package/payload/server/public/assets/browser-1QSCPl3X.js.gz +0 -0
- package/payload/server/public/assets/calendar-D009qWVV.js.br +0 -0
- package/payload/server/public/assets/calendar-D009qWVV.js.gz +0 -0
- package/payload/server/public/assets/chat-CUg1fTqJ.js +0 -1
- package/payload/server/public/assets/chat-CUg1fTqJ.js.br +0 -1
- package/payload/server/public/assets/chat-CUg1fTqJ.js.gz +0 -0
- package/payload/server/public/assets/chevron-left-C2JEy7uU.js +0 -1
- package/payload/server/public/assets/chevron-left-C2JEy7uU.js.br +0 -0
- package/payload/server/public/assets/chevron-right-DWa047Ys.js +0 -1
- package/payload/server/public/assets/chevron-right-DWa047Ys.js.br +0 -0
- package/payload/server/public/assets/clock-C3OqfCnn.js +0 -1
- package/payload/server/public/assets/clock-C3OqfCnn.js.br +0 -0
- package/payload/server/public/assets/clock-C3OqfCnn.js.gz +0 -0
- package/payload/server/public/assets/data-Bu_yCScC.js +0 -1
- package/payload/server/public/assets/data-Bu_yCScC.js.br +0 -0
- package/payload/server/public/assets/data-Bu_yCScC.js.gz +0 -0
- package/payload/server/public/assets/file-text-DwGAfCsd.js.br +0 -0
- package/payload/server/public/assets/file-text-DwGAfCsd.js.gz +0 -0
- package/payload/server/public/assets/graph-D-DGlfhW.js.br +0 -0
- package/payload/server/public/assets/graph-D-DGlfhW.js.gz +0 -0
- package/payload/server/public/assets/graph-labels-BbX3fXzl.js.br +0 -0
- package/payload/server/public/assets/graph-labels-BbX3fXzl.js.gz +0 -0
- package/payload/server/public/assets/maximize-2-C_-KapxH.js.br +0 -0
- package/payload/server/public/assets/maximize-2-C_-KapxH.js.gz +0 -0
- package/payload/server/public/assets/operator-CFrZVcv6.js.br +0 -0
- package/payload/server/public/assets/operator-CFrZVcv6.js.gz +0 -0
- package/payload/server/public/assets/page-CHt62f1_.js.br +0 -0
- package/payload/server/public/assets/page-CHt62f1_.js.gz +0 -0
- package/payload/server/public/assets/page-E74MYB18.js.br +0 -0
- package/payload/server/public/assets/page-E74MYB18.js.gz +0 -0
- package/payload/server/public/assets/public-CI6K1sNW.js.br +0 -0
- package/payload/server/public/assets/public-CI6K1sNW.js.gz +0 -0
- package/payload/server/public/assets/rotate-ccw-PWu_QGrD.js.br +0 -0
- package/payload/server/public/assets/rotate-ccw-PWu_QGrD.js.gz +0 -0
- package/payload/server/public/assets/routines-Cp3_XS7S.js.br +0 -0
- package/payload/server/public/assets/routines-Cp3_XS7S.js.gz +0 -0
- package/payload/server/public/assets/skills-g8HvunPA.js.br +0 -0
- package/payload/server/public/assets/skills-g8HvunPA.js.gz +0 -0
- package/payload/server/public/assets/tasks-CMC_jDVm.js +0 -1
- package/payload/server/public/assets/tasks-CMC_jDVm.js.br +0 -0
- package/payload/server/public/assets/tasks-CMC_jDVm.js.gz +0 -0
- package/payload/server/public/assets/time-entry-format-BXWuQ6Sw.js.br +0 -2
- package/payload/server/public/assets/time-entry-format-BXWuQ6Sw.js.gz +0 -0
- package/payload/server/public/assets/triangle-alert-BNshRC13.js.br +0 -0
- package/payload/server/public/assets/triangle-alert-BNshRC13.js.gz +0 -0
- package/payload/server/public/assets/useCopyFeedback-CwTZ9DqH.js.br +0 -0
- package/payload/server/public/assets/useCopyFeedback-CwTZ9DqH.js.gz +0 -0
- package/payload/server/public/assets/useSubAccountSwitcher-BuUYMDWl.js.br +0 -0
- package/payload/server/public/assets/useSubAccountSwitcher-dzDnmWq9.css.br +0 -0
- package/payload/server/public/assets/useVoiceRecorder-BGqV5R6u.js.br +0 -0
- package/payload/server/public/assets/useVoiceRecorder-BGqV5R6u.js.gz +0 -0
- package/payload/server/public/assets/wrench-DQ045Pyk.js.br +0 -0
- package/payload/server/public/assets/wrench-DQ045Pyk.js.gz +0 -0
|
@@ -332,32 +332,32 @@ function ledgerFilePath(account) {
|
|
|
332
332
|
}
|
|
333
333
|
return join(root, `${account}.jsonl`);
|
|
334
334
|
}
|
|
335
|
-
function appendStanza(
|
|
335
|
+
function appendStanza(entry2) {
|
|
336
336
|
try {
|
|
337
337
|
const row = {
|
|
338
|
-
msgId:
|
|
339
|
-
from:
|
|
340
|
-
type:
|
|
338
|
+
msgId: entry2.msgId,
|
|
339
|
+
from: entry2.from,
|
|
340
|
+
type: entry2.type,
|
|
341
341
|
ts: (/* @__PURE__ */ new Date()).toISOString()
|
|
342
342
|
};
|
|
343
|
-
appendFileSync(ledgerFilePath(
|
|
343
|
+
appendFileSync(ledgerFilePath(entry2.account), JSON.stringify(row) + "\n");
|
|
344
344
|
} catch (err) {
|
|
345
345
|
console.error(
|
|
346
|
-
`${TAG3} FAIL op=append msgId=${
|
|
346
|
+
`${TAG3} FAIL op=append msgId=${entry2.msgId} err=${err instanceof Error ? err.message : String(err)}`
|
|
347
347
|
);
|
|
348
348
|
}
|
|
349
349
|
}
|
|
350
|
-
function markDeclined(
|
|
350
|
+
function markDeclined(entry2) {
|
|
351
351
|
try {
|
|
352
352
|
const row = {
|
|
353
|
-
declined:
|
|
354
|
-
reason:
|
|
353
|
+
declined: entry2.msgId,
|
|
354
|
+
reason: entry2.reason,
|
|
355
355
|
ts: (/* @__PURE__ */ new Date()).toISOString()
|
|
356
356
|
};
|
|
357
|
-
appendFileSync(ledgerFilePath(
|
|
357
|
+
appendFileSync(ledgerFilePath(entry2.account), JSON.stringify(row) + "\n");
|
|
358
358
|
} catch (err) {
|
|
359
359
|
console.error(
|
|
360
|
-
`${TAG3} FAIL op=decline msgId=${
|
|
360
|
+
`${TAG3} FAIL op=decline msgId=${entry2.msgId} err=${err instanceof Error ? err.message : String(err)}`
|
|
361
361
|
);
|
|
362
362
|
}
|
|
363
363
|
}
|
|
@@ -375,36 +375,36 @@ function readDeclined(account) {
|
|
|
375
375
|
}
|
|
376
376
|
return out;
|
|
377
377
|
}
|
|
378
|
-
function appendNotifyEligible(
|
|
378
|
+
function appendNotifyEligible(entry2) {
|
|
379
379
|
try {
|
|
380
380
|
const row = {
|
|
381
|
-
notifyEligible:
|
|
382
|
-
upsertType:
|
|
383
|
-
fromMe:
|
|
384
|
-
allowed:
|
|
385
|
-
reason:
|
|
386
|
-
passive:
|
|
387
|
-
persistAccount:
|
|
381
|
+
notifyEligible: entry2.msgId,
|
|
382
|
+
upsertType: entry2.upsertType,
|
|
383
|
+
fromMe: entry2.fromMe,
|
|
384
|
+
allowed: entry2.allowed,
|
|
385
|
+
reason: entry2.reason,
|
|
386
|
+
passive: entry2.passive,
|
|
387
|
+
persistAccount: entry2.persistAccount,
|
|
388
388
|
ts: (/* @__PURE__ */ new Date()).toISOString()
|
|
389
389
|
};
|
|
390
|
-
appendFileSync(ledgerFilePath(
|
|
390
|
+
appendFileSync(ledgerFilePath(entry2.account), JSON.stringify(row) + "\n");
|
|
391
391
|
} catch (err) {
|
|
392
392
|
console.error(
|
|
393
|
-
`${TAG3} FAIL op=append-notify-eligible msgId=${
|
|
393
|
+
`${TAG3} FAIL op=append-notify-eligible msgId=${entry2.msgId} err=${err instanceof Error ? err.message : String(err)}`
|
|
394
394
|
);
|
|
395
395
|
}
|
|
396
396
|
}
|
|
397
|
-
function appendNotifyPublished(
|
|
397
|
+
function appendNotifyPublished(entry2) {
|
|
398
398
|
try {
|
|
399
399
|
const row = {
|
|
400
|
-
notifyPublished:
|
|
401
|
-
subscribers:
|
|
400
|
+
notifyPublished: entry2.msgId,
|
|
401
|
+
subscribers: entry2.subscribers,
|
|
402
402
|
ts: (/* @__PURE__ */ new Date()).toISOString()
|
|
403
403
|
};
|
|
404
|
-
appendFileSync(ledgerFilePath(
|
|
404
|
+
appendFileSync(ledgerFilePath(entry2.account), JSON.stringify(row) + "\n");
|
|
405
405
|
} catch (err) {
|
|
406
406
|
console.error(
|
|
407
|
-
`${TAG3} FAIL op=append-notify-published msgId=${
|
|
407
|
+
`${TAG3} FAIL op=append-notify-published msgId=${entry2.msgId} err=${err instanceof Error ? err.message : String(err)}`
|
|
408
408
|
);
|
|
409
409
|
}
|
|
410
410
|
}
|
|
@@ -1218,16 +1218,16 @@ function toMillis(v) {
|
|
|
1218
1218
|
if (typeof v.low === "number") return (v.low >>> 0) + (v.high ?? 0) * 4294967296;
|
|
1219
1219
|
return null;
|
|
1220
1220
|
}
|
|
1221
|
-
function readReactionEntry(
|
|
1222
|
-
const targetMsgKeyId =
|
|
1221
|
+
function readReactionEntry(entry2) {
|
|
1222
|
+
const targetMsgKeyId = entry2.key?.id ?? void 0;
|
|
1223
1223
|
if (!targetMsgKeyId) return null;
|
|
1224
|
-
const chatJid =
|
|
1224
|
+
const chatJid = entry2.key?.remoteJid ?? entry2.reaction?.key?.remoteJid ?? void 0;
|
|
1225
1225
|
if (!chatJid) return null;
|
|
1226
1226
|
const isGroup = isGroupJid(chatJid);
|
|
1227
|
-
const reactorKey =
|
|
1227
|
+
const reactorKey = entry2.reaction?.key;
|
|
1228
1228
|
const reactorJid = isGroup ? reactorKey?.participant ?? void 0 : reactorKey?.remoteJid ?? chatJid;
|
|
1229
1229
|
if (!reactorJid) return null;
|
|
1230
|
-
const emoji =
|
|
1230
|
+
const emoji = entry2.reaction?.text ?? "";
|
|
1231
1231
|
return {
|
|
1232
1232
|
targetMsgKeyId,
|
|
1233
1233
|
chatJid,
|
|
@@ -1236,7 +1236,7 @@ function readReactionEntry(entry) {
|
|
|
1236
1236
|
emoji,
|
|
1237
1237
|
removed: !emoji,
|
|
1238
1238
|
fromMe: !!reactorKey?.fromMe,
|
|
1239
|
-
timestampMs: toMillis(
|
|
1239
|
+
timestampMs: toMillis(entry2.reaction?.senderTimestampMs)
|
|
1240
1240
|
};
|
|
1241
1241
|
}
|
|
1242
1242
|
|
|
@@ -1381,7 +1381,7 @@ function summariseRecords(records) {
|
|
|
1381
1381
|
return {
|
|
1382
1382
|
first: sorted[0],
|
|
1383
1383
|
last: sorted[sorted.length - 1],
|
|
1384
|
-
firstNamedSender: sorted.find((r) => r.senderName)?.senderName ?? null
|
|
1384
|
+
firstNamedSender: sorted.find((r) => !r.fromMe && r.senderName)?.senderName ?? null
|
|
1385
1385
|
};
|
|
1386
1386
|
}
|
|
1387
1387
|
function readConversationSummaries(platformAccountId) {
|
|
@@ -1393,10 +1393,10 @@ function readConversationSummaries(platformAccountId) {
|
|
|
1393
1393
|
let bytesRead = 0;
|
|
1394
1394
|
try {
|
|
1395
1395
|
if (!existsSync3(dir)) return out;
|
|
1396
|
-
for (const
|
|
1397
|
-
if (!
|
|
1398
|
-
const remoteJid =
|
|
1399
|
-
const file = join2(dir,
|
|
1396
|
+
for (const entry2 of readdirSync2(dir)) {
|
|
1397
|
+
if (!entry2.endsWith(".jsonl")) continue;
|
|
1398
|
+
const remoteJid = entry2.slice(0, -".jsonl".length);
|
|
1399
|
+
const file = join2(dir, entry2);
|
|
1400
1400
|
seen.add(file);
|
|
1401
1401
|
let size;
|
|
1402
1402
|
let mtimeMs;
|
|
@@ -1445,9 +1445,9 @@ function readAllConversations(platformAccountId) {
|
|
|
1445
1445
|
const out = /* @__PURE__ */ new Map();
|
|
1446
1446
|
try {
|
|
1447
1447
|
if (!existsSync3(dir)) return out;
|
|
1448
|
-
for (const
|
|
1449
|
-
if (!
|
|
1450
|
-
const remoteJid =
|
|
1448
|
+
for (const entry2 of readdirSync2(dir)) {
|
|
1449
|
+
if (!entry2.endsWith(".jsonl")) continue;
|
|
1450
|
+
const remoteJid = entry2.slice(0, -".jsonl".length);
|
|
1451
1451
|
out.set(remoteJid, readConversation(platformAccountId, remoteJid));
|
|
1452
1452
|
}
|
|
1453
1453
|
} catch (err) {
|
|
@@ -1468,16 +1468,16 @@ function normalizeEntry(v) {
|
|
|
1468
1468
|
return typeof v === "string" ? { account: v, mode: "active" } : { account: v.managesAccount, mode: v.mode ?? "active" };
|
|
1469
1469
|
}
|
|
1470
1470
|
function managedAccountFor(accountManagers, phone) {
|
|
1471
|
-
for (const [managerPhone,
|
|
1472
|
-
const { account, mode } = normalizeEntry(
|
|
1471
|
+
for (const [managerPhone, entry2] of Object.entries(accountManagers)) {
|
|
1472
|
+
const { account, mode } = normalizeEntry(entry2);
|
|
1473
1473
|
if (account && phonesMatch(managerPhone, phone)) return { account, mode };
|
|
1474
1474
|
}
|
|
1475
1475
|
return null;
|
|
1476
1476
|
}
|
|
1477
1477
|
function managerPhonesForAccount(accountManagers, subAccountId) {
|
|
1478
1478
|
const phones = [];
|
|
1479
|
-
for (const [managerPhone,
|
|
1480
|
-
const { account } = normalizeEntry(
|
|
1479
|
+
for (const [managerPhone, entry2] of Object.entries(accountManagers)) {
|
|
1480
|
+
const { account } = normalizeEntry(entry2);
|
|
1481
1481
|
if (account && account === subAccountId) phones.push(managerPhone);
|
|
1482
1482
|
}
|
|
1483
1483
|
return phones;
|
|
@@ -1498,7 +1498,7 @@ function resolveAccountConfig(config, accountConfig) {
|
|
|
1498
1498
|
function isAdminPhone(phone, adminPhones) {
|
|
1499
1499
|
const normalized = normalizeE164(phone);
|
|
1500
1500
|
if (!normalized) return false;
|
|
1501
|
-
return adminPhones.some((
|
|
1501
|
+
return adminPhones.some((entry2) => phonesMatch(entry2, normalized));
|
|
1502
1502
|
}
|
|
1503
1503
|
function checkDmAccess(params) {
|
|
1504
1504
|
const { senderPhone, selfPhone, accountId } = params;
|
|
@@ -1520,9 +1520,9 @@ function checkDmAccess(params) {
|
|
|
1520
1520
|
case "open":
|
|
1521
1521
|
return { allowed: true, reason: "dm-policy-open", agentType: "public", effectiveAccountId: accountId };
|
|
1522
1522
|
case "allowlist": {
|
|
1523
|
-
const inList = cfg.allowFrom.some((
|
|
1524
|
-
if (
|
|
1525
|
-
return phonesMatch(
|
|
1523
|
+
const inList = cfg.allowFrom.some((entry2) => {
|
|
1524
|
+
if (entry2 === "*") return true;
|
|
1525
|
+
return phonesMatch(entry2, senderPhone);
|
|
1526
1526
|
});
|
|
1527
1527
|
if (inList) {
|
|
1528
1528
|
return { allowed: true, reason: "allowlist-match", agentType: "public", effectiveAccountId: accountId };
|
|
@@ -1597,8 +1597,8 @@ var NOTIFY_MAX = 5e3;
|
|
|
1597
1597
|
var notifyCache = /* @__PURE__ */ new Map();
|
|
1598
1598
|
function isNotifyDuplicate(msgKeyId) {
|
|
1599
1599
|
const now = Date.now();
|
|
1600
|
-
const
|
|
1601
|
-
if (
|
|
1600
|
+
const entry2 = notifyCache.get(msgKeyId);
|
|
1601
|
+
if (entry2 && now - entry2.ts <= NOTIFY_TTL_MS) return true;
|
|
1602
1602
|
for (const [key, e] of notifyCache) {
|
|
1603
1603
|
if (now - e.ts > NOTIFY_TTL_MS) notifyCache.delete(key);
|
|
1604
1604
|
}
|
|
@@ -1675,15 +1675,15 @@ var ACCESS_LOCKOUT_MS = 15 * 60 * 1e3;
|
|
|
1675
1675
|
var ACCESS_WINDOW_MS = 15 * 60 * 1e3;
|
|
1676
1676
|
function checkAccessRateLimit(ip, agentSlug) {
|
|
1677
1677
|
const key = `${ip}:${agentSlug}`;
|
|
1678
|
-
const
|
|
1679
|
-
if (!
|
|
1678
|
+
const entry2 = rateLimitMap.get(key);
|
|
1679
|
+
if (!entry2) return null;
|
|
1680
1680
|
const now = Date.now();
|
|
1681
|
-
if (
|
|
1682
|
-
const remainingMs =
|
|
1681
|
+
if (entry2.lockedUntil && now < entry2.lockedUntil) {
|
|
1682
|
+
const remainingMs = entry2.lockedUntil - now;
|
|
1683
1683
|
const remainingS = Math.ceil(remainingMs / 1e3);
|
|
1684
1684
|
return `Too many attempts. Try again in ${remainingS}s`;
|
|
1685
1685
|
}
|
|
1686
|
-
if (now -
|
|
1686
|
+
if (now - entry2.firstAttempt > ACCESS_WINDOW_MS) {
|
|
1687
1687
|
rateLimitMap.delete(key);
|
|
1688
1688
|
return null;
|
|
1689
1689
|
}
|
|
@@ -1692,14 +1692,14 @@ function checkAccessRateLimit(ip, agentSlug) {
|
|
|
1692
1692
|
function recordAccessFailedAttempt(ip, agentSlug) {
|
|
1693
1693
|
const key = `${ip}:${agentSlug}`;
|
|
1694
1694
|
const now = Date.now();
|
|
1695
|
-
const
|
|
1696
|
-
if (!
|
|
1695
|
+
const entry2 = rateLimitMap.get(key);
|
|
1696
|
+
if (!entry2 || now - entry2.firstAttempt > ACCESS_WINDOW_MS) {
|
|
1697
1697
|
rateLimitMap.set(key, { attempts: 1, firstAttempt: now });
|
|
1698
1698
|
return;
|
|
1699
1699
|
}
|
|
1700
|
-
|
|
1701
|
-
if (
|
|
1702
|
-
|
|
1700
|
+
entry2.attempts++;
|
|
1701
|
+
if (entry2.attempts >= ACCESS_MAX_ATTEMPTS) {
|
|
1702
|
+
entry2.lockedUntil = now + ACCESS_LOCKOUT_MS;
|
|
1703
1703
|
}
|
|
1704
1704
|
}
|
|
1705
1705
|
function clearAccessRateLimit(ip, agentSlug) {
|
|
@@ -1709,15 +1709,15 @@ var contactRateMap = /* @__PURE__ */ new Map();
|
|
|
1709
1709
|
var REQUEST_LINK_MAX = 3;
|
|
1710
1710
|
var REQUEST_LINK_WINDOW_MS = 60 * 60 * 1e3;
|
|
1711
1711
|
function checkRequestLinkRateLimit(contactValue) {
|
|
1712
|
-
const
|
|
1713
|
-
if (!
|
|
1712
|
+
const entry2 = contactRateMap.get(contactValue);
|
|
1713
|
+
if (!entry2) return null;
|
|
1714
1714
|
const now = Date.now();
|
|
1715
|
-
if (now -
|
|
1715
|
+
if (now - entry2.windowStart > REQUEST_LINK_WINDOW_MS) {
|
|
1716
1716
|
contactRateMap.delete(contactValue);
|
|
1717
1717
|
return null;
|
|
1718
1718
|
}
|
|
1719
|
-
if (
|
|
1720
|
-
const remainingMs = REQUEST_LINK_WINDOW_MS - (now -
|
|
1719
|
+
if (entry2.count >= REQUEST_LINK_MAX) {
|
|
1720
|
+
const remainingMs = REQUEST_LINK_WINDOW_MS - (now - entry2.windowStart);
|
|
1721
1721
|
const remainingM = Math.ceil(remainingMs / 6e4);
|
|
1722
1722
|
return `Too many requests. Try again in ${remainingM} minute(s)`;
|
|
1723
1723
|
}
|
|
@@ -1725,12 +1725,12 @@ function checkRequestLinkRateLimit(contactValue) {
|
|
|
1725
1725
|
}
|
|
1726
1726
|
function recordRequestLinkAttempt(contactValue) {
|
|
1727
1727
|
const now = Date.now();
|
|
1728
|
-
const
|
|
1729
|
-
if (!
|
|
1728
|
+
const entry2 = contactRateMap.get(contactValue);
|
|
1729
|
+
if (!entry2 || now - entry2.windowStart > REQUEST_LINK_WINDOW_MS) {
|
|
1730
1730
|
contactRateMap.set(contactValue, { count: 1, windowStart: now });
|
|
1731
1731
|
return;
|
|
1732
1732
|
}
|
|
1733
|
-
|
|
1733
|
+
entry2.count++;
|
|
1734
1734
|
}
|
|
1735
1735
|
function maskContact(value) {
|
|
1736
1736
|
if (value.includes("@")) {
|
|
@@ -1919,8 +1919,8 @@ var agentSentCache = /* @__PURE__ */ new Map();
|
|
|
1919
1919
|
var inboundCache = /* @__PURE__ */ new Map();
|
|
1920
1920
|
function pruneCache(cache3, ttl, max) {
|
|
1921
1921
|
const now = Date.now();
|
|
1922
|
-
for (const [key,
|
|
1923
|
-
if (now -
|
|
1922
|
+
for (const [key, entry2] of cache3) {
|
|
1923
|
+
if (now - entry2.ts > ttl) cache3.delete(key);
|
|
1924
1924
|
}
|
|
1925
1925
|
if (cache3.size > max) {
|
|
1926
1926
|
const excess = cache3.size - max;
|
|
@@ -1937,22 +1937,22 @@ function trackAgentSentMessage(messageId) {
|
|
|
1937
1937
|
agentSentCache.set(messageId, { ts: Date.now() });
|
|
1938
1938
|
}
|
|
1939
1939
|
function isAgentSentMessage(messageId) {
|
|
1940
|
-
const
|
|
1941
|
-
if (!
|
|
1942
|
-
if (Date.now() -
|
|
1940
|
+
const entry2 = agentSentCache.get(messageId);
|
|
1941
|
+
if (!entry2) return false;
|
|
1942
|
+
if (Date.now() - entry2.ts > AGENT_SENT_TTL_MS) {
|
|
1943
1943
|
agentSentCache.delete(messageId);
|
|
1944
1944
|
return false;
|
|
1945
1945
|
}
|
|
1946
1946
|
return true;
|
|
1947
1947
|
}
|
|
1948
1948
|
function isDuplicateInbound(key) {
|
|
1949
|
-
const
|
|
1950
|
-
if (!
|
|
1949
|
+
const entry2 = inboundCache.get(key);
|
|
1950
|
+
if (!entry2) {
|
|
1951
1951
|
pruneCache(inboundCache, INBOUND_TTL_MS, INBOUND_MAX);
|
|
1952
1952
|
inboundCache.set(key, { ts: Date.now() });
|
|
1953
1953
|
return false;
|
|
1954
1954
|
}
|
|
1955
|
-
if (Date.now() -
|
|
1955
|
+
if (Date.now() - entry2.ts > INBOUND_TTL_MS) {
|
|
1956
1956
|
inboundCache.delete(key);
|
|
1957
1957
|
inboundCache.set(key, { ts: Date.now() });
|
|
1958
1958
|
return false;
|
|
@@ -2013,7 +2013,7 @@ function recordActivity(event) {
|
|
|
2013
2013
|
}
|
|
2014
2014
|
acct[direction]++;
|
|
2015
2015
|
acct.lastEventAt = now;
|
|
2016
|
-
const
|
|
2016
|
+
const entry2 = {
|
|
2017
2017
|
channel: "whatsapp",
|
|
2018
2018
|
accountId,
|
|
2019
2019
|
direction,
|
|
@@ -2021,7 +2021,7 @@ function recordActivity(event) {
|
|
|
2021
2021
|
timestamp: now,
|
|
2022
2022
|
messageType
|
|
2023
2023
|
};
|
|
2024
|
-
recentEvents.push(
|
|
2024
|
+
recentEvents.push(entry2);
|
|
2025
2025
|
if (recentEvents.length > RECENT_EVENTS_MAX) {
|
|
2026
2026
|
recentEvents.splice(0, recentEvents.length - RECENT_EVENTS_MAX);
|
|
2027
2027
|
}
|
|
@@ -2295,6 +2295,216 @@ function sanitizeReason(err) {
|
|
|
2295
2295
|
return `${err.name}:${msg}`;
|
|
2296
2296
|
}
|
|
2297
2297
|
|
|
2298
|
+
// app/lib/whatsapp/chat-metadata-store.ts
|
|
2299
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync3, readFileSync as readFileSync5, writeFileSync as writeFileSync3, renameSync, statSync as statSync2 } from "fs";
|
|
2300
|
+
import { dirname as dirname2, join as join4 } from "path";
|
|
2301
|
+
|
|
2302
|
+
// app/lib/data-path.ts
|
|
2303
|
+
import { realpathSync } from "fs";
|
|
2304
|
+
import { resolve as resolve2, normalize, sep, relative, basename, join as join3 } from "path";
|
|
2305
|
+
var PLATFORM_ROOT2 = process.env.MAXY_PLATFORM_ROOT ?? resolve2(process.cwd(), "../platform");
|
|
2306
|
+
var DATA_ROOT = resolve2(PLATFORM_ROOT2, "..", "data");
|
|
2307
|
+
var CLAUDE_UPLOADS_ROOT = process.env.CLAUDE_CONFIG_DIR ? resolve2(process.env.CLAUDE_CONFIG_DIR, "uploads") : null;
|
|
2308
|
+
var ACCOUNT_PARTITION_DIRS = /* @__PURE__ */ new Set(["accounts"]);
|
|
2309
|
+
var ACCOUNT_UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
2310
|
+
function crossesForeignAccountPartition(relPath, accountId) {
|
|
2311
|
+
const segs = relPath.split("/").filter(Boolean);
|
|
2312
|
+
if (segs.length < 2) return false;
|
|
2313
|
+
if (!ACCOUNT_PARTITION_DIRS.has(segs[0])) return false;
|
|
2314
|
+
const owner = segs[1];
|
|
2315
|
+
if (!ACCOUNT_UUID_RE.test(owner)) return false;
|
|
2316
|
+
return owner !== accountId;
|
|
2317
|
+
}
|
|
2318
|
+
function isWithinOwnAccountPartition(relPath, accountId) {
|
|
2319
|
+
const segs = relPath.split("/").filter(Boolean);
|
|
2320
|
+
return segs.length >= 2 && segs[0] === "accounts" && segs[1] === accountId;
|
|
2321
|
+
}
|
|
2322
|
+
function resolveUnderRoot(raw, rootAbs, rootLabel) {
|
|
2323
|
+
const cleaned = normalize("/" + (raw ?? "").replace(/\\/g, "/")).replace(/^\/+/, "");
|
|
2324
|
+
const absolute = resolve2(rootAbs, cleaned);
|
|
2325
|
+
let rootReal;
|
|
2326
|
+
try {
|
|
2327
|
+
rootReal = realpathSync(rootAbs);
|
|
2328
|
+
} catch (err) {
|
|
2329
|
+
return {
|
|
2330
|
+
ok: false,
|
|
2331
|
+
status: 500,
|
|
2332
|
+
error: `${rootLabel} not accessible: ${err instanceof Error ? err.message : String(err)}`
|
|
2333
|
+
};
|
|
2334
|
+
}
|
|
2335
|
+
let resolvedReal;
|
|
2336
|
+
try {
|
|
2337
|
+
resolvedReal = realpathSync(absolute);
|
|
2338
|
+
} catch (err) {
|
|
2339
|
+
const code = err.code;
|
|
2340
|
+
if (code === "ENOENT") {
|
|
2341
|
+
if (absolute !== rootReal && !absolute.startsWith(rootReal + sep)) {
|
|
2342
|
+
return { ok: false, status: 403, error: `Path escapes ${rootLabel}`, resolved: absolute };
|
|
2343
|
+
}
|
|
2344
|
+
return { ok: false, status: 404, error: "Not found" };
|
|
2345
|
+
}
|
|
2346
|
+
return {
|
|
2347
|
+
ok: false,
|
|
2348
|
+
status: 500,
|
|
2349
|
+
error: err instanceof Error ? err.message : String(err)
|
|
2350
|
+
};
|
|
2351
|
+
}
|
|
2352
|
+
if (resolvedReal !== rootReal && !resolvedReal.startsWith(rootReal + sep)) {
|
|
2353
|
+
return { ok: false, status: 403, error: `Path escapes ${rootLabel}`, resolved: resolvedReal };
|
|
2354
|
+
}
|
|
2355
|
+
const relPath = relative(rootReal, resolvedReal) || ".";
|
|
2356
|
+
return { ok: true, absolute: resolvedReal, dataRootReal: rootReal, relative: relPath };
|
|
2357
|
+
}
|
|
2358
|
+
function resolveDataPath(raw) {
|
|
2359
|
+
return resolveUnderRoot(raw, DATA_ROOT, "DATA_ROOT");
|
|
2360
|
+
}
|
|
2361
|
+
function resolveClaudeUploadsPath(raw) {
|
|
2362
|
+
if (!CLAUDE_UPLOADS_ROOT) {
|
|
2363
|
+
return { ok: false, status: 404, error: "Not found" };
|
|
2364
|
+
}
|
|
2365
|
+
return resolveUnderRoot(raw, CLAUDE_UPLOADS_ROOT, "CLAUDE_UPLOADS_ROOT");
|
|
2366
|
+
}
|
|
2367
|
+
function scratchpadTmpBase() {
|
|
2368
|
+
return process.env.MAXY_SCRATCHPAD_BASE ?? "/tmp";
|
|
2369
|
+
}
|
|
2370
|
+
function sessionScratchpadRoot(projectDir, sessionId) {
|
|
2371
|
+
const uid = typeof process.geteuid === "function" ? process.geteuid() : 0;
|
|
2372
|
+
return join3(scratchpadTmpBase(), `claude-${uid}`, basename(projectDir), sessionId, "scratchpad");
|
|
2373
|
+
}
|
|
2374
|
+
function resolveScratchpadPath(raw, scratchpadRootAbs) {
|
|
2375
|
+
return resolveUnderRoot(raw, scratchpadRootAbs, "scratchpad");
|
|
2376
|
+
}
|
|
2377
|
+
|
|
2378
|
+
// app/lib/whatsapp/chat-metadata-store.ts
|
|
2379
|
+
var SOURCE_RANK = { contact: 3, group: 2, chat: 1 };
|
|
2380
|
+
function isSource(v) {
|
|
2381
|
+
return v === "contact" || v === "group" || v === "chat";
|
|
2382
|
+
}
|
|
2383
|
+
function preferEntry(existing, incoming) {
|
|
2384
|
+
if (existing === void 0) return incoming;
|
|
2385
|
+
const a = SOURCE_RANK[existing.source];
|
|
2386
|
+
const b = SOURCE_RANK[incoming.source];
|
|
2387
|
+
if (b > a) return incoming;
|
|
2388
|
+
if (b < a) return existing;
|
|
2389
|
+
return incoming.updatedAt > existing.updatedAt ? incoming : existing;
|
|
2390
|
+
}
|
|
2391
|
+
function parseChatMetadata(raw) {
|
|
2392
|
+
const out = /* @__PURE__ */ new Map();
|
|
2393
|
+
let parsed;
|
|
2394
|
+
try {
|
|
2395
|
+
parsed = JSON.parse(raw);
|
|
2396
|
+
} catch {
|
|
2397
|
+
return out;
|
|
2398
|
+
}
|
|
2399
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) return out;
|
|
2400
|
+
for (const [jid, value] of Object.entries(parsed)) {
|
|
2401
|
+
if (value === null || typeof value !== "object") continue;
|
|
2402
|
+
const v = value;
|
|
2403
|
+
if (typeof v.name !== "string" || v.name.length === 0) continue;
|
|
2404
|
+
if (!isSource(v.source)) continue;
|
|
2405
|
+
if (typeof v.updatedAt !== "string") continue;
|
|
2406
|
+
out.set(jid, {
|
|
2407
|
+
jid,
|
|
2408
|
+
name: v.name,
|
|
2409
|
+
source: v.source,
|
|
2410
|
+
isGroup: v.isGroup === true,
|
|
2411
|
+
updatedAt: v.updatedAt
|
|
2412
|
+
});
|
|
2413
|
+
}
|
|
2414
|
+
return out;
|
|
2415
|
+
}
|
|
2416
|
+
function serializeChatMetadata(map) {
|
|
2417
|
+
const obj = {};
|
|
2418
|
+
for (const [jid, entry2] of map) obj[jid] = entry2;
|
|
2419
|
+
return JSON.stringify(obj);
|
|
2420
|
+
}
|
|
2421
|
+
function chatMetadataPath(accountId) {
|
|
2422
|
+
return join4(DATA_ROOT, "accounts", accountId, "chat-metadata.json");
|
|
2423
|
+
}
|
|
2424
|
+
var readCache = /* @__PURE__ */ new Map();
|
|
2425
|
+
function readChatMetadata(accountId) {
|
|
2426
|
+
const path3 = chatMetadataPath(accountId);
|
|
2427
|
+
try {
|
|
2428
|
+
if (!existsSync4(path3)) return /* @__PURE__ */ new Map();
|
|
2429
|
+
const st = statSync2(path3);
|
|
2430
|
+
const cached = readCache.get(path3);
|
|
2431
|
+
if (cached && cached.size === st.size && cached.mtimeMs === st.mtimeMs) return cached.map;
|
|
2432
|
+
const map = parseChatMetadata(readFileSync5(path3, "utf8"));
|
|
2433
|
+
readCache.set(path3, { size: st.size, mtimeMs: st.mtimeMs, map });
|
|
2434
|
+
return map;
|
|
2435
|
+
} catch (err) {
|
|
2436
|
+
console.error(
|
|
2437
|
+
`[wa-chatmeta] op=read outcome=fail account=${accountId.slice(0, 8)} reason=${(err instanceof Error ? err.message : String(err)).slice(0, 100)}`
|
|
2438
|
+
);
|
|
2439
|
+
return /* @__PURE__ */ new Map();
|
|
2440
|
+
}
|
|
2441
|
+
}
|
|
2442
|
+
function writeChatMetadataMerged(accountId, updates) {
|
|
2443
|
+
if (updates.length === 0) return -1;
|
|
2444
|
+
const path3 = chatMetadataPath(accountId);
|
|
2445
|
+
try {
|
|
2446
|
+
mkdirSync3(dirname2(path3), { recursive: true });
|
|
2447
|
+
let merged = /* @__PURE__ */ new Map();
|
|
2448
|
+
if (existsSync4(path3)) merged = parseChatMetadata(readFileSync5(path3, "utf8"));
|
|
2449
|
+
for (const entry2 of updates) merged.set(entry2.jid, preferEntry(merged.get(entry2.jid), entry2));
|
|
2450
|
+
const tmp = `${path3}.tmp`;
|
|
2451
|
+
writeFileSync3(tmp, serializeChatMetadata(merged), "utf8");
|
|
2452
|
+
renameSync(tmp, path3);
|
|
2453
|
+
return parseChatMetadata(readFileSync5(path3, "utf8")).size;
|
|
2454
|
+
} catch (err) {
|
|
2455
|
+
console.error(
|
|
2456
|
+
`[wa-chatmeta] op=persist outcome=fail account=${accountId.slice(0, 8)} reason=${(err instanceof Error ? err.message : String(err)).slice(0, 100)}`
|
|
2457
|
+
);
|
|
2458
|
+
return -1;
|
|
2459
|
+
}
|
|
2460
|
+
}
|
|
2461
|
+
|
|
2462
|
+
// app/lib/whatsapp/chat-metadata-events.ts
|
|
2463
|
+
var NON_CHAT_JIDS = /* @__PURE__ */ new Set(["status@broadcast"]);
|
|
2464
|
+
function isGroupJid2(jid) {
|
|
2465
|
+
return jid.endsWith("@g.us");
|
|
2466
|
+
}
|
|
2467
|
+
function entry(jid, name, source, nowIso) {
|
|
2468
|
+
return { jid, name, source, isGroup: isGroupJid2(jid), updatedAt: nowIso };
|
|
2469
|
+
}
|
|
2470
|
+
function conversationKey(jid, phoneNumber) {
|
|
2471
|
+
if (!jid.endsWith("@lid")) return jid;
|
|
2472
|
+
if (typeof phoneNumber !== "string") return jid;
|
|
2473
|
+
const trimmed = phoneNumber.trim();
|
|
2474
|
+
return trimmed.endsWith("@s.whatsapp.net") ? trimmed : jid;
|
|
2475
|
+
}
|
|
2476
|
+
function usable(jid, name, phoneNumber) {
|
|
2477
|
+
if (typeof jid !== "string" || jid.length === 0) return null;
|
|
2478
|
+
if (NON_CHAT_JIDS.has(jid)) return null;
|
|
2479
|
+
const trimmed = typeof name === "string" ? name.trim() : "";
|
|
2480
|
+
if (trimmed.length === 0) return null;
|
|
2481
|
+
return { jid: conversationKey(jid, phoneNumber), name: trimmed };
|
|
2482
|
+
}
|
|
2483
|
+
function chatEntriesFromChats(chats, nowIso) {
|
|
2484
|
+
const out = [];
|
|
2485
|
+
for (const c of chats) {
|
|
2486
|
+
const ok = usable(c.id, c.name, c.phoneNumber);
|
|
2487
|
+
if (ok) out.push(entry(ok.jid, ok.name, "chat", nowIso));
|
|
2488
|
+
}
|
|
2489
|
+
return out;
|
|
2490
|
+
}
|
|
2491
|
+
function chatEntriesFromContacts(contacts, nowIso) {
|
|
2492
|
+
const out = [];
|
|
2493
|
+
for (const c of contacts) {
|
|
2494
|
+
const ok = usable(c.id, c.name, c.phoneNumber);
|
|
2495
|
+
if (ok) out.push(entry(ok.jid, ok.name, isGroupJid2(ok.jid) ? "chat" : "contact", nowIso));
|
|
2496
|
+
}
|
|
2497
|
+
return out;
|
|
2498
|
+
}
|
|
2499
|
+
function groupEntriesFromUpdates(updates, nowIso) {
|
|
2500
|
+
const out = [];
|
|
2501
|
+
for (const g of updates) {
|
|
2502
|
+
const ok = usable(g.id, g.subject);
|
|
2503
|
+
if (ok) out.push(entry(ok.jid, ok.name, "group", nowIso));
|
|
2504
|
+
}
|
|
2505
|
+
return out;
|
|
2506
|
+
}
|
|
2507
|
+
|
|
2298
2508
|
// app/lib/whatsapp/ensure-conversation.ts
|
|
2299
2509
|
var TAG10 = "[whatsapp-persist]";
|
|
2300
2510
|
async function ensureWhatsAppConversation(input) {
|
|
@@ -2327,8 +2537,8 @@ async function ensureWhatsAppConversation(input) {
|
|
|
2327
2537
|
}
|
|
2328
2538
|
|
|
2329
2539
|
// ../lib/account-enumeration/src/index.ts
|
|
2330
|
-
import { readdirSync as readdirSync3, readFileSync as
|
|
2331
|
-
import { resolve as
|
|
2540
|
+
import { readdirSync as readdirSync3, readFileSync as readFileSync6 } from "fs";
|
|
2541
|
+
import { resolve as resolve3 } from "path";
|
|
2332
2542
|
var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
2333
2543
|
var cache2 = /* @__PURE__ */ new Map();
|
|
2334
2544
|
function enumerateValidAccountIds(accountsDir) {
|
|
@@ -2347,9 +2557,9 @@ function enumerateValidAccountIds(accountsDir) {
|
|
|
2347
2557
|
const valid = [];
|
|
2348
2558
|
for (const name of names) {
|
|
2349
2559
|
if (!UUID_RE.test(name)) continue;
|
|
2350
|
-
const configPath =
|
|
2560
|
+
const configPath = resolve3(accountsDir, name, "account.json");
|
|
2351
2561
|
try {
|
|
2352
|
-
JSON.parse(
|
|
2562
|
+
JSON.parse(readFileSync6(configPath, "utf-8"));
|
|
2353
2563
|
valid.push(name);
|
|
2354
2564
|
} catch (err) {
|
|
2355
2565
|
const code = err.code;
|
|
@@ -2363,9 +2573,9 @@ function resolveHouseOrSoleAccountId(accountsDir) {
|
|
|
2363
2573
|
const valid = enumerateValidAccountIds(accountsDir);
|
|
2364
2574
|
const houses = [];
|
|
2365
2575
|
for (const id of valid) {
|
|
2366
|
-
const configPath =
|
|
2576
|
+
const configPath = resolve3(accountsDir, id, "account.json");
|
|
2367
2577
|
try {
|
|
2368
|
-
const cfg = JSON.parse(
|
|
2578
|
+
const cfg = JSON.parse(readFileSync6(configPath, "utf-8"));
|
|
2369
2579
|
if (cfg.role === "house") houses.push(id);
|
|
2370
2580
|
} catch {
|
|
2371
2581
|
}
|
|
@@ -2383,7 +2593,7 @@ function getAccountsDirFromEnv() {
|
|
|
2383
2593
|
"[graph-write] MAXY_PLATFORM_ROOT not set \u2014 cannot enforce accountId gate. Set MAXY_PLATFORM_ROOT in the spawning process or pass `accountsDir` explicitly."
|
|
2384
2594
|
);
|
|
2385
2595
|
}
|
|
2386
|
-
return
|
|
2596
|
+
return resolve3(root, "..", "data/accounts");
|
|
2387
2597
|
}
|
|
2388
2598
|
function validateAccountIdEnv(envValue, diskIds) {
|
|
2389
2599
|
if (!envValue) {
|
|
@@ -2399,8 +2609,8 @@ function validateAccountIdEnv(envValue, diskIds) {
|
|
|
2399
2609
|
}
|
|
2400
2610
|
|
|
2401
2611
|
// app/lib/whatsapp/platform-account-id.ts
|
|
2402
|
-
import { readFileSync as
|
|
2403
|
-
import { resolve as
|
|
2612
|
+
import { readFileSync as readFileSync7 } from "fs";
|
|
2613
|
+
import { resolve as resolve4 } from "path";
|
|
2404
2614
|
var cachedHouseId = null;
|
|
2405
2615
|
var cachedHouseAccountsDir = null;
|
|
2406
2616
|
function resolveHouseId(accountsDir) {
|
|
@@ -2411,10 +2621,10 @@ function resolveHouseId(accountsDir) {
|
|
|
2411
2621
|
return cachedHouseId;
|
|
2412
2622
|
}
|
|
2413
2623
|
function readChannelRoutingOverride(accountsDir, houseId) {
|
|
2414
|
-
const configPath =
|
|
2624
|
+
const configPath = resolve4(accountsDir, houseId, "account.json");
|
|
2415
2625
|
let raw;
|
|
2416
2626
|
try {
|
|
2417
|
-
raw =
|
|
2627
|
+
raw = readFileSync7(configPath, "utf-8");
|
|
2418
2628
|
} catch {
|
|
2419
2629
|
return null;
|
|
2420
2630
|
}
|
|
@@ -2455,8 +2665,8 @@ function warnNonUuidAccount(caller, accountId) {
|
|
|
2455
2665
|
}
|
|
2456
2666
|
|
|
2457
2667
|
// app/lib/managed-service.ts
|
|
2458
|
-
import { join as
|
|
2459
|
-
import { readFileSync as
|
|
2668
|
+
import { join as join5 } from "path";
|
|
2669
|
+
import { readFileSync as readFileSync8 } from "fs";
|
|
2460
2670
|
function isHouseManagedService(accountsDir = ACCOUNTS_DIR) {
|
|
2461
2671
|
let houseId;
|
|
2462
2672
|
try {
|
|
@@ -2466,7 +2676,7 @@ function isHouseManagedService(accountsDir = ACCOUNTS_DIR) {
|
|
|
2466
2676
|
}
|
|
2467
2677
|
try {
|
|
2468
2678
|
const cfg = JSON.parse(
|
|
2469
|
-
|
|
2679
|
+
readFileSync8(join5(accountsDir, houseId, "account.json"), "utf-8")
|
|
2470
2680
|
);
|
|
2471
2681
|
return cfg.managedService === true;
|
|
2472
2682
|
} catch {
|
|
@@ -2480,7 +2690,7 @@ function isCustomerTurnSuppressed(role, managedService) {
|
|
|
2480
2690
|
// app/lib/whatsapp/inbound/media.ts
|
|
2481
2691
|
import { randomUUID as randomUUID4, createHash } from "crypto";
|
|
2482
2692
|
import { writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
2483
|
-
import { join as
|
|
2693
|
+
import { join as join6 } from "path";
|
|
2484
2694
|
import {
|
|
2485
2695
|
downloadMediaMessage,
|
|
2486
2696
|
downloadContentFromMessage,
|
|
@@ -2490,85 +2700,9 @@ import {
|
|
|
2490
2700
|
// app/lib/attachments.ts
|
|
2491
2701
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
2492
2702
|
import { mkdir, writeFile } from "fs/promises";
|
|
2493
|
-
import { realpathSync as realpathSync2, existsSync as
|
|
2703
|
+
import { realpathSync as realpathSync2, existsSync as existsSync5, readFileSync as readFileSync9, readdirSync as readdirSync4 } from "fs";
|
|
2494
2704
|
import { resolve as resolve5, extname } from "path";
|
|
2495
2705
|
|
|
2496
|
-
// app/lib/data-path.ts
|
|
2497
|
-
import { realpathSync } from "fs";
|
|
2498
|
-
import { resolve as resolve4, normalize, sep, relative, basename, join as join4 } from "path";
|
|
2499
|
-
var PLATFORM_ROOT2 = process.env.MAXY_PLATFORM_ROOT ?? resolve4(process.cwd(), "../platform");
|
|
2500
|
-
var DATA_ROOT = resolve4(PLATFORM_ROOT2, "..", "data");
|
|
2501
|
-
var CLAUDE_UPLOADS_ROOT = process.env.CLAUDE_CONFIG_DIR ? resolve4(process.env.CLAUDE_CONFIG_DIR, "uploads") : null;
|
|
2502
|
-
var ACCOUNT_PARTITION_DIRS = /* @__PURE__ */ new Set(["accounts"]);
|
|
2503
|
-
var ACCOUNT_UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
2504
|
-
function crossesForeignAccountPartition(relPath, accountId) {
|
|
2505
|
-
const segs = relPath.split("/").filter(Boolean);
|
|
2506
|
-
if (segs.length < 2) return false;
|
|
2507
|
-
if (!ACCOUNT_PARTITION_DIRS.has(segs[0])) return false;
|
|
2508
|
-
const owner = segs[1];
|
|
2509
|
-
if (!ACCOUNT_UUID_RE.test(owner)) return false;
|
|
2510
|
-
return owner !== accountId;
|
|
2511
|
-
}
|
|
2512
|
-
function isWithinOwnAccountPartition(relPath, accountId) {
|
|
2513
|
-
const segs = relPath.split("/").filter(Boolean);
|
|
2514
|
-
return segs.length >= 2 && segs[0] === "accounts" && segs[1] === accountId;
|
|
2515
|
-
}
|
|
2516
|
-
function resolveUnderRoot(raw, rootAbs, rootLabel) {
|
|
2517
|
-
const cleaned = normalize("/" + (raw ?? "").replace(/\\/g, "/")).replace(/^\/+/, "");
|
|
2518
|
-
const absolute = resolve4(rootAbs, cleaned);
|
|
2519
|
-
let rootReal;
|
|
2520
|
-
try {
|
|
2521
|
-
rootReal = realpathSync(rootAbs);
|
|
2522
|
-
} catch (err) {
|
|
2523
|
-
return {
|
|
2524
|
-
ok: false,
|
|
2525
|
-
status: 500,
|
|
2526
|
-
error: `${rootLabel} not accessible: ${err instanceof Error ? err.message : String(err)}`
|
|
2527
|
-
};
|
|
2528
|
-
}
|
|
2529
|
-
let resolvedReal;
|
|
2530
|
-
try {
|
|
2531
|
-
resolvedReal = realpathSync(absolute);
|
|
2532
|
-
} catch (err) {
|
|
2533
|
-
const code = err.code;
|
|
2534
|
-
if (code === "ENOENT") {
|
|
2535
|
-
if (absolute !== rootReal && !absolute.startsWith(rootReal + sep)) {
|
|
2536
|
-
return { ok: false, status: 403, error: `Path escapes ${rootLabel}`, resolved: absolute };
|
|
2537
|
-
}
|
|
2538
|
-
return { ok: false, status: 404, error: "Not found" };
|
|
2539
|
-
}
|
|
2540
|
-
return {
|
|
2541
|
-
ok: false,
|
|
2542
|
-
status: 500,
|
|
2543
|
-
error: err instanceof Error ? err.message : String(err)
|
|
2544
|
-
};
|
|
2545
|
-
}
|
|
2546
|
-
if (resolvedReal !== rootReal && !resolvedReal.startsWith(rootReal + sep)) {
|
|
2547
|
-
return { ok: false, status: 403, error: `Path escapes ${rootLabel}`, resolved: resolvedReal };
|
|
2548
|
-
}
|
|
2549
|
-
const relPath = relative(rootReal, resolvedReal) || ".";
|
|
2550
|
-
return { ok: true, absolute: resolvedReal, dataRootReal: rootReal, relative: relPath };
|
|
2551
|
-
}
|
|
2552
|
-
function resolveDataPath(raw) {
|
|
2553
|
-
return resolveUnderRoot(raw, DATA_ROOT, "DATA_ROOT");
|
|
2554
|
-
}
|
|
2555
|
-
function resolveClaudeUploadsPath(raw) {
|
|
2556
|
-
if (!CLAUDE_UPLOADS_ROOT) {
|
|
2557
|
-
return { ok: false, status: 404, error: "Not found" };
|
|
2558
|
-
}
|
|
2559
|
-
return resolveUnderRoot(raw, CLAUDE_UPLOADS_ROOT, "CLAUDE_UPLOADS_ROOT");
|
|
2560
|
-
}
|
|
2561
|
-
function scratchpadTmpBase() {
|
|
2562
|
-
return process.env.MAXY_SCRATCHPAD_BASE ?? "/tmp";
|
|
2563
|
-
}
|
|
2564
|
-
function sessionScratchpadRoot(projectDir, sessionId) {
|
|
2565
|
-
const uid = typeof process.geteuid === "function" ? process.geteuid() : 0;
|
|
2566
|
-
return join4(scratchpadTmpBase(), `claude-${uid}`, basename(projectDir), sessionId, "scratchpad");
|
|
2567
|
-
}
|
|
2568
|
-
function resolveScratchpadPath(raw, scratchpadRootAbs) {
|
|
2569
|
-
return resolveUnderRoot(raw, scratchpadRootAbs, "scratchpad");
|
|
2570
|
-
}
|
|
2571
|
-
|
|
2572
2706
|
// app/lib/doc-to-text.ts
|
|
2573
2707
|
import { unzipSync, strFromU8 } from "fflate";
|
|
2574
2708
|
var DOCX = "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
|
|
@@ -2589,10 +2723,10 @@ function bareMime(mimeType) {
|
|
|
2589
2723
|
function unescapeXml(s) {
|
|
2590
2724
|
return s.replace(/</g, "<").replace(/>/g, ">").replace(/"/g, '"').replace(/'/g, "'").replace(/&/g, "&");
|
|
2591
2725
|
}
|
|
2592
|
-
function unzipEntry(buffer,
|
|
2726
|
+
function unzipEntry(buffer, entry2) {
|
|
2593
2727
|
const files = unzipSync(new Uint8Array(buffer));
|
|
2594
|
-
const bytes = files[
|
|
2595
|
-
if (!bytes) throw new Error(`zip entry not found: ${
|
|
2728
|
+
const bytes = files[entry2];
|
|
2729
|
+
if (!bytes) throw new Error(`zip entry not found: ${entry2}`);
|
|
2596
2730
|
return strFromU8(bytes);
|
|
2597
2731
|
}
|
|
2598
2732
|
function docxToText(buffer) {
|
|
@@ -2814,14 +2948,14 @@ async function storeMediaBuffer(accountId, attachmentId, filename, mimeType, buf
|
|
|
2814
2948
|
return writeAttachment(accountId, "", filename, mimeType, buffer.byteLength, buffer, attachmentId);
|
|
2815
2949
|
}
|
|
2816
2950
|
function attachmentExists(accountId, attachmentId) {
|
|
2817
|
-
return
|
|
2951
|
+
return existsSync5(resolve5(uploadsDirFor(accountId, "", attachmentId), `${attachmentId}.meta.json`));
|
|
2818
2952
|
}
|
|
2819
2953
|
function resolveStoredAttachment(accountId, attachmentId) {
|
|
2820
2954
|
if (!ATTACHMENT_ID_RE.test(attachmentId)) return null;
|
|
2821
2955
|
const dir = uploadsDirFor(accountId, "", attachmentId);
|
|
2822
2956
|
let meta;
|
|
2823
2957
|
try {
|
|
2824
|
-
meta = JSON.parse(
|
|
2958
|
+
meta = JSON.parse(readFileSync9(resolve5(dir, `${attachmentId}.meta.json`), "utf8"));
|
|
2825
2959
|
} catch {
|
|
2826
2960
|
return null;
|
|
2827
2961
|
}
|
|
@@ -3171,7 +3305,7 @@ async function downloadInboundMedia(msg, sock, opts) {
|
|
|
3171
3305
|
if (!res) return void 0;
|
|
3172
3306
|
await mkdir2(MEDIA_DIR, { recursive: true });
|
|
3173
3307
|
const ext = mimeToExt(res.mimetype);
|
|
3174
|
-
const filePath =
|
|
3308
|
+
const filePath = join6(MEDIA_DIR, `${randomUUID4()}.${ext}`);
|
|
3175
3309
|
await writeFile2(filePath, res.buffer);
|
|
3176
3310
|
const sizeKB = (res.buffer.length / 1024).toFixed(0);
|
|
3177
3311
|
console.error(`${TAG12} media downloaded type=${res.mimetype} size=${sizeKB}KB path=${filePath}`);
|
|
@@ -3607,13 +3741,13 @@ var messageStore = /* @__PURE__ */ new Map();
|
|
|
3607
3741
|
function messageStoreKey(accountId, remoteJid) {
|
|
3608
3742
|
return `${accountId}:${remoteJid}`;
|
|
3609
3743
|
}
|
|
3610
|
-
function storeMessage(storeKey,
|
|
3744
|
+
function storeMessage(storeKey, entry2) {
|
|
3611
3745
|
let entries = messageStore.get(storeKey);
|
|
3612
3746
|
if (!entries) {
|
|
3613
3747
|
entries = [];
|
|
3614
3748
|
messageStore.set(storeKey, entries);
|
|
3615
3749
|
}
|
|
3616
|
-
entries.push(
|
|
3750
|
+
entries.push(entry2);
|
|
3617
3751
|
if (entries.length > MESSAGE_STORE_MAX) {
|
|
3618
3752
|
const trimmed = entries.length - MESSAGE_STORE_MAX;
|
|
3619
3753
|
entries.splice(0, trimmed);
|
|
@@ -3870,6 +4004,7 @@ async function registerLoginSocket(accountId, sock, authDir) {
|
|
|
3870
4004
|
applySelfIdentity(conn, sock, "register");
|
|
3871
4005
|
connections.set(accountId, conn);
|
|
3872
4006
|
monitorInbound(conn);
|
|
4007
|
+
monitorChatMetadata(conn);
|
|
3873
4008
|
try {
|
|
3874
4009
|
await sock.sendPresenceUpdate("available");
|
|
3875
4010
|
console.error(`${TAG16} presence set to available account=${accountId}`);
|
|
@@ -3991,6 +4126,7 @@ async function connectWithReconnect(conn) {
|
|
|
3991
4126
|
conn.sock = sock;
|
|
3992
4127
|
applySelfIdentity(conn, sock, "pre-listener");
|
|
3993
4128
|
monitorInbound(conn);
|
|
4129
|
+
monitorChatMetadata(conn);
|
|
3994
4130
|
console.error(`${TAG16} socket created account=${conn.accountId} \u2014 waiting for connection`);
|
|
3995
4131
|
await waitForConnection(sock);
|
|
3996
4132
|
connectedAt = Date.now();
|
|
@@ -4122,6 +4258,95 @@ function applySelfIdentity(conn, sock, phase) {
|
|
|
4122
4258
|
);
|
|
4123
4259
|
}
|
|
4124
4260
|
}
|
|
4261
|
+
function monitorChatMetadata(conn) {
|
|
4262
|
+
if (!conn.sock) return;
|
|
4263
|
+
const sock = conn.sock;
|
|
4264
|
+
const account = conn.platformAccountId;
|
|
4265
|
+
const canonicalise = async (entries) => {
|
|
4266
|
+
const out = [];
|
|
4267
|
+
for (const e of entries) {
|
|
4268
|
+
if (!e.jid.endsWith("@lid")) {
|
|
4269
|
+
out.push(e);
|
|
4270
|
+
continue;
|
|
4271
|
+
}
|
|
4272
|
+
const { canonical, resolved } = await resolveCanonicalConversationJid(e.jid, conn.lidMapping);
|
|
4273
|
+
if (resolved === "lid2pn") {
|
|
4274
|
+
console.error(`${TAG16} [wa-chatmeta] op=canonical raw=${e.jid} canonical=${canonical}`);
|
|
4275
|
+
out.push({ ...e, jid: canonical });
|
|
4276
|
+
} else {
|
|
4277
|
+
out.push(e);
|
|
4278
|
+
}
|
|
4279
|
+
}
|
|
4280
|
+
return out;
|
|
4281
|
+
};
|
|
4282
|
+
const persist = (kind, entries, perJid) => {
|
|
4283
|
+
if (entries.length === 0) return;
|
|
4284
|
+
if (perJid) {
|
|
4285
|
+
for (const e of entries) {
|
|
4286
|
+
console.error(
|
|
4287
|
+
`${TAG16} [wa-chatmeta] op=event kind=${kind} jid=${e.jid} name=${e.name.length} source=${e.source}`
|
|
4288
|
+
);
|
|
4289
|
+
}
|
|
4290
|
+
}
|
|
4291
|
+
const total = writeChatMetadataMerged(account, entries);
|
|
4292
|
+
console.error(
|
|
4293
|
+
`${TAG16} [wa-chatmeta] op=persist kind=${kind} account=${account.slice(0, 8)} updates=${entries.length} outcome=${total < 0 ? "fail" : "ok"} entries=${total}`
|
|
4294
|
+
);
|
|
4295
|
+
};
|
|
4296
|
+
sock.ev.on("messaging-history.set", ({ chats, contacts }) => {
|
|
4297
|
+
const nowIso = (/* @__PURE__ */ new Date()).toISOString();
|
|
4298
|
+
const chatEntries = chatEntriesFromChats(chats ?? [], nowIso);
|
|
4299
|
+
const contactEntries = chatEntriesFromContacts(contacts ?? [], nowIso);
|
|
4300
|
+
console.error(
|
|
4301
|
+
`${TAG16} [wa-chatmeta] op=history-set account=${account.slice(0, 8)} chats=${chats?.length ?? 0} contacts=${contacts?.length ?? 0} groups=${chatEntries.filter((e) => e.isGroup).length} named=${chatEntries.length + contactEntries.length}`
|
|
4302
|
+
);
|
|
4303
|
+
void canonicalise([...chatEntries, ...contactEntries]).then((e) => persist("history-set", e, false));
|
|
4304
|
+
});
|
|
4305
|
+
sock.ev.on("chats.upsert", (chats) => {
|
|
4306
|
+
void canonicalise(chatEntriesFromChats(chats ?? [], (/* @__PURE__ */ new Date()).toISOString())).then((e) => persist("chats.upsert", e, true));
|
|
4307
|
+
});
|
|
4308
|
+
sock.ev.on("chats.update", (chats) => {
|
|
4309
|
+
void canonicalise(chatEntriesFromChats(chats ?? [], (/* @__PURE__ */ new Date()).toISOString())).then((e) => persist("chats.update", e, true));
|
|
4310
|
+
});
|
|
4311
|
+
sock.ev.on("contacts.upsert", (contacts) => {
|
|
4312
|
+
void canonicalise(chatEntriesFromContacts(contacts ?? [], (/* @__PURE__ */ new Date()).toISOString())).then((e) => persist("contacts.upsert", e, true));
|
|
4313
|
+
});
|
|
4314
|
+
sock.ev.on("contacts.update", (contacts) => {
|
|
4315
|
+
void canonicalise(chatEntriesFromContacts(contacts ?? [], (/* @__PURE__ */ new Date()).toISOString())).then((e) => persist("contacts.update", e, true));
|
|
4316
|
+
});
|
|
4317
|
+
sock.ev.on("groups.update", (updates) => {
|
|
4318
|
+
persist("groups.update", groupEntriesFromUpdates(updates ?? [], (/* @__PURE__ */ new Date()).toISOString()), true);
|
|
4319
|
+
});
|
|
4320
|
+
const groupFetchAttempted = /* @__PURE__ */ new Set();
|
|
4321
|
+
sock.ev.on("messages.upsert", async (upsert) => {
|
|
4322
|
+
for (const msg of upsert.messages ?? []) {
|
|
4323
|
+
const jid = msg.key?.remoteJid;
|
|
4324
|
+
if (!jid || !jid.endsWith("@g.us")) continue;
|
|
4325
|
+
if (groupFetchAttempted.has(jid)) continue;
|
|
4326
|
+
if (readChatMetadata(account).get(jid)?.source === "group") continue;
|
|
4327
|
+
groupFetchAttempted.add(jid);
|
|
4328
|
+
const startedAt = Date.now();
|
|
4329
|
+
try {
|
|
4330
|
+
const meta = await sock.groupMetadata(jid);
|
|
4331
|
+
console.error(
|
|
4332
|
+
`${TAG16} [wa-chatmeta] op=group-fetch jid=${jid} ms=${Date.now() - startedAt} outcome=ok`
|
|
4333
|
+
);
|
|
4334
|
+
persist(
|
|
4335
|
+
"group-fetch",
|
|
4336
|
+
groupEntriesFromUpdates([{ id: jid, subject: meta.subject }], (/* @__PURE__ */ new Date()).toISOString()),
|
|
4337
|
+
true
|
|
4338
|
+
);
|
|
4339
|
+
} catch (err) {
|
|
4340
|
+
console.error(
|
|
4341
|
+
`${TAG16} [wa-chatmeta] op=group-fetch jid=${jid} ms=${Date.now() - startedAt} outcome=fail reason=${(err instanceof Error ? err.message : String(err)).slice(0, 100)}`
|
|
4342
|
+
);
|
|
4343
|
+
}
|
|
4344
|
+
}
|
|
4345
|
+
});
|
|
4346
|
+
}
|
|
4347
|
+
function listConnections() {
|
|
4348
|
+
return [...connections.values()];
|
|
4349
|
+
}
|
|
4125
4350
|
function monitorInbound(conn) {
|
|
4126
4351
|
if (!conn.sock || !onInboundMessage) return;
|
|
4127
4352
|
const sock = conn.sock;
|
|
@@ -4130,8 +4355,8 @@ function monitorInbound(conn) {
|
|
|
4130
4355
|
conn.debouncer = createInboundDebouncer({
|
|
4131
4356
|
debounceMs,
|
|
4132
4357
|
buildKey: (payload) => {
|
|
4133
|
-
const
|
|
4134
|
-
return `${payload.accountId}:${
|
|
4358
|
+
const conversationKey2 = payload.isGroup ? payload.groupJid : payload.senderPhone;
|
|
4359
|
+
return `${payload.accountId}:${conversationKey2 ?? payload.senderPhone}:${payload.senderPhone}`;
|
|
4135
4360
|
},
|
|
4136
4361
|
onFlush: (entries) => {
|
|
4137
4362
|
if (!onInboundMessage) return;
|
|
@@ -4480,9 +4705,9 @@ function monitorInbound(conn) {
|
|
|
4480
4705
|
});
|
|
4481
4706
|
console.error(`[whatsapp-reaction] op=subscribed account=${conn.platformAccountId}`);
|
|
4482
4707
|
sock.ev.on("messages.reaction", async (reactions) => {
|
|
4483
|
-
for (const
|
|
4708
|
+
for (const entry2 of reactions) {
|
|
4484
4709
|
try {
|
|
4485
|
-
const sig = readReactionEntry(
|
|
4710
|
+
const sig = readReactionEntry(entry2);
|
|
4486
4711
|
if (!sig) continue;
|
|
4487
4712
|
if (sig.fromMe) continue;
|
|
4488
4713
|
const reactorTelephone = await resolveJidToE164(sig.reactorJid, conn.lidMapping) ?? sig.reactorJid;
|
|
@@ -4653,8 +4878,8 @@ async function handleInboundMessage(conn, msg) {
|
|
|
4653
4878
|
}
|
|
4654
4879
|
}
|
|
4655
4880
|
if (extracted.mediaType === "audio" && mediaResult?.path) {
|
|
4656
|
-
const
|
|
4657
|
-
const debounceKey = `${conn.accountId}:${
|
|
4881
|
+
const conversationKey2 = isGroup ? remoteJid : senderPhone;
|
|
4882
|
+
const debounceKey = `${conn.accountId}:${conversationKey2}:${senderPhone}`;
|
|
4658
4883
|
let resolvePending;
|
|
4659
4884
|
const sttPending = new Promise((resolve6) => {
|
|
4660
4885
|
resolvePending = resolve6;
|
|
@@ -4795,6 +5020,7 @@ export {
|
|
|
4795
5020
|
managerPhonesForAccount,
|
|
4796
5021
|
isAdminPhone,
|
|
4797
5022
|
shouldDispatchInbound,
|
|
5023
|
+
formatSenderPhone,
|
|
4798
5024
|
checkAccessRateLimit,
|
|
4799
5025
|
recordAccessFailedAttempt,
|
|
4800
5026
|
clearAccessRateLimit,
|
|
@@ -4813,10 +5039,6 @@ export {
|
|
|
4813
5039
|
sendComposing,
|
|
4814
5040
|
sendPaused,
|
|
4815
5041
|
sendMediaMessage,
|
|
4816
|
-
resolvePlatformAccountId,
|
|
4817
|
-
warnNonUuidAccount,
|
|
4818
|
-
isHouseManagedService,
|
|
4819
|
-
isCustomerTurnSuppressed,
|
|
4820
5042
|
DATA_ROOT,
|
|
4821
5043
|
crossesForeignAccountPartition,
|
|
4822
5044
|
isWithinOwnAccountPartition,
|
|
@@ -4824,6 +5046,13 @@ export {
|
|
|
4824
5046
|
resolveClaudeUploadsPath,
|
|
4825
5047
|
sessionScratchpadRoot,
|
|
4826
5048
|
resolveScratchpadPath,
|
|
5049
|
+
readChatMetadata,
|
|
5050
|
+
writeChatMetadataMerged,
|
|
5051
|
+
groupEntriesFromUpdates,
|
|
5052
|
+
resolvePlatformAccountId,
|
|
5053
|
+
warnNonUuidAccount,
|
|
5054
|
+
isHouseManagedService,
|
|
5055
|
+
isCustomerTurnSuppressed,
|
|
4827
5056
|
SUPPORTED_MIME_TYPES,
|
|
4828
5057
|
MAX_FILE_SIZE_BYTES,
|
|
4829
5058
|
MAX_FILES_PER_MESSAGE,
|
|
@@ -4866,5 +5095,6 @@ export {
|
|
|
4866
5095
|
getMessages,
|
|
4867
5096
|
listConversationJids,
|
|
4868
5097
|
listStoreAccountKeys,
|
|
4869
|
-
shutdown
|
|
5098
|
+
shutdown,
|
|
5099
|
+
listConnections
|
|
4870
5100
|
};
|