@rubytech/create-realagent-code 0.1.582 → 0.1.584
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/lib/routine-templates/dist/index.d.ts +1 -1
- package/payload/platform/lib/routine-templates/dist/index.d.ts.map +1 -1
- package/payload/platform/lib/routine-templates/dist/index.js +5 -1
- package/payload/platform/lib/routine-templates/dist/index.js.map +1 -1
- package/payload/platform/lib/routine-templates/src/index.ts +4 -0
- package/payload/platform/lib/telegram-managed-bot/dist/index.d.ts +269 -0
- package/payload/platform/lib/telegram-managed-bot/dist/index.d.ts.map +1 -0
- package/payload/platform/lib/telegram-managed-bot/dist/index.js +351 -0
- package/payload/platform/lib/telegram-managed-bot/dist/index.js.map +1 -0
- package/payload/platform/lib/telegram-managed-bot/src/__tests__/managed-bot.test.ts +251 -0
- package/payload/platform/lib/telegram-managed-bot/src/index.ts +529 -0
- package/payload/platform/lib/telegram-managed-bot/tsconfig.json +9 -0
- package/payload/platform/lib/telegram-managed-bot/vitest.config.ts +9 -0
- package/payload/platform/lib/telegram-reach/dist/index.d.ts +86 -0
- package/payload/platform/lib/telegram-reach/dist/index.d.ts.map +1 -0
- package/payload/platform/lib/telegram-reach/dist/index.js +246 -0
- package/payload/platform/lib/telegram-reach/dist/index.js.map +1 -0
- package/payload/platform/lib/telegram-reach/src/__tests__/telegram-reach.test.ts +268 -0
- package/payload/platform/lib/telegram-reach/src/index.ts +296 -0
- package/payload/platform/lib/telegram-reach/tsconfig.json +9 -0
- package/payload/platform/lib/telegram-reach/vitest.config.ts +9 -0
- package/payload/platform/package.json +2 -2
- package/payload/platform/plugins/admin/PLUGIN.md +1 -1
- package/payload/platform/plugins/admin/hooks/__tests__/fs-schema-guard.test.sh +9 -0
- package/payload/platform/plugins/admin/hooks/__tests__/preference-consult-directive.test.sh +26 -0
- package/payload/platform/plugins/admin/hooks/fs-schema-guard.sh +20 -3
- package/payload/platform/plugins/admin/hooks/preference-consult-directive.sh +50 -5
- package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +146 -8
- package/payload/platform/plugins/admin/skills/whats-new/SKILL.md +16 -0
- package/payload/platform/plugins/contacts/mcp/dist/__tests__/telegram-user-id.test.d.ts +2 -0
- package/payload/platform/plugins/contacts/mcp/dist/__tests__/telegram-user-id.test.d.ts.map +1 -0
- package/payload/platform/plugins/contacts/mcp/dist/__tests__/telegram-user-id.test.js +100 -0
- package/payload/platform/plugins/contacts/mcp/dist/__tests__/telegram-user-id.test.js.map +1 -0
- package/payload/platform/plugins/contacts/mcp/dist/index.js +11 -5
- package/payload/platform/plugins/contacts/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-create.d.ts +5 -0
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-create.d.ts.map +1 -1
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-create.js +21 -4
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-create.js.map +1 -1
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-lookup.d.ts +5 -0
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-lookup.d.ts.map +1 -1
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-lookup.js +29 -1
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-lookup.js.map +1 -1
- package/payload/platform/plugins/contacts/mcp/vitest.config.ts +14 -0
- package/payload/platform/plugins/docs/references/admin-ui.md +17 -3
- package/payload/platform/plugins/docs/references/channel-wake-and-prompt.md +15 -0
- package/payload/platform/plugins/docs/references/telegram-guide.md +113 -4
- package/payload/platform/plugins/filesystem/PLUGIN.md +2 -2
- package/payload/platform/plugins/filesystem/mcp/dist/lib/__tests__/account-schema.test.d.ts +2 -0
- package/payload/platform/plugins/filesystem/mcp/dist/lib/__tests__/account-schema.test.d.ts.map +1 -0
- package/payload/platform/plugins/filesystem/mcp/dist/lib/__tests__/account-schema.test.js +108 -0
- package/payload/platform/plugins/filesystem/mcp/dist/lib/__tests__/account-schema.test.js.map +1 -0
- package/payload/platform/plugins/filesystem/mcp/dist/lib/account-schema.d.ts +17 -0
- package/payload/platform/plugins/filesystem/mcp/dist/lib/account-schema.d.ts.map +1 -0
- package/payload/platform/plugins/filesystem/mcp/dist/lib/account-schema.js +162 -0
- package/payload/platform/plugins/filesystem/mcp/dist/lib/account-schema.js.map +1 -0
- package/payload/platform/plugins/filesystem/mcp/dist/tools/__tests__/schema-fence.test.d.ts +2 -0
- package/payload/platform/plugins/filesystem/mcp/dist/tools/__tests__/schema-fence.test.d.ts.map +1 -0
- package/payload/platform/plugins/filesystem/mcp/dist/tools/__tests__/schema-fence.test.js +116 -0
- package/payload/platform/plugins/filesystem/mcp/dist/tools/__tests__/schema-fence.test.js.map +1 -0
- package/payload/platform/plugins/filesystem/mcp/dist/tools/file-edit.d.ts.map +1 -1
- package/payload/platform/plugins/filesystem/mcp/dist/tools/file-edit.js +9 -0
- package/payload/platform/plugins/filesystem/mcp/dist/tools/file-edit.js.map +1 -1
- package/payload/platform/plugins/filesystem/mcp/dist/tools/file-write.d.ts.map +1 -1
- package/payload/platform/plugins/filesystem/mcp/dist/tools/file-write.js +10 -0
- package/payload/platform/plugins/filesystem/mcp/dist/tools/file-write.js.map +1 -1
- package/payload/platform/plugins/scheduling/PLUGIN.md +8 -2
- package/payload/platform/plugins/scheduling/mcp/dist/index.js +7 -2
- package/payload/platform/plugins/scheduling/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/gate-audits.test.js +53 -6
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/gate-audits.test.js.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/gate.test.js +38 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/gate.test.js.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/lib/gate.d.ts +5 -3
- package/payload/platform/plugins/scheduling/mcp/dist/lib/gate.d.ts.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/lib/gate.js +18 -3
- package/payload/platform/plugins/scheduling/mcp/dist/lib/gate.js.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/lib/schedule-audit.d.ts +30 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/schedule-audit.d.ts.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/lib/schedule-audit.js +112 -39
- package/payload/platform/plugins/scheduling/mcp/dist/lib/schedule-audit.js.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/check-due-events.js +14 -1
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/check-due-events.js.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-gate-write.test.js +258 -8
- package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-gate-write.test.js.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/scheduling-gate.test.js +206 -3
- package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/scheduling-gate.test.js.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-event.d.ts +29 -21
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-event.d.ts.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-event.js +94 -14
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-event.js.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-update.d.ts.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-update.js +29 -1
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-update.js.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/tools/scheduling-gate.d.ts +30 -0
- package/payload/platform/plugins/scheduling/mcp/dist/tools/scheduling-gate.d.ts.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/tools/scheduling-gate.js +69 -6
- package/payload/platform/plugins/scheduling/mcp/dist/tools/scheduling-gate.js.map +1 -1
- package/payload/platform/plugins/whatsapp/references/channels-whatsapp.md +12 -0
- package/payload/platform/services/claude-session-manager/dist/account-dir-schema-reconcile.d.ts +36 -5
- package/payload/platform/services/claude-session-manager/dist/account-dir-schema-reconcile.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/account-dir-schema-reconcile.js +103 -10
- package/payload/platform/services/claude-session-manager/dist/account-dir-schema-reconcile.js.map +1 -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 -1
- package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/http-server.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/http-server.js +42 -4
- package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.js +19 -5
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/rc-daemon.d.ts +9 -0
- package/payload/platform/services/claude-session-manager/dist/rc-daemon.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/rc-daemon.js +5 -1
- package/payload/platform/services/claude-session-manager/dist/rc-daemon.js.map +1 -1
- package/payload/platform/services/telegram-channel/dist/instructions.d.ts.map +1 -1
- package/payload/platform/services/telegram-channel/dist/instructions.js +13 -0
- package/payload/platform/services/telegram-channel/dist/instructions.js.map +1 -1
- package/payload/platform/services/telegram-channel/dist/notification.d.ts +13 -0
- package/payload/platform/services/telegram-channel/dist/notification.d.ts.map +1 -1
- package/payload/platform/services/telegram-channel/dist/notification.js +13 -2
- package/payload/platform/services/telegram-channel/dist/notification.js.map +1 -1
- package/payload/platform/services/whatsapp-channel/dist/notification.d.ts +52 -0
- package/payload/platform/services/whatsapp-channel/dist/notification.d.ts.map +1 -1
- package/payload/platform/services/whatsapp-channel/dist/notification.js +64 -1
- package/payload/platform/services/whatsapp-channel/dist/notification.js.map +1 -1
- package/payload/platform/services/whatsapp-channel/dist/server.d.ts.map +1 -1
- package/payload/platform/services/whatsapp-channel/dist/server.js +14 -2
- package/payload/platform/services/whatsapp-channel/dist/server.js.map +1 -1
- package/payload/platform/templates/account-schema/SCHEMA.md +17 -10
- package/payload/platform/templates/agents/admin/IDENTITY.md +5 -0
- package/payload/platform/templates/specialists/agents/data-manager.md +2 -2
- package/payload/platform/templates/specialists/agents/personal-assistant.md +1 -1
- package/payload/server/{chunk-BFRB2U75.js → chunk-CV6AEMKH.js} +1 -0
- package/payload/server/{manager-CTVTF4YB.js → manager-FQSQSSJ3.js} +1 -1
- package/payload/server/public/activity.html +6 -6
- package/payload/server/public/agents.html +5 -5
- package/payload/server/public/assets/{AdminLoginScreens-AnJZfgFz.js → AdminLoginScreens-BLsDYWVi.js} +1 -1
- package/payload/server/public/assets/AdminLoginScreens-BLsDYWVi.js.br +0 -0
- package/payload/server/public/assets/AdminLoginScreens-BLsDYWVi.js.gz +0 -0
- package/payload/server/public/assets/{AdminShell-CEVqyQqg.js → AdminShell-BZS8jzFA.js} +1 -1
- package/payload/server/public/assets/AdminShell-BZS8jzFA.js.br +0 -0
- package/payload/server/public/assets/AdminShell-BZS8jzFA.js.gz +0 -0
- package/payload/server/public/assets/{activity-czUaUl8I.js → activity-DOjvoLdd.js} +1 -1
- package/payload/server/public/assets/activity-DOjvoLdd.js.br +0 -0
- package/payload/server/public/assets/activity-DOjvoLdd.js.gz +0 -0
- package/payload/server/public/assets/{admin-B0iRcfuk.js → admin-JrSNg29g.js} +1 -1
- package/payload/server/public/assets/admin-JrSNg29g.js.br +0 -0
- package/payload/server/public/assets/admin-JrSNg29g.js.gz +0 -0
- package/payload/server/public/assets/{agents-BocONCeQ.js → agents--9WMJVLC.js} +1 -1
- package/payload/server/public/assets/agents--9WMJVLC.js.br +0 -0
- package/payload/server/public/assets/agents--9WMJVLC.js.gz +0 -0
- package/payload/server/public/assets/{browser-Cy-rJm3t.js → browser-DiiMmTrM.js} +1 -1
- package/payload/server/public/assets/browser-DiiMmTrM.js.br +0 -0
- package/payload/server/public/assets/browser-DiiMmTrM.js.gz +0 -0
- package/payload/server/public/assets/{calendar-DUTKxWtd.js → calendar-CazmCzgO.js} +1 -1
- package/payload/server/public/assets/calendar-CazmCzgO.js.br +0 -0
- package/payload/server/public/assets/calendar-CazmCzgO.js.gz +0 -0
- package/payload/server/public/assets/chat-CB0TcyXX.js +1 -0
- package/payload/server/public/assets/chat-CB0TcyXX.js.br +0 -0
- package/payload/server/public/assets/chat-CB0TcyXX.js.gz +0 -0
- package/payload/server/public/assets/chevron-left-0tVo23Dh.js +1 -0
- package/payload/server/public/assets/chevron-left-0tVo23Dh.js.br +0 -0
- package/payload/server/public/assets/clock-D2cZ0sge.js +1 -0
- package/payload/server/public/assets/clock-D2cZ0sge.js.br +0 -0
- package/payload/server/public/assets/clock-D2cZ0sge.js.gz +0 -0
- package/payload/server/public/assets/{copy-DE9qmAs5.js → copy-C87QIN_o.js} +1 -1
- package/payload/server/public/assets/copy-C87QIN_o.js.br +0 -0
- package/payload/server/public/assets/copy-C87QIN_o.js.gz +0 -0
- package/payload/server/public/assets/data-CK7AMxPt.js +1 -0
- package/payload/server/public/assets/data-CK7AMxPt.js.br +0 -0
- package/payload/server/public/assets/data-CK7AMxPt.js.gz +0 -0
- package/payload/server/public/assets/{field-DE2tEfTo.js → field-C3kQgGWE.js} +1 -1
- package/payload/server/public/assets/field-C3kQgGWE.js.br +0 -0
- package/payload/server/public/assets/field-C3kQgGWE.js.gz +0 -0
- package/payload/server/public/assets/{file-text-BHfL2gqo.js → file-text-rWowl9RN.js} +1 -1
- package/payload/server/public/assets/file-text-rWowl9RN.js.br +0 -0
- package/payload/server/public/assets/file-text-rWowl9RN.js.gz +0 -0
- package/payload/server/public/assets/{graph-08S_BofN.js → graph-CVJKz1Uo.js} +1 -1
- package/payload/server/public/assets/graph-CVJKz1Uo.js.br +0 -0
- package/payload/server/public/assets/graph-CVJKz1Uo.js.gz +0 -0
- package/payload/server/public/assets/{graph-labels-C640Er5R.js → graph-labels-kneGnsHB.js} +1 -1
- package/payload/server/public/assets/graph-labels-kneGnsHB.js.br +0 -0
- package/payload/server/public/assets/graph-labels-kneGnsHB.js.gz +0 -0
- package/payload/server/public/assets/{jsx-runtime-FxjwOUbA.css → jsx-runtime-rMaiYjNu.css} +1 -1
- package/payload/server/public/assets/jsx-runtime-rMaiYjNu.css.br +0 -0
- package/payload/server/public/assets/{jsx-runtime-FxjwOUbA.css.gz → jsx-runtime-rMaiYjNu.css.gz} +0 -0
- package/payload/server/public/assets/{operator-DH5Kw3fL.js → operator-BGvB4hu4.js} +1 -1
- package/payload/server/public/assets/operator-BGvB4hu4.js.br +0 -0
- package/payload/server/public/assets/operator-BGvB4hu4.js.gz +0 -0
- package/payload/server/public/assets/{page-BDpa4HWT.js → page-DBQLOasI.js} +1 -1
- package/payload/server/public/assets/page-DBQLOasI.js.br +0 -0
- package/payload/server/public/assets/page-DBQLOasI.js.gz +0 -0
- package/payload/server/public/assets/{page-yn7ivq8O.js → page-DyVAu7tm.js} +1 -1
- package/payload/server/public/assets/page-DyVAu7tm.js.br +0 -0
- package/payload/server/public/assets/page-DyVAu7tm.js.gz +0 -0
- package/payload/server/public/assets/play-DgP7vl0r.js +1 -0
- package/payload/server/public/assets/play-DgP7vl0r.js.br +0 -0
- package/payload/server/public/assets/play-DgP7vl0r.js.gz +0 -0
- package/payload/server/public/assets/{public-D_qjqet7.js → public-BvUQR3hd.js} +1 -1
- package/payload/server/public/assets/public-BvUQR3hd.js.br +0 -0
- package/payload/server/public/assets/public-BvUQR3hd.js.gz +0 -0
- package/payload/server/public/assets/{rotate-ccw-Ixkq1iUA.js → rotate-ccw-Dnn7gbPh.js} +1 -1
- package/payload/server/public/assets/rotate-ccw-Dnn7gbPh.js.br +0 -0
- package/payload/server/public/assets/rotate-ccw-Dnn7gbPh.js.gz +0 -0
- package/payload/server/public/assets/{routines-Cxjlmcp7.js → routines-N8NmxPOZ.js} +1 -1
- package/payload/server/public/assets/routines-N8NmxPOZ.js.br +0 -0
- package/payload/server/public/assets/routines-N8NmxPOZ.js.gz +0 -0
- package/payload/server/public/assets/{skills-DEo9hSVX.js → skills-B2CRgO-U.js} +1 -1
- package/payload/server/public/assets/skills-B2CRgO-U.js.br +0 -0
- package/payload/server/public/assets/skills-B2CRgO-U.js.gz +0 -0
- package/payload/server/public/assets/{tasks-BxApOIXI.js → tasks-8QLGtlSj.js} +1 -1
- package/payload/server/public/assets/tasks-8QLGtlSj.js.br +0 -0
- package/payload/server/public/assets/tasks-8QLGtlSj.js.gz +0 -0
- package/payload/server/public/assets/{triangle-alert-BYzMcXv8.js → triangle-alert-el772yUQ.js} +1 -1
- package/payload/server/public/assets/triangle-alert-el772yUQ.js.br +0 -0
- package/payload/server/public/assets/triangle-alert-el772yUQ.js.gz +0 -0
- package/payload/server/public/assets/{useCopyFeedback-Bk2HVcjy.js → useCopyFeedback-DfNAu8hQ.js} +1 -1
- package/payload/server/public/assets/useCopyFeedback-DfNAu8hQ.js.br +0 -0
- package/payload/server/public/assets/useCopyFeedback-DfNAu8hQ.js.gz +0 -0
- package/payload/server/public/assets/{useMediaQuery-DEti-vAx.js → useMediaQuery-B469O83-.js} +1 -1
- package/payload/server/public/assets/useMediaQuery-B469O83-.js.br +0 -0
- package/payload/server/public/assets/{useMediaQuery-DEti-vAx.js.gz → useMediaQuery-B469O83-.js.gz} +0 -0
- package/payload/server/public/assets/{useVoiceRecorder-kQ92rL-f.js → useVoiceRecorder-Bb-51cJv.js} +1 -1
- package/payload/server/public/assets/useVoiceRecorder-Bb-51cJv.js.br +0 -0
- package/payload/server/public/assets/useVoiceRecorder-Bb-51cJv.js.gz +0 -0
- package/payload/server/public/assets/{wrench-BhLkHffv.js → wrench-Ck4phkdB.js} +1 -1
- package/payload/server/public/assets/wrench-Ck4phkdB.js.br +0 -0
- package/payload/server/public/assets/wrench-Ck4phkdB.js.gz +0 -0
- package/payload/server/public/browser.html +5 -5
- package/payload/server/public/calendar.html +8 -8
- package/payload/server/public/chat.html +14 -14
- package/payload/server/public/data.html +12 -12
- package/payload/server/public/field.html +3 -3
- package/payload/server/public/graph.html +10 -10
- package/payload/server/public/index.html +14 -14
- package/payload/server/public/operator.html +15 -15
- package/payload/server/public/public.html +14 -14
- package/payload/server/public/routines.html +8 -8
- package/payload/server/public/skills.html +6 -6
- package/payload/server/public/tasks.html +7 -7
- package/payload/server/server.js +3114 -1371
- package/payload/server/public/assets/AdminLoginScreens-AnJZfgFz.js.br +0 -0
- package/payload/server/public/assets/AdminLoginScreens-AnJZfgFz.js.gz +0 -0
- package/payload/server/public/assets/AdminShell-CEVqyQqg.js.br +0 -0
- package/payload/server/public/assets/AdminShell-CEVqyQqg.js.gz +0 -0
- package/payload/server/public/assets/activity-czUaUl8I.js.br +0 -0
- package/payload/server/public/assets/activity-czUaUl8I.js.gz +0 -0
- package/payload/server/public/assets/admin-B0iRcfuk.js.br +0 -0
- package/payload/server/public/assets/admin-B0iRcfuk.js.gz +0 -0
- package/payload/server/public/assets/agents-BocONCeQ.js.br +0 -0
- package/payload/server/public/assets/agents-BocONCeQ.js.gz +0 -0
- package/payload/server/public/assets/browser-Cy-rJm3t.js.br +0 -0
- package/payload/server/public/assets/browser-Cy-rJm3t.js.gz +0 -0
- package/payload/server/public/assets/calendar-DUTKxWtd.js.br +0 -0
- package/payload/server/public/assets/calendar-DUTKxWtd.js.gz +0 -0
- package/payload/server/public/assets/chat-Cis7sHsZ.js +0 -1
- package/payload/server/public/assets/chat-Cis7sHsZ.js.br +0 -0
- package/payload/server/public/assets/chat-Cis7sHsZ.js.gz +0 -0
- package/payload/server/public/assets/chevron-left-Bhhx8j2r.js +0 -1
- package/payload/server/public/assets/chevron-left-Bhhx8j2r.js.br +0 -0
- package/payload/server/public/assets/clock-Ca3nx7Cc.js +0 -1
- package/payload/server/public/assets/clock-Ca3nx7Cc.js.br +0 -0
- package/payload/server/public/assets/clock-Ca3nx7Cc.js.gz +0 -0
- package/payload/server/public/assets/copy-DE9qmAs5.js.br +0 -0
- package/payload/server/public/assets/copy-DE9qmAs5.js.gz +0 -0
- package/payload/server/public/assets/data-CkaAEeP1.js +0 -1
- package/payload/server/public/assets/data-CkaAEeP1.js.br +0 -0
- package/payload/server/public/assets/data-CkaAEeP1.js.gz +0 -0
- package/payload/server/public/assets/field-DE2tEfTo.js.br +0 -0
- package/payload/server/public/assets/field-DE2tEfTo.js.gz +0 -0
- package/payload/server/public/assets/file-text-BHfL2gqo.js.br +0 -0
- package/payload/server/public/assets/file-text-BHfL2gqo.js.gz +0 -0
- package/payload/server/public/assets/graph-08S_BofN.js.br +0 -0
- package/payload/server/public/assets/graph-08S_BofN.js.gz +0 -0
- package/payload/server/public/assets/graph-labels-C640Er5R.js.br +0 -0
- package/payload/server/public/assets/graph-labels-C640Er5R.js.gz +0 -0
- package/payload/server/public/assets/jsx-runtime-FxjwOUbA.css.br +0 -0
- package/payload/server/public/assets/operator-DH5Kw3fL.js.br +0 -0
- package/payload/server/public/assets/operator-DH5Kw3fL.js.gz +0 -0
- package/payload/server/public/assets/page-BDpa4HWT.js.br +0 -0
- package/payload/server/public/assets/page-BDpa4HWT.js.gz +0 -0
- package/payload/server/public/assets/page-yn7ivq8O.js.br +0 -0
- package/payload/server/public/assets/page-yn7ivq8O.js.gz +0 -0
- package/payload/server/public/assets/play-CMN7ht-S.js +0 -1
- package/payload/server/public/assets/play-CMN7ht-S.js.br +0 -0
- package/payload/server/public/assets/play-CMN7ht-S.js.gz +0 -0
- package/payload/server/public/assets/public-D_qjqet7.js.br +0 -0
- package/payload/server/public/assets/public-D_qjqet7.js.gz +0 -0
- package/payload/server/public/assets/rotate-ccw-Ixkq1iUA.js.br +0 -0
- package/payload/server/public/assets/rotate-ccw-Ixkq1iUA.js.gz +0 -0
- package/payload/server/public/assets/routines-Cxjlmcp7.js.br +0 -0
- package/payload/server/public/assets/routines-Cxjlmcp7.js.gz +0 -0
- package/payload/server/public/assets/skills-DEo9hSVX.js.br +0 -0
- package/payload/server/public/assets/skills-DEo9hSVX.js.gz +0 -0
- package/payload/server/public/assets/tasks-BxApOIXI.js.br +0 -0
- package/payload/server/public/assets/tasks-BxApOIXI.js.gz +0 -0
- package/payload/server/public/assets/triangle-alert-BYzMcXv8.js.br +0 -0
- package/payload/server/public/assets/triangle-alert-BYzMcXv8.js.gz +0 -0
- package/payload/server/public/assets/useCopyFeedback-Bk2HVcjy.js.br +0 -0
- package/payload/server/public/assets/useCopyFeedback-Bk2HVcjy.js.gz +0 -0
- package/payload/server/public/assets/useMediaQuery-DEti-vAx.js.br +0 -0
- package/payload/server/public/assets/useVoiceRecorder-kQ92rL-f.js.br +0 -0
- package/payload/server/public/assets/useVoiceRecorder-kQ92rL-f.js.gz +0 -0
- package/payload/server/public/assets/wrench-BhLkHffv.js.br +0 -0
- package/payload/server/public/assets/wrench-BhLkHffv.js.gz +0 -0
- /package/payload/server/public/assets/{jsx-runtime-BL3pljPe.js → jsx-runtime-BmNepPEn.js} +0 -0
- /package/payload/server/public/assets/{jsx-runtime-BL3pljPe.js.br → jsx-runtime-BmNepPEn.js.br} +0 -0
- /package/payload/server/public/assets/{jsx-runtime-BL3pljPe.js.gz → jsx-runtime-BmNepPEn.js.gz} +0 -0
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isStorableChannelKey = isStorableChannelKey;
|
|
4
|
+
exports.isBusinessChannelKey = isBusinessChannelKey;
|
|
5
|
+
exports.channelKeyToFileStem = channelKeyToFileStem;
|
|
6
|
+
exports.fileStemToChannelKey = fileStemToChannelKey;
|
|
7
|
+
exports.collectTelegramReach = collectTelegramReach;
|
|
8
|
+
exports.telegramStoreRoot = telegramStoreRoot;
|
|
9
|
+
/**
|
|
10
|
+
* Task 2625 — which Telegram chats a registered bot can already message.
|
|
11
|
+
*
|
|
12
|
+
* Telegram does not let a bot open a conversation: the person must message it
|
|
13
|
+
* first (core.telegram.org/bots). So "can I reach X" is answerable only from
|
|
14
|
+
* what has already arrived, and the record of that is the message store the
|
|
15
|
+
* webhook writes. Before this module nothing enumerated it, and the agent
|
|
16
|
+
* refused a send that one telegram-card call would have made.
|
|
17
|
+
*
|
|
18
|
+
* Shared rather than duplicated plugin-side, unlike the live-location and card
|
|
19
|
+
* stores: BOTH callers need this same walk — the MCP tool answers one account on
|
|
20
|
+
* demand, the hourly census counts every account — so duplicating it would mean
|
|
21
|
+
* writing the walk twice and holding it together with a boundary test. The
|
|
22
|
+
* filename rule lives here too, and `ui/app/lib/telegram/message-store.ts`
|
|
23
|
+
* re-exports it, so the store that writes a filename and the walk that reads one
|
|
24
|
+
* cannot disagree.
|
|
25
|
+
*
|
|
26
|
+
* `collectTelegramReach` takes `storeDir` from its caller, but the DERIVATION of
|
|
27
|
+
* that directory lives here too, in `telegramStoreRoot`. Task 2625 left it to
|
|
28
|
+
* each caller on the grounds that a library must not know what a brand is, and
|
|
29
|
+
* the two callers promptly disagreed: the webhook wrote the brand config dir
|
|
30
|
+
* while `telegram-reach` read the brand install dir, so the tool reported every
|
|
31
|
+
* chat unreachable and named its own live sender as never having messaged
|
|
32
|
+
* (Task 2644). One derivation, for the same reason the filename rule is here.
|
|
33
|
+
*/
|
|
34
|
+
const node_fs_1 = require("node:fs");
|
|
35
|
+
const node_os_1 = require("node:os");
|
|
36
|
+
const node_path_1 = require("node:path");
|
|
37
|
+
/**
|
|
38
|
+
* Is this a channel key that can safely become a path segment?
|
|
39
|
+
*
|
|
40
|
+
* Both halves of an admin key are Telegram numeric ids (a bot id is positive; a
|
|
41
|
+
* chat or sender id is an integer and is negative for groups), so requiring
|
|
42
|
+
* exactly that is both the truthful shape check and a complete traversal guard:
|
|
43
|
+
* no `.`, `/` or `\` can survive it.
|
|
44
|
+
*
|
|
45
|
+
* Task 2615 — the business shape `<botId>:biz:<senderId>` joins the guard rather
|
|
46
|
+
* than bypassing it. Both alternatives are anchored and admit only digits, an
|
|
47
|
+
* optional leading minus and the literal `biz`, so neither can carry a `.`, `/`
|
|
48
|
+
* or `\` either.
|
|
49
|
+
*/
|
|
50
|
+
function isStorableChannelKey(channelKey) {
|
|
51
|
+
return /^-?\d+:-?\d+$/.test(channelKey) || /^-?\d+:biz:-?\d+$/.test(channelKey);
|
|
52
|
+
}
|
|
53
|
+
/** Task 2615 — is this the three-part business shape?
|
|
54
|
+
*
|
|
55
|
+
* Its own predicate rather than a `.includes(':biz:')` at each caller: a
|
|
56
|
+
* business conversation is reached through the business-connection send route
|
|
57
|
+
* and its 24-hour window, not by addressing a chat id, so anything that answers
|
|
58
|
+
* "can we message this person now" has to be able to tell the two apart. */
|
|
59
|
+
function isBusinessChannelKey(channelKey) {
|
|
60
|
+
return /^-?\d+:biz:-?\d+$/.test(channelKey);
|
|
61
|
+
}
|
|
62
|
+
/** The filename stem for a channel key.
|
|
63
|
+
*
|
|
64
|
+
* Throws on a key that is neither two numeric ids nor the business shape.
|
|
65
|
+
* Refusing loudly is correct here: every caller reaches this with a key it has
|
|
66
|
+
* already validated, so an unstorable key means a caller skipped its guard, and
|
|
67
|
+
* silently sanitising it would build a path nobody asked for. */
|
|
68
|
+
function channelKeyToFileStem(channelKey) {
|
|
69
|
+
if (!isStorableChannelKey(channelKey)) {
|
|
70
|
+
throw new Error(`telegram-message-store: refusing unstorable channelKey ${JSON.stringify(channelKey)}`);
|
|
71
|
+
}
|
|
72
|
+
// Task 2615 — every colon, not only the first: a business key has two.
|
|
73
|
+
return channelKey.replaceAll(":", "_");
|
|
74
|
+
}
|
|
75
|
+
/** The inverse of `channelKeyToFileStem`.
|
|
76
|
+
*
|
|
77
|
+
* Task 2615 — a stem whose second segment is exactly `biz` is a three-part
|
|
78
|
+
* business key. Otherwise only the FIRST `_` is a separator, because both halves
|
|
79
|
+
* of an admin key are numeric Telegram ids and nothing later in that stem is
|
|
80
|
+
* one. */
|
|
81
|
+
function fileStemToChannelKey(stem) {
|
|
82
|
+
const i = stem.indexOf("_");
|
|
83
|
+
if (i <= 0)
|
|
84
|
+
return stem;
|
|
85
|
+
const rest = stem.slice(i + 1);
|
|
86
|
+
if (rest.startsWith("biz_")) {
|
|
87
|
+
return `${stem.slice(0, i)}:biz:${rest.slice("biz_".length)}`;
|
|
88
|
+
}
|
|
89
|
+
return `${stem.slice(0, i)}:${rest}`;
|
|
90
|
+
}
|
|
91
|
+
function parseLines(raw) {
|
|
92
|
+
const out = [];
|
|
93
|
+
for (const line of raw.split("\n")) {
|
|
94
|
+
if (!line.trim())
|
|
95
|
+
continue;
|
|
96
|
+
try {
|
|
97
|
+
out.push(JSON.parse(line));
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
// A partial write from a crash mid-append is skipped, never fatal: one
|
|
101
|
+
// corrupt line must not hide every other chat in the account.
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return out;
|
|
105
|
+
}
|
|
106
|
+
/** Every conversation file's newest inbound PRIVATE row, keyed
|
|
107
|
+
* `<botId>:<senderId>`, plus the message rows read getting there. */
|
|
108
|
+
function scanStore(storeDir) {
|
|
109
|
+
const newest = new Map();
|
|
110
|
+
let storeRows = 0;
|
|
111
|
+
if (!storeDir || !(0, node_fs_1.existsSync)(storeDir))
|
|
112
|
+
return { newest, storeRows };
|
|
113
|
+
let entries;
|
|
114
|
+
try {
|
|
115
|
+
entries = (0, node_fs_1.readdirSync)(storeDir);
|
|
116
|
+
}
|
|
117
|
+
catch {
|
|
118
|
+
return { newest, storeRows };
|
|
119
|
+
}
|
|
120
|
+
for (const entry of entries) {
|
|
121
|
+
if (!entry.endsWith(".jsonl"))
|
|
122
|
+
continue;
|
|
123
|
+
const stem = entry.slice(0, -".jsonl".length);
|
|
124
|
+
const channelKey = fileStemToChannelKey(stem);
|
|
125
|
+
if (!isStorableChannelKey(channelKey))
|
|
126
|
+
continue;
|
|
127
|
+
// Task 2615's business conversations are skipped entirely, rows included.
|
|
128
|
+
// A business chat is NOT reached by addressing a chat id: it needs the
|
|
129
|
+
// business-connection send route and its own 24-hour window, which Task 2625
|
|
130
|
+
// put out of scope. Listing one here would report it as sendable with
|
|
131
|
+
// telegram-card, which is the same wrong answer in the opposite direction to
|
|
132
|
+
// the refusal this tool exists to prevent.
|
|
133
|
+
if (isBusinessChannelKey(channelKey))
|
|
134
|
+
continue;
|
|
135
|
+
const botId = channelKey.slice(0, channelKey.indexOf(":"));
|
|
136
|
+
let raw;
|
|
137
|
+
try {
|
|
138
|
+
raw = (0, node_fs_1.readFileSync)((0, node_path_1.join)(storeDir, entry), "utf8");
|
|
139
|
+
}
|
|
140
|
+
catch {
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
for (const row of parseLines(raw)) {
|
|
144
|
+
// An update row is a patch, not a message: counting it would inflate
|
|
145
|
+
// storeRows and it carries no sender at all.
|
|
146
|
+
if (row.kind === "update")
|
|
147
|
+
continue;
|
|
148
|
+
storeRows += 1;
|
|
149
|
+
if (row.origin !== "inbound")
|
|
150
|
+
continue;
|
|
151
|
+
const chatId = typeof row.chatId === "string" ? row.chatId : null;
|
|
152
|
+
const senderId = typeof row.senderId === "string" ? row.senderId : null;
|
|
153
|
+
const dateSent = typeof row.dateSent === "string" ? row.dateSent : null;
|
|
154
|
+
if (chatId === null || senderId === null || dateSent === null)
|
|
155
|
+
continue;
|
|
156
|
+
// Private chats only. A DM chat id equals the sender id; a group's differs
|
|
157
|
+
// (telegram-gateway.ts:66-68). A group is not a route to a person.
|
|
158
|
+
if (chatId !== senderId)
|
|
159
|
+
continue;
|
|
160
|
+
const key = `${botId}:${senderId}`;
|
|
161
|
+
const prev = newest.get(key);
|
|
162
|
+
if (prev !== undefined && prev.dateSent.localeCompare(dateSent) >= 0)
|
|
163
|
+
continue;
|
|
164
|
+
newest.set(key, {
|
|
165
|
+
chatId,
|
|
166
|
+
senderId,
|
|
167
|
+
senderName: typeof row.senderName === "string" ? row.senderName : null,
|
|
168
|
+
dateSent,
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return { newest, storeRows };
|
|
173
|
+
}
|
|
174
|
+
/** Reachability for one account: every registered bot, the private chats that
|
|
175
|
+
* have messaged it, and the allowlisted people who have not. */
|
|
176
|
+
function collectTelegramReach(input) {
|
|
177
|
+
const { newest, storeRows } = scanStore(input.storeDir);
|
|
178
|
+
const bots = input.bots.map((bot) => {
|
|
179
|
+
const adminUsers = bot.adminUsers ?? [];
|
|
180
|
+
const adminSet = new Set(adminUsers.map((u) => String(u)));
|
|
181
|
+
const chats = [];
|
|
182
|
+
const seen = new Set();
|
|
183
|
+
for (const [key, row] of newest) {
|
|
184
|
+
if (key.slice(0, key.indexOf(":")) !== bot.id)
|
|
185
|
+
continue;
|
|
186
|
+
seen.add(row.senderId);
|
|
187
|
+
chats.push({
|
|
188
|
+
chatId: row.chatId,
|
|
189
|
+
senderId: row.senderId,
|
|
190
|
+
displayName: row.senderName,
|
|
191
|
+
lastInbound: row.dateSent,
|
|
192
|
+
admin: adminSet.has(row.senderId),
|
|
193
|
+
reachable: true,
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
chats.sort((a, b) => (b.lastInbound ?? "").localeCompare(a.lastInbound ?? ""));
|
|
197
|
+
for (const user of adminUsers) {
|
|
198
|
+
const senderId = String(user);
|
|
199
|
+
if (seen.has(senderId))
|
|
200
|
+
continue;
|
|
201
|
+
// Listed, never messaged. Reported rather than omitted, because "we are
|
|
202
|
+
// allowed to talk to them" and "they have opened the chat" are different
|
|
203
|
+
// facts and only the second one makes a send possible.
|
|
204
|
+
chats.push({
|
|
205
|
+
chatId: null,
|
|
206
|
+
senderId,
|
|
207
|
+
displayName: null,
|
|
208
|
+
lastInbound: null,
|
|
209
|
+
admin: true,
|
|
210
|
+
reachable: false,
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
return { botId: bot.id, role: bot.role, adminUsers, chats };
|
|
214
|
+
});
|
|
215
|
+
return { bots, storeRows };
|
|
216
|
+
}
|
|
217
|
+
/** The brand's config directory NAME, from its own brand.json.
|
|
218
|
+
*
|
|
219
|
+
* Resolved WITHOUT the platform's `paths.ts`, which throws at module init on a
|
|
220
|
+
* brand.json missing a port field. The inbound Telegram webhook imports this
|
|
221
|
+
* path, so a throw here takes delivery down; a brand.json that cannot be read
|
|
222
|
+
* falls back to `.maxy` instead. */
|
|
223
|
+
function configDirName(platformRoot) {
|
|
224
|
+
const brandPath = (0, node_path_1.join)(platformRoot, "config", "brand.json");
|
|
225
|
+
if ((0, node_fs_1.existsSync)(brandPath)) {
|
|
226
|
+
try {
|
|
227
|
+
const parsed = JSON.parse((0, node_fs_1.readFileSync)(brandPath, "utf8"));
|
|
228
|
+
if (typeof parsed.configDir === "string")
|
|
229
|
+
return parsed.configDir;
|
|
230
|
+
}
|
|
231
|
+
catch {
|
|
232
|
+
// A brand.json that will not parse falls back rather than throwing.
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
return ".maxy";
|
|
236
|
+
}
|
|
237
|
+
/** The brand's Telegram message store root, for a given platform root.
|
|
238
|
+
*
|
|
239
|
+
* The ONE derivation of this path. `ui/app/lib/telegram/message-store.ts`
|
|
240
|
+
* writes under it and `plugins/telegram/mcp` reads under it, both through this
|
|
241
|
+
* function, so the writer and the reader cannot name different directories. */
|
|
242
|
+
function telegramStoreRoot(platformRoot) {
|
|
243
|
+
const brandDir = process.env.MAXY_DIR_OVERRIDE ?? (0, node_path_1.resolve)((0, node_os_1.homedir)(), configDirName(platformRoot));
|
|
244
|
+
return (0, node_path_1.join)(brandDir, "data", "telegram-messages");
|
|
245
|
+
}
|
|
246
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;AA0CA,oDAEC;AAQD,oDAEC;AAQD,oDAQC;AAQD,oDAQC;AA2ID,oDAyCC;AA0BD,8CAGC;AAvSD;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,qCAAgE;AAChE,qCAAkC;AAClC,yCAA0C;AAE1C;;;;;;;;;;;;GAYG;AACH,SAAgB,oBAAoB,CAAC,UAAkB;IACrD,OAAO,eAAe,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,mBAAmB,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;AAClF,CAAC;AAED;;;;;6EAK6E;AAC7E,SAAgB,oBAAoB,CAAC,UAAkB;IACrD,OAAO,mBAAmB,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;AAC9C,CAAC;AAED;;;;;kEAKkE;AAClE,SAAgB,oBAAoB,CAAC,UAAkB;IACrD,IAAI,CAAC,oBAAoB,CAAC,UAAU,CAAC,EAAE,CAAC;QACtC,MAAM,IAAI,KAAK,CACb,0DAA0D,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,EAAE,CACvF,CAAC;IACJ,CAAC;IACD,uEAAuE;IACvE,OAAO,UAAU,CAAC,UAAU,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;AACzC,CAAC;AAED;;;;;WAKW;AACX,SAAgB,oBAAoB,CAAC,IAAY;IAC/C,MAAM,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAC5B,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IACxB,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IAC/B,IAAI,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;QAC5B,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,QAAQ,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC;IAChE,CAAC;IACD,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC;AACvC,CAAC;AAwDD,SAAS,UAAU,CAAC,GAAW;IAC7B,MAAM,GAAG,GAAmB,EAAE,CAAC;IAC/B,KAAK,MAAM,IAAI,IAAI,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QACnC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE;YAAE,SAAS;QAC3B,IAAI,CAAC;YACH,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAiB,CAAC,CAAC;QAC7C,CAAC;QAAC,MAAM,CAAC;YACP,uEAAuE;YACvE,8DAA8D;QAChE,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED;sEACsE;AACtE,SAAS,SAAS,CAAC,QAAgB;IAOjC,MAAM,MAAM,GAAG,IAAI,GAAG,EAGnB,CAAC;IACJ,IAAI,SAAS,GAAG,CAAC,CAAC;IAClB,IAAI,CAAC,QAAQ,IAAI,CAAC,IAAA,oBAAU,EAAC,QAAQ,CAAC;QAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;IACrE,IAAI,OAAiB,CAAC;IACtB,IAAI,CAAC;QACH,OAAO,GAAG,IAAA,qBAAW,EAAC,QAAQ,CAAC,CAAC;IAClC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;IAC/B,CAAC;IACD,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,QAAQ,CAAC;YAAE,SAAS;QACxC,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QAC9C,MAAM,UAAU,GAAG,oBAAoB,CAAC,IAAI,CAAC,CAAC;QAC9C,IAAI,CAAC,oBAAoB,CAAC,UAAU,CAAC;YAAE,SAAS;QAChD,0EAA0E;QAC1E,uEAAuE;QACvE,6EAA6E;QAC7E,sEAAsE;QACtE,6EAA6E;QAC7E,2CAA2C;QAC3C,IAAI,oBAAoB,CAAC,UAAU,CAAC;YAAE,SAAS;QAC/C,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC;QAC3D,IAAI,GAAW,CAAC;QAChB,IAAI,CAAC;YACH,GAAG,GAAG,IAAA,sBAAY,EAAC,IAAA,gBAAI,EAAC,QAAQ,EAAE,KAAK,CAAC,EAAE,MAAM,CAAC,CAAC;QACpD,CAAC;QAAC,MAAM,CAAC;YACP,SAAS;QACX,CAAC;QACD,KAAK,MAAM,GAAG,IAAI,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YAClC,qEAAqE;YACrE,6CAA6C;YAC7C,IAAI,GAAG,CAAC,IAAI,KAAK,QAAQ;gBAAE,SAAS;YACpC,SAAS,IAAI,CAAC,CAAC;YACf,IAAI,GAAG,CAAC,MAAM,KAAK,SAAS;gBAAE,SAAS;YACvC,MAAM,MAAM,GAAG,OAAO,GAAG,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC;YAClE,MAAM,QAAQ,GAAG,OAAO,GAAG,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC;YACxE,MAAM,QAAQ,GAAG,OAAO,GAAG,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC;YACxE,IAAI,MAAM,KAAK,IAAI,IAAI,QAAQ,KAAK,IAAI,IAAI,QAAQ,KAAK,IAAI;gBAAE,SAAS;YACxE,2EAA2E;YAC3E,mEAAmE;YACnE,IAAI,MAAM,KAAK,QAAQ;gBAAE,SAAS;YAClC,MAAM,GAAG,GAAG,GAAG,KAAK,IAAI,QAAQ,EAAE,CAAC;YACnC,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YAC7B,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,CAAC,QAAQ,CAAC,aAAa,CAAC,QAAQ,CAAC,IAAI,CAAC;gBAAE,SAAS;YAC/E,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE;gBACd,MAAM;gBACN,QAAQ;gBACR,UAAU,EAAE,OAAO,GAAG,CAAC,UAAU,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI;gBACtE,QAAQ;aACT,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IACD,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;AAC/B,CAAC;AAED;iEACiE;AACjE,SAAgB,oBAAoB,CAAC,KAGpC;IACC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,SAAS,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IACxD,MAAM,IAAI,GAAe,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE;QAC9C,MAAM,UAAU,GAAG,GAAG,CAAC,UAAU,IAAI,EAAE,CAAC;QACxC,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC3D,MAAM,KAAK,GAAgB,EAAE,CAAC;QAC9B,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;QAC/B,KAAK,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,IAAI,MAAM,EAAE,CAAC;YAChC,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,GAAG,CAAC,EAAE;gBAAE,SAAS;YACxD,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;YACvB,KAAK,CAAC,IAAI,CAAC;gBACT,MAAM,EAAE,GAAG,CAAC,MAAM;gBAClB,QAAQ,EAAE,GAAG,CAAC,QAAQ;gBACtB,WAAW,EAAE,GAAG,CAAC,UAAU;gBAC3B,WAAW,EAAE,GAAG,CAAC,QAAQ;gBACzB,KAAK,EAAE,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC;gBACjC,SAAS,EAAE,IAAI;aAChB,CAAC,CAAC;QACL,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC,CAAC;QAC/E,KAAK,MAAM,IAAI,IAAI,UAAU,EAAE,CAAC;YAC9B,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC;YAC9B,IAAI,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC;gBAAE,SAAS;YACjC,wEAAwE;YACxE,yEAAyE;YACzE,uDAAuD;YACvD,KAAK,CAAC,IAAI,CAAC;gBACT,MAAM,EAAE,IAAI;gBACZ,QAAQ;gBACR,WAAW,EAAE,IAAI;gBACjB,WAAW,EAAE,IAAI;gBACjB,KAAK,EAAE,IAAI;gBACX,SAAS,EAAE,KAAK;aACjB,CAAC,CAAC;QACL,CAAC;QACD,OAAO,EAAE,KAAK,EAAE,GAAG,CAAC,EAAE,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC;IAC9D,CAAC,CAAC,CAAC;IACH,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;AAC7B,CAAC;AAED;;;;;qCAKqC;AACrC,SAAS,aAAa,CAAC,YAAoB;IACzC,MAAM,SAAS,GAAG,IAAA,gBAAI,EAAC,YAAY,EAAE,QAAQ,EAAE,YAAY,CAAC,CAAC;IAC7D,IAAI,IAAA,oBAAU,EAAC,SAAS,CAAC,EAAE,CAAC;QAC1B,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAA,sBAAY,EAAC,SAAS,EAAE,MAAM,CAAC,CAA4B,CAAC;YACtF,IAAI,OAAO,MAAM,CAAC,SAAS,KAAK,QAAQ;gBAAE,OAAO,MAAM,CAAC,SAAS,CAAC;QACpE,CAAC;QAAC,MAAM,CAAC;YACP,oEAAoE;QACtE,CAAC;IACH,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;;gFAIgF;AAChF,SAAgB,iBAAiB,CAAC,YAAoB;IACpD,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,IAAA,mBAAO,EAAC,IAAA,iBAAO,GAAE,EAAE,aAAa,CAAC,YAAY,CAAC,CAAC,CAAC;IAClG,OAAO,IAAA,gBAAI,EAAC,QAAQ,EAAE,MAAM,EAAE,mBAAmB,CAAC,CAAC;AACrD,CAAC"}
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
|
2
|
+
import { mkdtempSync, mkdirSync, writeFileSync, rmSync } from "node:fs";
|
|
3
|
+
import { tmpdir, homedir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import {
|
|
6
|
+
collectTelegramReach,
|
|
7
|
+
isStorableChannelKey,
|
|
8
|
+
isBusinessChannelKey,
|
|
9
|
+
channelKeyToFileStem,
|
|
10
|
+
fileStemToChannelKey,
|
|
11
|
+
telegramStoreRoot,
|
|
12
|
+
} from "../index.js";
|
|
13
|
+
|
|
14
|
+
const BOT = "8801383568";
|
|
15
|
+
const KIT = "8860040117";
|
|
16
|
+
const NEO = "1311221864";
|
|
17
|
+
const GROUP = "-1002222222222";
|
|
18
|
+
|
|
19
|
+
let storeDir: string;
|
|
20
|
+
|
|
21
|
+
/** One inbound message row, in the shape message-store.ts writes. */
|
|
22
|
+
const inbound = (over: Record<string, unknown> = {}) => ({
|
|
23
|
+
messageId: `telegram:acct:${BOT}:${KIT}:in:1`,
|
|
24
|
+
sessionId: "s",
|
|
25
|
+
dateSent: "2026-08-09T07:46:31.000Z",
|
|
26
|
+
body: "hello",
|
|
27
|
+
fromMe: false,
|
|
28
|
+
senderId: KIT,
|
|
29
|
+
senderName: "Kit Smalley",
|
|
30
|
+
chatId: KIT,
|
|
31
|
+
channelKey: `${BOT}:${KIT}`,
|
|
32
|
+
scope: "admin",
|
|
33
|
+
origin: "inbound",
|
|
34
|
+
createdAt: "2026-08-09T07:46:31.000Z",
|
|
35
|
+
...over,
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
function writeConversation(channelKey: string, rows: unknown[]): void {
|
|
39
|
+
const file = join(storeDir, `${channelKeyToFileStem(channelKey)}.jsonl`);
|
|
40
|
+
writeFileSync(file, rows.map((r) => JSON.stringify(r)).join("\n") + "\n", "utf8");
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
beforeEach(() => {
|
|
44
|
+
storeDir = mkdtempSync(join(tmpdir(), "tg-reach-"));
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
afterEach(() => {
|
|
48
|
+
rmSync(storeDir, { recursive: true, force: true });
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
describe("the key helpers", () => {
|
|
52
|
+
it("accepts two numeric ids and rejects a traversal", () => {
|
|
53
|
+
expect(isStorableChannelKey(`${BOT}:${KIT}`)).toBe(true);
|
|
54
|
+
expect(isStorableChannelKey(`${BOT}:${GROUP}`)).toBe(true);
|
|
55
|
+
expect(isStorableChannelKey("../../../../etc/passwd:x")).toBe(false);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it("round-trips a key through its filename stem", () => {
|
|
59
|
+
expect(fileStemToChannelKey(channelKeyToFileStem(`${BOT}:${KIT}`))).toBe(`${BOT}:${KIT}`);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
// Task 2615's three-part business shape. Round-tripping it is what keeps its
|
|
63
|
+
// conversations readable at all; telling it apart is what keeps them out of
|
|
64
|
+
// the reachable list.
|
|
65
|
+
it("round-trips a business key and recognises it as one", () => {
|
|
66
|
+
const biz = `${BOT}:biz:${KIT}`;
|
|
67
|
+
expect(isStorableChannelKey(biz)).toBe(true);
|
|
68
|
+
expect(channelKeyToFileStem(biz)).toBe(`${BOT}_biz_${KIT}`);
|
|
69
|
+
expect(fileStemToChannelKey(`${BOT}_biz_${KIT}`)).toBe(biz);
|
|
70
|
+
expect(isBusinessChannelKey(biz)).toBe(true);
|
|
71
|
+
expect(isBusinessChannelKey(`${BOT}:${KIT}`)).toBe(false);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it("refuses to build a stem from an unstorable key", () => {
|
|
75
|
+
expect(() => channelKeyToFileStem("a/b:c")).toThrow(/refusing unstorable channelKey/);
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
describe("collectTelegramReach", () => {
|
|
80
|
+
it("reports one entry per registered bot", () => {
|
|
81
|
+
const out = collectTelegramReach({
|
|
82
|
+
storeDir,
|
|
83
|
+
bots: [
|
|
84
|
+
{ id: BOT, role: "admin", adminUsers: [] },
|
|
85
|
+
{ id: "7777777777", role: "public" },
|
|
86
|
+
],
|
|
87
|
+
});
|
|
88
|
+
expect(out.bots.map((b) => b.botId)).toEqual([BOT, "7777777777"]);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it("returns an empty chat list for a bot with no inbound, not an error", () => {
|
|
92
|
+
const out = collectTelegramReach({ storeDir, bots: [{ id: BOT, role: "admin" }] });
|
|
93
|
+
expect(out.bots[0].chats).toEqual([]);
|
|
94
|
+
expect(out.storeRows).toBe(0);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it("lists a chat that has messaged, with the newest inbound name and time", () => {
|
|
98
|
+
writeConversation(`${BOT}:${KIT}`, [
|
|
99
|
+
inbound(),
|
|
100
|
+
inbound({
|
|
101
|
+
messageId: "m2",
|
|
102
|
+
dateSent: "2026-08-09T09:00:00.000Z",
|
|
103
|
+
senderName: "Kit S",
|
|
104
|
+
}),
|
|
105
|
+
]);
|
|
106
|
+
const out = collectTelegramReach({ storeDir, bots: [{ id: BOT, role: "admin" }] });
|
|
107
|
+
expect(out.bots[0].chats).toEqual([
|
|
108
|
+
{
|
|
109
|
+
chatId: KIT,
|
|
110
|
+
senderId: KIT,
|
|
111
|
+
displayName: "Kit S",
|
|
112
|
+
lastInbound: "2026-08-09T09:00:00.000Z",
|
|
113
|
+
admin: false,
|
|
114
|
+
reachable: true,
|
|
115
|
+
},
|
|
116
|
+
]);
|
|
117
|
+
expect(out.storeRows).toBe(2);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it("marks a chat that is also an adminUsers entry", () => {
|
|
121
|
+
writeConversation(`${BOT}:${KIT}`, [inbound()]);
|
|
122
|
+
const out = collectTelegramReach({
|
|
123
|
+
storeDir,
|
|
124
|
+
bots: [{ id: BOT, role: "admin", adminUsers: [Number(KIT)] }],
|
|
125
|
+
});
|
|
126
|
+
expect(out.bots[0].chats[0].admin).toBe(true);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it("lists an adminUsers entry that has never messaged as not reachable", () => {
|
|
130
|
+
const out = collectTelegramReach({
|
|
131
|
+
storeDir,
|
|
132
|
+
bots: [{ id: BOT, role: "admin", adminUsers: [Number(KIT)] }],
|
|
133
|
+
});
|
|
134
|
+
expect(out.bots[0].chats).toEqual([
|
|
135
|
+
{
|
|
136
|
+
chatId: null,
|
|
137
|
+
senderId: KIT,
|
|
138
|
+
displayName: null,
|
|
139
|
+
lastInbound: null,
|
|
140
|
+
admin: true,
|
|
141
|
+
reachable: false,
|
|
142
|
+
},
|
|
143
|
+
]);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
it("excludes a group: its chatId differs from its senderId", () => {
|
|
147
|
+
writeConversation(`${BOT}:${KIT}`, [inbound({ chatId: GROUP })]);
|
|
148
|
+
const out = collectTelegramReach({ storeDir, bots: [{ id: BOT, role: "admin" }] });
|
|
149
|
+
expect(out.bots[0].chats).toEqual([]);
|
|
150
|
+
expect(out.storeRows).toBe(1);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it("does not make a chat reachable on agent or operator rows alone", () => {
|
|
154
|
+
writeConversation(`${BOT}:${KIT}`, [
|
|
155
|
+
inbound({ origin: "agent" }),
|
|
156
|
+
inbound({ messageId: "m2", origin: "operator-manual" }),
|
|
157
|
+
]);
|
|
158
|
+
const out = collectTelegramReach({ storeDir, bots: [{ id: BOT, role: "admin" }] });
|
|
159
|
+
expect(out.bots[0].chats).toEqual([]);
|
|
160
|
+
expect(out.storeRows).toBe(2);
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
it("never counts an update row as a message and never makes it a chat", () => {
|
|
164
|
+
writeConversation(`${BOT}:${KIT}`, [
|
|
165
|
+
{ kind: "update", targetMessageId: "m1", body: "x", createdAt: "2026-08-09T07:47:00.000Z" },
|
|
166
|
+
]);
|
|
167
|
+
const out = collectTelegramReach({ storeDir, bots: [{ id: BOT, role: "admin" }] });
|
|
168
|
+
expect(out.storeRows).toBe(0);
|
|
169
|
+
expect(out.bots[0].chats).toEqual([]);
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
it("counts rows belonging to an unregistered bot, so a mis-scoped bot list is visible", () => {
|
|
173
|
+
writeConversation(`9999999999:${KIT}`, [inbound({ channelKey: `9999999999:${KIT}` })]);
|
|
174
|
+
const out = collectTelegramReach({ storeDir, bots: [{ id: BOT, role: "admin" }] });
|
|
175
|
+
expect(out.bots[0].chats).toEqual([]);
|
|
176
|
+
expect(out.storeRows).toBe(1);
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
// A business chat is reached through the business-connection route and its
|
|
180
|
+
// 24-hour window, never by addressing a chat id, so reporting one here would
|
|
181
|
+
// be the same wrong answer as the refusal this tool exists to prevent, only in
|
|
182
|
+
// the opposite direction. Its rows are not counted either: they were never
|
|
183
|
+
// walked.
|
|
184
|
+
it("skips a business conversation entirely, rows included", () => {
|
|
185
|
+
writeConversation(`${BOT}:biz:${KIT}`, [inbound({ channelKey: `${BOT}:biz:${KIT}` })]);
|
|
186
|
+
const out = collectTelegramReach({ storeDir, bots: [{ id: BOT, role: "admin" }] });
|
|
187
|
+
expect(out.bots[0].chats).toEqual([]);
|
|
188
|
+
expect(out.storeRows).toBe(0);
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
it("reads an absent store directory as no rows rather than throwing", () => {
|
|
192
|
+
const out = collectTelegramReach({
|
|
193
|
+
storeDir: join(storeDir, "nope"),
|
|
194
|
+
bots: [{ id: BOT, role: "admin" }],
|
|
195
|
+
});
|
|
196
|
+
expect(out.storeRows).toBe(0);
|
|
197
|
+
expect(out.bots[0].chats).toEqual([]);
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
it("orders reachable chats newest first, then the never-messaged admins", () => {
|
|
201
|
+
writeConversation(`${BOT}:${KIT}`, [inbound()]);
|
|
202
|
+
writeConversation(`${BOT}:${NEO}`, [
|
|
203
|
+
inbound({
|
|
204
|
+
senderId: NEO,
|
|
205
|
+
chatId: NEO,
|
|
206
|
+
channelKey: `${BOT}:${NEO}`,
|
|
207
|
+
senderName: "Neo",
|
|
208
|
+
dateSent: "2026-08-09T08:08:12.000Z",
|
|
209
|
+
}),
|
|
210
|
+
]);
|
|
211
|
+
const out = collectTelegramReach({
|
|
212
|
+
storeDir,
|
|
213
|
+
bots: [{ id: BOT, role: "admin", adminUsers: [Number(KIT), 5555555555] }],
|
|
214
|
+
});
|
|
215
|
+
expect(out.bots[0].chats.map((c) => c.senderId)).toEqual([NEO, KIT, "5555555555"]);
|
|
216
|
+
});
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
describe("telegramStoreRoot", () => {
|
|
220
|
+
let root: string;
|
|
221
|
+
|
|
222
|
+
beforeEach(() => {
|
|
223
|
+
root = mkdtempSync(join(tmpdir(), "tg-store-root-"));
|
|
224
|
+
delete process.env.MAXY_DIR_OVERRIDE;
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
afterEach(() => {
|
|
228
|
+
delete process.env.MAXY_DIR_OVERRIDE;
|
|
229
|
+
rmSync(root, { recursive: true, force: true });
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
function writeBrand(configDir: unknown): string {
|
|
233
|
+
const platformRoot = join(root, "platform");
|
|
234
|
+
mkdirSync(join(platformRoot, "config"), { recursive: true });
|
|
235
|
+
writeFileSync(
|
|
236
|
+
join(platformRoot, "config", "brand.json"),
|
|
237
|
+
typeof configDir === "string" ? JSON.stringify({ configDir }) : "{ not json",
|
|
238
|
+
"utf8",
|
|
239
|
+
);
|
|
240
|
+
return platformRoot;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
it("reads configDir from brand.json", () => {
|
|
244
|
+
expect(telegramStoreRoot(writeBrand(".maxy-code"))).toBe(
|
|
245
|
+
join(homedir(), ".maxy-code", "data", "telegram-messages"),
|
|
246
|
+
);
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
it("falls back to .maxy when brand.json is absent", () => {
|
|
250
|
+
const platformRoot = join(root, "platform");
|
|
251
|
+
mkdirSync(platformRoot, { recursive: true });
|
|
252
|
+
expect(telegramStoreRoot(platformRoot)).toBe(
|
|
253
|
+
join(homedir(), ".maxy", "data", "telegram-messages"),
|
|
254
|
+
);
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
it("falls back to .maxy when brand.json is unparseable, without throwing", () => {
|
|
258
|
+
expect(telegramStoreRoot(writeBrand(null))).toBe(
|
|
259
|
+
join(homedir(), ".maxy", "data", "telegram-messages"),
|
|
260
|
+
);
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
it("MAXY_DIR_OVERRIDE wins over brand.json", () => {
|
|
264
|
+
const platformRoot = writeBrand(".maxy-code");
|
|
265
|
+
process.env.MAXY_DIR_OVERRIDE = root;
|
|
266
|
+
expect(telegramStoreRoot(platformRoot)).toBe(join(root, "data", "telegram-messages"));
|
|
267
|
+
});
|
|
268
|
+
});
|