@rubytech/create-sitedesk-code 0.1.519 → 0.1.521
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/payload/platform/docs/superpowers/plans/2026-07-28-task-2097-email-signature-set.md +65 -0
- package/payload/platform/docs/superpowers/specs/2026-07-28-task-2097-email-signature-set-design.md +155 -0
- package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +4 -2
- package/payload/platform/plugins/admin/skills/whats-new/SKILL.md +12 -0
- package/payload/platform/plugins/cloudflare/bin/portal-assemble.mjs +187 -0
- package/payload/platform/plugins/cloudflare/bin/portal-enrol.mjs +46 -5
- package/payload/platform/plugins/cloudflare/mcp/__tests__/auth-route.test.ts +19 -7
- package/payload/platform/plugins/cloudflare/mcp/__tests__/authorize.test.ts +154 -38
- package/payload/platform/plugins/cloudflare/mcp/__tests__/file-routes.test.ts +100 -29
- package/payload/platform/plugins/cloudflare/mcp/__tests__/portal-assemble.test.ts +143 -0
- package/payload/platform/plugins/cloudflare/mcp/__tests__/portal-delete-withdraw.test.ts +12 -5
- package/payload/platform/plugins/cloudflare/mcp/__tests__/portal-directory-listing.test.ts +15 -3
- package/payload/platform/plugins/cloudflare/mcp/__tests__/portal-enrol.test.ts +105 -11
- package/payload/platform/plugins/cloudflare/mcp/__tests__/portal-indexed-download.test.ts +15 -2
- package/payload/platform/plugins/cloudflare/mcp/__tests__/portal-moved-download.test.ts +9 -5
- package/payload/platform/plugins/cloudflare/mcp/__tests__/portal-schema-shape.test.ts +47 -5
- package/payload/platform/plugins/cloudflare/mcp/__tests__/portal-session-grant.test.ts +26 -2
- package/payload/platform/plugins/cloudflare/mcp/__tests__/portal-target.test.ts +12 -4
- package/payload/platform/plugins/cloudflare/mcp/__tests__/portal-upload-multipart.test.ts +23 -15
- package/payload/platform/plugins/cloudflare/mcp/__tests__/portal-upload-target.test.ts +23 -15
- package/payload/platform/plugins/cloudflare/mcp/__tests__/portal-uploads-listing.test.ts +8 -0
- package/payload/platform/plugins/cloudflare/mcp/__tests__/skill-contract.test.ts +23 -6
- package/payload/platform/plugins/cloudflare/mcp/__tests__/upload-route.test.ts +36 -20
- package/payload/platform/plugins/cloudflare/skills/data-portal/SKILL.md +85 -22
- package/payload/platform/plugins/cloudflare/skills/data-portal/schema.sql +47 -7
- package/payload/platform/plugins/cloudflare/skills/data-portal/template/functions/api/_lib/authorize.ts +27 -18
- package/payload/platform/plugins/cloudflare/skills/data-portal/template/functions/api/_lib/ratelimit.ts +10 -3
- package/payload/platform/plugins/cloudflare/skills/data-portal/template/functions/api/_lib/session.ts +27 -11
- package/payload/platform/plugins/cloudflare/skills/data-portal/template/functions/api/_lib/target.ts +18 -9
- package/payload/platform/plugins/cloudflare/skills/data-portal/template/functions/api/_lib/types.ts +16 -0
- package/payload/platform/plugins/cloudflare/skills/data-portal/template/functions/api/auth.ts +16 -4
- package/payload/platform/plugins/cloudflare/skills/data-portal/template/functions/api/delete.ts +26 -11
- package/payload/platform/plugins/cloudflare/skills/data-portal/template/functions/api/download.ts +14 -10
- package/payload/platform/plugins/cloudflare/skills/data-portal/template/functions/api/files.ts +13 -9
- package/payload/platform/plugins/cloudflare/skills/data-portal/template/functions/api/upload.ts +42 -22
- package/payload/platform/plugins/cloudflare/skills/data-portal/template/wrangler.toml +17 -4
- package/payload/platform/plugins/docs/references/admin-ui.md +1 -1
- package/payload/platform/plugins/email/.claude-plugin/plugin.json +1 -1
- package/payload/platform/plugins/email/PLUGIN.md +20 -1
- package/payload/platform/plugins/email/mcp/dist/__tests__/attachment-metadata-calendar.test.js +46 -1
- package/payload/platform/plugins/email/mcp/dist/__tests__/attachment-metadata-calendar.test.js.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/__tests__/email-draft-list-tool.test.d.ts +2 -0
- package/payload/platform/plugins/email/mcp/dist/__tests__/email-draft-list-tool.test.d.ts.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/__tests__/email-draft-list-tool.test.js +142 -0
- package/payload/platform/plugins/email/mcp/dist/__tests__/email-draft-list-tool.test.js.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/__tests__/email-draft-list.test.d.ts +2 -0
- package/payload/platform/plugins/email/mcp/dist/__tests__/email-draft-list.test.d.ts.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/__tests__/email-draft-list.test.js +153 -0
- package/payload/platform/plugins/email/mcp/dist/__tests__/email-draft-list.test.js.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/__tests__/email-signature-set.test.d.ts +2 -0
- package/payload/platform/plugins/email/mcp/dist/__tests__/email-signature-set.test.d.ts.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/__tests__/email-signature-set.test.js +203 -0
- package/payload/platform/plugins/email/mcp/dist/__tests__/email-signature-set.test.js.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/index.js +69 -0
- package/payload/platform/plugins/email/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/lib/compose.d.ts.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/lib/compose.js +5 -7
- package/payload/platform/plugins/email/mcp/dist/lib/compose.js.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/lib/imap.d.ts +56 -0
- package/payload/platform/plugins/email/mcp/dist/lib/imap.d.ts.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/lib/imap.js +111 -4
- package/payload/platform/plugins/email/mcp/dist/lib/imap.js.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/lib/signature.d.ts +38 -0
- package/payload/platform/plugins/email/mcp/dist/lib/signature.d.ts.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/lib/signature.js +67 -2
- package/payload/platform/plugins/email/mcp/dist/lib/signature.js.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/tools/email-draft-list.d.ts +10 -0
- package/payload/platform/plugins/email/mcp/dist/tools/email-draft-list.d.ts.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/tools/email-draft-list.js +73 -0
- package/payload/platform/plugins/email/mcp/dist/tools/email-draft-list.js.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/tools/email-signature-set.d.ts +18 -0
- package/payload/platform/plugins/email/mcp/dist/tools/email-signature-set.d.ts.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/tools/email-signature-set.js +38 -0
- package/payload/platform/plugins/email/mcp/dist/tools/email-signature-set.js.map +1 -0
- package/payload/platform/plugins/email/references/email-reference.md +15 -3
- package/payload/platform/plugins/email/skills/email-composition/SKILL.md +10 -0
- package/payload/platform/plugins/email/skills/email-signature/SKILL.md +61 -0
- package/payload/platform/plugins/scheduling/PLUGIN.md +8 -0
- package/payload/platform/plugins/scheduling/mcp/dist/index.js +26 -15
- package/payload/platform/plugins/scheduling/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/schedule-audit.test.d.ts +2 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/schedule-audit.test.d.ts.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/schedule-audit.test.js +48 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/schedule-audit.test.js.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/schedule-audit.d.ts +24 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/schedule-audit.d.ts.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/schedule-audit.js +33 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/schedule-audit.js.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/reconcile-bookings.js +22 -1
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/reconcile-bookings.js.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-cancel-meeting.test.d.ts +2 -0
- package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-cancel-meeting.test.d.ts.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-cancel-meeting.test.js +30 -0
- package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-cancel-meeting.test.js.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-get-meeting.test.d.ts +2 -0
- package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-get-meeting.test.d.ts.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-get-meeting.test.js +43 -0
- package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-get-meeting.test.js.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-list.test.d.ts +2 -0
- package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-list.test.d.ts.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-list.test.js +87 -0
- package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-list.test.js.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-update-meeting.test.d.ts +2 -0
- package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-update-meeting.test.d.ts.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-update-meeting.test.js +48 -0
- package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-update-meeting.test.js.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-cancel.d.ts.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-cancel.js +14 -1
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-cancel.js.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-get.d.ts +4 -1
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-get.d.ts.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-get.js +39 -2
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-get.js.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-list.d.ts +4 -1
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-list.d.ts.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-list.js +50 -1
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-list.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 +43 -1
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-update.js.map +1 -1
- package/payload/platform/plugins/whatsapp/references/channels-whatsapp.md +3 -1
- package/payload/platform/scripts/check-architecture-skill-no-drift.mjs +20 -6
- 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/services/whatsapp-channel/dist/notification.d.ts +17 -5
- package/payload/platform/services/whatsapp-channel/dist/notification.d.ts.map +1 -1
- package/payload/platform/services/whatsapp-channel/dist/notification.js +30 -6
- 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 +53 -21
- package/payload/platform/services/whatsapp-channel/dist/server.js.map +1 -1
- package/payload/platform/services/whatsapp-channel/dist/turn-follow.d.ts +16 -0
- package/payload/platform/services/whatsapp-channel/dist/turn-follow.d.ts.map +1 -1
- package/payload/platform/services/whatsapp-channel/dist/turn-follow.js +41 -2
- package/payload/platform/services/whatsapp-channel/dist/turn-follow.js.map +1 -1
- package/payload/platform/templates/specialists/agents/personal-assistant.md +1 -1
- package/payload/server/{chunk-TIIWMF5G.js → chunk-AZLT4LVO.js} +40 -7
- package/payload/server/{manager-B6OFOXSW.js → manager-CQBSUJXR.js} +1 -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 +1 -0
- 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 +2 -0
- package/payload/server/public/assets/AdminShell-CpaI2F_p.js.br +0 -0
- package/payload/server/public/assets/AdminShell-CpaI2F_p.js.gz +0 -0
- package/payload/server/public/assets/{activity-DOsfKzT3.js → activity-B1rt4GIy.js} +1 -1
- 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 +1 -0
- 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-DqDnZTH_.js → agents-BXq8woVf.js} +1 -1
- 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-FL17Dfp0.js → browser-1QSCPl3X.js} +1 -1
- 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-CLfLvsnV.js → calendar-D009qWVV.js} +1 -1
- 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 +1 -0
- package/payload/server/public/assets/chat-CUg1fTqJ.js.br +1 -0
- package/payload/server/public/assets/chat-CUg1fTqJ.js.gz +0 -0
- package/payload/server/public/assets/chevron-left-C2JEy7uU.js +1 -0
- package/payload/server/public/assets/chevron-left-C2JEy7uU.js.br +0 -0
- package/payload/server/public/assets/chevron-right-DWa047Ys.js +1 -0
- package/payload/server/public/assets/chevron-right-DWa047Ys.js.br +0 -0
- package/payload/server/public/assets/clock-C3OqfCnn.js +1 -0
- 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 +1 -0
- 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-DNm0AzUz.js → file-text-DwGAfCsd.js} +1 -1
- 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-fAvmlVzh.js → graph-D-DGlfhW.js} +2 -2
- 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-CwdkCxfs.js → graph-labels-BbX3fXzl.js} +1 -1
- 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-B6tAPhC2.js → maximize-2-C_-KapxH.js} +1 -1
- 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-CTKvBVjk.js → operator-CFrZVcv6.js} +1 -1
- 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-O5HkHQw4.js → page-CHt62f1_.js} +1 -1
- 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 +32 -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-BF1rnVPa.js → public-CI6K1sNW.js} +1 -1
- 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-B8TqfCy4.js → rotate-ccw-PWu_QGrD.js} +1 -1
- 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 +1 -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 +1 -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-DePyOrRk.js → tasks-CMC_jDVm.js} +1 -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-D9gtnG01.js → time-entry-format-BXWuQ6Sw.js} +1 -1
- package/payload/server/public/assets/time-entry-format-BXWuQ6Sw.js.br +2 -0
- package/payload/server/public/assets/time-entry-format-BXWuQ6Sw.js.gz +0 -0
- package/payload/server/public/assets/{triangle-alert-DAtm2mLL.js → triangle-alert-BNshRC13.js} +1 -1
- 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-uMJT-p6i.js → useCopyFeedback-CwTZ9DqH.js} +1 -1
- 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 +14 -0
- package/payload/server/public/assets/useSubAccountSwitcher-BuUYMDWl.js.br +0 -0
- package/payload/server/public/assets/useSubAccountSwitcher-BuUYMDWl.js.gz +0 -0
- package/payload/server/public/assets/useSubAccountSwitcher-dzDnmWq9.css +1 -0
- package/payload/server/public/assets/useSubAccountSwitcher-dzDnmWq9.css.br +0 -0
- package/payload/server/public/assets/useSubAccountSwitcher-dzDnmWq9.css.gz +0 -0
- package/payload/server/public/assets/{useVoiceRecorder-BFrPC-s7.js → useVoiceRecorder-BGqV5R6u.js} +1 -1
- 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-CYgaN7K9.js → wrench-DQ045Pyk.js} +1 -1
- package/payload/server/public/assets/wrench-DQ045Pyk.js.br +0 -0
- package/payload/server/public/assets/wrench-DQ045Pyk.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 +687 -400
- package/payload/server/public/assets/AdminLoginScreens-BZPbCk8g.js +0 -1
- package/payload/server/public/assets/AdminLoginScreens-BZPbCk8g.js.br +0 -0
- package/payload/server/public/assets/AdminLoginScreens-BZPbCk8g.js.gz +0 -0
- package/payload/server/public/assets/AdminShell-Bpo_wgxm.js +0 -2
- package/payload/server/public/assets/AdminShell-Bpo_wgxm.js.br +0 -0
- package/payload/server/public/assets/AdminShell-Bpo_wgxm.js.gz +0 -0
- package/payload/server/public/assets/activity-DOsfKzT3.js.br +0 -0
- package/payload/server/public/assets/activity-DOsfKzT3.js.gz +0 -0
- package/payload/server/public/assets/admin-COgD1z5a.js +0 -1
- package/payload/server/public/assets/admin-COgD1z5a.js.br +0 -0
- package/payload/server/public/assets/admin-COgD1z5a.js.gz +0 -0
- package/payload/server/public/assets/agents-DqDnZTH_.js.br +0 -0
- package/payload/server/public/assets/agents-DqDnZTH_.js.gz +0 -0
- package/payload/server/public/assets/browser-FL17Dfp0.js.br +0 -0
- package/payload/server/public/assets/browser-FL17Dfp0.js.gz +0 -0
- package/payload/server/public/assets/calendar-CLfLvsnV.js.br +0 -0
- package/payload/server/public/assets/calendar-CLfLvsnV.js.gz +0 -0
- package/payload/server/public/assets/chat-DYq4FxMG.js +0 -1
- package/payload/server/public/assets/chat-DYq4FxMG.js.br +0 -0
- package/payload/server/public/assets/chat-DYq4FxMG.js.gz +0 -0
- package/payload/server/public/assets/chevron-left-DWt5cdUo.js +0 -1
- package/payload/server/public/assets/chevron-right-B9suVrYy.js +0 -1
- package/payload/server/public/assets/chevron-right-B9suVrYy.js.br +0 -0
- package/payload/server/public/assets/clock-DW6qVXtv.js +0 -1
- package/payload/server/public/assets/clock-DW6qVXtv.js.br +0 -0
- package/payload/server/public/assets/clock-DW6qVXtv.js.gz +0 -0
- package/payload/server/public/assets/data-C8rbOgds.js +0 -1
- package/payload/server/public/assets/data-C8rbOgds.js.br +0 -0
- package/payload/server/public/assets/data-C8rbOgds.js.gz +0 -0
- package/payload/server/public/assets/file-text-DNm0AzUz.js.br +0 -0
- package/payload/server/public/assets/file-text-DNm0AzUz.js.gz +0 -0
- package/payload/server/public/assets/graph-fAvmlVzh.js.br +0 -0
- package/payload/server/public/assets/graph-fAvmlVzh.js.gz +0 -0
- package/payload/server/public/assets/graph-labels-CwdkCxfs.js.br +0 -0
- package/payload/server/public/assets/graph-labels-CwdkCxfs.js.gz +0 -0
- package/payload/server/public/assets/maximize-2-B6tAPhC2.js.br +0 -0
- package/payload/server/public/assets/maximize-2-B6tAPhC2.js.gz +0 -0
- package/payload/server/public/assets/operator-CTKvBVjk.js.br +0 -0
- package/payload/server/public/assets/operator-CTKvBVjk.js.gz +0 -0
- package/payload/server/public/assets/page-BWoNW3sa.js +0 -32
- package/payload/server/public/assets/page-BWoNW3sa.js.br +0 -0
- package/payload/server/public/assets/page-BWoNW3sa.js.gz +0 -0
- package/payload/server/public/assets/page-O5HkHQw4.js.br +0 -0
- package/payload/server/public/assets/page-O5HkHQw4.js.gz +0 -0
- package/payload/server/public/assets/public-BF1rnVPa.js.br +0 -0
- package/payload/server/public/assets/public-BF1rnVPa.js.gz +0 -0
- package/payload/server/public/assets/rotate-ccw-B8TqfCy4.js.br +0 -0
- package/payload/server/public/assets/rotate-ccw-B8TqfCy4.js.gz +0 -0
- package/payload/server/public/assets/routines-4R7BLZyH.js +0 -1
- package/payload/server/public/assets/routines-4R7BLZyH.js.br +0 -0
- package/payload/server/public/assets/routines-4R7BLZyH.js.gz +0 -0
- package/payload/server/public/assets/skills-CQ9Qs2tg.js +0 -1
- package/payload/server/public/assets/skills-CQ9Qs2tg.js.br +0 -0
- package/payload/server/public/assets/skills-CQ9Qs2tg.js.gz +0 -0
- package/payload/server/public/assets/tasks-DePyOrRk.js.br +0 -0
- package/payload/server/public/assets/tasks-DePyOrRk.js.gz +0 -0
- package/payload/server/public/assets/time-entry-format-D9gtnG01.js.br +0 -0
- package/payload/server/public/assets/time-entry-format-D9gtnG01.js.gz +0 -0
- package/payload/server/public/assets/triangle-alert-DAtm2mLL.js.br +0 -0
- package/payload/server/public/assets/triangle-alert-DAtm2mLL.js.gz +0 -0
- package/payload/server/public/assets/useCopyFeedback-uMJT-p6i.js.br +0 -0
- package/payload/server/public/assets/useCopyFeedback-uMJT-p6i.js.gz +0 -0
- package/payload/server/public/assets/useSubAccountSwitcher-BufCPDDH.js +0 -14
- package/payload/server/public/assets/useSubAccountSwitcher-BufCPDDH.js.br +0 -0
- package/payload/server/public/assets/useSubAccountSwitcher-BufCPDDH.js.gz +0 -0
- package/payload/server/public/assets/useSubAccountSwitcher-iCzsV96x.css +0 -1
- package/payload/server/public/assets/useSubAccountSwitcher-iCzsV96x.css.br +0 -0
- package/payload/server/public/assets/useSubAccountSwitcher-iCzsV96x.css.gz +0 -0
- package/payload/server/public/assets/useVoiceRecorder-BFrPC-s7.js.br +0 -0
- package/payload/server/public/assets/useVoiceRecorder-BFrPC-s7.js.gz +0 -0
- package/payload/server/public/assets/wrench-CYgaN7K9.js.br +0 -1
- package/payload/server/public/assets/wrench-CYgaN7K9.js.gz +0 -0
package/payload/server/server.js
CHANGED
|
@@ -163,7 +163,6 @@ import {
|
|
|
163
163
|
sendMediaMessage,
|
|
164
164
|
sendPaused,
|
|
165
165
|
sendTextMessage,
|
|
166
|
-
senderJidFromTelephone,
|
|
167
166
|
sessionScratchpadRoot,
|
|
168
167
|
shouldDispatchInbound,
|
|
169
168
|
shutdown,
|
|
@@ -178,7 +177,7 @@ import {
|
|
|
178
177
|
validateFilePathInAccount,
|
|
179
178
|
waitForConnection,
|
|
180
179
|
warnNonUuidAccount
|
|
181
|
-
} from "./chunk-
|
|
180
|
+
} from "./chunk-AZLT4LVO.js";
|
|
182
181
|
import {
|
|
183
182
|
ACCOUNTS_DIR,
|
|
184
183
|
BIN_DIR,
|
|
@@ -1719,10 +1718,10 @@ var require_dist5 = __commonJS({
|
|
|
1719
1718
|
});
|
|
1720
1719
|
|
|
1721
1720
|
// server/index.ts
|
|
1722
|
-
import { readFileSync as
|
|
1721
|
+
import { readFileSync as readFileSync44, existsSync as existsSync48, watchFile, readdirSync as readdirSync28, statSync as statSync24 } from "fs";
|
|
1723
1722
|
import { spawn as spawn3 } from "child_process";
|
|
1724
1723
|
import { createHash as createHash7 } from "crypto";
|
|
1725
|
-
import { resolve as resolve42, join as
|
|
1724
|
+
import { resolve as resolve42, join as join50, basename as basename15 } from "path";
|
|
1726
1725
|
import { homedir as homedir4 } from "os";
|
|
1727
1726
|
|
|
1728
1727
|
// server/lib/route-cache-watchers.ts
|
|
@@ -2288,11 +2287,11 @@ function staticCacheHeaders(root) {
|
|
|
2288
2287
|
}
|
|
2289
2288
|
|
|
2290
2289
|
// server/lib/static-mounts.ts
|
|
2291
|
-
function mountStaticFiles(
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2290
|
+
function mountStaticFiles(app73, root) {
|
|
2291
|
+
app73.use("/*", staticCacheHeaders(root));
|
|
2292
|
+
app73.use("/assets/*", assetsVary);
|
|
2293
|
+
app73.use("/assets/*", serveStatic({ root, precompressed: true }));
|
|
2294
|
+
app73.use("/*", serveStatic({ root }));
|
|
2296
2295
|
}
|
|
2297
2296
|
var assetsVary = async (c, next) => {
|
|
2298
2297
|
await next();
|
|
@@ -3503,8 +3502,8 @@ function webchatTurnTimeoutMs() {
|
|
|
3503
3502
|
return Number(process.env.WEBCHAT_TURN_TIMEOUT_MS ?? String(2 * 6e4));
|
|
3504
3503
|
}
|
|
3505
3504
|
function createChatRoutes(deps) {
|
|
3506
|
-
const
|
|
3507
|
-
|
|
3505
|
+
const app73 = new Hono();
|
|
3506
|
+
app73.post("/", async (c) => {
|
|
3508
3507
|
console.log(`[chat-route] entered route=public method=POST`);
|
|
3509
3508
|
const contentType = c.req.header("content-type") ?? "";
|
|
3510
3509
|
const account = resolveAccount();
|
|
@@ -3776,7 +3775,7 @@ ${result.result.text}` : result.result.text;
|
|
|
3776
3775
|
}
|
|
3777
3776
|
});
|
|
3778
3777
|
});
|
|
3779
|
-
return
|
|
3778
|
+
return app73;
|
|
3780
3779
|
}
|
|
3781
3780
|
|
|
3782
3781
|
// app/lib/channel-pty-bridge/admin-session-id.ts
|
|
@@ -3810,8 +3809,8 @@ function isLoopbackAddr(addr) {
|
|
|
3810
3809
|
return addr === "127.0.0.1" || addr === "::1" || addr === "::ffff:127.0.0.1";
|
|
3811
3810
|
}
|
|
3812
3811
|
function createScheduleInjectRoutes(deps) {
|
|
3813
|
-
const
|
|
3814
|
-
|
|
3812
|
+
const app73 = new Hono();
|
|
3813
|
+
app73.post("/", async (c) => {
|
|
3815
3814
|
const env = c.env;
|
|
3816
3815
|
const remoteAddr = env?.incoming?.socket?.remoteAddress ?? "";
|
|
3817
3816
|
if (!isLoopbackAddr(remoteAddr)) {
|
|
@@ -3912,14 +3911,14 @@ function createScheduleInjectRoutes(deps) {
|
|
|
3912
3911
|
console.error(`${TAG5} op=inject-spawn eventId=${eventId} sessionId=${sessionId} result=ok status=-`);
|
|
3913
3912
|
return c.json({ ok: true }, 200);
|
|
3914
3913
|
});
|
|
3915
|
-
return
|
|
3914
|
+
return app73;
|
|
3916
3915
|
}
|
|
3917
3916
|
|
|
3918
3917
|
// server/index.ts
|
|
3919
3918
|
var import_dist9 = __toESM(require_dist(), 1);
|
|
3920
3919
|
|
|
3921
3920
|
// app/lib/whatsapp/avatar-store.ts
|
|
3922
|
-
import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync5, writeFileSync as writeFileSync3, readdirSync as readdirSync2, statSync as statSync4 } from "fs";
|
|
3921
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync5, writeFileSync as writeFileSync3, readdirSync as readdirSync2, statSync as statSync4, rmSync } from "fs";
|
|
3923
3922
|
import { join as join6 } from "path";
|
|
3924
3923
|
function safeName(jid) {
|
|
3925
3924
|
if (jid.length === 0 || jid.includes("/") || jid.includes("\\") || jid.includes("..")) {
|
|
@@ -3964,30 +3963,41 @@ function writeAvatarAbsent(accountId, jid) {
|
|
|
3964
3963
|
const dir = dirFor(accountId);
|
|
3965
3964
|
const base = join6(dir, safeName(jid));
|
|
3966
3965
|
mkdirSync2(dir, { recursive: true });
|
|
3966
|
+
rmSync(`${base}.bin`, { force: true });
|
|
3967
3967
|
const meta = { jid, state: "absent", fetchedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
3968
3968
|
writeFileSync3(`${base}.json`, JSON.stringify(meta));
|
|
3969
3969
|
}
|
|
3970
|
-
function
|
|
3970
|
+
function removeAvatar(accountId, jid) {
|
|
3971
|
+
const base = join6(dirFor(accountId), safeName(jid));
|
|
3972
|
+
rmSync(`${base}.bin`, { force: true });
|
|
3973
|
+
rmSync(`${base}.json`, { force: true });
|
|
3974
|
+
}
|
|
3975
|
+
function listAvatarEntries(accountId) {
|
|
3971
3976
|
const dir = dirFor(accountId);
|
|
3972
3977
|
if (!existsSync3(dir)) return [];
|
|
3973
3978
|
const out = [];
|
|
3974
3979
|
for (const f of readdirSync2(dir)) {
|
|
3975
3980
|
if (!f.endsWith(".json")) continue;
|
|
3976
3981
|
const meta = readMeta(join6(dir, f));
|
|
3977
|
-
if (meta?.jid)
|
|
3982
|
+
if (!meta?.jid) continue;
|
|
3983
|
+
let bytes = 0;
|
|
3984
|
+
if (meta.state === "present") {
|
|
3985
|
+
try {
|
|
3986
|
+
bytes = statSync4(join6(dir, `${f.slice(0, -".json".length)}.bin`)).size;
|
|
3987
|
+
} catch {
|
|
3988
|
+
bytes = 0;
|
|
3989
|
+
}
|
|
3990
|
+
}
|
|
3991
|
+
out.push({ jid: meta.jid, state: meta.state, bytes, fetchedAt: meta.fetchedAt });
|
|
3978
3992
|
}
|
|
3979
3993
|
return out;
|
|
3980
3994
|
}
|
|
3995
|
+
function listAvatarJids(accountId) {
|
|
3996
|
+
return listAvatarEntries(accountId).map((e) => e.jid);
|
|
3997
|
+
}
|
|
3981
3998
|
function avatarCensus(accountId) {
|
|
3982
|
-
const
|
|
3983
|
-
|
|
3984
|
-
let entries2 = 0;
|
|
3985
|
-
let bytes = 0;
|
|
3986
|
-
for (const f of readdirSync2(dir)) {
|
|
3987
|
-
if (f.endsWith(".json")) entries2++;
|
|
3988
|
-
else if (f.endsWith(".bin")) bytes += statSync4(join6(dir, f)).size;
|
|
3989
|
-
}
|
|
3990
|
-
return { entries: entries2, bytes };
|
|
3999
|
+
const entries2 = listAvatarEntries(accountId);
|
|
4000
|
+
return { entries: entries2.length, bytes: entries2.reduce((n, e) => n + e.bytes, 0) };
|
|
3991
4001
|
}
|
|
3992
4002
|
|
|
3993
4003
|
// app/lib/whatsapp/avatar-fetch.ts
|
|
@@ -4007,7 +4017,7 @@ async function defaultHttpGet(url) {
|
|
|
4007
4017
|
}
|
|
4008
4018
|
}
|
|
4009
4019
|
async function defaultResolve(accountId) {
|
|
4010
|
-
const { resolveSocket: resolveSocket2 } = await import("./manager-
|
|
4020
|
+
const { resolveSocket: resolveSocket2 } = await import("./manager-CQBSUJXR.js");
|
|
4011
4021
|
return resolveSocket2(accountId);
|
|
4012
4022
|
}
|
|
4013
4023
|
async function fetchAvatar(accountId, jid, deps = {}) {
|
|
@@ -4055,25 +4065,66 @@ async function fetchAvatar(accountId, jid, deps = {}) {
|
|
|
4055
4065
|
// app/lib/whatsapp/avatar-sweep.ts
|
|
4056
4066
|
var TAG7 = "[wa-avatar]";
|
|
4057
4067
|
var CENSUS_TAG = "[wa-avatar-census]";
|
|
4068
|
+
var AVATAR_STORE_MAX_BYTES = 16 * 1024 * 1024;
|
|
4058
4069
|
function liveJidsForAccount(accountId) {
|
|
4059
4070
|
const live = /* @__PURE__ */ new Set();
|
|
4060
4071
|
for (const [remoteJid, records] of readAllConversations(accountId)) {
|
|
4061
|
-
live.add(
|
|
4062
|
-
for (const r of records) {
|
|
4063
|
-
if (r.fromMe) continue;
|
|
4064
|
-
const jid = senderJidFromTelephone(r.senderTelephone);
|
|
4065
|
-
if (jid) live.add(jid);
|
|
4066
|
-
}
|
|
4072
|
+
for (const jid of avatarJidsForConversation(remoteJid, records)) live.add(jid);
|
|
4067
4073
|
}
|
|
4068
4074
|
return live;
|
|
4069
4075
|
}
|
|
4076
|
+
function avatarJidsForConversation(remoteJid, records) {
|
|
4077
|
+
const jids = /* @__PURE__ */ new Set([remoteJid]);
|
|
4078
|
+
if (!isGroupJid(remoteJid)) return jids;
|
|
4079
|
+
for (const r of records) {
|
|
4080
|
+
if (r.fromMe) continue;
|
|
4081
|
+
if (r.senderJid && isUserJid(r.senderJid)) jids.add(r.senderJid);
|
|
4082
|
+
}
|
|
4083
|
+
return jids;
|
|
4084
|
+
}
|
|
4085
|
+
function evictAvatars(accountId, live) {
|
|
4086
|
+
let unreferenced = 0;
|
|
4087
|
+
let overBound = 0;
|
|
4088
|
+
let freedBytes = 0;
|
|
4089
|
+
const kept = [];
|
|
4090
|
+
for (const e of listAvatarEntries(accountId)) {
|
|
4091
|
+
if (live.has(e.jid)) {
|
|
4092
|
+
kept.push(e);
|
|
4093
|
+
continue;
|
|
4094
|
+
}
|
|
4095
|
+
removeAvatar(accountId, e.jid);
|
|
4096
|
+
unreferenced++;
|
|
4097
|
+
freedBytes += e.bytes;
|
|
4098
|
+
}
|
|
4099
|
+
let bytes = kept.reduce((n, e) => n + e.bytes, 0);
|
|
4100
|
+
if (bytes > AVATAR_STORE_MAX_BYTES) {
|
|
4101
|
+
const oldestFirst = kept.filter((e) => e.state === "present").sort((a, b) => a.fetchedAt.localeCompare(b.fetchedAt));
|
|
4102
|
+
for (const e of oldestFirst) {
|
|
4103
|
+
if (bytes <= AVATAR_STORE_MAX_BYTES) break;
|
|
4104
|
+
removeAvatar(accountId, e.jid);
|
|
4105
|
+
overBound++;
|
|
4106
|
+
freedBytes += e.bytes;
|
|
4107
|
+
bytes -= e.bytes;
|
|
4108
|
+
}
|
|
4109
|
+
}
|
|
4110
|
+
return { unreferenced, overBound, freedBytes };
|
|
4111
|
+
}
|
|
4070
4112
|
async function sweepAvatars() {
|
|
4071
4113
|
const started2 = Date.now();
|
|
4072
4114
|
let jids = 0;
|
|
4073
4115
|
let refetched = 0;
|
|
4074
4116
|
let skipped = 0;
|
|
4075
4117
|
let failed = 0;
|
|
4118
|
+
let evicted = 0;
|
|
4076
4119
|
for (const accountId of listStoredAccountIds()) {
|
|
4120
|
+
const live = liveJidsForAccount(accountId);
|
|
4121
|
+
const ev = evictAvatars(accountId, live);
|
|
4122
|
+
evicted += ev.unreferenced + ev.overBound;
|
|
4123
|
+
if (ev.unreferenced > 0 || ev.overBound > 0) {
|
|
4124
|
+
console.log(
|
|
4125
|
+
`${TAG7} op=evict account=${accountId.slice(0, 8)} unreferenced=${ev.unreferenced} overBound=${ev.overBound} freedBytes=${ev.freedBytes}`
|
|
4126
|
+
);
|
|
4127
|
+
}
|
|
4077
4128
|
const cached3 = listAvatarJids(accountId);
|
|
4078
4129
|
for (const jid of cached3) {
|
|
4079
4130
|
jids++;
|
|
@@ -4089,11 +4140,10 @@ async function sweepAvatars() {
|
|
|
4089
4140
|
else if (outcome === "failed") failed++;
|
|
4090
4141
|
else skipped++;
|
|
4091
4142
|
}
|
|
4092
|
-
const live = liveJidsForAccount(accountId);
|
|
4093
4143
|
const census = avatarCensus(accountId);
|
|
4094
4144
|
console.log(`${CENSUS_TAG} account=${accountId.slice(0, 8)} entries=${census.entries} bytes=${census.bytes} live=${live.size}`);
|
|
4095
4145
|
}
|
|
4096
|
-
const note = `jids=${jids} refetched=${refetched} skipped=${skipped} failed=${failed} ms=${Date.now() - started2}`;
|
|
4146
|
+
const note = `jids=${jids} refetched=${refetched} skipped=${skipped} failed=${failed} evicted=${evicted} ms=${Date.now() - started2}`;
|
|
4097
4147
|
console.log(`${TAG7} op=sweep ${note}`);
|
|
4098
4148
|
return note;
|
|
4099
4149
|
}
|
|
@@ -4104,7 +4154,9 @@ var OFFENDER_CAP = 10;
|
|
|
4104
4154
|
function auditGroupOutbound() {
|
|
4105
4155
|
let convos = 0;
|
|
4106
4156
|
let outbound = 0;
|
|
4107
|
-
let
|
|
4157
|
+
let operator = 0;
|
|
4158
|
+
let agent = 0;
|
|
4159
|
+
let unstamped = 0;
|
|
4108
4160
|
const offenders = [];
|
|
4109
4161
|
for (const accountId of listStoredAccountIds()) {
|
|
4110
4162
|
for (const [remoteJid, records] of readAllConversations(accountId)) {
|
|
@@ -4113,18 +4165,20 @@ function auditGroupOutbound() {
|
|
|
4113
4165
|
for (const r of records) {
|
|
4114
4166
|
if (!r.fromMe) continue;
|
|
4115
4167
|
outbound++;
|
|
4116
|
-
if (r.origin === "operator-manual")
|
|
4117
|
-
|
|
4118
|
-
|
|
4168
|
+
if (r.origin === "operator-manual") operator++;
|
|
4169
|
+
else if (r.origin === "agent") {
|
|
4170
|
+
agent++;
|
|
4171
|
+
if (offenders.length < OFFENDER_CAP) offenders.push(r.msgKeyId);
|
|
4172
|
+
} else unstamped++;
|
|
4119
4173
|
}
|
|
4120
4174
|
}
|
|
4121
4175
|
}
|
|
4122
|
-
return { convos, outbound,
|
|
4176
|
+
return { convos, outbound, operator, agent, unstamped, offenders };
|
|
4123
4177
|
}
|
|
4124
4178
|
function auditGroupOutboundNote() {
|
|
4125
4179
|
const a = auditGroupOutbound();
|
|
4126
|
-
const tail = a.
|
|
4127
|
-
const note = `convos=${a.convos} outbound=${a.outbound}
|
|
4180
|
+
const tail = a.agent > 0 ? ` offenders=${a.offenders.join(",")}` : "";
|
|
4181
|
+
const note = `convos=${a.convos} outbound=${a.outbound} operator=${a.operator} agent=${a.agent} unstamped=${a.unstamped}${tail}`;
|
|
4128
4182
|
console.log(`${TAG8} op=group-outbound-audit ${note}`);
|
|
4129
4183
|
return note;
|
|
4130
4184
|
}
|
|
@@ -5868,6 +5922,7 @@ app2.post("/reply", async (c) => {
|
|
|
5868
5922
|
quotedId: null,
|
|
5869
5923
|
quotedSender: null,
|
|
5870
5924
|
scope: template.scope,
|
|
5925
|
+
origin: "agent",
|
|
5871
5926
|
createdAt: nowIso
|
|
5872
5927
|
};
|
|
5873
5928
|
appendMessage(storeKey, rec);
|
|
@@ -6813,7 +6868,51 @@ function countConfigAbsent(platformRoot5, present) {
|
|
|
6813
6868
|
return 0;
|
|
6814
6869
|
}
|
|
6815
6870
|
}
|
|
6816
|
-
|
|
6871
|
+
var SAFE_AUDIT_ACCOUNT_ID = /^[a-z0-9-]+$/;
|
|
6872
|
+
function auditAccountClause(portal) {
|
|
6873
|
+
if (portal.storageModel !== "shared") {
|
|
6874
|
+
return { manifestWhere: "", directoryWhere: "", objectPrefix: void 0 };
|
|
6875
|
+
}
|
|
6876
|
+
if (!SAFE_AUDIT_ACCOUNT_ID.test(portal.accountId)) {
|
|
6877
|
+
throw new Error(
|
|
6878
|
+
`storage-broker: refusing to audit unsafe accountId "${portal.accountId}"`
|
|
6879
|
+
);
|
|
6880
|
+
}
|
|
6881
|
+
return {
|
|
6882
|
+
manifestWhere: ` WHERE accountId = '${portal.accountId}'`,
|
|
6883
|
+
directoryWhere: ` WHERE d.accountId = '${portal.accountId}'`,
|
|
6884
|
+
objectPrefix: `${portal.accountId}/`
|
|
6885
|
+
};
|
|
6886
|
+
}
|
|
6887
|
+
function readSharedStoreNames(platformRootDir) {
|
|
6888
|
+
let raw;
|
|
6889
|
+
try {
|
|
6890
|
+
raw = readFileSync8(join10(platformRootDir, "config", "cloudflare-house.env"), "utf-8");
|
|
6891
|
+
} catch {
|
|
6892
|
+
return null;
|
|
6893
|
+
}
|
|
6894
|
+
const env = {};
|
|
6895
|
+
for (const line of raw.split("\n")) {
|
|
6896
|
+
const m = line.match(/^\s*([A-Za-z_][A-Za-z0-9_]*)=(.*)$/);
|
|
6897
|
+
if (m) env[m[1]] = m[2].trim();
|
|
6898
|
+
}
|
|
6899
|
+
const dbName = env.DATA_PORTAL_DB_NAME;
|
|
6900
|
+
const bucket = env.DATA_PORTAL_BUCKET;
|
|
6901
|
+
return dbName && bucket ? { dbName, bucket } : null;
|
|
6902
|
+
}
|
|
6903
|
+
function resolveStorageModel(cfg, shared, onDisagree) {
|
|
6904
|
+
const labelled = cfg.storageModel === "shared" ? "shared" : "dedicated";
|
|
6905
|
+
if (!shared) return labelled;
|
|
6906
|
+
const namesShared = cfg.portalDbName === shared.dbName || cfg.bucketName === shared.bucket;
|
|
6907
|
+
const derived = namesShared ? "shared" : "dedicated";
|
|
6908
|
+
if (derived !== labelled && onDisagree) {
|
|
6909
|
+
onDisagree(
|
|
6910
|
+
`[storage-broker] op=storage-model result=overridden labelled=${labelled} derived=${derived} db=${cfg.portalDbName} bucket=${cfg.bucketName}`
|
|
6911
|
+
);
|
|
6912
|
+
}
|
|
6913
|
+
return derived;
|
|
6914
|
+
}
|
|
6915
|
+
function discoverPortalAccounts(accountsDir, shared = null) {
|
|
6817
6916
|
if (!existsSync6(accountsDir)) return [];
|
|
6818
6917
|
const found = [];
|
|
6819
6918
|
for (const accountId of readdirSync5(accountsDir)) {
|
|
@@ -6822,7 +6921,20 @@ function discoverPortalAccounts(accountsDir) {
|
|
|
6822
6921
|
try {
|
|
6823
6922
|
const cfg = JSON.parse(readFileSync8(configPath2, "utf-8"));
|
|
6824
6923
|
if (typeof cfg.portalDbName === "string" && cfg.portalDbName.length > 0 && typeof cfg.bucketName === "string" && cfg.bucketName.length > 0) {
|
|
6825
|
-
found.push({
|
|
6924
|
+
found.push({
|
|
6925
|
+
accountId,
|
|
6926
|
+
portalDbName: cfg.portalDbName,
|
|
6927
|
+
bucketName: cfg.bucketName,
|
|
6928
|
+
storageModel: resolveStorageModel(
|
|
6929
|
+
{
|
|
6930
|
+
portalDbName: cfg.portalDbName,
|
|
6931
|
+
bucketName: cfg.bucketName,
|
|
6932
|
+
storageModel: cfg.storageModel
|
|
6933
|
+
},
|
|
6934
|
+
shared,
|
|
6935
|
+
(line) => console.error(`${line} account=${accountId}`)
|
|
6936
|
+
)
|
|
6937
|
+
});
|
|
6826
6938
|
}
|
|
6827
6939
|
} catch {
|
|
6828
6940
|
}
|
|
@@ -6830,7 +6942,10 @@ function discoverPortalAccounts(accountsDir) {
|
|
|
6830
6942
|
return found;
|
|
6831
6943
|
}
|
|
6832
6944
|
async function runDataPortalAudit(root) {
|
|
6833
|
-
const portals = discoverPortalAccounts(
|
|
6945
|
+
const portals = discoverPortalAccounts(
|
|
6946
|
+
resolve7(root, "..", "data", "accounts"),
|
|
6947
|
+
readSharedStoreNames(root)
|
|
6948
|
+
);
|
|
6834
6949
|
const configAbsent = countConfigAbsent(root, portals);
|
|
6835
6950
|
if (portals.length === 0) {
|
|
6836
6951
|
if (configAbsent > 0) {
|
|
@@ -6841,13 +6956,14 @@ async function runDataPortalAudit(root) {
|
|
|
6841
6956
|
const cf = await makeHouseCfExec(root);
|
|
6842
6957
|
let degraded = 0;
|
|
6843
6958
|
for (const portal of portals) {
|
|
6959
|
+
const scope = auditAccountClause(portal);
|
|
6844
6960
|
let rows = null;
|
|
6845
6961
|
let d1Error = null;
|
|
6846
6962
|
try {
|
|
6847
6963
|
rows = parseManifestRows(
|
|
6848
6964
|
await cf.d1Query(
|
|
6849
6965
|
portal.portalDbName,
|
|
6850
|
-
"SELECT objectKey, ingested, uploadedAt, devicePath, deleted FROM manifest"
|
|
6966
|
+
"SELECT objectKey, ingested, uploadedAt, devicePath, deleted FROM manifest" + scope.manifestWhere
|
|
6851
6967
|
)
|
|
6852
6968
|
);
|
|
6853
6969
|
} catch (err) {
|
|
@@ -6857,7 +6973,7 @@ async function runDataPortalAudit(root) {
|
|
|
6857
6973
|
try {
|
|
6858
6974
|
const published = await cf.d1Query(
|
|
6859
6975
|
portal.portalDbName,
|
|
6860
|
-
"SELECT d.relPath AS relPath FROM directory d JOIN directory_state s ON s.accountId = d.accountId AND s.currentGeneration = d.generation"
|
|
6976
|
+
"SELECT d.relPath AS relPath FROM directory d JOIN directory_state s ON s.accountId = d.accountId AND s.currentGeneration = d.generation" + scope.directoryWhere
|
|
6861
6977
|
);
|
|
6862
6978
|
const envelope = Array.isArray(published) ? published[0] : null;
|
|
6863
6979
|
const results = envelope?.results;
|
|
@@ -6872,7 +6988,7 @@ async function runDataPortalAudit(root) {
|
|
|
6872
6988
|
let objects = null;
|
|
6873
6989
|
let r2Error = null;
|
|
6874
6990
|
try {
|
|
6875
|
-
objects = await cf.r2ObjectList(portal.bucketName);
|
|
6991
|
+
objects = await cf.r2ObjectList(portal.bucketName, scope.objectPrefix);
|
|
6876
6992
|
} catch (err) {
|
|
6877
6993
|
r2Error = err.message;
|
|
6878
6994
|
}
|
|
@@ -7157,7 +7273,17 @@ function exposedOrOwnUploads(segments, exposed, ownerId) {
|
|
|
7157
7273
|
if (exposed.includes(segments[0])) return true;
|
|
7158
7274
|
return segments.length > 2 && segments[0] === "uploads" && segments[1] === ownerId;
|
|
7159
7275
|
}
|
|
7160
|
-
var
|
|
7276
|
+
var PENDING_COLUMNS = "SELECT fileId, ownerId, filename, objectKey, size, targetPath, devicePath, deleted FROM manifest WHERE (ingested = 0 OR deleted = 1)";
|
|
7277
|
+
var SAFE_ACCOUNT_ID = /^[a-z0-9-]+$/;
|
|
7278
|
+
function pendingSelect(portal) {
|
|
7279
|
+
if (portal.storageModel !== "shared") return PENDING_COLUMNS;
|
|
7280
|
+
if (!SAFE_ACCOUNT_ID.test(portal.accountId)) {
|
|
7281
|
+
throw new Error(
|
|
7282
|
+
`portal-uploads-pull: refusing to build a query for unsafe accountId "${portal.accountId}"`
|
|
7283
|
+
);
|
|
7284
|
+
}
|
|
7285
|
+
return `${PENDING_COLUMNS} AND accountId = '${portal.accountId}'`;
|
|
7286
|
+
}
|
|
7161
7287
|
function parsePendingRows(raw) {
|
|
7162
7288
|
const envelope = Array.isArray(raw) ? raw[0] : null;
|
|
7163
7289
|
const results = envelope?.results;
|
|
@@ -7208,7 +7334,7 @@ async function readExposedDirs(accountDir) {
|
|
|
7208
7334
|
}
|
|
7209
7335
|
async function runPortalUploadsPull(root) {
|
|
7210
7336
|
const accountsRoot = resolve8(root, "..", "data", "accounts");
|
|
7211
|
-
const portals = discoverPortalAccounts(accountsRoot);
|
|
7337
|
+
const portals = discoverPortalAccounts(accountsRoot, readSharedStoreNames(root));
|
|
7212
7338
|
if (portals.length === 0) return "portals=0 pulled=0";
|
|
7213
7339
|
const cf = await makeHouseCfExec(root);
|
|
7214
7340
|
let pulledTotal = 0;
|
|
@@ -7217,7 +7343,7 @@ async function runPortalUploadsPull(root) {
|
|
|
7217
7343
|
for (const portal of portals) {
|
|
7218
7344
|
try {
|
|
7219
7345
|
const { rows, malformed } = parsePendingRows(
|
|
7220
|
-
await cf.d1Query(portal.portalDbName,
|
|
7346
|
+
await cf.d1Query(portal.portalDbName, pendingSelect(portal))
|
|
7221
7347
|
);
|
|
7222
7348
|
if (malformed > 0) {
|
|
7223
7349
|
console.error(`${TAG14} op=malformed account=${portal.accountId} rows=${malformed}`);
|
|
@@ -7949,12 +8075,7 @@ function resolveReplyAppendAccount(args) {
|
|
|
7949
8075
|
return args.resolve(args.sendAccountId, args.counterpartyPhone) || args.sendAccountId;
|
|
7950
8076
|
}
|
|
7951
8077
|
function warmConversationAvatars(accountId, remoteJid, records) {
|
|
7952
|
-
const jids =
|
|
7953
|
-
for (const r of records) {
|
|
7954
|
-
if (r.fromMe) continue;
|
|
7955
|
-
const jid = senderJidFromTelephone(r.senderTelephone);
|
|
7956
|
-
if (jid) jids.add(jid);
|
|
7957
|
-
}
|
|
8078
|
+
const jids = avatarJidsForConversation(remoteJid, records);
|
|
7958
8079
|
console.log(`[wa-avatar] op=request account=${accountId.slice(0, 8)} convo=${remoteJid.slice(0, 12)} jids=${jids.size} reason=reader-open`);
|
|
7959
8080
|
for (const jid of jids) {
|
|
7960
8081
|
void fetchAvatar(accountId, jid).catch((e) => {
|
|
@@ -8025,6 +8146,10 @@ app4.get("/store-stream", requireAdminSession, (c) => {
|
|
|
8025
8146
|
// Task 2081 — the group transcript's per-sender colour key. Stable across a
|
|
8026
8147
|
// display-name change, which `senderName` is not.
|
|
8027
8148
|
senderTelephone: r.senderTelephone,
|
|
8149
|
+
// Task 2096 — the sender's wire JID, for the per-bubble avatar. Omitted
|
|
8150
|
+
// rather than sent null when the record has none, so the client's own
|
|
8151
|
+
// presence check is the whole test. Pre-2096 records have none.
|
|
8152
|
+
...r.senderJid ? { senderJid: r.senderJid } : {},
|
|
8028
8153
|
origin: r.origin,
|
|
8029
8154
|
msgKeyId: r.msgKeyId,
|
|
8030
8155
|
// Task 1619 — folded inbound reactions for this bubble (omitted when none).
|
|
@@ -11009,8 +11134,8 @@ async function reapplyLeversViaManager() {
|
|
|
11009
11134
|
var WEBCHAT_SEND_TURN_WINDOW_MS = Number(process.env.WEBCHAT_SEND_TURN_WINDOW_MS ?? String(15e3));
|
|
11010
11135
|
var sendSeq = 0;
|
|
11011
11136
|
function createWebchatRoutes(deps) {
|
|
11012
|
-
const
|
|
11013
|
-
|
|
11137
|
+
const app73 = new Hono();
|
|
11138
|
+
app73.post("/send", requireAdminSession, async (c) => {
|
|
11014
11139
|
let accountId;
|
|
11015
11140
|
try {
|
|
11016
11141
|
accountId = resolvePlatformAccountId();
|
|
@@ -11208,7 +11333,7 @@ ${note}` : note;
|
|
|
11208
11333
|
if (turnTimer.unref) turnTimer.unref();
|
|
11209
11334
|
return c.json({ ok: true });
|
|
11210
11335
|
});
|
|
11211
|
-
|
|
11336
|
+
app73.post("/interrupt", requireAdminSession, async (c) => {
|
|
11212
11337
|
const cacheKey = c.get("cacheKey");
|
|
11213
11338
|
const requesterUserId = (cacheKey ? getUserIdForSession(cacheKey) : void 0) ?? null;
|
|
11214
11339
|
const primaryUserId = resolvePrimaryAdminUserId();
|
|
@@ -11236,7 +11361,7 @@ ${note}` : note;
|
|
|
11236
11361
|
}
|
|
11237
11362
|
return c.json({ ok: true, stopped: outcome.stopped, intId: outcome.intId, deadChild: outcome.deadChild }, 200);
|
|
11238
11363
|
});
|
|
11239
|
-
|
|
11364
|
+
app73.post("/settings", requireAdminSession, async (c) => {
|
|
11240
11365
|
const body = await c.req.json().catch(() => null);
|
|
11241
11366
|
const op = body?.op;
|
|
11242
11367
|
const value = body?.value;
|
|
@@ -11272,7 +11397,7 @@ ${note}` : note;
|
|
|
11272
11397
|
console.log(`[webchat:settings] op=${op} outcome=accepted accountId=${account.accountId.slice(0, 8)} value=${value} settingsApplied=${settingsApplied}`);
|
|
11273
11398
|
return c.json({ ok: true, settingsApplied });
|
|
11274
11399
|
});
|
|
11275
|
-
|
|
11400
|
+
app73.get("/session", requireAdminSession, async (c) => {
|
|
11276
11401
|
let accountId;
|
|
11277
11402
|
try {
|
|
11278
11403
|
accountId = resolvePlatformAccountId();
|
|
@@ -11345,7 +11470,7 @@ ${note}` : note;
|
|
|
11345
11470
|
const indicators = await fetchComposerIndicators(sessionId);
|
|
11346
11471
|
return c.json({ sessionId, projectDir, source, sizeBytes: jsonlSizeBytes(projectDir, sessionId), pendingPermissionPrompt: deps.pendingPromptFor?.(`session:${sessionId}`) ?? null, deliveryFailure: deps.deliveryFailureFor?.(`session:${sessionId}`) ?? null, ...indicators, ...readLevers(account) });
|
|
11347
11472
|
});
|
|
11348
|
-
|
|
11473
|
+
app73.post("/permission-verdict", requireAdminSession, async (c) => {
|
|
11349
11474
|
const body = await c.req.json().catch(() => null);
|
|
11350
11475
|
const sessionId = body?.sessionId;
|
|
11351
11476
|
const requestId = body?.request_id;
|
|
@@ -11362,7 +11487,7 @@ ${note}` : note;
|
|
|
11362
11487
|
const ok = deps.resolvePermissionVerdict?.(`session:${sessionId}`, requestId, behavior) ?? false;
|
|
11363
11488
|
return c.json({ ok });
|
|
11364
11489
|
});
|
|
11365
|
-
return
|
|
11490
|
+
return app73;
|
|
11366
11491
|
}
|
|
11367
11492
|
|
|
11368
11493
|
// server/routes/webchat-greeting.ts
|
|
@@ -11656,7 +11781,7 @@ var telegram_default = app10;
|
|
|
11656
11781
|
|
|
11657
11782
|
// server/routes/quickbooks.ts
|
|
11658
11783
|
import { join as join28 } from "path";
|
|
11659
|
-
import { existsSync as existsSync21, readFileSync as readFileSync23, writeFileSync as writeFileSync9, mkdirSync as mkdirSync4, rmSync, renameSync as renameSync5 } from "fs";
|
|
11784
|
+
import { existsSync as existsSync21, readFileSync as readFileSync23, writeFileSync as writeFileSync9, mkdirSync as mkdirSync4, rmSync as rmSync2, renameSync as renameSync5 } from "fs";
|
|
11660
11785
|
var TAG18 = "[quickbooks]";
|
|
11661
11786
|
var INTUIT_TOKEN_URL = "https://oauth.platform.intuit.com/oauth2/v1/tokens/bearer";
|
|
11662
11787
|
var STATE_RE = /^[A-Za-z0-9_-]+$/;
|
|
@@ -11737,7 +11862,7 @@ async function completeConsent(args) {
|
|
|
11737
11862
|
console.error(`${TAG18} op=token-exchange realmId=${realmId} persisted=${persisted}`);
|
|
11738
11863
|
return { ok: persisted, status: persisted ? 200 : 500, message: persisted ? `Connected company ${realmId}.` : "Failed to persist the connection.", stateValid: true };
|
|
11739
11864
|
} finally {
|
|
11740
|
-
|
|
11865
|
+
rmSync2(claimFile, { force: true });
|
|
11741
11866
|
}
|
|
11742
11867
|
}
|
|
11743
11868
|
function page(title, detail) {
|
|
@@ -13172,7 +13297,7 @@ var session_upload_default = app19;
|
|
|
13172
13297
|
|
|
13173
13298
|
// server/routes/admin/agents.ts
|
|
13174
13299
|
import { resolve as resolve20 } from "path";
|
|
13175
|
-
import { readdirSync as readdirSync21, readFileSync as readFileSync28, existsSync as existsSync30, rmSync as
|
|
13300
|
+
import { readdirSync as readdirSync21, readFileSync as readFileSync28, existsSync as existsSync30, rmSync as rmSync3, renameSync as renameSync9, mkdirSync as mkdirSync6, writeFileSync as writeFileSync12 } from "fs";
|
|
13176
13301
|
import { execFileSync as execFileSync6 } from "child_process";
|
|
13177
13302
|
|
|
13178
13303
|
// ../services/claude-session-manager/src/tool-surface.ts
|
|
@@ -14065,7 +14190,7 @@ app20.delete("/:slug", async (c) => {
|
|
|
14065
14190
|
return c.json({ error: `Graph cleanup failed: ${msg}` }, 500);
|
|
14066
14191
|
}
|
|
14067
14192
|
try {
|
|
14068
|
-
|
|
14193
|
+
rmSync3(agentDir, { recursive: true, force: true });
|
|
14069
14194
|
console.log(`[admin/agents] op=delete accountId=${account.accountId.slice(0, 8)} slug=${slug} outcome=ok`);
|
|
14070
14195
|
return c.json({ ok: true });
|
|
14071
14196
|
} catch (err) {
|
|
@@ -20163,6 +20288,7 @@ function resolveSpawnPlan(host, operatorDomains, body, mintId = randomUUID7, adm
|
|
|
20163
20288
|
const operator = isOperatorHost(host, operatorDomains);
|
|
20164
20289
|
const resumeId = typeof body.sessionId === "string" && body.sessionId.length > 0 ? body.sessionId : void 0;
|
|
20165
20290
|
const name = typeof body.name === "string" && body.name.length > 0 ? body.name : void 0;
|
|
20291
|
+
const rawTitle = typeof body.title === "string" ? body.title.trim() : "";
|
|
20166
20292
|
const webchatBind = resumeId === void 0 || operator;
|
|
20167
20293
|
const kind = resumeId === void 0 ? "new" : "resume";
|
|
20168
20294
|
const origin = operator ? "operator" : "admin";
|
|
@@ -20193,13 +20319,25 @@ function resolveSpawnPlan(host, operatorDomains, body, mintId = randomUUID7, adm
|
|
|
20193
20319
|
payload.sessionId = resumeId;
|
|
20194
20320
|
if (name) payload.name = name;
|
|
20195
20321
|
}
|
|
20196
|
-
return {
|
|
20322
|
+
return {
|
|
20323
|
+
webchatBind,
|
|
20324
|
+
origin,
|
|
20325
|
+
kind,
|
|
20326
|
+
sessionId,
|
|
20327
|
+
payload,
|
|
20328
|
+
model: pickedModel,
|
|
20329
|
+
effort: pickedEffort,
|
|
20330
|
+
paramsSource,
|
|
20331
|
+
// Never rides `payload`: the manager's rc-spawn takes no title. The route
|
|
20332
|
+
// applies it through the rename endpoint once the session id exists.
|
|
20333
|
+
title: kind === "new" && webchatBind && rawTitle.length > 0 ? rawTitle : null
|
|
20334
|
+
};
|
|
20197
20335
|
}
|
|
20198
|
-
function shapeWebchatResponse(sessionId, manager) {
|
|
20336
|
+
function shapeWebchatResponse(sessionId, manager, titleSource = "none") {
|
|
20199
20337
|
const outcome = typeof manager?.outcome === "string" ? manager.outcome : "bound";
|
|
20200
20338
|
const redirected = manager?.redirected === true && typeof manager?.sessionId === "string";
|
|
20201
20339
|
const landed = redirected ? manager.sessionId : sessionId;
|
|
20202
|
-
return { sessionId: landed, outcome, target: `/chat?session=${landed}
|
|
20340
|
+
return { sessionId: landed, outcome, target: `/chat?session=${landed}`, titleSource };
|
|
20203
20341
|
}
|
|
20204
20342
|
var app38 = new Hono();
|
|
20205
20343
|
app38.post("/", requireAdminSession, async (c) => {
|
|
@@ -20278,9 +20416,40 @@ app38.post("/", requireAdminSession, async (c) => {
|
|
|
20278
20416
|
);
|
|
20279
20417
|
}
|
|
20280
20418
|
}
|
|
20419
|
+
let titleSource = "none";
|
|
20420
|
+
if (plan.kind === "new") {
|
|
20421
|
+
if (plan.title === null) {
|
|
20422
|
+
console.log(
|
|
20423
|
+
`[chat-spawn] op=title spawnReqId=${spawnReqId} sessionId=${plan.sessionId} titleLen=0 source=absent outcome=skipped`
|
|
20424
|
+
);
|
|
20425
|
+
} else {
|
|
20426
|
+
try {
|
|
20427
|
+
const tRes = await fetch(`${managerBase("session-rc-spawn:title")}/${plan.sessionId}/rename`, {
|
|
20428
|
+
method: "POST",
|
|
20429
|
+
headers: { "Content-Type": "application/json" },
|
|
20430
|
+
body: JSON.stringify({ title: plan.title })
|
|
20431
|
+
});
|
|
20432
|
+
if (tRes.status === 200) {
|
|
20433
|
+
titleSource = "user";
|
|
20434
|
+
console.log(
|
|
20435
|
+
`[chat-spawn] op=title spawnReqId=${spawnReqId} sessionId=${plan.sessionId} titleLen=${plan.title.length} source=modal outcome=applied`
|
|
20436
|
+
);
|
|
20437
|
+
} else {
|
|
20438
|
+
const reason = (await tRes.json().catch(() => ({}))).reason ?? `status-${tRes.status}`;
|
|
20439
|
+
console.error(
|
|
20440
|
+
`[chat-spawn] op=title spawnReqId=${spawnReqId} sessionId=${plan.sessionId} titleLen=${plan.title.length} source=modal outcome=rejected reason=${reason}`
|
|
20441
|
+
);
|
|
20442
|
+
}
|
|
20443
|
+
} catch (err) {
|
|
20444
|
+
console.error(
|
|
20445
|
+
`[chat-spawn] op=title spawnReqId=${spawnReqId} sessionId=${plan.sessionId} titleLen=${plan.title.length} source=modal outcome=failed reason=${err instanceof Error ? err.message : String(err)}`
|
|
20446
|
+
);
|
|
20447
|
+
}
|
|
20448
|
+
}
|
|
20449
|
+
}
|
|
20281
20450
|
console.log(`[chat-spawn] op=navigate spawnReqId=${spawnReqId} target=${target}`);
|
|
20282
20451
|
console.log(`[chat-spawn] op=exit spawnReqId=${spawnReqId} outcome=bound`);
|
|
20283
|
-
return c.json(shapeWebchatResponse(plan.sessionId, parsed));
|
|
20452
|
+
return c.json(shapeWebchatResponse(plan.sessionId, parsed, titleSource));
|
|
20284
20453
|
}
|
|
20285
20454
|
return c.json(parsed ?? {});
|
|
20286
20455
|
});
|
|
@@ -21877,10 +22046,40 @@ app49.get("/booking-link", requireAdminSession, (c) => {
|
|
|
21877
22046
|
});
|
|
21878
22047
|
var calendar_default = app49;
|
|
21879
22048
|
|
|
22049
|
+
// server/routes/admin/data-portal.ts
|
|
22050
|
+
import { existsSync as existsSync38, readFileSync as readFileSync35 } from "fs";
|
|
22051
|
+
import { join as join44 } from "path";
|
|
22052
|
+
var app50 = new Hono();
|
|
22053
|
+
var PORTAL_FILENAME = "data-portal.json";
|
|
22054
|
+
app50.get("/portal-link", requireAdminSession, (c) => {
|
|
22055
|
+
const account = accountDirForSession(c.var.cacheKey);
|
|
22056
|
+
if (!account) {
|
|
22057
|
+
console.error("[data-portal] op=portal-link auth-rejected reason=no-account-for-session accountId=none");
|
|
22058
|
+
return c.json({ error: "no account for session", reason: "no-account-for-session" }, 401);
|
|
22059
|
+
}
|
|
22060
|
+
const id82 = account.accountId.slice(0, 8);
|
|
22061
|
+
try {
|
|
22062
|
+
const path = join44(account.accountDir, PORTAL_FILENAME);
|
|
22063
|
+
if (!existsSync38(path)) {
|
|
22064
|
+
console.log(`[data-portal] op=portal-link accountId=${id82} portalDomain=none source=data-portal-config`);
|
|
22065
|
+
return c.json({ portalDomain: null });
|
|
22066
|
+
}
|
|
22067
|
+
const cfg = JSON.parse(readFileSync35(path, "utf-8"));
|
|
22068
|
+
const portalDomain = typeof cfg.portalDomain === "string" && cfg.portalDomain.trim() ? cfg.portalDomain.trim() : null;
|
|
22069
|
+
console.log(`[data-portal] op=portal-link accountId=${id82} portalDomain=${portalDomain ?? "none"} source=data-portal-config`);
|
|
22070
|
+
return c.json({ portalDomain });
|
|
22071
|
+
} catch (err) {
|
|
22072
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
22073
|
+
console.error(`[data-portal] op=portal-link-fail accountId=${id82} err="${message}"`);
|
|
22074
|
+
return c.json({ portalDomain: null });
|
|
22075
|
+
}
|
|
22076
|
+
});
|
|
22077
|
+
var data_portal_default = app50;
|
|
22078
|
+
|
|
21880
22079
|
// server/routes/admin/routines.ts
|
|
21881
22080
|
import cronParser from "cron-parser";
|
|
21882
22081
|
var { parseExpression } = cronParser;
|
|
21883
|
-
var
|
|
22082
|
+
var app51 = new Hono();
|
|
21884
22083
|
var AUTOMATION_WHERE = "(e.recurrence IS NOT NULL OR e.agentChannel IS NOT NULL OR e.actionPlugin IS NOT NULL)";
|
|
21885
22084
|
var ROUTINE_COLS = `e.eventId AS eventId, e.name AS name, e.eventStatus AS eventStatus,
|
|
21886
22085
|
e.recurrence AS recurrence, e.nextRun AS nextRun, e.lastTriggered AS lastTriggered,
|
|
@@ -21910,7 +22109,7 @@ function mapRoutine(r) {
|
|
|
21910
22109
|
lastDispatchAt: g("lastDispatchAt")
|
|
21911
22110
|
};
|
|
21912
22111
|
}
|
|
21913
|
-
|
|
22112
|
+
app51.get("/", requireAdminSession, async (c) => {
|
|
21914
22113
|
const accountId = getAccountIdForSession(c.var.cacheKey);
|
|
21915
22114
|
if (!accountId) {
|
|
21916
22115
|
console.error('[admin:routines] op=list auth-rejected reason="no account for session"');
|
|
@@ -21936,7 +22135,7 @@ app50.get("/", requireAdminSession, async (c) => {
|
|
|
21936
22135
|
await session.close();
|
|
21937
22136
|
}
|
|
21938
22137
|
});
|
|
21939
|
-
|
|
22138
|
+
app51.get("/:eventId", requireAdminSession, async (c) => {
|
|
21940
22139
|
const accountId = getAccountIdForSession(c.var.cacheKey);
|
|
21941
22140
|
if (!accountId) return c.json({ error: "Account not found for session" }, 401);
|
|
21942
22141
|
const eventId = c.req.param("eventId");
|
|
@@ -21960,7 +22159,7 @@ app50.get("/:eventId", requireAdminSession, async (c) => {
|
|
|
21960
22159
|
await session.close();
|
|
21961
22160
|
}
|
|
21962
22161
|
});
|
|
21963
|
-
|
|
22162
|
+
app51.patch("/:eventId", requireAdminSession, async (c) => {
|
|
21964
22163
|
const accountId = getAccountIdForSession(c.var.cacheKey);
|
|
21965
22164
|
if (!accountId) return c.json({ error: "Account not found for session" }, 401);
|
|
21966
22165
|
const eventId = c.req.param("eventId");
|
|
@@ -22030,7 +22229,7 @@ app50.patch("/:eventId", requireAdminSession, async (c) => {
|
|
|
22030
22229
|
await session.close();
|
|
22031
22230
|
}
|
|
22032
22231
|
});
|
|
22033
|
-
|
|
22232
|
+
app51.delete("/:eventId", requireAdminSession, async (c) => {
|
|
22034
22233
|
const accountId = getAccountIdForSession(c.var.cacheKey);
|
|
22035
22234
|
if (!accountId) {
|
|
22036
22235
|
console.error('[admin:routines] op=delete auth-rejected reason="no account for session"');
|
|
@@ -22058,12 +22257,12 @@ app50.delete("/:eventId", requireAdminSession, async (c) => {
|
|
|
22058
22257
|
await session.close();
|
|
22059
22258
|
}
|
|
22060
22259
|
});
|
|
22061
|
-
var routines_default =
|
|
22260
|
+
var routines_default = app51;
|
|
22062
22261
|
|
|
22063
22262
|
// server/routes/admin/skills.ts
|
|
22064
|
-
import { existsSync as
|
|
22263
|
+
import { existsSync as existsSync39, readdirSync as readdirSync24, readFileSync as readFileSync36, rmSync as rmSync4 } from "fs";
|
|
22065
22264
|
import { resolve as resolve30 } from "path";
|
|
22066
|
-
var
|
|
22265
|
+
var app52 = new Hono();
|
|
22067
22266
|
var NAME_RE = /^[a-z0-9][a-z0-9-]*$/;
|
|
22068
22267
|
function splitSkillMd(text) {
|
|
22069
22268
|
const m = text.match(/^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/);
|
|
@@ -22085,14 +22284,14 @@ function stripQuotes(s) {
|
|
|
22085
22284
|
return s;
|
|
22086
22285
|
}
|
|
22087
22286
|
function subdirs2(dir) {
|
|
22088
|
-
if (!
|
|
22287
|
+
if (!existsSync39(dir)) return [];
|
|
22089
22288
|
try {
|
|
22090
22289
|
return readdirSync24(dir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
22091
22290
|
} catch {
|
|
22092
22291
|
return [];
|
|
22093
22292
|
}
|
|
22094
22293
|
}
|
|
22095
|
-
|
|
22294
|
+
app52.get("/", requireAdminSession, async (c) => {
|
|
22096
22295
|
const acct = accountDirForSession(c.var.cacheKey);
|
|
22097
22296
|
if (!acct) {
|
|
22098
22297
|
console.error('[admin:skills] op=list auth-rejected reason="no account for session"');
|
|
@@ -22108,11 +22307,11 @@ app51.get("/", requireAdminSession, async (c) => {
|
|
|
22108
22307
|
const skillsDir = resolve30(pluginsDir, plugin, "skills");
|
|
22109
22308
|
for (const skill of subdirs2(skillsDir)) {
|
|
22110
22309
|
const skillMd = resolve30(skillsDir, skill, "SKILL.md");
|
|
22111
|
-
if (!
|
|
22310
|
+
if (!existsSync39(skillMd)) continue;
|
|
22112
22311
|
try {
|
|
22113
|
-
const { frontmatter } = splitSkillMd(
|
|
22312
|
+
const { frontmatter } = splitSkillMd(readFileSync36(skillMd, "utf-8"));
|
|
22114
22313
|
const refsDir = resolve30(skillsDir, skill, "references");
|
|
22115
|
-
const refCount =
|
|
22314
|
+
const refCount = existsSync39(refsDir) ? readdirSync24(refsDir, { withFileTypes: true }).filter((e) => e.isFile()).length : 0;
|
|
22116
22315
|
skills.push({ plugin, skill, description: frontmatter.description, refCount });
|
|
22117
22316
|
} catch (err) {
|
|
22118
22317
|
console.error(`[admin:skills] op=list-skip plugin=${plugin} skill=${skill} reason="${err instanceof Error ? err.message : String(err)}"`);
|
|
@@ -22125,7 +22324,7 @@ app51.get("/", requireAdminSession, async (c) => {
|
|
|
22125
22324
|
console.log(`[admin:skills] op=list accountId=${acct.accountId.slice(0, 8)} plugins=${value.pluginCount} skills=${value.skills.length} cache=${state}`);
|
|
22126
22325
|
return c.json({ skills: value.skills });
|
|
22127
22326
|
});
|
|
22128
|
-
|
|
22327
|
+
app52.get("/:plugin/:skill", requireAdminSession, async (c) => {
|
|
22129
22328
|
const acct = accountDirForSession(c.var.cacheKey);
|
|
22130
22329
|
if (!acct) return c.json({ error: "Account not found for session" }, 401);
|
|
22131
22330
|
const plugin = c.req.param("plugin");
|
|
@@ -22136,13 +22335,13 @@ app51.get("/:plugin/:skill", requireAdminSession, async (c) => {
|
|
|
22136
22335
|
}
|
|
22137
22336
|
const skillDir = resolve30(acct.accountDir, "plugins", plugin, "skills", skill);
|
|
22138
22337
|
const skillMd = resolve30(skillDir, "SKILL.md");
|
|
22139
|
-
const found =
|
|
22338
|
+
const found = existsSync39(skillMd);
|
|
22140
22339
|
console.log(`[admin:skills] op=get plugin=${plugin} skill=${skill} found=${found}`);
|
|
22141
22340
|
if (!found) return c.json({ error: "Skill not found" }, 404);
|
|
22142
22341
|
try {
|
|
22143
|
-
const { frontmatter, body } = splitSkillMd(
|
|
22342
|
+
const { frontmatter, body } = splitSkillMd(readFileSync36(skillMd, "utf-8"));
|
|
22144
22343
|
const refsDir = resolve30(skillDir, "references");
|
|
22145
|
-
const references =
|
|
22344
|
+
const references = existsSync39(refsDir) ? readdirSync24(refsDir, { withFileTypes: true }).filter((e) => e.isFile()).map((e) => e.name).sort() : [];
|
|
22146
22345
|
return c.json({ skill: { plugin, skill, frontmatter, body, references } });
|
|
22147
22346
|
} catch (err) {
|
|
22148
22347
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -22150,7 +22349,7 @@ app51.get("/:plugin/:skill", requireAdminSession, async (c) => {
|
|
|
22150
22349
|
return c.json({ error: `Skill unavailable: ${message}` }, 503);
|
|
22151
22350
|
}
|
|
22152
22351
|
});
|
|
22153
|
-
|
|
22352
|
+
app52.delete("/:plugin/:skill", requireAdminSession, async (c) => {
|
|
22154
22353
|
const acct = accountDirForSession(c.var.cacheKey);
|
|
22155
22354
|
if (!acct) return c.json({ error: "Account not found for session" }, 401);
|
|
22156
22355
|
const plugin = c.req.param("plugin");
|
|
@@ -22162,15 +22361,15 @@ app51.delete("/:plugin/:skill", requireAdminSession, async (c) => {
|
|
|
22162
22361
|
const pluginDir = resolve30(acct.accountDir, "plugins", plugin);
|
|
22163
22362
|
const skillsDir = resolve30(pluginDir, "skills");
|
|
22164
22363
|
const skillDir = resolve30(skillsDir, skill);
|
|
22165
|
-
if (!
|
|
22364
|
+
if (!existsSync39(resolve30(skillDir, "SKILL.md"))) {
|
|
22166
22365
|
console.error(`[admin:skills] op=delete outcome=rejected reason=not-found plugin=${plugin} skill=${skill}`);
|
|
22167
22366
|
return c.json({ error: "Skill not found" }, 404);
|
|
22168
22367
|
}
|
|
22169
22368
|
try {
|
|
22170
|
-
|
|
22369
|
+
rmSync4(skillDir, { recursive: true, force: true });
|
|
22171
22370
|
const pluginDirRemoved = subdirs2(skillsDir).length === 0;
|
|
22172
|
-
if (pluginDirRemoved)
|
|
22173
|
-
const skillDirGone = !
|
|
22371
|
+
if (pluginDirRemoved) rmSync4(pluginDir, { recursive: true, force: true });
|
|
22372
|
+
const skillDirGone = !existsSync39(skillDir);
|
|
22174
22373
|
if (!skillDirGone) {
|
|
22175
22374
|
console.error(`[admin:skills] op=delete plugin=${plugin} skill=${skill} outcome=rejected reason=rm-incomplete skillDirGone=false`);
|
|
22176
22375
|
return c.json({ error: "Delete did not remove the skill directory" }, 503);
|
|
@@ -22183,7 +22382,7 @@ app51.delete("/:plugin/:skill", requireAdminSession, async (c) => {
|
|
|
22183
22382
|
return c.json({ error: `Delete failed: ${message}` }, 503);
|
|
22184
22383
|
}
|
|
22185
22384
|
});
|
|
22186
|
-
var skills_default =
|
|
22385
|
+
var skills_default = app52;
|
|
22187
22386
|
|
|
22188
22387
|
// app/lib/time-entry-format.ts
|
|
22189
22388
|
function secondsBetween(startIso, endIso) {
|
|
@@ -22206,8 +22405,8 @@ function toNum(v) {
|
|
|
22206
22405
|
var TAG27 = "[task-timer]";
|
|
22207
22406
|
var COMPLETED = /* @__PURE__ */ new Set(["completed"]);
|
|
22208
22407
|
var HIDDEN_FROM_OPEN = /* @__PURE__ */ new Set(["completed", "cancelled"]);
|
|
22209
|
-
var
|
|
22210
|
-
|
|
22408
|
+
var app53 = new Hono();
|
|
22409
|
+
app53.get("/", requireAdminSession, async (c) => {
|
|
22211
22410
|
const cacheKey = c.var.cacheKey;
|
|
22212
22411
|
const accountId = getAccountIdForSession(cacheKey);
|
|
22213
22412
|
if (!accountId) {
|
|
@@ -22313,13 +22512,13 @@ async function computeTaskList(accountId) {
|
|
|
22313
22512
|
await session.close();
|
|
22314
22513
|
}
|
|
22315
22514
|
}
|
|
22316
|
-
var tasks_list_default =
|
|
22515
|
+
var tasks_list_default = app53;
|
|
22317
22516
|
|
|
22318
22517
|
// server/routes/admin/task-timer-start.ts
|
|
22319
22518
|
import { randomUUID as randomUUID10 } from "crypto";
|
|
22320
22519
|
var TAG28 = "[task-timer]";
|
|
22321
|
-
var
|
|
22322
|
-
|
|
22520
|
+
var app54 = new Hono();
|
|
22521
|
+
app54.post("/", requireAdminSession, async (c) => {
|
|
22323
22522
|
const cacheKey = c.var.cacheKey;
|
|
22324
22523
|
const accountId = getAccountIdForSession(cacheKey);
|
|
22325
22524
|
if (!accountId) return c.json({ error: "Account not found for session" }, 401);
|
|
@@ -22376,13 +22575,13 @@ app53.post("/", requireAdminSession, async (c) => {
|
|
|
22376
22575
|
await session.close();
|
|
22377
22576
|
}
|
|
22378
22577
|
});
|
|
22379
|
-
var task_timer_start_default =
|
|
22578
|
+
var task_timer_start_default = app54;
|
|
22380
22579
|
|
|
22381
22580
|
// server/routes/admin/task-timer-stop.ts
|
|
22382
22581
|
import neo4j4 from "neo4j-driver";
|
|
22383
22582
|
var TAG29 = "[task-timer]";
|
|
22384
|
-
var
|
|
22385
|
-
|
|
22583
|
+
var app55 = new Hono();
|
|
22584
|
+
app55.post("/", requireAdminSession, async (c) => {
|
|
22386
22585
|
const cacheKey = c.var.cacheKey;
|
|
22387
22586
|
const accountId = getAccountIdForSession(cacheKey);
|
|
22388
22587
|
if (!accountId) return c.json({ error: "Account not found for session" }, 401);
|
|
@@ -22451,12 +22650,12 @@ app54.post("/", requireAdminSession, async (c) => {
|
|
|
22451
22650
|
await session.close();
|
|
22452
22651
|
}
|
|
22453
22652
|
});
|
|
22454
|
-
var task_timer_stop_default =
|
|
22653
|
+
var task_timer_stop_default = app55;
|
|
22455
22654
|
|
|
22456
22655
|
// server/routes/admin/task-complete.ts
|
|
22457
22656
|
var TAG30 = "[task-timer]";
|
|
22458
|
-
var
|
|
22459
|
-
|
|
22657
|
+
var app56 = new Hono();
|
|
22658
|
+
app56.post("/", requireAdminSession, async (c) => {
|
|
22460
22659
|
const cacheKey = c.var.cacheKey;
|
|
22461
22660
|
const accountId = getAccountIdForSession(cacheKey);
|
|
22462
22661
|
if (!accountId) return c.json({ error: "Account not found for session" }, 401);
|
|
@@ -22495,14 +22694,14 @@ app55.post("/", requireAdminSession, async (c) => {
|
|
|
22495
22694
|
await session.close();
|
|
22496
22695
|
}
|
|
22497
22696
|
});
|
|
22498
|
-
var task_complete_default =
|
|
22697
|
+
var task_complete_default = app56;
|
|
22499
22698
|
|
|
22500
22699
|
// server/routes/admin/task-time-adjust.ts
|
|
22501
22700
|
import { randomUUID as randomUUID11 } from "crypto";
|
|
22502
22701
|
import neo4j5 from "neo4j-driver";
|
|
22503
22702
|
var TAG31 = "[task-time-adjust]";
|
|
22504
|
-
var
|
|
22505
|
-
|
|
22703
|
+
var app57 = new Hono();
|
|
22704
|
+
app57.post("/", requireAdminSession, async (c) => {
|
|
22506
22705
|
const cacheKey = c.var.cacheKey;
|
|
22507
22706
|
const accountId = getAccountIdForSession(cacheKey);
|
|
22508
22707
|
if (!accountId) return c.json({ error: "Account not found for session" }, 401);
|
|
@@ -22577,10 +22776,10 @@ app56.post("/", requireAdminSession, async (c) => {
|
|
|
22577
22776
|
await session.close();
|
|
22578
22777
|
}
|
|
22579
22778
|
});
|
|
22580
|
-
var task_time_adjust_default =
|
|
22779
|
+
var task_time_adjust_default = app57;
|
|
22581
22780
|
|
|
22582
22781
|
// app/lib/admin-roster.ts
|
|
22583
|
-
import { existsSync as
|
|
22782
|
+
import { existsSync as existsSync40, readdirSync as readdirSync25, readFileSync as readFileSync37 } from "fs";
|
|
22584
22783
|
import { resolve as resolve31 } from "path";
|
|
22585
22784
|
function readHouseAdminUserIds(accountsDir = ACCOUNTS_DIR) {
|
|
22586
22785
|
let entries2;
|
|
@@ -22591,9 +22790,9 @@ function readHouseAdminUserIds(accountsDir = ACCOUNTS_DIR) {
|
|
|
22591
22790
|
}
|
|
22592
22791
|
for (const entry of entries2) {
|
|
22593
22792
|
const cfgPath = resolve31(accountsDir, entry, "account.json");
|
|
22594
|
-
if (!
|
|
22793
|
+
if (!existsSync40(cfgPath)) continue;
|
|
22595
22794
|
try {
|
|
22596
|
-
const cfg = JSON.parse(
|
|
22795
|
+
const cfg = JSON.parse(readFileSync37(cfgPath, "utf-8"));
|
|
22597
22796
|
if (cfg.role !== "house") continue;
|
|
22598
22797
|
if (!Array.isArray(cfg.admins)) return [];
|
|
22599
22798
|
return cfg.admins.map(
|
|
@@ -22665,8 +22864,8 @@ async function resolveAssignableAdmins(accountId, accountsDir = ACCOUNTS_DIR) {
|
|
|
22665
22864
|
|
|
22666
22865
|
// server/routes/admin/task-assign.ts
|
|
22667
22866
|
var TAG32 = "[task-assign]";
|
|
22668
|
-
var
|
|
22669
|
-
|
|
22867
|
+
var app58 = new Hono();
|
|
22868
|
+
app58.post("/", requireAdminSession, async (c) => {
|
|
22670
22869
|
const cacheKey = c.var.cacheKey;
|
|
22671
22870
|
const accountId = getAccountIdForSession(cacheKey);
|
|
22672
22871
|
if (!accountId) return c.json({ error: "Account not found for session" }, 401);
|
|
@@ -22748,12 +22947,12 @@ app57.post("/", requireAdminSession, async (c) => {
|
|
|
22748
22947
|
await session.close();
|
|
22749
22948
|
}
|
|
22750
22949
|
});
|
|
22751
|
-
var task_assign_default =
|
|
22950
|
+
var task_assign_default = app58;
|
|
22752
22951
|
|
|
22753
22952
|
// server/routes/admin/admin-users-list.ts
|
|
22754
22953
|
var TAG33 = "[admin-users]";
|
|
22755
|
-
var
|
|
22756
|
-
|
|
22954
|
+
var app59 = new Hono();
|
|
22955
|
+
app59.get("/", requireAdminSession, async (c) => {
|
|
22757
22956
|
const cacheKey = c.var.cacheKey;
|
|
22758
22957
|
const accountId = getAccountIdForSession(cacheKey);
|
|
22759
22958
|
if (!accountId) {
|
|
@@ -22769,11 +22968,11 @@ app58.get("/", requireAdminSession, async (c) => {
|
|
|
22769
22968
|
return c.json({ error: "admin-users-list-failed" }, 500);
|
|
22770
22969
|
}
|
|
22771
22970
|
});
|
|
22772
|
-
var admin_users_list_default =
|
|
22971
|
+
var admin_users_list_default = app59;
|
|
22773
22972
|
|
|
22774
22973
|
// server/routes/admin/activity.ts
|
|
22775
22974
|
import { readdirSync as readdirSync26 } from "fs";
|
|
22776
|
-
var
|
|
22975
|
+
var app60 = new Hono();
|
|
22777
22976
|
var TAG34 = "[activity]";
|
|
22778
22977
|
function resolveHouseAccountId(accounts) {
|
|
22779
22978
|
const houses = accounts.filter((a) => a.config.role === "house");
|
|
@@ -22795,7 +22994,7 @@ function countAccountDirsOnDisk() {
|
|
|
22795
22994
|
return null;
|
|
22796
22995
|
}
|
|
22797
22996
|
}
|
|
22798
|
-
|
|
22997
|
+
app60.get("/", requireAdminSession, async (c) => {
|
|
22799
22998
|
const req = requestToken();
|
|
22800
22999
|
const cacheKey = c.var.cacheKey;
|
|
22801
23000
|
const caller2 = getAccountIdForSession(cacheKey) ?? null;
|
|
@@ -22904,65 +23103,66 @@ app59.get("/", requireAdminSession, async (c) => {
|
|
|
22904
23103
|
}
|
|
22905
23104
|
});
|
|
22906
23105
|
});
|
|
22907
|
-
var activity_default =
|
|
23106
|
+
var activity_default = app60;
|
|
22908
23107
|
|
|
22909
23108
|
// server/routes/admin/index.ts
|
|
22910
|
-
var
|
|
22911
|
-
|
|
22912
|
-
|
|
22913
|
-
|
|
22914
|
-
|
|
22915
|
-
|
|
22916
|
-
|
|
22917
|
-
|
|
22918
|
-
|
|
22919
|
-
|
|
22920
|
-
|
|
22921
|
-
|
|
22922
|
-
|
|
22923
|
-
|
|
22924
|
-
|
|
22925
|
-
|
|
22926
|
-
|
|
22927
|
-
|
|
22928
|
-
|
|
22929
|
-
|
|
22930
|
-
|
|
22931
|
-
|
|
22932
|
-
|
|
22933
|
-
|
|
22934
|
-
|
|
22935
|
-
|
|
22936
|
-
|
|
22937
|
-
|
|
22938
|
-
|
|
22939
|
-
|
|
22940
|
-
|
|
22941
|
-
|
|
22942
|
-
|
|
22943
|
-
|
|
22944
|
-
|
|
22945
|
-
|
|
22946
|
-
|
|
22947
|
-
|
|
22948
|
-
|
|
22949
|
-
|
|
22950
|
-
|
|
22951
|
-
|
|
22952
|
-
|
|
22953
|
-
|
|
22954
|
-
|
|
22955
|
-
|
|
22956
|
-
|
|
22957
|
-
|
|
22958
|
-
|
|
23109
|
+
var app61 = new Hono();
|
|
23110
|
+
app61.route("/session", session_default);
|
|
23111
|
+
app61.route("/accounts", accounts_default);
|
|
23112
|
+
app61.route("/logs", logs_default);
|
|
23113
|
+
app61.route("/claude-info", claude_info_default);
|
|
23114
|
+
app61.route("/attachment", attachment_default);
|
|
23115
|
+
app61.route("/session-upload", session_upload_default);
|
|
23116
|
+
app61.route("/agents", agents_default);
|
|
23117
|
+
app61.route("/sessions", sessions_default);
|
|
23118
|
+
app61.route("/claude-sessions", claude_sessions_default);
|
|
23119
|
+
app61.route("/log-ingest", log_ingest_default);
|
|
23120
|
+
app61.route("/events", events_default);
|
|
23121
|
+
app61.route("/files", files_default);
|
|
23122
|
+
app61.route("/graph-search", graph_search_default);
|
|
23123
|
+
app61.route("/graph-subgraph", graph_subgraph_default);
|
|
23124
|
+
app61.route("/graph-delete", graph_delete_default);
|
|
23125
|
+
app61.route("/graph-restore", graph_restore_default);
|
|
23126
|
+
app61.route("/graph-labels-in-graph", graph_labels_in_graph_default);
|
|
23127
|
+
app61.route("/graph-default-view", graph_default_view_default);
|
|
23128
|
+
app61.route("/sidebar-artefacts", sidebar_artefacts_default);
|
|
23129
|
+
app61.route("/sidebar-sessions", sidebar_sessions_default);
|
|
23130
|
+
app61.route("/session-delete", session_delete_default);
|
|
23131
|
+
app61.route("/session-stop", session_stop_default);
|
|
23132
|
+
app61.route("/session-archive", session_archive_default);
|
|
23133
|
+
app61.route("/session-rename", session_rename_default);
|
|
23134
|
+
app61.route("/session-usage", session_usage_default);
|
|
23135
|
+
app61.route("/browser", browser_default);
|
|
23136
|
+
app61.route("/session-rc-spawn", session_rc_spawn_default);
|
|
23137
|
+
app61.route("/session-reseat", session_reseat_default);
|
|
23138
|
+
app61.route("/system-stats", system_stats_default);
|
|
23139
|
+
app61.route("/health-brand", health_default2);
|
|
23140
|
+
app61.route("/linkedin-ingest", linkedin_ingest_default);
|
|
23141
|
+
app61.route("/post-turn-context", post_turn_context_default);
|
|
23142
|
+
app61.route("/public-session-context", public_session_context_default);
|
|
23143
|
+
app61.route("/public-session-exit", public_session_exit_default);
|
|
23144
|
+
app61.route("/access-session-evict", access_session_evict_default);
|
|
23145
|
+
app61.route("/enrol-person", enrol_person_default);
|
|
23146
|
+
app61.route("/calendar", calendar_default);
|
|
23147
|
+
app61.route("/data-portal", data_portal_default);
|
|
23148
|
+
app61.route("/routines", routines_default);
|
|
23149
|
+
app61.route("/skills", skills_default);
|
|
23150
|
+
app61.route("/tasks-list", tasks_list_default);
|
|
23151
|
+
app61.route("/task-timer-start", task_timer_start_default);
|
|
23152
|
+
app61.route("/task-timer-stop", task_timer_stop_default);
|
|
23153
|
+
app61.route("/task-complete", task_complete_default);
|
|
23154
|
+
app61.route("/task-time-adjust", task_time_adjust_default);
|
|
23155
|
+
app61.route("/task-assign", task_assign_default);
|
|
23156
|
+
app61.route("/admin-users-list", admin_users_list_default);
|
|
23157
|
+
app61.route("/activity", activity_default);
|
|
23158
|
+
var admin_default = app61;
|
|
22959
23159
|
|
|
22960
23160
|
// server/routes/access/verify-token.ts
|
|
22961
23161
|
var TAG35 = "[access-verify]";
|
|
22962
23162
|
var MINT_TAG = "[access-session-mint]";
|
|
22963
23163
|
var COOKIE_NAME = "__access_session";
|
|
22964
|
-
var
|
|
22965
|
-
|
|
23164
|
+
var app62 = new Hono();
|
|
23165
|
+
app62.post("/", async (c) => {
|
|
22966
23166
|
const ip = c.var.clientIp || "unknown";
|
|
22967
23167
|
let body;
|
|
22968
23168
|
try {
|
|
@@ -23044,7 +23244,7 @@ app61.post("/", async (c) => {
|
|
|
23044
23244
|
displayName: grant.displayName
|
|
23045
23245
|
});
|
|
23046
23246
|
});
|
|
23047
|
-
var verify_token_default =
|
|
23247
|
+
var verify_token_default = app62;
|
|
23048
23248
|
|
|
23049
23249
|
// app/lib/access-email.ts
|
|
23050
23250
|
import { spawn as spawn2 } from "child_process";
|
|
@@ -23107,9 +23307,9 @@ async function sendMagicLinkEmail(payload) {
|
|
|
23107
23307
|
|
|
23108
23308
|
// server/routes/access/request-magic-link.ts
|
|
23109
23309
|
var TAG36 = "[access-request-link]";
|
|
23110
|
-
var
|
|
23310
|
+
var app63 = new Hono();
|
|
23111
23311
|
var VISITOR_MESSAGE = "If that email is on the invite list, a fresh link is on the way.";
|
|
23112
|
-
|
|
23312
|
+
app63.post("/", async (c) => {
|
|
23113
23313
|
let body;
|
|
23114
23314
|
try {
|
|
23115
23315
|
body = await c.req.json();
|
|
@@ -23183,16 +23383,16 @@ app62.post("/", async (c) => {
|
|
|
23183
23383
|
);
|
|
23184
23384
|
return c.json({ message: VISITOR_MESSAGE }, 200);
|
|
23185
23385
|
});
|
|
23186
|
-
var request_magic_link_default =
|
|
23386
|
+
var request_magic_link_default = app63;
|
|
23187
23387
|
|
|
23188
23388
|
// server/routes/access/index.ts
|
|
23189
|
-
var
|
|
23190
|
-
|
|
23191
|
-
|
|
23192
|
-
var access_default =
|
|
23389
|
+
var app64 = new Hono();
|
|
23390
|
+
app64.route("/verify-token", verify_token_default);
|
|
23391
|
+
app64.route("/request-magic-link", request_magic_link_default);
|
|
23392
|
+
var access_default = app64;
|
|
23193
23393
|
|
|
23194
23394
|
// server/routes/sites.ts
|
|
23195
|
-
import { existsSync as
|
|
23395
|
+
import { existsSync as existsSync41, readFileSync as readFileSync38, realpathSync as realpathSync8, statSync as statSync21 } from "fs";
|
|
23196
23396
|
import { resolve as resolve33 } from "path";
|
|
23197
23397
|
var SAFE_SEG_RE = /^[a-z0-9_][a-z0-9_.-]{0,99}$/i;
|
|
23198
23398
|
var MIME = {
|
|
@@ -23224,8 +23424,8 @@ function getExt(p) {
|
|
|
23224
23424
|
if (idx < p.lastIndexOf("/")) return "";
|
|
23225
23425
|
return p.slice(idx).toLowerCase();
|
|
23226
23426
|
}
|
|
23227
|
-
var
|
|
23228
|
-
|
|
23427
|
+
var app65 = new Hono();
|
|
23428
|
+
app65.get("/:rel{.*}", (c) => {
|
|
23229
23429
|
const reqPath = c.req.path;
|
|
23230
23430
|
const rawRel = c.req.param("rel") ?? "";
|
|
23231
23431
|
const trimmed = rawRel.replace(/^\/+/, "").replace(/\/+$/, "");
|
|
@@ -23258,7 +23458,7 @@ app64.get("/:rel{.*}", (c) => {
|
|
|
23258
23458
|
}
|
|
23259
23459
|
let stat11;
|
|
23260
23460
|
try {
|
|
23261
|
-
stat11 =
|
|
23461
|
+
stat11 = existsSync41(filePath) ? statSync21(filePath) : null;
|
|
23262
23462
|
} catch {
|
|
23263
23463
|
stat11 = null;
|
|
23264
23464
|
}
|
|
@@ -23277,7 +23477,7 @@ app64.get("/:rel{.*}", (c) => {
|
|
|
23277
23477
|
console.error(`[sites] path-traversal-rejected path=${reqPath} reason=escape status=403`);
|
|
23278
23478
|
return c.text("Forbidden", 403);
|
|
23279
23479
|
}
|
|
23280
|
-
if (!
|
|
23480
|
+
if (!existsSync41(filePath)) {
|
|
23281
23481
|
console.error(`[sites] not-found path=${reqPath} status=404`);
|
|
23282
23482
|
return c.text("Not found", 404);
|
|
23283
23483
|
}
|
|
@@ -23296,7 +23496,7 @@ app64.get("/:rel{.*}", (c) => {
|
|
|
23296
23496
|
}
|
|
23297
23497
|
let body;
|
|
23298
23498
|
try {
|
|
23299
|
-
body =
|
|
23499
|
+
body = readFileSync38(realPath);
|
|
23300
23500
|
} catch (err) {
|
|
23301
23501
|
const code = err?.code;
|
|
23302
23502
|
if (code === "EISDIR") {
|
|
@@ -23328,11 +23528,11 @@ app64.get("/:rel{.*}", (c) => {
|
|
|
23328
23528
|
"X-Content-Type-Options": "nosniff"
|
|
23329
23529
|
});
|
|
23330
23530
|
});
|
|
23331
|
-
var sites_default =
|
|
23531
|
+
var sites_default = app65;
|
|
23332
23532
|
|
|
23333
23533
|
// app/lib/visitor-token.ts
|
|
23334
23534
|
import { createHmac, randomBytes, timingSafeEqual } from "crypto";
|
|
23335
|
-
import { mkdirSync as mkdirSync8, readFileSync as
|
|
23535
|
+
import { mkdirSync as mkdirSync8, readFileSync as readFileSync39, writeFileSync as writeFileSync14 } from "fs";
|
|
23336
23536
|
import { dirname as dirname15 } from "path";
|
|
23337
23537
|
var TOKEN_PREFIX = "v1.";
|
|
23338
23538
|
var SECRET_BYTES = 32;
|
|
@@ -23341,7 +23541,7 @@ var cachedSecret = null;
|
|
|
23341
23541
|
function getSecret() {
|
|
23342
23542
|
if (cachedSecret) return cachedSecret;
|
|
23343
23543
|
try {
|
|
23344
|
-
const hex2 =
|
|
23544
|
+
const hex2 = readFileSync39(VISITOR_TOKEN_SECRET_FILE, "utf-8").trim();
|
|
23345
23545
|
if (hex2.length === SECRET_BYTES * 2) {
|
|
23346
23546
|
cachedSecret = Buffer.from(hex2, "hex");
|
|
23347
23547
|
return cachedSecret;
|
|
@@ -23355,7 +23555,7 @@ function getSecret() {
|
|
|
23355
23555
|
console.log(`[visitor-token] secret minted path=${VISITOR_TOKEN_SECRET_FILE}`);
|
|
23356
23556
|
} catch {
|
|
23357
23557
|
}
|
|
23358
|
-
const hex =
|
|
23558
|
+
const hex = readFileSync39(VISITOR_TOKEN_SECRET_FILE, "utf-8").trim();
|
|
23359
23559
|
cachedSecret = Buffer.from(hex, "hex");
|
|
23360
23560
|
return cachedSecret;
|
|
23361
23561
|
}
|
|
@@ -23408,8 +23608,8 @@ var VISITOR_COOKIE_NAME = "mxy_v";
|
|
|
23408
23608
|
var VISITOR_COOKIE_MAX_AGE_SECONDS = Math.floor(DEFAULT_TTL_MS / 1e3);
|
|
23409
23609
|
|
|
23410
23610
|
// app/lib/brand-config.ts
|
|
23411
|
-
import { existsSync as
|
|
23412
|
-
import { join as
|
|
23611
|
+
import { existsSync as existsSync42, readFileSync as readFileSync40 } from "fs";
|
|
23612
|
+
import { join as join45 } from "path";
|
|
23413
23613
|
var cached2 = null;
|
|
23414
23614
|
var cachedAttempted = false;
|
|
23415
23615
|
function readBrandConfig() {
|
|
@@ -23417,10 +23617,10 @@ function readBrandConfig() {
|
|
|
23417
23617
|
cachedAttempted = true;
|
|
23418
23618
|
const platformRoot5 = process.env.MAXY_PLATFORM_ROOT;
|
|
23419
23619
|
if (!platformRoot5) return null;
|
|
23420
|
-
const brandPath =
|
|
23421
|
-
if (!
|
|
23620
|
+
const brandPath = join45(platformRoot5, "config", "brand.json");
|
|
23621
|
+
if (!existsSync42(brandPath)) return null;
|
|
23422
23622
|
try {
|
|
23423
|
-
cached2 = JSON.parse(
|
|
23623
|
+
cached2 = JSON.parse(readFileSync40(brandPath, "utf-8"));
|
|
23424
23624
|
return cached2;
|
|
23425
23625
|
} catch {
|
|
23426
23626
|
return null;
|
|
@@ -23428,7 +23628,7 @@ function readBrandConfig() {
|
|
|
23428
23628
|
}
|
|
23429
23629
|
|
|
23430
23630
|
// server/routes/visitor-consent.ts
|
|
23431
|
-
var
|
|
23631
|
+
var app66 = new Hono();
|
|
23432
23632
|
var CONSENT_COOKIE_NAME = "mxy_consent";
|
|
23433
23633
|
var CONSENT_COOKIE_MAX_AGE_SECONDS = 60 * 60 * 24 * 365;
|
|
23434
23634
|
var DEFAULT_CONSENT_COPY = {
|
|
@@ -23473,17 +23673,17 @@ function siteSlugFromReferer(referer) {
|
|
|
23473
23673
|
return "";
|
|
23474
23674
|
}
|
|
23475
23675
|
}
|
|
23476
|
-
|
|
23676
|
+
app66.options("/consent", (c) => {
|
|
23477
23677
|
const origin = getOrigin(c);
|
|
23478
23678
|
setCorsHeaders(c, origin);
|
|
23479
23679
|
return c.body(null, 204);
|
|
23480
23680
|
});
|
|
23481
|
-
|
|
23681
|
+
app66.options("/brand-config", (c) => {
|
|
23482
23682
|
const origin = getOrigin(c);
|
|
23483
23683
|
setCorsHeaders(c, origin);
|
|
23484
23684
|
return c.body(null, 204);
|
|
23485
23685
|
});
|
|
23486
|
-
|
|
23686
|
+
app66.post("/consent", async (c) => {
|
|
23487
23687
|
const origin = getOrigin(c);
|
|
23488
23688
|
setCorsHeaders(c, origin);
|
|
23489
23689
|
let raw;
|
|
@@ -23523,7 +23723,7 @@ app65.post("/consent", async (c) => {
|
|
|
23523
23723
|
console.log(`[consent] ${parsed.decision} site=${site} brand=${brandName} tokenBound=${tokenBound}`);
|
|
23524
23724
|
return c.body(null, 204);
|
|
23525
23725
|
});
|
|
23526
|
-
|
|
23726
|
+
app66.get("/brand-config", (c) => {
|
|
23527
23727
|
const origin = getOrigin(c);
|
|
23528
23728
|
setCorsHeaders(c, origin);
|
|
23529
23729
|
const brand = readBrandConfig();
|
|
@@ -23533,7 +23733,7 @@ app65.get("/brand-config", (c) => {
|
|
|
23533
23733
|
c.header("Cache-Control", "public, max-age=300");
|
|
23534
23734
|
return c.json({ consent: { copy, palette } });
|
|
23535
23735
|
});
|
|
23536
|
-
var visitor_consent_default =
|
|
23736
|
+
var visitor_consent_default = app66;
|
|
23537
23737
|
|
|
23538
23738
|
// server/routes/listings.ts
|
|
23539
23739
|
function getCookie(headerValue, name) {
|
|
@@ -23560,8 +23760,8 @@ function appendConsentParams(pageUrl, token) {
|
|
|
23560
23760
|
}
|
|
23561
23761
|
var SAFE_SLUG_RE = /^[a-z0-9](?:[a-z0-9-]{0,118}[a-z0-9])?$/;
|
|
23562
23762
|
var CHAT_SESSION_KEY_RE = /^[A-Za-z0-9][A-Za-z0-9_-]{7,127}$/;
|
|
23563
|
-
var
|
|
23564
|
-
|
|
23763
|
+
var app67 = new Hono();
|
|
23764
|
+
app67.get("/:slug/click", async (c) => {
|
|
23565
23765
|
const slug = c.req.param("slug") ?? "";
|
|
23566
23766
|
const rawSession = c.req.query("session") ?? "";
|
|
23567
23767
|
const sessionKey = CHAT_SESSION_KEY_RE.test(rawSession) ? rawSession : "invalid";
|
|
@@ -23625,10 +23825,10 @@ app66.get("/:slug/click", async (c) => {
|
|
|
23625
23825
|
console.log(`[property-card-click] sessionKey=${sessionKey} listingSlug=${slug} consent=${consentCookie ?? "absent"} ts=${(/* @__PURE__ */ new Date()).toISOString()}`);
|
|
23626
23826
|
return c.redirect(redirectUrl, 302);
|
|
23627
23827
|
});
|
|
23628
|
-
var listings_default =
|
|
23828
|
+
var listings_default = app67;
|
|
23629
23829
|
|
|
23630
23830
|
// server/routes/visitor-event.ts
|
|
23631
|
-
var
|
|
23831
|
+
var app68 = new Hono();
|
|
23632
23832
|
var BOT_UA_RE = /\b(bot|crawl|spider|slurp|headlesschrome|phantomjs|googlebot|bingbot|yandex|baiduspider|ahrefsbot|semrushbot|mj12bot|dotbot|petalbot)\b/i;
|
|
23633
23833
|
var buckets = /* @__PURE__ */ new Map();
|
|
23634
23834
|
var RATE_LIMIT = 60;
|
|
@@ -23704,12 +23904,12 @@ function originAllowed(origin, allowlist) {
|
|
|
23704
23904
|
return false;
|
|
23705
23905
|
}
|
|
23706
23906
|
}
|
|
23707
|
-
|
|
23907
|
+
app68.options("/event", (c) => {
|
|
23708
23908
|
const origin = getOrigin2(c);
|
|
23709
23909
|
setCorsHeaders2(c, origin);
|
|
23710
23910
|
return c.body(null, 204);
|
|
23711
23911
|
});
|
|
23712
|
-
|
|
23912
|
+
app68.post("/event", async (c) => {
|
|
23713
23913
|
const origin = getOrigin2(c);
|
|
23714
23914
|
setCorsHeaders2(c, origin);
|
|
23715
23915
|
const ua = c.req.header("user-agent") ?? "";
|
|
@@ -23914,11 +24114,11 @@ async function writeEvent(opts) {
|
|
|
23914
24114
|
);
|
|
23915
24115
|
}
|
|
23916
24116
|
}
|
|
23917
|
-
var visitor_event_default =
|
|
24117
|
+
var visitor_event_default = app68;
|
|
23918
24118
|
|
|
23919
24119
|
// server/routes/session.ts
|
|
23920
24120
|
import { resolve as resolve34 } from "path";
|
|
23921
|
-
import { existsSync as
|
|
24121
|
+
import { existsSync as existsSync43, writeFileSync as writeFileSync15, mkdirSync as mkdirSync9 } from "fs";
|
|
23922
24122
|
var UUID_RE4 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
23923
24123
|
function writeBrandingCache(accountId, agentSlug, branding) {
|
|
23924
24124
|
try {
|
|
@@ -23960,8 +24160,8 @@ function withVisitorCookie(response, visitorId) {
|
|
|
23960
24160
|
headers
|
|
23961
24161
|
});
|
|
23962
24162
|
}
|
|
23963
|
-
var
|
|
23964
|
-
|
|
24163
|
+
var app69 = new Hono();
|
|
24164
|
+
app69.post("/", async (c) => {
|
|
23965
24165
|
let body;
|
|
23966
24166
|
try {
|
|
23967
24167
|
body = await c.req.json();
|
|
@@ -24002,7 +24202,7 @@ app68.post("/", async (c) => {
|
|
|
24002
24202
|
let agentConfig = null;
|
|
24003
24203
|
if (account) {
|
|
24004
24204
|
const agentDir = resolve34(account.accountDir, "agents", agentSlug);
|
|
24005
|
-
if (!
|
|
24205
|
+
if (!existsSync43(agentDir) || !existsSync43(resolve34(agentDir, "config.json"))) {
|
|
24006
24206
|
return c.json({ error: "Agent not found" }, 404);
|
|
24007
24207
|
}
|
|
24008
24208
|
agentConfig = resolveAgentConfig(account.accountDir, agentSlug);
|
|
@@ -24108,7 +24308,7 @@ app68.post("/", async (c) => {
|
|
|
24108
24308
|
newVisitorId
|
|
24109
24309
|
);
|
|
24110
24310
|
});
|
|
24111
|
-
var session_default2 =
|
|
24311
|
+
var session_default2 = app69;
|
|
24112
24312
|
|
|
24113
24313
|
// server/lib/calendar-slots.ts
|
|
24114
24314
|
var WEEKDAYS = ["sun", "mon", "tue", "wed", "thu", "fri", "sat"];
|
|
@@ -24191,17 +24391,17 @@ function computeOpenSlots(config, busy, fromISO, toISO) {
|
|
|
24191
24391
|
}
|
|
24192
24392
|
|
|
24193
24393
|
// server/routes/calendar-public.ts
|
|
24194
|
-
var
|
|
24394
|
+
var app70 = new Hono();
|
|
24195
24395
|
function setCors(c) {
|
|
24196
24396
|
c.header("Access-Control-Allow-Origin", "*");
|
|
24197
24397
|
c.header("Access-Control-Allow-Methods", "GET, OPTIONS");
|
|
24198
24398
|
c.header("Access-Control-Allow-Headers", "content-type");
|
|
24199
24399
|
}
|
|
24200
|
-
|
|
24400
|
+
app70.options("/free-busy", (c) => {
|
|
24201
24401
|
setCors(c);
|
|
24202
24402
|
return c.body(null, 204);
|
|
24203
24403
|
});
|
|
24204
|
-
|
|
24404
|
+
app70.get("/free-busy", async (c) => {
|
|
24205
24405
|
setCors(c);
|
|
24206
24406
|
const from = c.req.query("from");
|
|
24207
24407
|
const to = c.req.query("to");
|
|
@@ -24257,14 +24457,14 @@ app69.get("/free-busy", async (c) => {
|
|
|
24257
24457
|
await session.close();
|
|
24258
24458
|
}
|
|
24259
24459
|
});
|
|
24260
|
-
var calendar_public_default =
|
|
24460
|
+
var calendar_public_default = app70;
|
|
24261
24461
|
|
|
24262
24462
|
// server/routes/portal-fetch.ts
|
|
24263
24463
|
import { createReadStream as createReadStream3 } from "fs";
|
|
24264
24464
|
import { stat as stat8, readFile as readFile11, realpath } from "fs/promises";
|
|
24265
|
-
import { join as
|
|
24465
|
+
import { join as join46, resolve as resolve35, sep as sep11 } from "path";
|
|
24266
24466
|
import { Readable as Readable3 } from "stream";
|
|
24267
|
-
var
|
|
24467
|
+
var app71 = new Hono();
|
|
24268
24468
|
var TAG37 = "[portal-fetch]";
|
|
24269
24469
|
async function verifySignature(secret, accountId, relPath, expiresAtMs, signature, nowMs) {
|
|
24270
24470
|
if (!secret || !signature) return false;
|
|
@@ -24335,7 +24535,7 @@ function isValidAccountId2(value) {
|
|
|
24335
24535
|
}
|
|
24336
24536
|
async function readExposeFolders2(accountDir) {
|
|
24337
24537
|
try {
|
|
24338
|
-
const cfg = JSON.parse(await readFile11(
|
|
24538
|
+
const cfg = JSON.parse(await readFile11(join46(accountDir, "data-portal.json"), "utf8"));
|
|
24339
24539
|
return Array.isArray(cfg?.exposeFolders) ? cfg.exposeFolders.filter((f) => typeof f === "string") : [];
|
|
24340
24540
|
} catch {
|
|
24341
24541
|
return [];
|
|
@@ -24344,7 +24544,7 @@ async function readExposeFolders2(accountDir) {
|
|
|
24344
24544
|
async function resolveFetchTarget(accountDir, relPath, ownerId) {
|
|
24345
24545
|
let schemaText = null;
|
|
24346
24546
|
try {
|
|
24347
|
-
schemaText = await readFile11(
|
|
24547
|
+
schemaText = await readFile11(join46(accountDir, "SCHEMA.md"), "utf8");
|
|
24348
24548
|
} catch {
|
|
24349
24549
|
schemaText = null;
|
|
24350
24550
|
}
|
|
@@ -24427,9 +24627,9 @@ async function handle(c, headOnly) {
|
|
|
24427
24627
|
}
|
|
24428
24628
|
});
|
|
24429
24629
|
}
|
|
24430
|
-
|
|
24431
|
-
|
|
24432
|
-
var portal_fetch_default =
|
|
24630
|
+
app71.get("/fetch", (c) => handle(c, false));
|
|
24631
|
+
app71.on("HEAD", "/fetch", (c) => handle(c, true));
|
|
24632
|
+
var portal_fetch_default = app71;
|
|
24433
24633
|
|
|
24434
24634
|
// app/lib/timeentry-census.ts
|
|
24435
24635
|
var TAG38 = "[timeentry-census]";
|
|
@@ -24514,8 +24714,8 @@ function startTimeEntryCensus(openSession, opts = {}) {
|
|
|
24514
24714
|
}
|
|
24515
24715
|
|
|
24516
24716
|
// app/lib/ledger-census.ts
|
|
24517
|
-
import { readdirSync as readdirSync27, readFileSync as
|
|
24518
|
-
import { join as
|
|
24717
|
+
import { readdirSync as readdirSync27, readFileSync as readFileSync41, statSync as statSync22 } from "fs";
|
|
24718
|
+
import { join as join47 } from "path";
|
|
24519
24719
|
|
|
24520
24720
|
// ../lib/ledger-core/dist/reconcile.js
|
|
24521
24721
|
var LEDGER_INDEX_NAMES = [
|
|
@@ -24569,10 +24769,10 @@ function countWriteRejects24h(logDir, now) {
|
|
|
24569
24769
|
}
|
|
24570
24770
|
let total = 0;
|
|
24571
24771
|
for (const name of entries2) {
|
|
24572
|
-
const path =
|
|
24772
|
+
const path = join47(logDir, name);
|
|
24573
24773
|
try {
|
|
24574
24774
|
if (now - statSync22(path).mtimeMs > DAY_MS) continue;
|
|
24575
|
-
for (const line of
|
|
24775
|
+
for (const line of readFileSync41(path, "utf8").split("\n")) {
|
|
24576
24776
|
if (line.includes("[graph-write] reject")) total += 1;
|
|
24577
24777
|
}
|
|
24578
24778
|
} catch {
|
|
@@ -25067,8 +25267,8 @@ async function migrateUploads(opts = {}) {
|
|
|
25067
25267
|
|
|
25068
25268
|
// app/lib/migrate-admin-webchat-sidecars.ts
|
|
25069
25269
|
import { readdir as readdir7, readFile as readFile12, rename as rename5, writeFile as writeFile4, unlink as unlink4 } from "fs/promises";
|
|
25070
|
-
import { existsSync as
|
|
25071
|
-
import { join as
|
|
25270
|
+
import { existsSync as existsSync44 } from "fs";
|
|
25271
|
+
import { join as join48 } from "path";
|
|
25072
25272
|
var ADMIN_ROLE2 = "admin";
|
|
25073
25273
|
var WEBCHAT_CHANNEL2 = "webchat";
|
|
25074
25274
|
var SESSION_META_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\.meta\.json$/i;
|
|
@@ -25097,7 +25297,7 @@ async function writeRaw(path, obj) {
|
|
|
25097
25297
|
await rename5(tmp, path);
|
|
25098
25298
|
} catch (err) {
|
|
25099
25299
|
try {
|
|
25100
|
-
if (
|
|
25300
|
+
if (existsSync44(tmp)) await unlink4(tmp);
|
|
25101
25301
|
} catch {
|
|
25102
25302
|
}
|
|
25103
25303
|
throw err;
|
|
@@ -25113,7 +25313,7 @@ async function collectLiveSidecars(projectsRoot) {
|
|
|
25113
25313
|
}
|
|
25114
25314
|
for (const slug of slugs) {
|
|
25115
25315
|
if (!slug.isDirectory()) continue;
|
|
25116
|
-
const slugDir =
|
|
25316
|
+
const slugDir = join48(projectsRoot, slug.name);
|
|
25117
25317
|
let entries2;
|
|
25118
25318
|
try {
|
|
25119
25319
|
entries2 = await readdir7(slugDir, { withFileTypes: true });
|
|
@@ -25122,9 +25322,9 @@ async function collectLiveSidecars(projectsRoot) {
|
|
|
25122
25322
|
}
|
|
25123
25323
|
for (const entry of entries2) {
|
|
25124
25324
|
if (entry.isFile() && SESSION_META_RE.test(entry.name)) {
|
|
25125
|
-
out.push(
|
|
25325
|
+
out.push(join48(slugDir, entry.name));
|
|
25126
25326
|
} else if (entry.isDirectory() && entry.name === "subagents") {
|
|
25127
|
-
const subDir =
|
|
25327
|
+
const subDir = join48(slugDir, entry.name);
|
|
25128
25328
|
let subs;
|
|
25129
25329
|
try {
|
|
25130
25330
|
subs = await readdir7(subDir, { withFileTypes: true });
|
|
@@ -25132,7 +25332,7 @@ async function collectLiveSidecars(projectsRoot) {
|
|
|
25132
25332
|
continue;
|
|
25133
25333
|
}
|
|
25134
25334
|
for (const s of subs) {
|
|
25135
|
-
if (s.isFile() && SESSION_META_RE.test(s.name)) out.push(
|
|
25335
|
+
if (s.isFile() && SESSION_META_RE.test(s.name)) out.push(join48(subDir, s.name));
|
|
25136
25336
|
}
|
|
25137
25337
|
}
|
|
25138
25338
|
}
|
|
@@ -25144,7 +25344,7 @@ function shortId(path) {
|
|
|
25144
25344
|
return base.slice(0, 8);
|
|
25145
25345
|
}
|
|
25146
25346
|
async function migrateAdminWebchatSidecars(opts = {}) {
|
|
25147
|
-
const projectsRoot = opts.projectsRoot ?? (process.env.CLAUDE_CONFIG_DIR ?
|
|
25347
|
+
const projectsRoot = opts.projectsRoot ?? (process.env.CLAUDE_CONFIG_DIR ? join48(process.env.CLAUDE_CONFIG_DIR, "projects") : null) ?? "";
|
|
25148
25348
|
const usersFile = opts.usersFile ?? USERS_FILE;
|
|
25149
25349
|
const accountId = opts.accountId ?? resolveAccount()?.accountId ?? null;
|
|
25150
25350
|
const resolveName = opts.resolveName ?? defaultResolveName;
|
|
@@ -25220,8 +25420,8 @@ async function migrateAdminWebchatSidecars(opts = {}) {
|
|
|
25220
25420
|
|
|
25221
25421
|
// app/lib/migrate-sidecar-account-ids.ts
|
|
25222
25422
|
import { readdir as readdir8, readFile as readFile13, rename as rename6, writeFile as writeFile5, unlink as unlink5 } from "fs/promises";
|
|
25223
|
-
import { existsSync as
|
|
25224
|
-
import { join as
|
|
25423
|
+
import { existsSync as existsSync45 } from "fs";
|
|
25424
|
+
import { join as join49 } from "path";
|
|
25225
25425
|
var SESSION_META_RE2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\.meta\.json$/i;
|
|
25226
25426
|
function projectSlugForCwd(cwd) {
|
|
25227
25427
|
return cwd.replace(/\//g, "-");
|
|
@@ -25247,7 +25447,7 @@ async function writeRaw2(path, obj) {
|
|
|
25247
25447
|
await rename6(tmp, path);
|
|
25248
25448
|
} catch (err) {
|
|
25249
25449
|
try {
|
|
25250
|
-
if (
|
|
25450
|
+
if (existsSync45(tmp)) await unlink5(tmp);
|
|
25251
25451
|
} catch {
|
|
25252
25452
|
}
|
|
25253
25453
|
throw err;
|
|
@@ -25267,7 +25467,7 @@ async function collectSidecars(projectsRoot) {
|
|
|
25267
25467
|
}
|
|
25268
25468
|
for (const slug of slugs) {
|
|
25269
25469
|
if (!slug.isDirectory()) continue;
|
|
25270
|
-
const slugDir =
|
|
25470
|
+
const slugDir = join49(projectsRoot, slug.name);
|
|
25271
25471
|
let entries2;
|
|
25272
25472
|
try {
|
|
25273
25473
|
entries2 = await readdir8(slugDir, { withFileTypes: true });
|
|
@@ -25276,9 +25476,9 @@ async function collectSidecars(projectsRoot) {
|
|
|
25276
25476
|
}
|
|
25277
25477
|
for (const entry of entries2) {
|
|
25278
25478
|
if (entry.isFile() && SESSION_META_RE2.test(entry.name)) {
|
|
25279
|
-
out.push({ path:
|
|
25479
|
+
out.push({ path: join49(slugDir, entry.name), slug: slug.name });
|
|
25280
25480
|
} else if (entry.isDirectory() && (entry.name === "subagents" || entry.name === "archive")) {
|
|
25281
|
-
const subDir =
|
|
25481
|
+
const subDir = join49(slugDir, entry.name);
|
|
25282
25482
|
let subs;
|
|
25283
25483
|
try {
|
|
25284
25484
|
subs = await readdir8(subDir, { withFileTypes: true });
|
|
@@ -25287,7 +25487,7 @@ async function collectSidecars(projectsRoot) {
|
|
|
25287
25487
|
}
|
|
25288
25488
|
for (const s of subs) {
|
|
25289
25489
|
if (s.isFile() && SESSION_META_RE2.test(s.name)) {
|
|
25290
|
-
out.push({ path:
|
|
25490
|
+
out.push({ path: join49(subDir, s.name), slug: slug.name });
|
|
25291
25491
|
}
|
|
25292
25492
|
}
|
|
25293
25493
|
}
|
|
@@ -25296,7 +25496,7 @@ async function collectSidecars(projectsRoot) {
|
|
|
25296
25496
|
return out;
|
|
25297
25497
|
}
|
|
25298
25498
|
async function migrateSidecarAccountIds(opts = {}) {
|
|
25299
|
-
const projectsRoot = opts.projectsRoot ?? (process.env.CLAUDE_CONFIG_DIR ?
|
|
25499
|
+
const projectsRoot = opts.projectsRoot ?? (process.env.CLAUDE_CONFIG_DIR ? join49(process.env.CLAUDE_CONFIG_DIR, "projects") : null) ?? "";
|
|
25300
25500
|
const accounts = opts.accounts ?? listValidAccounts();
|
|
25301
25501
|
const result = {
|
|
25302
25502
|
scanned: 0,
|
|
@@ -25609,8 +25809,8 @@ var streamSSE = (c, cb, onError) => {
|
|
|
25609
25809
|
|
|
25610
25810
|
// app/lib/whatsapp/gateway/routes.ts
|
|
25611
25811
|
function createWaChannelRoutes(deps) {
|
|
25612
|
-
const
|
|
25613
|
-
|
|
25812
|
+
const app73 = new Hono();
|
|
25813
|
+
app73.get("/wa-channel/inbound", (c) => {
|
|
25614
25814
|
const senderId = c.req.query("senderId");
|
|
25615
25815
|
if (!senderId) return c.json({ error: "senderId required" }, 400);
|
|
25616
25816
|
return streamSSE(c, async (stream2) => {
|
|
@@ -25628,7 +25828,7 @@ function createWaChannelRoutes(deps) {
|
|
|
25628
25828
|
}
|
|
25629
25829
|
});
|
|
25630
25830
|
});
|
|
25631
|
-
|
|
25831
|
+
app73.post("/wa-channel/reply", async (c) => {
|
|
25632
25832
|
const body = await c.req.json().catch(() => null);
|
|
25633
25833
|
const senderId = body?.senderId;
|
|
25634
25834
|
const text = body?.text;
|
|
@@ -25647,9 +25847,10 @@ function createWaChannelRoutes(deps) {
|
|
|
25647
25847
|
return c.json({ error: message, terminal }, 500);
|
|
25648
25848
|
}
|
|
25649
25849
|
console.error(`[whatsapp-native] op=reply-dispatch senderId=${senderId} bytes=${bytes}`);
|
|
25850
|
+
deps.onReplyDispatch?.(senderId, Date.now());
|
|
25650
25851
|
return c.json({ ok: true });
|
|
25651
25852
|
});
|
|
25652
|
-
|
|
25853
|
+
app73.post("/wa-channel/reply-document", async (c) => {
|
|
25653
25854
|
const body = await c.req.json().catch(() => null);
|
|
25654
25855
|
const senderId = body?.senderId;
|
|
25655
25856
|
const files = body?.files;
|
|
@@ -25688,7 +25889,7 @@ function createWaChannelRoutes(deps) {
|
|
|
25688
25889
|
}
|
|
25689
25890
|
return c.json({ ok: true, results });
|
|
25690
25891
|
});
|
|
25691
|
-
|
|
25892
|
+
app73.post("/wa-channel/ready", async (c) => {
|
|
25692
25893
|
const body = await c.req.json().catch(() => null);
|
|
25693
25894
|
const senderId = body?.senderId;
|
|
25694
25895
|
if (typeof senderId !== "string") {
|
|
@@ -25697,7 +25898,7 @@ function createWaChannelRoutes(deps) {
|
|
|
25697
25898
|
deps.onReady?.(senderId);
|
|
25698
25899
|
return c.json({ ok: true });
|
|
25699
25900
|
});
|
|
25700
|
-
|
|
25901
|
+
app73.post("/wa-channel/received", async (c) => {
|
|
25701
25902
|
const body = await c.req.json().catch(() => null);
|
|
25702
25903
|
const senderId = body?.senderId;
|
|
25703
25904
|
const waMessageId = body?.waMessageId;
|
|
@@ -25707,7 +25908,7 @@ function createWaChannelRoutes(deps) {
|
|
|
25707
25908
|
deps.onReceived?.(senderId, waMessageId);
|
|
25708
25909
|
return c.json({ ok: true });
|
|
25709
25910
|
});
|
|
25710
|
-
|
|
25911
|
+
app73.post("/wa-channel/turn-end", async (c) => {
|
|
25711
25912
|
const body = await c.req.json().catch(() => null);
|
|
25712
25913
|
const senderId = body?.senderId;
|
|
25713
25914
|
const sessionId = body?.sessionId;
|
|
@@ -25721,9 +25922,10 @@ function createWaChannelRoutes(deps) {
|
|
|
25721
25922
|
const finalBytes = num(body?.finalBytes);
|
|
25722
25923
|
const queueDepth = num(body?.queueDepth);
|
|
25723
25924
|
const pendingTurns = num(body?.pendingTurns);
|
|
25925
|
+
const consumed2 = num(body?.consumed);
|
|
25724
25926
|
const sid = sessionId.slice(0, 8);
|
|
25725
25927
|
const deliver = interactive && !replied && finalText.trim().length > 0;
|
|
25726
|
-
const decision = `sessionId=${sid} replied=${replied ? "yes" : "no"} interactive=${interactive ? "yes" : "no"} finalBytes=${finalBytes} queueDepth=${queueDepth} pendingTurns=${pendingTurns}`;
|
|
25928
|
+
const decision = `sessionId=${sid} replied=${replied ? "yes" : "no"} interactive=${interactive ? "yes" : "no"} finalBytes=${finalBytes} queueDepth=${queueDepth} pendingTurns=${pendingTurns} consumed=${consumed2}`;
|
|
25727
25929
|
if (!replied && finalBytes > 0 && !deliver) {
|
|
25728
25930
|
const op = interactive ? "turn-answer-dropped" : "turn-answer-withheld";
|
|
25729
25931
|
console.error(
|
|
@@ -25742,6 +25944,7 @@ function createWaChannelRoutes(deps) {
|
|
|
25742
25944
|
return c.json({ ok: true, delivered: "undelivered" });
|
|
25743
25945
|
}
|
|
25744
25946
|
console.error(`[whatsapp-native] op=turn-end ${decision} delivered=fallback bytes=${bytes}`);
|
|
25947
|
+
deps.onReplyDispatch?.(senderId, Date.now());
|
|
25745
25948
|
return c.json({ ok: true, delivered: "fallback" });
|
|
25746
25949
|
}
|
|
25747
25950
|
if (replied) {
|
|
@@ -25751,13 +25954,14 @@ function createWaChannelRoutes(deps) {
|
|
|
25751
25954
|
console.error(`[whatsapp-native] op=turn-end ${decision} delivered=none`);
|
|
25752
25955
|
return c.json({ ok: true, delivered: "none" });
|
|
25753
25956
|
});
|
|
25754
|
-
return
|
|
25957
|
+
return app73;
|
|
25755
25958
|
}
|
|
25756
25959
|
|
|
25757
25960
|
// app/lib/whatsapp/gateway/wa-gateway.ts
|
|
25758
25961
|
var QUEUED_THRESHOLD_MS = 3e4;
|
|
25759
25962
|
var ACK_THRESHOLD_MS = 3e4;
|
|
25760
25963
|
var SWEEP_INTERVAL_MS = 15e3;
|
|
25964
|
+
var MEDIA_STANZA_STALE_MS = 12e4;
|
|
25761
25965
|
var WaGateway = class {
|
|
25762
25966
|
constructor(deps) {
|
|
25763
25967
|
this.deps = deps;
|
|
@@ -25772,6 +25976,66 @@ var WaGateway = class {
|
|
|
25772
25976
|
docContexts = /* @__PURE__ */ new Map();
|
|
25773
25977
|
spawning = /* @__PURE__ */ new Set();
|
|
25774
25978
|
seq = 0;
|
|
25979
|
+
// Task 2110 — senderId -> the arrival ms of every still-unemitted non-audio
|
|
25980
|
+
// media stanza for that sender, oldest first. This is the race window: a reply
|
|
25981
|
+
// dispatched while the list is non-empty was composed by a turn that could not
|
|
25982
|
+
// yet see an incoming file, and the window is measured from the OLDEST entry.
|
|
25983
|
+
//
|
|
25984
|
+
// One entry per stanza, not one per sender. Downloads finish out of order and
|
|
25985
|
+
// the debouncer flushes whatever has landed, so a small file can be emitted
|
|
25986
|
+
// while a large one is still downloading. Holding a single timestamp meant that
|
|
25987
|
+
// first inbound closed the window and the still-blind replies that followed
|
|
25988
|
+
// were never counted, with the census reading zero throughout.
|
|
25989
|
+
pendingMediaStanza = /* @__PURE__ */ new Map();
|
|
25990
|
+
// Counters for the per-sweep census, reset on every print.
|
|
25991
|
+
mediaRaces = 0;
|
|
25992
|
+
mediaRaceSenders = /* @__PURE__ */ new Set();
|
|
25993
|
+
mediaStanzaExpired = 0;
|
|
25994
|
+
/** Task 2110 — the WhatsApp manager reports that a non-audio media stanza for
|
|
25995
|
+
* this sender has started downloading. The gateway cannot derive this itself:
|
|
25996
|
+
* it first learns of the media when the completed inbound arrives, which is
|
|
25997
|
+
* the CLOSE of the window, not its open. Audio is excluded by the caller —
|
|
25998
|
+
* its transcript rides in as `text`, so no turn is ever blind to a voice note.
|
|
25999
|
+
* The key is canonicalised on write, the same rule handleInbound applies, so
|
|
26000
|
+
* a "+"-prefixed value and a digits-only one address one window. */
|
|
26001
|
+
noteMediaStanza(senderId, atMs) {
|
|
26002
|
+
const key = normalizeTarget(senderId) ?? senderId;
|
|
26003
|
+
const inFlight2 = this.pendingMediaStanza.get(key);
|
|
26004
|
+
if (inFlight2) inFlight2.push(atMs);
|
|
26005
|
+
else this.pendingMediaStanza.set(key, [atMs]);
|
|
26006
|
+
}
|
|
26007
|
+
/** Task 2110 — drop every entry past the staleness cap, oldest first, counting
|
|
26008
|
+
* each into the census. Entries are appended in arrival order, so the scan
|
|
26009
|
+
* stops at the first one still inside the cap. A download that never produced
|
|
26010
|
+
* an inbound leaves its entry here; without this it would hold the window open
|
|
26011
|
+
* forever and make every later reply to that sender read as a race. */
|
|
26012
|
+
expireStaleStanzas(key, inFlight2, now) {
|
|
26013
|
+
while (inFlight2.length > 0 && now - inFlight2[0] > MEDIA_STANZA_STALE_MS) {
|
|
26014
|
+
inFlight2.shift();
|
|
26015
|
+
this.mediaStanzaExpired++;
|
|
26016
|
+
}
|
|
26017
|
+
if (inFlight2.length === 0) this.pendingMediaStanza.delete(key);
|
|
26018
|
+
}
|
|
26019
|
+
/** Task 2110 — a reply was dispatched to this sender. When a media stanza for
|
|
26020
|
+
* the same sender is in flight, the turn that composed this reply could not
|
|
26021
|
+
* see the incoming file, so the exposure is recorded whether or not the agent
|
|
26022
|
+
* actually misspoke: a steering clause has no machine-checkable
|
|
26023
|
+
* post-condition, and this counts entry into the window, never the phrasing.
|
|
26024
|
+
* A window past the staleness cap is a download that never produced an
|
|
26025
|
+
* inbound; it is dropped and counted as expired, not reported as a race. */
|
|
26026
|
+
noteReplyDispatch(senderId, atMs) {
|
|
26027
|
+
const key = normalizeTarget(senderId) ?? senderId;
|
|
26028
|
+
const inFlight2 = this.pendingMediaStanza.get(key);
|
|
26029
|
+
if (inFlight2 === void 0) return;
|
|
26030
|
+
this.expireStaleStanzas(key, inFlight2, atMs);
|
|
26031
|
+
if (inFlight2.length === 0) return;
|
|
26032
|
+
const stanzaAt = inFlight2[0];
|
|
26033
|
+
this.mediaRaces++;
|
|
26034
|
+
this.mediaRaceSenders.add(key);
|
|
26035
|
+
console.error(
|
|
26036
|
+
`[whatsapp-native] op=media-race senderId=${key} replyAt=${new Date(atMs).toISOString()} mediaStanzaAt=${new Date(stanzaAt).toISOString()} gapMs=${atMs - stanzaAt}`
|
|
26037
|
+
);
|
|
26038
|
+
}
|
|
25775
26039
|
/** Hono sub-app exposing the loopback channel routes; mount on the UI app.
|
|
25776
26040
|
* Task 1836 — the process-local unseated-sender census that used to live here
|
|
25777
26041
|
* was retired. Its quiet state was ambiguous (a UI restart emptied it, so
|
|
@@ -25793,7 +26057,8 @@ var WaGateway = class {
|
|
|
25793
26057
|
console.error(
|
|
25794
26058
|
`[whatsapp-native] op=channel-received senderId=${senderId} waMessageId=${waMessageId}${found ? "" : " note=unknown"}`
|
|
25795
26059
|
);
|
|
25796
|
-
}
|
|
26060
|
+
},
|
|
26061
|
+
onReplyDispatch: (senderId, atMs) => this.noteReplyDispatch(senderId, atMs)
|
|
25797
26062
|
});
|
|
25798
26063
|
}
|
|
25799
26064
|
/** Run one reconciliation pass: log every terminal delivery event the sweep
|
|
@@ -25813,6 +26078,15 @@ var WaGateway = class {
|
|
|
25813
26078
|
`[whatsapp-native] op=${e.kind} senderId=${e.senderId} waMessageId=${e.waMessageId} ageMs=${e.ageMs}`
|
|
25814
26079
|
);
|
|
25815
26080
|
}
|
|
26081
|
+
for (const [key, inFlight2] of [...this.pendingMediaStanza]) {
|
|
26082
|
+
this.expireStaleStanzas(key, inFlight2, now);
|
|
26083
|
+
}
|
|
26084
|
+
console.error(
|
|
26085
|
+
`[whatsapp-native] op=media-race-census races=${this.mediaRaces} senders=${this.mediaRaceSenders.size} expired=${this.mediaStanzaExpired}`
|
|
26086
|
+
);
|
|
26087
|
+
this.mediaRaces = 0;
|
|
26088
|
+
this.mediaRaceSenders.clear();
|
|
26089
|
+
this.mediaStanzaExpired = 0;
|
|
25816
26090
|
return events;
|
|
25817
26091
|
}
|
|
25818
26092
|
/** Arm the periodic reconciliation sweep in the long-lived UI process.
|
|
@@ -25844,6 +26118,14 @@ var WaGateway = class {
|
|
|
25844
26118
|
if (senderId !== input.senderId) {
|
|
25845
26119
|
console.error(`[whatsapp-native] op=sender-key raw=${input.senderId} canonical=${senderId}`);
|
|
25846
26120
|
}
|
|
26121
|
+
const arrived = input.media.filter((m) => m.type !== "audio").length;
|
|
26122
|
+
if (arrived > 0) {
|
|
26123
|
+
const inFlight2 = this.pendingMediaStanza.get(senderId);
|
|
26124
|
+
if (inFlight2) {
|
|
26125
|
+
inFlight2.splice(0, arrived);
|
|
26126
|
+
if (inFlight2.length === 0) this.pendingMediaStanza.delete(senderId);
|
|
26127
|
+
}
|
|
26128
|
+
}
|
|
25847
26129
|
if (input.passive) {
|
|
25848
26130
|
const msgId = `wa-${++this.seq}`;
|
|
25849
26131
|
await this.deps.ensureChannelSession({
|
|
@@ -26181,8 +26463,8 @@ var InboundHub2 = class {
|
|
|
26181
26463
|
|
|
26182
26464
|
// app/lib/webchat/gateway/routes.ts
|
|
26183
26465
|
function createWebchatChannelRoutes(deps) {
|
|
26184
|
-
const
|
|
26185
|
-
|
|
26466
|
+
const app73 = new Hono();
|
|
26467
|
+
app73.get("/webchat-channel/inbound", (c) => {
|
|
26186
26468
|
const key = c.req.query("key");
|
|
26187
26469
|
if (!key) return c.json({ error: "key required" }, 400);
|
|
26188
26470
|
const role = c.req.query("role");
|
|
@@ -26207,7 +26489,7 @@ function createWebchatChannelRoutes(deps) {
|
|
|
26207
26489
|
}
|
|
26208
26490
|
});
|
|
26209
26491
|
});
|
|
26210
|
-
|
|
26492
|
+
app73.post("/webchat-channel/reply", async (c) => {
|
|
26211
26493
|
const body = await c.req.json().catch(() => null);
|
|
26212
26494
|
const key = body?.key;
|
|
26213
26495
|
const text = body?.text;
|
|
@@ -26217,7 +26499,7 @@ function createWebchatChannelRoutes(deps) {
|
|
|
26217
26499
|
deps.onReply?.(key, text);
|
|
26218
26500
|
return c.json({ ok: true });
|
|
26219
26501
|
});
|
|
26220
|
-
|
|
26502
|
+
app73.post("/webchat-channel/connector-auth", async (c) => {
|
|
26221
26503
|
const body = await c.req.json().catch(() => null);
|
|
26222
26504
|
const key = body?.key;
|
|
26223
26505
|
const sessionId = body?.sessionId;
|
|
@@ -26230,7 +26512,7 @@ function createWebchatChannelRoutes(deps) {
|
|
|
26230
26512
|
const result = await deps.onConnectorAuth({ key, sessionId, name, completed });
|
|
26231
26513
|
return c.json(result);
|
|
26232
26514
|
});
|
|
26233
|
-
|
|
26515
|
+
app73.post("/webchat-channel/ready", async (c) => {
|
|
26234
26516
|
const body = await c.req.json().catch(() => null);
|
|
26235
26517
|
const key = body?.key;
|
|
26236
26518
|
if (typeof key !== "string") {
|
|
@@ -26239,7 +26521,7 @@ function createWebchatChannelRoutes(deps) {
|
|
|
26239
26521
|
deps.onReady?.(key);
|
|
26240
26522
|
return c.json({ ok: true });
|
|
26241
26523
|
});
|
|
26242
|
-
|
|
26524
|
+
app73.post("/webchat-channel/permission-request", async (c) => {
|
|
26243
26525
|
const body = await c.req.json().catch(() => null);
|
|
26244
26526
|
const key = body?.key;
|
|
26245
26527
|
const requestId = body?.request_id;
|
|
@@ -26253,7 +26535,7 @@ function createWebchatChannelRoutes(deps) {
|
|
|
26253
26535
|
const verdict = await deps.awaitPermissionVerdict({ key, requestId, toolName, description, inputPreview });
|
|
26254
26536
|
return c.json(verdict);
|
|
26255
26537
|
});
|
|
26256
|
-
|
|
26538
|
+
app73.post("/webchat-channel/received", async (c) => {
|
|
26257
26539
|
const body = await c.req.json().catch(() => null);
|
|
26258
26540
|
const key = body?.key;
|
|
26259
26541
|
const messageId = body?.messageId;
|
|
@@ -26263,7 +26545,7 @@ function createWebchatChannelRoutes(deps) {
|
|
|
26263
26545
|
deps.onReceived?.(key, messageId);
|
|
26264
26546
|
return c.json({ ok: true });
|
|
26265
26547
|
});
|
|
26266
|
-
|
|
26548
|
+
app73.post("/webchat-channel/turn-end", async (c) => {
|
|
26267
26549
|
const body = await c.req.json().catch(() => null);
|
|
26268
26550
|
const key = body?.key;
|
|
26269
26551
|
const sessionId = body?.sessionId;
|
|
@@ -26283,7 +26565,7 @@ function createWebchatChannelRoutes(deps) {
|
|
|
26283
26565
|
deps.onTurnEnd?.(key, role);
|
|
26284
26566
|
return c.json({ ok: true });
|
|
26285
26567
|
});
|
|
26286
|
-
return
|
|
26568
|
+
return app73;
|
|
26287
26569
|
}
|
|
26288
26570
|
|
|
26289
26571
|
// app/lib/webchat/gateway/webchat-gateway.ts
|
|
@@ -27379,8 +27661,8 @@ var InboundHub3 = class {
|
|
|
27379
27661
|
|
|
27380
27662
|
// app/lib/telegram/gateway/routes.ts
|
|
27381
27663
|
function createTelegramChannelRoutes(deps) {
|
|
27382
|
-
const
|
|
27383
|
-
|
|
27664
|
+
const app73 = new Hono();
|
|
27665
|
+
app73.get("/tg-channel/inbound", (c) => {
|
|
27384
27666
|
const key = c.req.query("key");
|
|
27385
27667
|
if (!key) return c.json({ error: "key required" }, 400);
|
|
27386
27668
|
return streamSSE(c, async (stream2) => {
|
|
@@ -27398,7 +27680,7 @@ function createTelegramChannelRoutes(deps) {
|
|
|
27398
27680
|
}
|
|
27399
27681
|
});
|
|
27400
27682
|
});
|
|
27401
|
-
|
|
27683
|
+
app73.post("/tg-channel/reply", async (c) => {
|
|
27402
27684
|
const body = await c.req.json().catch(() => null);
|
|
27403
27685
|
const key = body?.key;
|
|
27404
27686
|
const text = body?.text;
|
|
@@ -27414,7 +27696,7 @@ function createTelegramChannelRoutes(deps) {
|
|
|
27414
27696
|
console.error(`[telegram-native] op=reply key=${key} bytes=${Buffer.byteLength(text, "utf8")}`);
|
|
27415
27697
|
return c.json({ ok: true });
|
|
27416
27698
|
});
|
|
27417
|
-
|
|
27699
|
+
app73.post("/tg-channel/ready", async (c) => {
|
|
27418
27700
|
const body = await c.req.json().catch(() => null);
|
|
27419
27701
|
const key = body?.key;
|
|
27420
27702
|
if (typeof key !== "string") {
|
|
@@ -27423,7 +27705,7 @@ function createTelegramChannelRoutes(deps) {
|
|
|
27423
27705
|
deps.onReady?.(key);
|
|
27424
27706
|
return c.json({ ok: true });
|
|
27425
27707
|
});
|
|
27426
|
-
|
|
27708
|
+
app73.post("/tg-channel/received", async (c) => {
|
|
27427
27709
|
const body = await c.req.json().catch(() => null);
|
|
27428
27710
|
const key = body?.key;
|
|
27429
27711
|
const messageId = body?.messageId;
|
|
@@ -27433,7 +27715,7 @@ function createTelegramChannelRoutes(deps) {
|
|
|
27433
27715
|
deps.onReceived?.(key, messageId);
|
|
27434
27716
|
return c.json({ ok: true });
|
|
27435
27717
|
});
|
|
27436
|
-
|
|
27718
|
+
app73.post("/tg-channel/turn-end", async (c) => {
|
|
27437
27719
|
const body = await c.req.json().catch(() => null);
|
|
27438
27720
|
const key = body?.key;
|
|
27439
27721
|
const sessionId = body?.sessionId;
|
|
@@ -27466,7 +27748,7 @@ function createTelegramChannelRoutes(deps) {
|
|
|
27466
27748
|
console.error(`[telegram-native] op=turn-end key=${key} sessionId=${sid} replied=no delivered=none`);
|
|
27467
27749
|
return c.json({ ok: true, delivered: "none" });
|
|
27468
27750
|
});
|
|
27469
|
-
return
|
|
27751
|
+
return app73;
|
|
27470
27752
|
}
|
|
27471
27753
|
|
|
27472
27754
|
// app/lib/telegram/gateway/telegram-gateway.ts
|
|
@@ -28035,14 +28317,14 @@ function resolveScheduledEffectiveAccountForInstall(accountDir, destination) {
|
|
|
28035
28317
|
}
|
|
28036
28318
|
|
|
28037
28319
|
// server/lib/booking-site-accounts.ts
|
|
28038
|
-
import { existsSync as
|
|
28320
|
+
import { existsSync as existsSync46, readFileSync as readFileSync42 } from "fs";
|
|
28039
28321
|
import { resolve as resolve40 } from "path";
|
|
28040
28322
|
function listBookingSiteAccounts(accounts) {
|
|
28041
28323
|
return accounts.filter((a) => {
|
|
28042
28324
|
const availPath = resolve40(a.accountDir, "calendar-availability.json");
|
|
28043
|
-
if (!
|
|
28325
|
+
if (!existsSync46(availPath)) return false;
|
|
28044
28326
|
try {
|
|
28045
|
-
const cfg = JSON.parse(
|
|
28327
|
+
const cfg = JSON.parse(readFileSync42(availPath, "utf-8"));
|
|
28046
28328
|
return typeof cfg.bookingDbName === "string" && cfg.bookingDbName.length > 0;
|
|
28047
28329
|
} catch {
|
|
28048
28330
|
return false;
|
|
@@ -28080,7 +28362,7 @@ function broadcastAdminShutdown(reason) {
|
|
|
28080
28362
|
|
|
28081
28363
|
// ../lib/entitlement/src/index.ts
|
|
28082
28364
|
import { createPublicKey, createHash as createHash6, verify as cryptoVerify } from "crypto";
|
|
28083
|
-
import { existsSync as
|
|
28365
|
+
import { existsSync as existsSync47, readFileSync as readFileSync43, statSync as statSync23 } from "fs";
|
|
28084
28366
|
import { resolve as resolve41 } from "path";
|
|
28085
28367
|
|
|
28086
28368
|
// ../lib/entitlement/src/canonicalize.ts
|
|
@@ -28129,7 +28411,7 @@ function resolveEntitlement(brand, account) {
|
|
|
28129
28411
|
return logResolved(implicitTrust(account), null);
|
|
28130
28412
|
}
|
|
28131
28413
|
const entitlementPath = resolve41(brand.configDir, "entitlement.json");
|
|
28132
|
-
if (!
|
|
28414
|
+
if (!existsSync47(entitlementPath)) {
|
|
28133
28415
|
return logResolved(anonymousFallback("missing"), { reason: "missing" });
|
|
28134
28416
|
}
|
|
28135
28417
|
const stat11 = statSync23(entitlementPath);
|
|
@@ -28144,7 +28426,7 @@ function resolveEntitlement(brand, account) {
|
|
|
28144
28426
|
function verifyAndResolve(brand, entitlementPath, account) {
|
|
28145
28427
|
let pubkeyPem;
|
|
28146
28428
|
try {
|
|
28147
|
-
pubkeyPem =
|
|
28429
|
+
pubkeyPem = readFileSync43(pubkeyPath(brand), "utf-8");
|
|
28148
28430
|
} catch (err) {
|
|
28149
28431
|
return logResolved(anonymousFallback("pubkey-missing"), {
|
|
28150
28432
|
reason: "pubkey-missing"
|
|
@@ -28158,7 +28440,7 @@ function verifyAndResolve(brand, entitlementPath, account) {
|
|
|
28158
28440
|
}
|
|
28159
28441
|
let envelope;
|
|
28160
28442
|
try {
|
|
28161
|
-
envelope = JSON.parse(
|
|
28443
|
+
envelope = JSON.parse(readFileSync43(entitlementPath, "utf-8"));
|
|
28162
28444
|
} catch {
|
|
28163
28445
|
return logResolved(anonymousFallback("malformed"), { reason: "malformed" });
|
|
28164
28446
|
}
|
|
@@ -28320,14 +28602,14 @@ function clientFrom(c) {
|
|
|
28320
28602
|
}
|
|
28321
28603
|
installMediaDownloadGuard();
|
|
28322
28604
|
var PLATFORM_ROOT6 = process.env.MAXY_PLATFORM_ROOT || "";
|
|
28323
|
-
var BRAND_JSON_PATH = PLATFORM_ROOT6 ?
|
|
28605
|
+
var BRAND_JSON_PATH = PLATFORM_ROOT6 ? join50(PLATFORM_ROOT6, "config", "brand.json") : "";
|
|
28324
28606
|
var BRAND = { productName: "Maxy", hostname: "maxy", configDir: ".maxy", marketingUrl: "https://getmaxy.com" };
|
|
28325
|
-
if (BRAND_JSON_PATH && !
|
|
28607
|
+
if (BRAND_JSON_PATH && !existsSync48(BRAND_JSON_PATH)) {
|
|
28326
28608
|
console.error(`[brand] WARNING: brand.json not found at ${BRAND_JSON_PATH} \u2014 using Maxy defaults`);
|
|
28327
28609
|
}
|
|
28328
|
-
if (BRAND_JSON_PATH &&
|
|
28610
|
+
if (BRAND_JSON_PATH && existsSync48(BRAND_JSON_PATH)) {
|
|
28329
28611
|
try {
|
|
28330
|
-
const parsed = JSON.parse(
|
|
28612
|
+
const parsed = JSON.parse(readFileSync44(BRAND_JSON_PATH, "utf-8"));
|
|
28331
28613
|
BRAND = { ...BRAND, ...parsed };
|
|
28332
28614
|
} catch (err) {
|
|
28333
28615
|
console.error(`[brand] Failed to parse brand.json: ${err.message}`);
|
|
@@ -28359,11 +28641,11 @@ var brandLoginOpts = {
|
|
|
28359
28641
|
appleTouchIconPath: brandAppIcon512Path,
|
|
28360
28642
|
themeColor: BRAND.defaultColors?.primary
|
|
28361
28643
|
};
|
|
28362
|
-
var ALIAS_DOMAINS_PATH =
|
|
28644
|
+
var ALIAS_DOMAINS_PATH = join50(homedir4(), BRAND.configDir, "alias-domains.json");
|
|
28363
28645
|
function loadAliasDomains() {
|
|
28364
28646
|
try {
|
|
28365
|
-
if (!
|
|
28366
|
-
const parsed = JSON.parse(
|
|
28647
|
+
if (!existsSync48(ALIAS_DOMAINS_PATH)) return null;
|
|
28648
|
+
const parsed = JSON.parse(readFileSync44(ALIAS_DOMAINS_PATH, "utf-8"));
|
|
28367
28649
|
if (!Array.isArray(parsed)) {
|
|
28368
28650
|
console.error("[alias-domains] malformed alias-domains.json \u2014 expected array");
|
|
28369
28651
|
return null;
|
|
@@ -28389,7 +28671,7 @@ startManagerEventsSubscriber();
|
|
|
28389
28671
|
function isPublicHost(host) {
|
|
28390
28672
|
return host.startsWith("public.") || aliasDomains.has(host);
|
|
28391
28673
|
}
|
|
28392
|
-
var
|
|
28674
|
+
var app72 = new Hono();
|
|
28393
28675
|
function resolveChannelOverride(accountId, channel, senderId) {
|
|
28394
28676
|
try {
|
|
28395
28677
|
const dir = listValidAccounts().find((a) => a.accountId === accountId)?.accountDir;
|
|
@@ -28438,7 +28720,7 @@ var waGateway = new WaGateway({
|
|
|
28438
28720
|
fetchStandingRules: fetchAccountStandingRules,
|
|
28439
28721
|
resolveSessionOverride: (accountId, senderId) => resolveChannelOverride(accountId, "whatsapp", senderId),
|
|
28440
28722
|
gatewayUrl: `http://127.0.0.1:${process.env.MAXY_UI_INTERNAL_PORT ?? ""}`,
|
|
28441
|
-
serverPath: process.env.MAXY_WA_CHANNEL_SERVER_PATH ?? resolve42(process.env.MAXY_PLATFORM_ROOT ??
|
|
28723
|
+
serverPath: process.env.MAXY_WA_CHANNEL_SERVER_PATH ?? resolve42(process.env.MAXY_PLATFORM_ROOT ?? join50(__dirname, ".."), "services/whatsapp-channel/dist/server.js"),
|
|
28442
28724
|
// Task 751 / 1390 — file delivery on the native channel. `maxyAccountId` (the
|
|
28443
28725
|
// path-validation scope) is the sender's effective SESSION account, resolved
|
|
28444
28726
|
// once at the inbound gate and threaded here via the gateway's per-sender doc
|
|
@@ -28453,7 +28735,7 @@ var waGateway = new WaGateway({
|
|
|
28453
28735
|
caption,
|
|
28454
28736
|
accountId,
|
|
28455
28737
|
maxyAccountId,
|
|
28456
|
-
platformRoot: resolve42(process.env.MAXY_PLATFORM_ROOT ??
|
|
28738
|
+
platformRoot: resolve42(process.env.MAXY_PLATFORM_ROOT ?? join50(__dirname, ".."))
|
|
28457
28739
|
});
|
|
28458
28740
|
return result.ok ? { ok: true, messageId: result.messageId } : { ok: false, error: result.error };
|
|
28459
28741
|
},
|
|
@@ -28566,13 +28848,13 @@ var waGateway = new WaGateway({
|
|
|
28566
28848
|
nativeFileFollowers.set(senderId, ac);
|
|
28567
28849
|
}
|
|
28568
28850
|
});
|
|
28569
|
-
|
|
28851
|
+
app72.route("/", waGateway.routes());
|
|
28570
28852
|
waGateway.startSweeper();
|
|
28571
28853
|
function runDuplicateSenderAudit() {
|
|
28572
28854
|
try {
|
|
28573
28855
|
const configDir = process.env.CLAUDE_CONFIG_DIR;
|
|
28574
28856
|
if (!configDir) return;
|
|
28575
|
-
const projectsRoot =
|
|
28857
|
+
const projectsRoot = join50(configDir, "projects");
|
|
28576
28858
|
const records = [];
|
|
28577
28859
|
for (const { path: jsonlPath, isSubagent, archived } of enumerateJsonls(projectsRoot)) {
|
|
28578
28860
|
if (isSubagent || archived) continue;
|
|
@@ -28696,7 +28978,7 @@ var webchatGateway = new WebchatGateway({
|
|
|
28696
28978
|
firePublicSessionEndReview: (input) => firePublicSessionEndReview(input)
|
|
28697
28979
|
});
|
|
28698
28980
|
setWebchatGateway(webchatGateway);
|
|
28699
|
-
|
|
28981
|
+
app72.route("/", webchatGateway.routes());
|
|
28700
28982
|
webchatGateway.startSweeper();
|
|
28701
28983
|
webchatGateway.startPublicReaper();
|
|
28702
28984
|
var telegramFileFollowers = /* @__PURE__ */ new Map();
|
|
@@ -28740,7 +29022,7 @@ var telegramGateway = new TelegramGateway({
|
|
|
28740
29022
|
}
|
|
28741
29023
|
});
|
|
28742
29024
|
setTelegramGateway(telegramGateway);
|
|
28743
|
-
|
|
29025
|
+
app72.route("/", telegramGateway.routes());
|
|
28744
29026
|
telegramGateway.startSweeper();
|
|
28745
29027
|
var chatRoutes = createChatRoutes({
|
|
28746
29028
|
handleInbound: (input) => webchatGateway.handleInbound(input),
|
|
@@ -28774,19 +29056,19 @@ var scheduleInjectRoutes = createScheduleInjectRoutes({
|
|
|
28774
29056
|
// and why the real dependencies are bound there rather than assembled here.
|
|
28775
29057
|
effectiveAccountFor: resolveScheduledEffectiveAccountForInstall
|
|
28776
29058
|
});
|
|
28777
|
-
|
|
28778
|
-
|
|
29059
|
+
app72.route("/api/channel/schedule-inject", scheduleInjectRoutes);
|
|
29060
|
+
app72.use("*", clientIpMiddleware);
|
|
28779
29061
|
function allowsSameOriginFraming(path) {
|
|
28780
29062
|
return path === "/vnc-viewer.html" || path.startsWith("/api/admin/attachment/") || path.startsWith("/api/public-reader/attachment/") || path === "/api/admin/files/download" || path === "/api/admin/session-upload";
|
|
28781
29063
|
}
|
|
28782
|
-
|
|
29064
|
+
app72.use("*", async (c, next) => {
|
|
28783
29065
|
await next();
|
|
28784
29066
|
c.header("X-Content-Type-Options", "nosniff");
|
|
28785
29067
|
c.header("Referrer-Policy", "strict-origin-when-cross-origin");
|
|
28786
29068
|
c.header("X-Frame-Options", allowsSameOriginFraming(c.req.path) ? "SAMEORIGIN" : "DENY");
|
|
28787
29069
|
});
|
|
28788
29070
|
var HTTP_LOG_PATHS = /* @__PURE__ */ new Set(["/vnc-viewer.html", "/vnc-popout.html"]);
|
|
28789
|
-
|
|
29071
|
+
app72.use("*", async (c, next) => {
|
|
28790
29072
|
if (!HTTP_LOG_PATHS.has(c.req.path)) {
|
|
28791
29073
|
await next();
|
|
28792
29074
|
return;
|
|
@@ -28804,7 +29086,7 @@ app71.use("*", async (c, next) => {
|
|
|
28804
29086
|
});
|
|
28805
29087
|
}
|
|
28806
29088
|
});
|
|
28807
|
-
|
|
29089
|
+
app72.use("*", async (c, next) => {
|
|
28808
29090
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
28809
29091
|
if (isOperatorHost(host, getOperatorDomains()) || !isPublicHost(host)) {
|
|
28810
29092
|
await next();
|
|
@@ -28842,7 +29124,7 @@ function resolveRemoteAuthOpts(c) {
|
|
|
28842
29124
|
return { ...brandLoginOpts, origin };
|
|
28843
29125
|
}
|
|
28844
29126
|
var MAX_LOGIN_BODY = 8 * 1024;
|
|
28845
|
-
|
|
29127
|
+
app72.post("/__remote-auth/login", async (c) => {
|
|
28846
29128
|
const client = clientFrom(c);
|
|
28847
29129
|
const clientIp = client.ip || "unknown";
|
|
28848
29130
|
if (!requestIsTlsTerminated(c)) {
|
|
@@ -28887,7 +29169,7 @@ app71.post("/__remote-auth/login", async (c) => {
|
|
|
28887
29169
|
}
|
|
28888
29170
|
});
|
|
28889
29171
|
});
|
|
28890
|
-
|
|
29172
|
+
app72.get("/__remote-auth/logout", (c) => {
|
|
28891
29173
|
const client = clientFrom(c);
|
|
28892
29174
|
const clientIp = client.ip || "unknown";
|
|
28893
29175
|
console.error(`[remote-auth] logout ip=${clientIp}`);
|
|
@@ -28900,7 +29182,7 @@ app71.get("/__remote-auth/logout", (c) => {
|
|
|
28900
29182
|
}
|
|
28901
29183
|
});
|
|
28902
29184
|
});
|
|
28903
|
-
|
|
29185
|
+
app72.post("/__remote-auth/change-password", async (c) => {
|
|
28904
29186
|
const client = clientFrom(c);
|
|
28905
29187
|
const clientIp = client.ip || "unknown";
|
|
28906
29188
|
const rateLimited = checkRateLimit(client);
|
|
@@ -28959,13 +29241,13 @@ app71.post("/__remote-auth/change-password", async (c) => {
|
|
|
28959
29241
|
return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(c), mode: "change", changeError: "Failed to save password", redirect }), 200);
|
|
28960
29242
|
}
|
|
28961
29243
|
});
|
|
28962
|
-
|
|
29244
|
+
app72.get("/__remote-auth/setup", (c) => {
|
|
28963
29245
|
if (isRemoteAuthConfigured()) {
|
|
28964
29246
|
return c.redirect("/");
|
|
28965
29247
|
}
|
|
28966
29248
|
return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(c), mode: "setup" }), 200);
|
|
28967
29249
|
});
|
|
28968
|
-
|
|
29250
|
+
app72.post("/__remote-auth/set-initial-password", async (c) => {
|
|
28969
29251
|
if (isRemoteAuthConfigured()) {
|
|
28970
29252
|
return c.redirect("/");
|
|
28971
29253
|
}
|
|
@@ -29003,10 +29285,10 @@ app71.post("/__remote-auth/set-initial-password", async (c) => {
|
|
|
29003
29285
|
return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(c), mode: "setup", setupError: "Failed to save password. Please try again." }), 200);
|
|
29004
29286
|
}
|
|
29005
29287
|
});
|
|
29006
|
-
|
|
29288
|
+
app72.get("/api/remote-auth/status", (c) => {
|
|
29007
29289
|
return c.json({ configured: isRemoteAuthConfigured() });
|
|
29008
29290
|
});
|
|
29009
|
-
|
|
29291
|
+
app72.post("/api/remote-auth/set-password", async (c) => {
|
|
29010
29292
|
let body;
|
|
29011
29293
|
try {
|
|
29012
29294
|
body = await c.req.json();
|
|
@@ -29045,10 +29327,10 @@ app71.post("/api/remote-auth/set-password", async (c) => {
|
|
|
29045
29327
|
return c.json({ error: "Failed to save password" }, 500);
|
|
29046
29328
|
}
|
|
29047
29329
|
});
|
|
29048
|
-
|
|
29330
|
+
app72.route("/api/_client-error", client_error_default);
|
|
29049
29331
|
console.log("[client-error-route] mounted");
|
|
29050
29332
|
var PWA_PUBLIC_PATHS = /* @__PURE__ */ new Set(["/sw.js", ...PWA_SURFACES.map((s) => s.manifestPath)]);
|
|
29051
|
-
|
|
29333
|
+
app72.use("*", async (c, next) => {
|
|
29052
29334
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
29053
29335
|
const path = c.req.path;
|
|
29054
29336
|
if (path === "/favicon.ico" || path.startsWith("/assets/") || path.startsWith("/brand/") || // Public free/busy is read by the booking page (a separate Cloudflare Pages
|
|
@@ -29092,30 +29374,30 @@ app71.use("*", async (c, next) => {
|
|
|
29092
29374
|
}
|
|
29093
29375
|
return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(c), redirect: path }), 200);
|
|
29094
29376
|
});
|
|
29095
|
-
|
|
29096
|
-
|
|
29097
|
-
|
|
29098
|
-
|
|
29099
|
-
|
|
29100
|
-
|
|
29101
|
-
|
|
29102
|
-
|
|
29103
|
-
|
|
29377
|
+
app72.route("/api/health", health_default);
|
|
29378
|
+
app72.route("/api/chat", chatRoutes);
|
|
29379
|
+
app72.route("/api/whatsapp", whatsapp_default);
|
|
29380
|
+
app72.route("/api/storage", storage_broker_default);
|
|
29381
|
+
app72.route("/api/whatsapp-reader", whatsapp_reader_default);
|
|
29382
|
+
app72.route("/api/session-reader", session_reader_default);
|
|
29383
|
+
app72.route("/api/operator-conversations", operator_conversations_default);
|
|
29384
|
+
app72.route("/api/public-reader", public_reader_default);
|
|
29385
|
+
app72.route("/api/webchat", createWebchatRoutes({
|
|
29104
29386
|
handleInbound: (input) => webchatGateway.handleInbound(input),
|
|
29105
29387
|
// Task 940 — the permission relay's pointer read + verdict write.
|
|
29106
29388
|
pendingPromptFor: (key) => webchatGateway.pendingPromptFor(key),
|
|
29107
29389
|
deliveryFailureFor: (key) => webchatGateway.deliveryFailureFor(key),
|
|
29108
29390
|
resolvePermissionVerdict: (key, requestId, behavior) => webchatGateway.resolvePermissionVerdict(key, requestId, behavior)
|
|
29109
29391
|
}));
|
|
29110
|
-
|
|
29111
|
-
|
|
29112
|
-
|
|
29113
|
-
|
|
29114
|
-
|
|
29115
|
-
|
|
29116
|
-
|
|
29117
|
-
|
|
29118
|
-
|
|
29392
|
+
app72.route("/api/webchat/greeting", webchat_greeting_default);
|
|
29393
|
+
app72.route("/api/telegram", telegram_default);
|
|
29394
|
+
app72.route("/api/quickbooks", quickbooks_default);
|
|
29395
|
+
app72.route("/api/onboarding", onboarding_default);
|
|
29396
|
+
app72.route("/api/admin", admin_default);
|
|
29397
|
+
app72.route("/api/access", access_default);
|
|
29398
|
+
app72.route("/api/session", session_default2);
|
|
29399
|
+
app72.route("/api/calendar", calendar_public_default);
|
|
29400
|
+
app72.route("/api/portal", portal_fetch_default);
|
|
29119
29401
|
var SAFE_SLUG_RE2 = /^[a-z][a-z0-9-]{2,49}$/;
|
|
29120
29402
|
var SAFE_FILENAME_RE = /^[a-z0-9_][a-z0-9_.-]{0,99}$/i;
|
|
29121
29403
|
var IMAGE_MIME = {
|
|
@@ -29127,7 +29409,7 @@ var IMAGE_MIME = {
|
|
|
29127
29409
|
".svg": "image/svg+xml",
|
|
29128
29410
|
".ico": "image/x-icon"
|
|
29129
29411
|
};
|
|
29130
|
-
|
|
29412
|
+
app72.get("/agent-assets/:slug/:filename", (c) => {
|
|
29131
29413
|
const slug = c.req.param("slug");
|
|
29132
29414
|
const filename = c.req.param("filename");
|
|
29133
29415
|
if (!SAFE_SLUG_RE2.test(slug)) {
|
|
@@ -29149,20 +29431,20 @@ app71.get("/agent-assets/:slug/:filename", (c) => {
|
|
|
29149
29431
|
console.error(`[agent-assets] path-traversal-rejected slug=${slug} file=${filename}`);
|
|
29150
29432
|
return c.text("Forbidden", 403);
|
|
29151
29433
|
}
|
|
29152
|
-
if (!
|
|
29434
|
+
if (!existsSync48(filePath)) {
|
|
29153
29435
|
console.error(`[agent-assets] serve slug=${slug} file=${filename} status=404`);
|
|
29154
29436
|
return c.text("Not found", 404);
|
|
29155
29437
|
}
|
|
29156
29438
|
const ext = "." + filename.split(".").pop()?.toLowerCase();
|
|
29157
29439
|
const contentType = IMAGE_MIME[ext] || "application/octet-stream";
|
|
29158
29440
|
console.log(`[agent-assets] serve slug=${slug} file=${filename} status=200`);
|
|
29159
|
-
const body =
|
|
29441
|
+
const body = readFileSync44(filePath);
|
|
29160
29442
|
return c.body(body, 200, {
|
|
29161
29443
|
"Content-Type": contentType,
|
|
29162
29444
|
"Cache-Control": "public, max-age=3600"
|
|
29163
29445
|
});
|
|
29164
29446
|
});
|
|
29165
|
-
|
|
29447
|
+
app72.get("/generated/:filename", (c) => {
|
|
29166
29448
|
const filename = c.req.param("filename");
|
|
29167
29449
|
if (!SAFE_FILENAME_RE.test(filename) || filename.includes("..")) {
|
|
29168
29450
|
console.error(`[generated] serve file=${filename} status=403`);
|
|
@@ -29179,29 +29461,29 @@ app71.get("/generated/:filename", (c) => {
|
|
|
29179
29461
|
console.error(`[generated] serve file=${filename} status=403`);
|
|
29180
29462
|
return c.text("Forbidden", 403);
|
|
29181
29463
|
}
|
|
29182
|
-
if (!
|
|
29464
|
+
if (!existsSync48(filePath)) {
|
|
29183
29465
|
console.error(`[generated] serve file=${filename} status=404`);
|
|
29184
29466
|
return c.text("Not found", 404);
|
|
29185
29467
|
}
|
|
29186
29468
|
const ext = "." + filename.split(".").pop()?.toLowerCase();
|
|
29187
29469
|
const contentType = IMAGE_MIME[ext] || "application/octet-stream";
|
|
29188
29470
|
console.log(`[generated] serve file=${filename} status=200`);
|
|
29189
|
-
const body =
|
|
29471
|
+
const body = readFileSync44(filePath);
|
|
29190
29472
|
return c.body(body, 200, {
|
|
29191
29473
|
"Content-Type": contentType,
|
|
29192
29474
|
"Cache-Control": "public, max-age=86400"
|
|
29193
29475
|
});
|
|
29194
29476
|
});
|
|
29195
|
-
|
|
29196
|
-
|
|
29197
|
-
|
|
29198
|
-
|
|
29477
|
+
app72.route("/sites", sites_default);
|
|
29478
|
+
app72.route("/listings", listings_default);
|
|
29479
|
+
app72.route("/v", visitor_event_default);
|
|
29480
|
+
app72.route("/v", visitor_consent_default);
|
|
29199
29481
|
var htmlCache = /* @__PURE__ */ new Map();
|
|
29200
29482
|
var brandLogoPath = "/brand/maxy-monochrome.png";
|
|
29201
29483
|
var brandIconPath = "/brand/maxy-monochrome.png";
|
|
29202
|
-
if (BRAND_JSON_PATH &&
|
|
29484
|
+
if (BRAND_JSON_PATH && existsSync48(BRAND_JSON_PATH)) {
|
|
29203
29485
|
try {
|
|
29204
|
-
const fullBrand = JSON.parse(
|
|
29486
|
+
const fullBrand = JSON.parse(readFileSync44(BRAND_JSON_PATH, "utf-8"));
|
|
29205
29487
|
if (fullBrand.assets?.logo) brandLogoPath = `/brand/${fullBrand.assets.logo}`;
|
|
29206
29488
|
brandIconPath = fullBrand.assets?.icon ? `/brand/${fullBrand.assets.icon}` : brandLogoPath;
|
|
29207
29489
|
} catch {
|
|
@@ -29228,11 +29510,11 @@ var brandScript = `<script>window.__BRAND__=${JSON.stringify({
|
|
|
29228
29510
|
var brandThemeColor = BRAND.defaultColors?.primary ?? "#000000";
|
|
29229
29511
|
var brandBackgroundColor = BRAND.defaultColors?.background ?? "#ffffff";
|
|
29230
29512
|
var brandAppIconsExist = [brandAppIcon192Path, brandAppIcon512Path, brandMaskableIconPath].every(
|
|
29231
|
-
(p) =>
|
|
29513
|
+
(p) => existsSync48(resolve42(process.cwd(), "public", p.replace(/^\//, "")))
|
|
29232
29514
|
);
|
|
29233
29515
|
var SW_SOURCE = (() => {
|
|
29234
29516
|
try {
|
|
29235
|
-
return
|
|
29517
|
+
return readFileSync44(resolve42(process.cwd(), "public", "sw.js"), "utf-8");
|
|
29236
29518
|
} catch {
|
|
29237
29519
|
return null;
|
|
29238
29520
|
}
|
|
@@ -29240,9 +29522,9 @@ var SW_SOURCE = (() => {
|
|
|
29240
29522
|
function readInstalledVersion() {
|
|
29241
29523
|
try {
|
|
29242
29524
|
if (!PLATFORM_ROOT6) return "unknown";
|
|
29243
|
-
const versionFile =
|
|
29244
|
-
if (!
|
|
29245
|
-
const content =
|
|
29525
|
+
const versionFile = join50(PLATFORM_ROOT6, "config", `.${BRAND.hostname}-version`);
|
|
29526
|
+
if (!existsSync48(versionFile)) return "unknown";
|
|
29527
|
+
const content = readFileSync44(versionFile, "utf-8").trim();
|
|
29246
29528
|
return content || "unknown";
|
|
29247
29529
|
} catch {
|
|
29248
29530
|
return "unknown";
|
|
@@ -29253,7 +29535,7 @@ var versionScript = `<script>window.__MAXY_VERSION__=${JSON.stringify(INSTALLED_
|
|
|
29253
29535
|
function cachedHtml(file) {
|
|
29254
29536
|
let html = htmlCache.get(file);
|
|
29255
29537
|
if (!html) {
|
|
29256
|
-
html =
|
|
29538
|
+
html = readFileSync44(resolve42(process.cwd(), "public", file), "utf-8");
|
|
29257
29539
|
const productNameEsc = escapeHtml(BRAND.productName);
|
|
29258
29540
|
html = html.replace(/<title>([^<]*)<\/title>/, (_match, inner) => `<title>${escapeHtml(inner).replace(/Maxy/g, productNameEsc)}</title>`);
|
|
29259
29541
|
html = html.replace('href="/favicon.ico"', `href="${escapeHtml(brandFaviconPath)}"`);
|
|
@@ -29270,13 +29552,13 @@ ${clientErrorReporterScript}
|
|
|
29270
29552
|
return html;
|
|
29271
29553
|
}
|
|
29272
29554
|
function loadBrandingCache(agentSlug) {
|
|
29273
|
-
const configDir =
|
|
29555
|
+
const configDir = join50(homedir4(), BRAND.configDir);
|
|
29274
29556
|
try {
|
|
29275
29557
|
const accountId = getDefaultAccountId();
|
|
29276
29558
|
if (!accountId) return null;
|
|
29277
|
-
const cachePath =
|
|
29278
|
-
if (!
|
|
29279
|
-
return JSON.parse(
|
|
29559
|
+
const cachePath = join50(configDir, "branding-cache", accountId, `${agentSlug}.json`);
|
|
29560
|
+
if (!existsSync48(cachePath)) return null;
|
|
29561
|
+
return JSON.parse(readFileSync44(cachePath, "utf-8"));
|
|
29280
29562
|
} catch {
|
|
29281
29563
|
return null;
|
|
29282
29564
|
}
|
|
@@ -29320,7 +29602,7 @@ function brandedPublicHtml(agentSlug) {
|
|
|
29320
29602
|
function agentUnavailableHtml() {
|
|
29321
29603
|
return `<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>${escapeHtml(BRAND.productName)}</title></head><body style="font-family:system-ui,sans-serif;max-width:32rem;margin:4rem auto;padding:0 1.5rem;color:#222"><h1 style="font-size:1.25rem">Agent unavailable</h1><p>This agent isn't available right now. If you reached this page from a saved link, the agent may have been turned off.</p></body></html>`;
|
|
29322
29604
|
}
|
|
29323
|
-
|
|
29605
|
+
app72.get("/", (c) => {
|
|
29324
29606
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
29325
29607
|
const klass = classifyHost(host, getOperatorDomains(), isPublicHost);
|
|
29326
29608
|
if (klass === "operator") {
|
|
@@ -29342,12 +29624,12 @@ app71.get("/", (c) => {
|
|
|
29342
29624
|
console.log(`[host-class] host=${host} class=admin served=index.html`);
|
|
29343
29625
|
return c.html(cachedHtml("index.html"));
|
|
29344
29626
|
});
|
|
29345
|
-
|
|
29627
|
+
app72.get("/public", (c) => {
|
|
29346
29628
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
29347
29629
|
if (isPublicHost(host)) return c.text("Not found", 404);
|
|
29348
29630
|
return c.html(cachedHtml("public.html"));
|
|
29349
29631
|
});
|
|
29350
|
-
|
|
29632
|
+
app72.get("/public-chat", (c) => {
|
|
29351
29633
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
29352
29634
|
if (isPublicHost(host)) return c.text("Not found", 404);
|
|
29353
29635
|
return c.html(cachedHtml("public.html"));
|
|
@@ -29366,12 +29648,12 @@ async function logViewerFetch(c, next) {
|
|
|
29366
29648
|
duration_ms: Date.now() - start
|
|
29367
29649
|
});
|
|
29368
29650
|
}
|
|
29369
|
-
|
|
29370
|
-
|
|
29371
|
-
|
|
29651
|
+
app72.use("/vnc-viewer.html", logViewerFetch);
|
|
29652
|
+
app72.use("/vnc-popout.html", logViewerFetch);
|
|
29653
|
+
app72.get("/vnc-popout.html", (c) => {
|
|
29372
29654
|
let html = htmlCache.get("vnc-popout.html");
|
|
29373
29655
|
if (!html) {
|
|
29374
|
-
html =
|
|
29656
|
+
html = readFileSync44(resolve42(process.cwd(), "public", "vnc-popout.html"), "utf-8");
|
|
29375
29657
|
const name = escapeHtml(BRAND.productName);
|
|
29376
29658
|
html = html.replace("<title>Browser \u2014 Maxy</title>", `<title>${name}</title>`);
|
|
29377
29659
|
html = html.replace("</head>", ` ${brandScript}
|
|
@@ -29381,7 +29663,7 @@ app71.get("/vnc-popout.html", (c) => {
|
|
|
29381
29663
|
}
|
|
29382
29664
|
return c.html(html);
|
|
29383
29665
|
});
|
|
29384
|
-
|
|
29666
|
+
app72.post("/api/vnc/client-event", async (c) => {
|
|
29385
29667
|
let body;
|
|
29386
29668
|
try {
|
|
29387
29669
|
body = await c.req.json();
|
|
@@ -29402,11 +29684,11 @@ app71.post("/api/vnc/client-event", async (c) => {
|
|
|
29402
29684
|
});
|
|
29403
29685
|
return c.json({ ok: true });
|
|
29404
29686
|
});
|
|
29405
|
-
|
|
29687
|
+
app72.get("/g/:slug", (c) => {
|
|
29406
29688
|
return c.html(brandedPublicHtml(resolveDefaultSlug() ?? void 0));
|
|
29407
29689
|
});
|
|
29408
29690
|
for (const pwa of PWA_SURFACES) {
|
|
29409
|
-
|
|
29691
|
+
app72.get(pwa.manifestPath, (c) => {
|
|
29410
29692
|
const manifest = buildManifest(pwa, {
|
|
29411
29693
|
productName: BRAND.productName,
|
|
29412
29694
|
appIcon192: brandAppIcon192Path,
|
|
@@ -29422,7 +29704,7 @@ for (const pwa of PWA_SURFACES) {
|
|
|
29422
29704
|
return c.body(JSON.stringify(manifest));
|
|
29423
29705
|
});
|
|
29424
29706
|
}
|
|
29425
|
-
|
|
29707
|
+
app72.get("/sw.js", (c) => {
|
|
29426
29708
|
if (SW_SOURCE == null) {
|
|
29427
29709
|
console.error("[pwa] op=sw status=500 reason=sw-source-missing");
|
|
29428
29710
|
return c.text("Service worker unavailable", 500);
|
|
@@ -29431,12 +29713,12 @@ app71.get("/sw.js", (c) => {
|
|
|
29431
29713
|
console.log(`[pwa] op=sw status=200 ct=${SW_CONTENT_TYPE}`);
|
|
29432
29714
|
return c.body(SW_SOURCE);
|
|
29433
29715
|
});
|
|
29434
|
-
|
|
29716
|
+
app72.get("/graph", (c) => {
|
|
29435
29717
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
29436
29718
|
if (isPublicHost(host) || isOperatorHost(host, getOperatorDomains())) return c.text("Not found", 404);
|
|
29437
29719
|
return c.html(cachedHtml("graph.html"));
|
|
29438
29720
|
});
|
|
29439
|
-
|
|
29721
|
+
app72.get("/chat", (c) => {
|
|
29440
29722
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
29441
29723
|
if (isOperatorHost(host, getOperatorDomains())) {
|
|
29442
29724
|
console.log(`[host-class] host=${host} class=operator served=operator.html`);
|
|
@@ -29445,47 +29727,47 @@ app71.get("/chat", (c) => {
|
|
|
29445
29727
|
if (isPublicHost(host)) return c.text("Not found", 404);
|
|
29446
29728
|
return c.html(cachedHtml("chat.html"));
|
|
29447
29729
|
});
|
|
29448
|
-
|
|
29730
|
+
app72.get("/data", (c) => {
|
|
29449
29731
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
29450
29732
|
if (isPublicHost(host)) return c.text("Not found", 404);
|
|
29451
29733
|
return c.html(cachedHtml("data.html"));
|
|
29452
29734
|
});
|
|
29453
|
-
|
|
29735
|
+
app72.get("/tasks", (c) => {
|
|
29454
29736
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
29455
29737
|
if (isPublicHost(host)) return c.text("Not found", 404);
|
|
29456
29738
|
return c.html(cachedHtml("tasks.html"));
|
|
29457
29739
|
});
|
|
29458
|
-
|
|
29740
|
+
app72.get("/activity", (c) => {
|
|
29459
29741
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
29460
29742
|
if (isPublicHost(host)) return c.text("Not found", 404);
|
|
29461
29743
|
return c.html(cachedHtml("activity.html"));
|
|
29462
29744
|
});
|
|
29463
|
-
|
|
29745
|
+
app72.get("/calendar", (c) => {
|
|
29464
29746
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
29465
29747
|
if (isPublicHost(host)) return c.text("Not found", 404);
|
|
29466
29748
|
return c.html(cachedHtml("calendar.html"));
|
|
29467
29749
|
});
|
|
29468
|
-
|
|
29750
|
+
app72.get("/routines", (c) => {
|
|
29469
29751
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
29470
29752
|
if (isPublicHost(host)) return c.text("Not found", 404);
|
|
29471
29753
|
return c.html(cachedHtml("routines.html"));
|
|
29472
29754
|
});
|
|
29473
|
-
|
|
29755
|
+
app72.get("/skills", (c) => {
|
|
29474
29756
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
29475
29757
|
if (isPublicHost(host)) return c.text("Not found", 404);
|
|
29476
29758
|
return c.html(cachedHtml("skills.html"));
|
|
29477
29759
|
});
|
|
29478
|
-
|
|
29760
|
+
app72.get("/agents", (c) => {
|
|
29479
29761
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
29480
29762
|
if (isPublicHost(host)) return c.text("Not found", 404);
|
|
29481
29763
|
return c.html(cachedHtml("agents.html"));
|
|
29482
29764
|
});
|
|
29483
|
-
|
|
29765
|
+
app72.get("/browser", (c) => {
|
|
29484
29766
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
29485
29767
|
if (isPublicHost(host) || isOperatorHost(host, getOperatorDomains())) return c.text("Not found", 404);
|
|
29486
29768
|
return c.html(cachedHtml("browser.html"));
|
|
29487
29769
|
});
|
|
29488
|
-
|
|
29770
|
+
app72.get("/:slug", async (c, next) => {
|
|
29489
29771
|
const slug = c.req.param("slug");
|
|
29490
29772
|
if (AGENT_SLUG_PATTERN.test(`/${slug}`)) {
|
|
29491
29773
|
const account = resolveAccount();
|
|
@@ -29500,13 +29782,13 @@ app71.get("/:slug", async (c, next) => {
|
|
|
29500
29782
|
await next();
|
|
29501
29783
|
});
|
|
29502
29784
|
if (brandFaviconPath !== "/favicon.ico") {
|
|
29503
|
-
|
|
29785
|
+
app72.get("/favicon.ico", (c) => {
|
|
29504
29786
|
c.header("Cache-Control", "public, max-age=300");
|
|
29505
29787
|
return c.redirect(brandFaviconPath, 302);
|
|
29506
29788
|
});
|
|
29507
29789
|
}
|
|
29508
|
-
mountStaticFiles(
|
|
29509
|
-
|
|
29790
|
+
mountStaticFiles(app72, "./public");
|
|
29791
|
+
app72.all("*", (c) => {
|
|
29510
29792
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
29511
29793
|
const path = c.req.path;
|
|
29512
29794
|
if (isPublicHost(host)) {
|
|
@@ -29520,13 +29802,13 @@ app71.all("*", (c) => {
|
|
|
29520
29802
|
});
|
|
29521
29803
|
var port = requirePortEnv("MAXY_UI_INTERNAL_PORT", { tag: "ui-server" });
|
|
29522
29804
|
var hostname = process.env.HOSTNAME ?? "127.0.0.1";
|
|
29523
|
-
var httpServer = serve({ fetch:
|
|
29805
|
+
var httpServer = serve({ fetch: app72.fetch, port, hostname });
|
|
29524
29806
|
console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
29525
29807
|
{
|
|
29526
|
-
const reconcilePlatformRoot = process.env.MAXY_PLATFORM_ROOT ??
|
|
29808
|
+
const reconcilePlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join50(__dirname, "..");
|
|
29527
29809
|
const reconcileScript = resolve42(reconcilePlatformRoot, "plugins/scheduling/mcp/dist/scripts/reconcile-bookings.js");
|
|
29528
29810
|
const runReconcile = (ctx) => {
|
|
29529
|
-
if (!
|
|
29811
|
+
if (!existsSync48(reconcileScript)) return;
|
|
29530
29812
|
try {
|
|
29531
29813
|
const child = spawn3(process.execPath, [reconcileScript], {
|
|
29532
29814
|
env: { ...process.env, PLATFORM_ROOT: reconcilePlatformRoot },
|
|
@@ -29546,7 +29828,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
29546
29828
|
detached: true,
|
|
29547
29829
|
run: runReconcile
|
|
29548
29830
|
});
|
|
29549
|
-
const reconcileStatePath =
|
|
29831
|
+
const reconcileStatePath = join50(MAXY_DIR, "booking-reconcile-state.json");
|
|
29550
29832
|
registerLoop({
|
|
29551
29833
|
name: "calendar-reconcile-liveness",
|
|
29552
29834
|
intervalMs: 6e5,
|
|
@@ -29564,7 +29846,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
29564
29846
|
}
|
|
29565
29847
|
{
|
|
29566
29848
|
const DREAM_CYCLE_SWEEP_INTERVAL_MS = 36e5;
|
|
29567
|
-
const dreamPlatformRoot = process.env.MAXY_PLATFORM_ROOT ??
|
|
29849
|
+
const dreamPlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join50(__dirname, "..");
|
|
29568
29850
|
const dreamSweepScript = resolve42(dreamPlatformRoot, "plugins/memory/mcp/dist/scripts/dream-cycle-sweep.js");
|
|
29569
29851
|
registerLoop({
|
|
29570
29852
|
name: "dream-cycle-sweep",
|
|
@@ -29572,7 +29854,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
29572
29854
|
firstRunDelayMs: 3e4,
|
|
29573
29855
|
detached: true,
|
|
29574
29856
|
run: (ctx) => {
|
|
29575
|
-
if (!
|
|
29857
|
+
if (!existsSync48(dreamSweepScript)) return;
|
|
29576
29858
|
try {
|
|
29577
29859
|
const child = spawn3(process.execPath, [dreamSweepScript], {
|
|
29578
29860
|
env: { ...process.env, PLATFORM_ROOT: dreamPlatformRoot },
|
|
@@ -29588,11 +29870,11 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
29588
29870
|
});
|
|
29589
29871
|
}
|
|
29590
29872
|
{
|
|
29591
|
-
const outlookPlatformRoot = process.env.MAXY_PLATFORM_ROOT ??
|
|
29873
|
+
const outlookPlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join50(__dirname, "..");
|
|
29592
29874
|
const outlookScript = resolve42(outlookPlatformRoot, "plugins/outlook/mcp/dist/scripts/complete-registration.js");
|
|
29593
29875
|
const OUTLOOK_COMPLETE_INTERVAL_MS = 3e4;
|
|
29594
29876
|
const runOutlookComplete = (ctx) => {
|
|
29595
|
-
if (!
|
|
29877
|
+
if (!existsSync48(outlookScript)) return;
|
|
29596
29878
|
try {
|
|
29597
29879
|
const child = spawn3(process.execPath, [outlookScript], {
|
|
29598
29880
|
env: { ...process.env, PLATFORM_ROOT: outlookPlatformRoot },
|
|
@@ -29614,19 +29896,19 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
29614
29896
|
});
|
|
29615
29897
|
}
|
|
29616
29898
|
{
|
|
29617
|
-
const auditRoot = process.env.MAXY_PLATFORM_ROOT ??
|
|
29899
|
+
const auditRoot = process.env.MAXY_PLATFORM_ROOT ?? join50(__dirname, "..");
|
|
29618
29900
|
const strandedAccountsDir = resolve42(auditRoot, "..", "data/accounts");
|
|
29619
29901
|
const STRANDED_AUDIT_INTERVAL_MS = 3e5;
|
|
29620
29902
|
const STRANDED_AGE_MS = 16 * 6e4;
|
|
29621
29903
|
const STRANDED_UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
29622
29904
|
const runStrandedAudit = () => {
|
|
29623
29905
|
try {
|
|
29624
|
-
if (!
|
|
29906
|
+
if (!existsSync48(strandedAccountsDir)) return;
|
|
29625
29907
|
const now = Date.now();
|
|
29626
29908
|
for (const name of readdirSync28(strandedAccountsDir)) {
|
|
29627
29909
|
if (!STRANDED_UUID_RE.test(name)) continue;
|
|
29628
29910
|
const pendingPath2 = resolve42(strandedAccountsDir, name, "secrets/outlook/pending-devicecode.enc");
|
|
29629
|
-
if (!
|
|
29911
|
+
if (!existsSync48(pendingPath2)) continue;
|
|
29630
29912
|
const ageMs2 = now - statSync24(pendingPath2).mtimeMs;
|
|
29631
29913
|
if (ageMs2 > STRANDED_AGE_MS) {
|
|
29632
29914
|
console.error(`[outlook-mcp] devicecode-stranded account=${name} ageSec=${Math.floor(ageMs2 / 1e3)}`);
|
|
@@ -29644,7 +29926,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
29644
29926
|
});
|
|
29645
29927
|
}
|
|
29646
29928
|
{
|
|
29647
|
-
const googleRoot = process.env.MAXY_PLATFORM_ROOT ??
|
|
29929
|
+
const googleRoot = process.env.MAXY_PLATFORM_ROOT ?? join50(__dirname, "..");
|
|
29648
29930
|
const googleAccountsDir = resolve42(googleRoot, "..", "data/accounts");
|
|
29649
29931
|
const GOOGLE_PENDING_AUDIT_INTERVAL_MS = 3e5;
|
|
29650
29932
|
const runGooglePendingAuditSafe = () => {
|
|
@@ -29663,7 +29945,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
29663
29945
|
const googleAuditScript = resolve42(googleRoot, "plugins/google/mcp/dist/scripts/account-audit.js");
|
|
29664
29946
|
const GOOGLE_ACCOUNT_AUDIT_INTERVAL_MS = 36e5;
|
|
29665
29947
|
const runGoogleAccountAudit = (ctx) => {
|
|
29666
|
-
if (!
|
|
29948
|
+
if (!existsSync48(googleAuditScript)) return;
|
|
29667
29949
|
try {
|
|
29668
29950
|
const child = spawn3(process.execPath, [googleAuditScript], {
|
|
29669
29951
|
env: { ...process.env, PLATFORM_ROOT: googleRoot },
|
|
@@ -29688,7 +29970,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
29688
29970
|
});
|
|
29689
29971
|
}
|
|
29690
29972
|
{
|
|
29691
|
-
const auditPlatformRoot = process.env.MAXY_PLATFORM_ROOT ??
|
|
29973
|
+
const auditPlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join50(__dirname, "..");
|
|
29692
29974
|
const STORAGE_AUDIT_INTERVAL_MS = 3e5;
|
|
29693
29975
|
const runStorageAuditSafe = () => runStorageAudit(auditPlatformRoot).catch((err) => {
|
|
29694
29976
|
console.error(`[storage-audit] error="${err.message}"`);
|
|
@@ -29749,7 +30031,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
29749
30031
|
});
|
|
29750
30032
|
}
|
|
29751
30033
|
{
|
|
29752
|
-
const publishPlatformRoot = process.env.MAXY_PLATFORM_ROOT ??
|
|
30034
|
+
const publishPlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join50(__dirname, "..");
|
|
29753
30035
|
const publishScript = resolve42(publishPlatformRoot, "plugins/scheduling/mcp/dist/scripts/publish-availability.js");
|
|
29754
30036
|
const PUBLISH_INTERVAL_MS = 3e5;
|
|
29755
30037
|
const bookingAccounts = () => {
|
|
@@ -29761,7 +30043,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
29761
30043
|
}
|
|
29762
30044
|
};
|
|
29763
30045
|
const spawnPublish = (account, ctx) => {
|
|
29764
|
-
if (!
|
|
30046
|
+
if (!existsSync48(publishScript)) return;
|
|
29765
30047
|
try {
|
|
29766
30048
|
const child = spawn3(process.execPath, [publishScript], {
|
|
29767
30049
|
env: {
|
|
@@ -29783,8 +30065,8 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
29783
30065
|
const availPath = resolve42(account.accountDir, "calendar-availability.json");
|
|
29784
30066
|
let hasBookingSite = false;
|
|
29785
30067
|
try {
|
|
29786
|
-
if (
|
|
29787
|
-
const cfg = JSON.parse(
|
|
30068
|
+
if (existsSync48(availPath)) {
|
|
30069
|
+
const cfg = JSON.parse(readFileSync44(availPath, "utf-8"));
|
|
29788
30070
|
hasBookingSite = typeof cfg.bookingDbName === "string" && cfg.bookingDbName.length > 0;
|
|
29789
30071
|
}
|
|
29790
30072
|
} catch {
|
|
@@ -29792,9 +30074,9 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
29792
30074
|
if (!hasBookingSite) return;
|
|
29793
30075
|
const statePath = resolve42(account.accountDir, "state", "booking-availability", "last-publish.json");
|
|
29794
30076
|
let lastSuccessAt = null;
|
|
29795
|
-
if (
|
|
30077
|
+
if (existsSync48(statePath)) {
|
|
29796
30078
|
try {
|
|
29797
|
-
const rec = JSON.parse(
|
|
30079
|
+
const rec = JSON.parse(readFileSync44(statePath, "utf-8"));
|
|
29798
30080
|
lastSuccessAt = rec.lastSuccessAt ?? null;
|
|
29799
30081
|
} catch {
|
|
29800
30082
|
console.error(`[calendar-availability] op=audit accountId=${account.accountId} snapshotAgeMs=na reason=bad-state-file`);
|
|
@@ -29826,12 +30108,12 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
29826
30108
|
startTimeEntryCensus(() => getSession());
|
|
29827
30109
|
startLedgerCensus(() => getSession());
|
|
29828
30110
|
{
|
|
29829
|
-
const auditPlatformRoot = process.env.MAXY_PLATFORM_ROOT ??
|
|
30111
|
+
const auditPlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join50(__dirname, "..");
|
|
29830
30112
|
const auditScript = resolve42(auditPlatformRoot, "plugins/connector/mcp/dist/scripts/audit-connectors.js");
|
|
29831
30113
|
const auditLogDir = process.env.LOG_DIR ?? LOG_DIR;
|
|
29832
30114
|
const CONNECTOR_AUDIT_INTERVAL_MS = 3e5;
|
|
29833
30115
|
const runConnectorAudit = (ctx) => {
|
|
29834
|
-
if (!
|
|
30116
|
+
if (!existsSync48(auditScript)) return;
|
|
29835
30117
|
try {
|
|
29836
30118
|
const child = spawn3(process.execPath, [auditScript], {
|
|
29837
30119
|
env: { ...process.env, PLATFORM_ROOT: auditPlatformRoot, LOG_DIR: auditLogDir },
|
|
@@ -29911,7 +30193,7 @@ for (const m of SUBAPP_MANIFEST) {
|
|
|
29911
30193
|
}
|
|
29912
30194
|
try {
|
|
29913
30195
|
const registered = [];
|
|
29914
|
-
for (const r of
|
|
30196
|
+
for (const r of app72.routes ?? []) {
|
|
29915
30197
|
if (typeof r.path !== "string" || r.path.includes(":") || r.path.includes("*")) continue;
|
|
29916
30198
|
if (AGENT_SLUG_PATTERN.test(r.path)) {
|
|
29917
30199
|
registered.push({ method: (r.method ?? "ALL").toUpperCase(), path: r.path });
|
|
@@ -29951,11 +30233,11 @@ try {
|
|
|
29951
30233
|
var ADMINUSER_RECONCILE_INTERVAL_MS = 60 * 60 * 1e3;
|
|
29952
30234
|
async function runAdminUserReconcileTick() {
|
|
29953
30235
|
try {
|
|
29954
|
-
if (!
|
|
30236
|
+
if (!existsSync48(USERS_FILE)) {
|
|
29955
30237
|
console.error("[adminuser-self-heal] skip reason=no-users-file");
|
|
29956
30238
|
return;
|
|
29957
30239
|
}
|
|
29958
|
-
const usersRaw =
|
|
30240
|
+
const usersRaw = readFileSync44(USERS_FILE, "utf-8").trim();
|
|
29959
30241
|
if (!usersRaw) {
|
|
29960
30242
|
console.error("[adminuser-self-heal] skip reason=empty-users-file");
|
|
29961
30243
|
return;
|
|
@@ -30007,8 +30289,8 @@ registerLoop({
|
|
|
30007
30289
|
});
|
|
30008
30290
|
var USERS_RECONCILE_INTERVAL_MS = 60 * 60 * 1e3;
|
|
30009
30291
|
function countUsersRows() {
|
|
30010
|
-
if (!
|
|
30011
|
-
const raw =
|
|
30292
|
+
if (!existsSync48(USERS_FILE)) return 0;
|
|
30293
|
+
const raw = readFileSync44(USERS_FILE, "utf-8").trim();
|
|
30012
30294
|
if (!raw) return 0;
|
|
30013
30295
|
const users = JSON.parse(raw);
|
|
30014
30296
|
return users.filter((u) => typeof u.userId === "string").length;
|
|
@@ -30142,7 +30424,7 @@ if (bootAccountConfig?.whatsapp) {
|
|
|
30142
30424
|
}
|
|
30143
30425
|
init({
|
|
30144
30426
|
configDir: configDirForWhatsApp,
|
|
30145
|
-
platformRoot: resolve42(process.env.MAXY_PLATFORM_ROOT ??
|
|
30427
|
+
platformRoot: resolve42(process.env.MAXY_PLATFORM_ROOT ?? join50(__dirname, "..")),
|
|
30146
30428
|
accountConfig: bootAccountConfig,
|
|
30147
30429
|
// Task 2074 — enrich with the sub-account name and fan out to every open
|
|
30148
30430
|
// admin SSE stream. Fire-and-forget: the manager's persist path must not
|
|
@@ -30174,6 +30456,11 @@ init({
|
|
|
30174
30456
|
);
|
|
30175
30457
|
});
|
|
30176
30458
|
},
|
|
30459
|
+
// Task 2110 — the manager is the only place that sees a media stanza start
|
|
30460
|
+
// downloading; the gateway first sees the media on the completed inbound,
|
|
30461
|
+
// which is the race window's close. Wiring it here keeps the manager free of
|
|
30462
|
+
// any gateway import, exactly as onMessage and onNotify are wired.
|
|
30463
|
+
onMediaStanza: (senderId, atMs) => waGateway.noteMediaStanza(senderId, atMs),
|
|
30177
30464
|
onMessage: async (msg) => {
|
|
30178
30465
|
if (isObserveAccount(msg.accountId)) {
|
|
30179
30466
|
console.error(`[whatsapp:observe] op=BREACH account=${msg.accountId} sender=${msg.senderPhone} \u2014 dispatch reached the router from an observe socket`);
|