@rubytech/create-sitedesk-code 0.1.521 → 0.1.523
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +17 -3
- package/package.json +1 -1
- package/payload/platform/lib/account-schema-regions/package-lock.json +12 -0
- package/payload/platform/lib/active-rules/package-lock.json +12 -0
- package/payload/platform/lib/graph-style/dist/index.d.ts.map +1 -1
- package/payload/platform/lib/graph-style/dist/index.js +28 -0
- package/payload/platform/lib/graph-style/dist/index.js.map +1 -1
- package/payload/platform/lib/graph-style/src/__tests__/caption-coverage.test.ts +7 -0
- package/payload/platform/lib/graph-style/src/index.ts +24 -0
- package/payload/platform/lib/graph-write/dist/__tests__/merge-on.test.d.ts.map +1 -0
- package/payload/platform/lib/graph-write/dist/__tests__/merge-on.test.js.map +1 -0
- package/payload/platform/lib/graph-write/dist/index.d.ts +16 -0
- package/payload/platform/lib/graph-write/dist/index.d.ts.map +1 -1
- package/payload/platform/lib/graph-write/dist/index.js +83 -6
- package/payload/platform/lib/graph-write/dist/index.js.map +1 -1
- package/payload/platform/lib/graph-write/src/index.ts +117 -10
- package/payload/platform/lib/mcp-lifeline/package-lock.json +12 -0
- package/payload/platform/lib/routine-templates/dist/index.d.ts +151 -0
- package/payload/platform/lib/routine-templates/dist/index.d.ts.map +1 -0
- package/payload/platform/lib/routine-templates/dist/index.js +207 -0
- package/payload/platform/lib/routine-templates/dist/index.js.map +1 -0
- package/payload/platform/lib/routine-templates/dist/roster.d.ts +57 -0
- package/payload/platform/lib/routine-templates/dist/roster.d.ts.map +1 -0
- package/payload/platform/lib/routine-templates/dist/roster.js +182 -0
- package/payload/platform/lib/routine-templates/dist/roster.js.map +1 -0
- package/payload/platform/lib/routine-templates/dist/seed-cli.d.ts +2 -0
- package/payload/platform/lib/routine-templates/dist/seed-cli.d.ts.map +1 -0
- package/payload/platform/lib/routine-templates/dist/seed-cli.js +64 -0
- package/payload/platform/lib/routine-templates/dist/seed-cli.js.map +1 -0
- package/payload/platform/lib/routine-templates/package.json +6 -0
- package/payload/platform/lib/routine-templates/src/__tests__/roster.test.ts +170 -0
- package/payload/platform/lib/routine-templates/src/__tests__/seed.test.ts +298 -0
- package/payload/platform/lib/routine-templates/src/index.ts +299 -0
- package/payload/platform/lib/routine-templates/src/roster.ts +206 -0
- package/payload/platform/lib/routine-templates/src/seed-cli.ts +65 -0
- package/payload/platform/lib/routine-templates/tsconfig.json +9 -0
- package/payload/platform/lib/routine-templates/vitest.config.ts +9 -0
- package/payload/platform/neo4j/edge-annotations.json +2 -2
- package/payload/platform/neo4j/schema.cypher +25 -1
- package/payload/platform/package.json +3 -2
- package/payload/platform/plugins/admin/PLUGIN.md +2 -2
- package/payload/platform/plugins/admin/mcp/dist/index.js +39 -1
- package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +319 -66
- package/payload/platform/plugins/admin/skills/whats-new/SKILL.md +12 -0
- package/payload/platform/plugins/docs/references/admin-ui.md +275 -59
- package/payload/platform/plugins/docs/references/internals.md +4 -0
- package/payload/platform/plugins/docs/references/memory-guide.md +11 -1
- package/payload/platform/plugins/docs/references/outlook-guide.md +27 -4
- package/payload/platform/plugins/graph/.claude-plugin/plugin.json +1 -1
- package/payload/platform/plugins/graph/PLUGIN.md +1 -1
- package/payload/platform/plugins/memory/PLUGIN.md +1 -1
- package/payload/platform/plugins/memory/mcp/dist/index.js +12 -5
- package/payload/platform/plugins/memory/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/phase-estate-census.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/phase-estate-census.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/phase-estate-census.test.js +197 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/phase-estate-census.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/schema-loader.test.js +55 -9
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/schema-loader.test.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/schema-validator.test.js +96 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/schema-validator.test.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/dream-cycle/index.d.ts +7 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/dream-cycle/index.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/dream-cycle/phase-estate-census.d.ts +29 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/dream-cycle/phase-estate-census.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/dream-cycle/phase-estate-census.js +126 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/dream-cycle/phase-estate-census.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/scripts/dream-cycle-sweep.js +7 -4
- package/payload/platform/plugins/memory/mcp/dist/scripts/dream-cycle-sweep.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-dream-run.d.ts +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-dream-run.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-dream-run.js +18 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-dream-run.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.d.ts +7 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.js +4 -3
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/vitest.config.ts +2 -0
- package/payload/platform/plugins/memory/references/schema-estate-agent.md +2 -2
- package/payload/platform/plugins/memory/skills/conversation-archive-enrich/SKILL.md +4 -4
- package/payload/platform/plugins/outlook/.claude-plugin/plugin.json +1 -1
- package/payload/platform/plugins/outlook/PLUGIN.md +9 -1
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/delegated-resolve.test.d.ts +2 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/delegated-resolve.test.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/delegated-resolve.test.js +151 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/delegated-resolve.test.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/delegated-store.test.d.ts +2 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/delegated-store.test.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/delegated-store.test.js +151 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/delegated-store.test.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/device-flow.test.js +38 -4
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/device-flow.test.js.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/graph-client-prefix-wiring.test.d.ts +2 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/graph-client-prefix-wiring.test.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/graph-client-prefix-wiring.test.js +146 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/graph-client-prefix-wiring.test.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/mailbox-list-delegated.test.d.ts +2 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/mailbox-list-delegated.test.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/mailbox-list-delegated.test.js +120 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/mailbox-list-delegated.test.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/mailbox-list.test.js +8 -8
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/mailbox-list.test.js.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/mailbox-path-prefix.test.d.ts +2 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/mailbox-path-prefix.test.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/mailbox-path-prefix.test.js +27 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/mailbox-path-prefix.test.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/shared-mail-only-guard.test.d.ts +2 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/shared-mail-only-guard.test.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/shared-mail-only-guard.test.js +70 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/shared-mail-only-guard.test.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/shared-mailbox-add.test.d.ts +2 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/shared-mailbox-add.test.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/shared-mailbox-add.test.js +152 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/shared-mailbox-add.test.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/auth/delegated-store.d.ts +57 -0
- package/payload/platform/plugins/outlook/mcp/dist/auth/delegated-store.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/auth/delegated-store.js +163 -0
- package/payload/platform/plugins/outlook/mcp/dist/auth/delegated-store.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/auth/device-flow.d.ts +12 -0
- package/payload/platform/plugins/outlook/mcp/dist/auth/device-flow.d.ts.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/auth/device-flow.js +21 -1
- package/payload/platform/plugins/outlook/mcp/dist/auth/device-flow.js.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/auth/mailbox-registry.d.ts +35 -4
- package/payload/platform/plugins/outlook/mcp/dist/auth/mailbox-registry.d.ts.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/auth/mailbox-registry.js +92 -10
- package/payload/platform/plugins/outlook/mcp/dist/auth/mailbox-registry.js.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/index.js +106 -3
- package/payload/platform/plugins/outlook/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/lib/graph-client.d.ts +49 -0
- package/payload/platform/plugins/outlook/mcp/dist/lib/graph-client.d.ts.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/lib/graph-client.js +126 -8
- package/payload/platform/plugins/outlook/mcp/dist/lib/graph-client.js.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/tools/mailbox-info.d.ts +13 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/mailbox-info.d.ts.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/tools/mailbox-info.js +9 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/mailbox-info.js.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/tools/mailbox-list.d.ts +42 -8
- package/payload/platform/plugins/outlook/mcp/dist/tools/mailbox-list.d.ts.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/tools/mailbox-list.js +59 -19
- package/payload/platform/plugins/outlook/mcp/dist/tools/mailbox-list.js.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/tools/shared-mailbox-add.d.ts +47 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/shared-mailbox-add.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/shared-mailbox-add.js +67 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/shared-mailbox-add.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/shared-mailbox-remove.d.ts +12 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/shared-mailbox-remove.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/shared-mailbox-remove.js +18 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/shared-mailbox-remove.js.map +1 -0
- package/payload/platform/plugins/outlook/references/auth.md +15 -2
- package/payload/platform/plugins/outlook/references/graph-surfaces.md +8 -0
- package/payload/platform/plugins/outlook/skills/outlook/SKILL.md +21 -1
- package/payload/platform/plugins/quickbooks/PLUGIN.md +10 -10
- package/payload/platform/plugins/scheduling/PLUGIN.md +49 -1
- package/payload/platform/plugins/scheduling/mcp/dist/index.js +48 -0
- package/payload/platform/plugins/scheduling/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/notes-cap.test.d.ts +2 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/notes-cap.test.d.ts.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/notes-cap.test.js +78 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/notes-cap.test.js.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/routine-run.test.d.ts +2 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/routine-run.test.d.ts.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/routine-run.test.js +457 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/routine-run.test.js.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/notes-cap.d.ts +35 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/notes-cap.d.ts.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/notes-cap.js +39 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/notes-cap.js.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/routine-run.d.ts +175 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/routine-run.d.ts.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/routine-run.js +533 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/routine-run.js.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/agent-turn-dispatch.test.js +123 -9
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/agent-turn-dispatch.test.js.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/routine-roster-audit.test.d.ts +2 -0
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/routine-roster-audit.test.d.ts.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/routine-roster-audit.test.js +189 -0
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/routine-roster-audit.test.js.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/suspension-census.test.d.ts +2 -0
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/suspension-census.test.d.ts.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/suspension-census.test.js +95 -0
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/suspension-census.test.js.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/sweep-excludes-suspended.test.d.ts +2 -0
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/sweep-excludes-suspended.test.d.ts.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/sweep-excludes-suspended.test.js +114 -0
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/sweep-excludes-suspended.test.js.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/agent-turn-dispatch.d.ts +109 -2
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/agent-turn-dispatch.d.ts.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/agent-turn-dispatch.js +340 -9
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/agent-turn-dispatch.js.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/check-due-events.d.ts +9 -1
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/check-due-events.d.ts.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/check-due-events.js +210 -45
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/check-due-events.js.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/reconcile-bookings.js +43 -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-get-agent.test.js +55 -7
- package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-get-agent.test.js.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-list.test.js +45 -1
- package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-list.test.js.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-runs.test.d.ts +2 -0
- package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-runs.test.d.ts.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-runs.test.js +96 -0
- package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-runs.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 +2 -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 +5 -0
- 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 +13 -0
- 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 -0
- 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 +24 -3
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-list.js.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-runs.d.ts +33 -0
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-runs.d.ts.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-runs.js +51 -0
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-runs.js.map +1 -0
- 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 +12 -2
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-update.js.map +1 -1
- package/payload/platform/plugins/whatsapp/references/channels-whatsapp.md +30 -2
- package/payload/platform/plugins/whatsapp/skills/connect-whatsapp/SKILL.md +1 -1
- package/payload/platform/scripts/check-specialist-tool-surface.mjs +92 -16
- package/payload/platform/scripts/seed-neo4j.sh +41 -0
- package/payload/platform/scripts/verify-skill-tool-surface.sh +91 -2
- 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 +3 -0
- package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js.map +1 -1
- package/payload/platform/templates/specialists/agents/librarian.md +1 -1
- package/payload/platform/templates/specialists/agents/personal-assistant.md +1 -1
- package/payload/server/{chunk-AZLT4LVO.js → chunk-VUXKXOJU.js} +755 -335
- package/payload/server/{chunk-UX2FRNO7.js → chunk-WT2PFGY4.js} +37 -265
- package/payload/server/{chunk-PXNY2TAP.js → chunk-XCR5D2G3.js} +239 -10
- package/payload/server/{manager-CQBSUJXR.js → manager-4TJK3YOY.js} +4 -2
- package/payload/server/maxy-edge.js +2 -2
- package/payload/server/public/activity.html +5 -5
- package/payload/server/public/agents.html +4 -4
- package/payload/server/public/assets/{AdminLoginScreens-Dar_MMJm.js → AdminLoginScreens-uknqh2Pm.js} +1 -1
- package/payload/server/public/assets/AdminLoginScreens-uknqh2Pm.js.br +0 -0
- package/payload/server/public/assets/AdminLoginScreens-uknqh2Pm.js.gz +0 -0
- package/payload/server/public/assets/AdminShell-GqtMImyQ.js +2 -0
- package/payload/server/public/assets/AdminShell-GqtMImyQ.js.br +0 -0
- package/payload/server/public/assets/AdminShell-GqtMImyQ.js.gz +0 -0
- package/payload/server/public/assets/{activity-B1rt4GIy.js → activity-B4KfMJqO.js} +1 -1
- package/payload/server/public/assets/activity-B4KfMJqO.js.br +0 -0
- package/payload/server/public/assets/activity-B4KfMJqO.js.gz +0 -0
- package/payload/server/public/assets/admin-DxCZeVw0.js +1 -0
- package/payload/server/public/assets/admin-DxCZeVw0.js.br +0 -0
- package/payload/server/public/assets/admin-DxCZeVw0.js.gz +0 -0
- package/payload/server/public/assets/agents-BtwBuO4G.js +1 -0
- package/payload/server/public/assets/agents-BtwBuO4G.js.br +0 -0
- package/payload/server/public/assets/agents-BtwBuO4G.js.gz +0 -0
- package/payload/server/public/assets/{browser-1QSCPl3X.js → browser-B3hSzI3O.js} +1 -1
- package/payload/server/public/assets/browser-B3hSzI3O.js.br +0 -0
- package/payload/server/public/assets/browser-B3hSzI3O.js.gz +0 -0
- package/payload/server/public/assets/{calendar-D009qWVV.js → calendar-BggxXxhS.js} +1 -1
- package/payload/server/public/assets/calendar-BggxXxhS.js.br +0 -0
- package/payload/server/public/assets/calendar-BggxXxhS.js.gz +0 -0
- package/payload/server/public/assets/chat-flivHt0t.js +1 -0
- package/payload/server/public/assets/chat-flivHt0t.js.br +0 -0
- package/payload/server/public/assets/chat-flivHt0t.js.gz +0 -0
- package/payload/server/public/assets/chevron-left-nVRThX7f.js +1 -0
- package/payload/server/public/assets/chevron-right-CLqfrkRA.js +1 -0
- package/payload/server/public/assets/chevron-right-CLqfrkRA.js.br +0 -0
- package/payload/server/public/assets/clock-Dj3SRZKo.js +1 -0
- package/payload/server/public/assets/clock-Dj3SRZKo.js.br +0 -0
- package/payload/server/public/assets/clock-Dj3SRZKo.js.gz +0 -0
- package/payload/server/public/assets/data-Ckjkne54.js +1 -0
- package/payload/server/public/assets/data-Ckjkne54.js.br +3 -0
- package/payload/server/public/assets/data-Ckjkne54.js.gz +0 -0
- package/payload/server/public/assets/{file-text-DwGAfCsd.js → file-text-BKs9eyiw.js} +1 -1
- package/payload/server/public/assets/file-text-BKs9eyiw.js.br +0 -0
- package/payload/server/public/assets/file-text-BKs9eyiw.js.gz +0 -0
- package/payload/server/public/assets/{graph-D-DGlfhW.js → graph-DTi71NaZ.js} +3 -3
- package/payload/server/public/assets/graph-DTi71NaZ.js.br +0 -0
- package/payload/server/public/assets/graph-DTi71NaZ.js.gz +0 -0
- package/payload/server/public/assets/graph-labels-BT3S6e9H.js +1 -0
- package/payload/server/public/assets/graph-labels-BT3S6e9H.js.br +0 -0
- package/payload/server/public/assets/graph-labels-BT3S6e9H.js.gz +0 -0
- package/payload/server/public/assets/{maximize-2-C_-KapxH.js → maximize-2-zC94tsxY.js} +1 -1
- package/payload/server/public/assets/maximize-2-zC94tsxY.js.br +0 -0
- package/payload/server/public/assets/maximize-2-zC94tsxY.js.gz +0 -0
- package/payload/server/public/assets/operator-BtWNEMh-.js +1 -0
- package/payload/server/public/assets/operator-BtWNEMh-.js.br +0 -0
- package/payload/server/public/assets/operator-BtWNEMh-.js.gz +0 -0
- package/payload/server/public/assets/page-BFm91p0e.js +1 -0
- package/payload/server/public/assets/page-BFm91p0e.js.br +0 -0
- package/payload/server/public/assets/page-BFm91p0e.js.gz +0 -0
- package/payload/server/public/assets/{page-E74MYB18.js → page-Ccl0FazM.js} +3 -3
- package/payload/server/public/assets/page-Ccl0FazM.js.br +0 -0
- package/payload/server/public/assets/page-Ccl0FazM.js.gz +0 -0
- package/payload/server/public/assets/{public-CI6K1sNW.js → public-BpC6cn9S.js} +1 -1
- package/payload/server/public/assets/public-BpC6cn9S.js.br +0 -0
- package/payload/server/public/assets/public-BpC6cn9S.js.gz +0 -0
- package/payload/server/public/assets/{rotate-ccw-PWu_QGrD.js → rotate-ccw-BNcqbHkZ.js} +1 -1
- package/payload/server/public/assets/rotate-ccw-BNcqbHkZ.js.br +0 -0
- package/payload/server/public/assets/rotate-ccw-BNcqbHkZ.js.gz +0 -0
- package/payload/server/public/assets/routines-D5iia-XG.js +1 -0
- package/payload/server/public/assets/routines-D5iia-XG.js.br +0 -0
- package/payload/server/public/assets/routines-D5iia-XG.js.gz +0 -0
- package/payload/server/public/assets/{skills-g8HvunPA.js → skills-COLAw_jt.js} +1 -1
- package/payload/server/public/assets/skills-COLAw_jt.js.br +0 -0
- package/payload/server/public/assets/skills-COLAw_jt.js.gz +0 -0
- package/payload/server/public/assets/tasks-CZDQI4nB.js +1 -0
- package/payload/server/public/assets/tasks-CZDQI4nB.js.br +0 -0
- package/payload/server/public/assets/tasks-CZDQI4nB.js.gz +0 -0
- package/payload/server/public/assets/{time-entry-format-BXWuQ6Sw.js → time-entry-format-laTFzawg.js} +1 -1
- package/payload/server/public/assets/time-entry-format-laTFzawg.js.br +0 -0
- package/payload/server/public/assets/time-entry-format-laTFzawg.js.gz +0 -0
- package/payload/server/public/assets/{triangle-alert-BNshRC13.js → triangle-alert-DrwqhEog.js} +1 -1
- package/payload/server/public/assets/triangle-alert-DrwqhEog.js.br +0 -0
- package/payload/server/public/assets/triangle-alert-DrwqhEog.js.gz +0 -0
- package/payload/server/public/assets/{useCopyFeedback-CwTZ9DqH.js → useCopyFeedback-Cxme5GK6.js} +1 -1
- package/payload/server/public/assets/useCopyFeedback-Cxme5GK6.js.br +0 -0
- package/payload/server/public/assets/useCopyFeedback-Cxme5GK6.js.gz +0 -0
- package/payload/server/public/assets/useSubAccountSwitcher-B-K8y5a4.css +1 -0
- package/payload/server/public/assets/useSubAccountSwitcher-B-K8y5a4.css.br +0 -0
- package/payload/server/public/assets/useSubAccountSwitcher-B-K8y5a4.css.gz +0 -0
- package/payload/server/public/assets/useSubAccountSwitcher-C_1E11Dv.js +14 -0
- package/payload/server/public/assets/useSubAccountSwitcher-C_1E11Dv.js.br +0 -0
- package/payload/server/public/assets/useSubAccountSwitcher-C_1E11Dv.js.gz +0 -0
- package/payload/server/public/assets/{useVoiceRecorder-BGqV5R6u.js → useVoiceRecorder-DUBpVa2T.js} +1 -1
- package/payload/server/public/assets/useVoiceRecorder-DUBpVa2T.js.br +0 -0
- package/payload/server/public/assets/useVoiceRecorder-DUBpVa2T.js.gz +0 -0
- package/payload/server/public/assets/{wrench-DQ045Pyk.js → wrench-DWuxPnzF.js} +1 -1
- package/payload/server/public/assets/wrench-DWuxPnzF.js.br +0 -0
- package/payload/server/public/assets/wrench-DWuxPnzF.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 +6631 -5690
- 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 +0 -2
- 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-B1rt4GIy.js.br +0 -0
- package/payload/server/public/assets/activity-B1rt4GIy.js.gz +0 -0
- package/payload/server/public/assets/admin-DE3lLNje.js +0 -1
- package/payload/server/public/assets/admin-DE3lLNje.js.br +0 -0
- package/payload/server/public/assets/admin-DE3lLNje.js.gz +0 -0
- package/payload/server/public/assets/agents-BXq8woVf.js +0 -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-1QSCPl3X.js.br +0 -0
- package/payload/server/public/assets/browser-1QSCPl3X.js.gz +0 -0
- package/payload/server/public/assets/calendar-D009qWVV.js.br +0 -0
- package/payload/server/public/assets/calendar-D009qWVV.js.gz +0 -0
- package/payload/server/public/assets/chat-CUg1fTqJ.js +0 -1
- package/payload/server/public/assets/chat-CUg1fTqJ.js.br +0 -1
- package/payload/server/public/assets/chat-CUg1fTqJ.js.gz +0 -0
- package/payload/server/public/assets/chevron-left-C2JEy7uU.js +0 -1
- package/payload/server/public/assets/chevron-left-C2JEy7uU.js.br +0 -0
- package/payload/server/public/assets/chevron-right-DWa047Ys.js +0 -1
- package/payload/server/public/assets/chevron-right-DWa047Ys.js.br +0 -0
- package/payload/server/public/assets/clock-C3OqfCnn.js +0 -1
- package/payload/server/public/assets/clock-C3OqfCnn.js.br +0 -0
- package/payload/server/public/assets/clock-C3OqfCnn.js.gz +0 -0
- package/payload/server/public/assets/data-Bu_yCScC.js +0 -1
- package/payload/server/public/assets/data-Bu_yCScC.js.br +0 -0
- package/payload/server/public/assets/data-Bu_yCScC.js.gz +0 -0
- package/payload/server/public/assets/file-text-DwGAfCsd.js.br +0 -0
- package/payload/server/public/assets/file-text-DwGAfCsd.js.gz +0 -0
- package/payload/server/public/assets/graph-D-DGlfhW.js.br +0 -0
- package/payload/server/public/assets/graph-D-DGlfhW.js.gz +0 -0
- package/payload/server/public/assets/graph-labels-BbX3fXzl.js +0 -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-C_-KapxH.js.br +0 -0
- package/payload/server/public/assets/maximize-2-C_-KapxH.js.gz +0 -0
- package/payload/server/public/assets/operator-CFrZVcv6.js +0 -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-CHt62f1_.js +0 -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.br +0 -0
- package/payload/server/public/assets/page-E74MYB18.js.gz +0 -0
- package/payload/server/public/assets/public-CI6K1sNW.js.br +0 -0
- package/payload/server/public/assets/public-CI6K1sNW.js.gz +0 -0
- package/payload/server/public/assets/rotate-ccw-PWu_QGrD.js.br +0 -0
- package/payload/server/public/assets/rotate-ccw-PWu_QGrD.js.gz +0 -0
- package/payload/server/public/assets/routines-Cp3_XS7S.js +0 -1
- package/payload/server/public/assets/routines-Cp3_XS7S.js.br +0 -0
- package/payload/server/public/assets/routines-Cp3_XS7S.js.gz +0 -0
- package/payload/server/public/assets/skills-g8HvunPA.js.br +0 -0
- package/payload/server/public/assets/skills-g8HvunPA.js.gz +0 -0
- package/payload/server/public/assets/tasks-CMC_jDVm.js +0 -1
- package/payload/server/public/assets/tasks-CMC_jDVm.js.br +0 -0
- package/payload/server/public/assets/tasks-CMC_jDVm.js.gz +0 -0
- package/payload/server/public/assets/time-entry-format-BXWuQ6Sw.js.br +0 -2
- package/payload/server/public/assets/time-entry-format-BXWuQ6Sw.js.gz +0 -0
- package/payload/server/public/assets/triangle-alert-BNshRC13.js.br +0 -0
- package/payload/server/public/assets/triangle-alert-BNshRC13.js.gz +0 -0
- package/payload/server/public/assets/useCopyFeedback-CwTZ9DqH.js.br +0 -0
- package/payload/server/public/assets/useCopyFeedback-CwTZ9DqH.js.gz +0 -0
- package/payload/server/public/assets/useSubAccountSwitcher-BuUYMDWl.js +0 -14
- 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 +0 -1
- 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-BGqV5R6u.js.br +0 -0
- package/payload/server/public/assets/useVoiceRecorder-BGqV5R6u.js.gz +0 -0
- package/payload/server/public/assets/wrench-DQ045Pyk.js.br +0 -0
- package/payload/server/public/assets/wrench-DQ045Pyk.js.gz +0 -0
|
@@ -6,8 +6,9 @@ import {
|
|
|
6
6
|
ensureConversation,
|
|
7
7
|
getSession,
|
|
8
8
|
getUserTimezone,
|
|
9
|
-
isValidAccountId
|
|
10
|
-
|
|
9
|
+
isValidAccountId,
|
|
10
|
+
registerLoop
|
|
11
|
+
} from "./chunk-XCR5D2G3.js";
|
|
11
12
|
|
|
12
13
|
// app/lib/whatsapp/schema.ts
|
|
13
14
|
import { z } from "zod";
|
|
@@ -257,7 +258,6 @@ import { inspect } from "util";
|
|
|
257
258
|
import {
|
|
258
259
|
Browsers,
|
|
259
260
|
DisconnectReason,
|
|
260
|
-
fetchLatestBaileysVersion,
|
|
261
261
|
makeCacheableSignalKeyStore,
|
|
262
262
|
makeWASocket,
|
|
263
263
|
useMultiFileAuthState
|
|
@@ -318,6 +318,18 @@ function resolveDebugWindow(authDir, nowMs = Date.now()) {
|
|
|
318
318
|
return state;
|
|
319
319
|
}
|
|
320
320
|
|
|
321
|
+
// app/lib/whatsapp/companion-reg.ts
|
|
322
|
+
var PRIMARY_IDENTITY_PUB = "primary_identity_pub";
|
|
323
|
+
function classifyCompanionReg(node) {
|
|
324
|
+
const outer = node?.content;
|
|
325
|
+
const child = Array.isArray(outer) ? outer.find((c) => c?.tag === "link_code_companion_reg") : void 0;
|
|
326
|
+
const rawStage = child?.attrs?.stage;
|
|
327
|
+
const stage = typeof rawStage === "string" && rawStage ? rawStage : "absent";
|
|
328
|
+
const inner = child?.content;
|
|
329
|
+
const children = Array.isArray(inner) ? inner.map((c) => c?.tag).filter((t) => typeof t === "string") : [];
|
|
330
|
+
return { stage, children, parseable: children.includes(PRIMARY_IDENTITY_PUB) };
|
|
331
|
+
}
|
|
332
|
+
|
|
321
333
|
// app/lib/whatsapp/ingest-ledger.ts
|
|
322
334
|
import { existsSync, mkdirSync, appendFileSync, readFileSync, writeFileSync, readdirSync } from "fs";
|
|
323
335
|
import { join } from "path";
|
|
@@ -332,32 +344,32 @@ function ledgerFilePath(account) {
|
|
|
332
344
|
}
|
|
333
345
|
return join(root, `${account}.jsonl`);
|
|
334
346
|
}
|
|
335
|
-
function appendStanza(
|
|
347
|
+
function appendStanza(entry2) {
|
|
336
348
|
try {
|
|
337
349
|
const row = {
|
|
338
|
-
msgId:
|
|
339
|
-
from:
|
|
340
|
-
type:
|
|
350
|
+
msgId: entry2.msgId,
|
|
351
|
+
from: entry2.from,
|
|
352
|
+
type: entry2.type,
|
|
341
353
|
ts: (/* @__PURE__ */ new Date()).toISOString()
|
|
342
354
|
};
|
|
343
|
-
appendFileSync(ledgerFilePath(
|
|
355
|
+
appendFileSync(ledgerFilePath(entry2.account), JSON.stringify(row) + "\n");
|
|
344
356
|
} catch (err) {
|
|
345
357
|
console.error(
|
|
346
|
-
`${TAG3} FAIL op=append msgId=${
|
|
358
|
+
`${TAG3} FAIL op=append msgId=${entry2.msgId} err=${err instanceof Error ? err.message : String(err)}`
|
|
347
359
|
);
|
|
348
360
|
}
|
|
349
361
|
}
|
|
350
|
-
function markDeclined(
|
|
362
|
+
function markDeclined(entry2) {
|
|
351
363
|
try {
|
|
352
364
|
const row = {
|
|
353
|
-
declined:
|
|
354
|
-
reason:
|
|
365
|
+
declined: entry2.msgId,
|
|
366
|
+
reason: entry2.reason,
|
|
355
367
|
ts: (/* @__PURE__ */ new Date()).toISOString()
|
|
356
368
|
};
|
|
357
|
-
appendFileSync(ledgerFilePath(
|
|
369
|
+
appendFileSync(ledgerFilePath(entry2.account), JSON.stringify(row) + "\n");
|
|
358
370
|
} catch (err) {
|
|
359
371
|
console.error(
|
|
360
|
-
`${TAG3} FAIL op=decline msgId=${
|
|
372
|
+
`${TAG3} FAIL op=decline msgId=${entry2.msgId} err=${err instanceof Error ? err.message : String(err)}`
|
|
361
373
|
);
|
|
362
374
|
}
|
|
363
375
|
}
|
|
@@ -375,36 +387,36 @@ function readDeclined(account) {
|
|
|
375
387
|
}
|
|
376
388
|
return out;
|
|
377
389
|
}
|
|
378
|
-
function appendNotifyEligible(
|
|
390
|
+
function appendNotifyEligible(entry2) {
|
|
379
391
|
try {
|
|
380
392
|
const row = {
|
|
381
|
-
notifyEligible:
|
|
382
|
-
upsertType:
|
|
383
|
-
fromMe:
|
|
384
|
-
allowed:
|
|
385
|
-
reason:
|
|
386
|
-
passive:
|
|
387
|
-
persistAccount:
|
|
393
|
+
notifyEligible: entry2.msgId,
|
|
394
|
+
upsertType: entry2.upsertType,
|
|
395
|
+
fromMe: entry2.fromMe,
|
|
396
|
+
allowed: entry2.allowed,
|
|
397
|
+
reason: entry2.reason,
|
|
398
|
+
passive: entry2.passive,
|
|
399
|
+
persistAccount: entry2.persistAccount,
|
|
388
400
|
ts: (/* @__PURE__ */ new Date()).toISOString()
|
|
389
401
|
};
|
|
390
|
-
appendFileSync(ledgerFilePath(
|
|
402
|
+
appendFileSync(ledgerFilePath(entry2.account), JSON.stringify(row) + "\n");
|
|
391
403
|
} catch (err) {
|
|
392
404
|
console.error(
|
|
393
|
-
`${TAG3} FAIL op=append-notify-eligible msgId=${
|
|
405
|
+
`${TAG3} FAIL op=append-notify-eligible msgId=${entry2.msgId} err=${err instanceof Error ? err.message : String(err)}`
|
|
394
406
|
);
|
|
395
407
|
}
|
|
396
408
|
}
|
|
397
|
-
function appendNotifyPublished(
|
|
409
|
+
function appendNotifyPublished(entry2) {
|
|
398
410
|
try {
|
|
399
411
|
const row = {
|
|
400
|
-
notifyPublished:
|
|
401
|
-
subscribers:
|
|
412
|
+
notifyPublished: entry2.msgId,
|
|
413
|
+
subscribers: entry2.subscribers,
|
|
402
414
|
ts: (/* @__PURE__ */ new Date()).toISOString()
|
|
403
415
|
};
|
|
404
|
-
appendFileSync(ledgerFilePath(
|
|
416
|
+
appendFileSync(ledgerFilePath(entry2.account), JSON.stringify(row) + "\n");
|
|
405
417
|
} catch (err) {
|
|
406
418
|
console.error(
|
|
407
|
-
`${TAG3} FAIL op=append-notify-published msgId=${
|
|
419
|
+
`${TAG3} FAIL op=append-notify-published msgId=${entry2.msgId} err=${err instanceof Error ? err.message : String(err)}`
|
|
408
420
|
);
|
|
409
421
|
}
|
|
410
422
|
}
|
|
@@ -471,8 +483,102 @@ function listLedgerAccounts() {
|
|
|
471
483
|
}
|
|
472
484
|
}
|
|
473
485
|
|
|
486
|
+
// app/lib/whatsapp/version-census.ts
|
|
487
|
+
import { DEFAULT_CONNECTION_CONFIG, fetchLatestBaileysVersion } from "@whiskeysockets/baileys";
|
|
488
|
+
var TAG4 = "[whatsapp:version]";
|
|
489
|
+
var BAILEYS_VERSION_URL = "https://raw.githubusercontent.com/WhiskeySockets/Baileys/master/src/Defaults/index.ts";
|
|
490
|
+
var VERSION_RE = /const version = \[(\d+),\s*(\d+),\s*(\d+)\]/;
|
|
491
|
+
var presented = /* @__PURE__ */ new Map();
|
|
492
|
+
var behindSince = null;
|
|
493
|
+
function packagedVersion() {
|
|
494
|
+
return DEFAULT_CONNECTION_CONFIG.version.join(".");
|
|
495
|
+
}
|
|
496
|
+
function messageOf(err) {
|
|
497
|
+
return err instanceof Error ? err.message : String(err);
|
|
498
|
+
}
|
|
499
|
+
var PROBE_TIMEOUT_MS = 5e3;
|
|
500
|
+
async function probeSourceAge(fetchImpl) {
|
|
501
|
+
const none = (probe) => ({ sourceAgeSec: null, edge: null, probe });
|
|
502
|
+
try {
|
|
503
|
+
const res = await fetchImpl(BAILEYS_VERSION_URL, {
|
|
504
|
+
method: "HEAD",
|
|
505
|
+
signal: AbortSignal.timeout(PROBE_TIMEOUT_MS)
|
|
506
|
+
});
|
|
507
|
+
if (!res.ok) return none(`http-${res.status}`);
|
|
508
|
+
const header = res.headers.get("age") !== null ? "age" : "source-age";
|
|
509
|
+
const raw = res.headers.get(header);
|
|
510
|
+
const n = raw === null ? Number.NaN : Number.parseInt(raw, 10);
|
|
511
|
+
if (!Number.isFinite(n)) return none("no-header");
|
|
512
|
+
return { sourceAgeSec: n, edge: res.headers.get("x-served-by"), probe: header };
|
|
513
|
+
} catch (err) {
|
|
514
|
+
return none(err instanceof Error && err.name === "TimeoutError" ? "timeout" : "net-error");
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
async function resolveWaVersion(opts) {
|
|
518
|
+
const { account } = opts;
|
|
519
|
+
const fetchImpl = opts.fetchImpl ?? fetch;
|
|
520
|
+
const fetchVersion = opts.fetchVersion ?? fetchLatestBaileysVersion;
|
|
521
|
+
const now = opts.now ?? Date.now;
|
|
522
|
+
const timed = (async () => {
|
|
523
|
+
const t0 = now();
|
|
524
|
+
const r2 = await fetchVersion();
|
|
525
|
+
return { r: r2, ms: now() - t0 };
|
|
526
|
+
})();
|
|
527
|
+
const [{ r, ms }, probe] = await Promise.all([timed, probeSourceAge(fetchImpl)]);
|
|
528
|
+
const err = r.error;
|
|
529
|
+
const origin = r.isLatest === false || err !== void 0 ? "packaged-fallback" : "fetched";
|
|
530
|
+
const versionStr = r.version.join(".");
|
|
531
|
+
const packaged = packagedVersion();
|
|
532
|
+
presented.set(account, versionStr);
|
|
533
|
+
console.error(
|
|
534
|
+
`${TAG4} op=resolve account=${account} version=${versionStr} origin=${origin} sourceAgeSec=${probe.sourceAgeSec ?? "unknown"} probe=${probe.probe} edge=${probe.edge ?? "unknown"} packaged=${packaged} matchesPackaged=${versionStr === packaged} fetchMs=${ms} error=${err === void 0 ? "none" : `"${messageOf(err)}"`}`
|
|
535
|
+
);
|
|
536
|
+
return { version: r.version };
|
|
537
|
+
}
|
|
538
|
+
var CENSUS_INTERVAL_MS = 15 * 60 * 1e3;
|
|
539
|
+
async function runVersionDriftCensus(opts = {}) {
|
|
540
|
+
const fetchImpl = opts.fetchImpl ?? fetch;
|
|
541
|
+
const now = opts.now ?? (() => /* @__PURE__ */ new Date());
|
|
542
|
+
let upstream = null;
|
|
543
|
+
let reason = "net-error";
|
|
544
|
+
try {
|
|
545
|
+
const res = await fetchImpl(BAILEYS_VERSION_URL);
|
|
546
|
+
if (!res.ok) {
|
|
547
|
+
reason = `http-${res.status}`;
|
|
548
|
+
} else {
|
|
549
|
+
const m = VERSION_RE.exec(await res.text());
|
|
550
|
+
if (m) upstream = `${m[1]}.${m[2]}.${m[3]}`;
|
|
551
|
+
else reason = "parse-failed";
|
|
552
|
+
}
|
|
553
|
+
} catch {
|
|
554
|
+
upstream = null;
|
|
555
|
+
reason = "net-error";
|
|
556
|
+
}
|
|
557
|
+
const distinct = [...new Set(presented.values())].sort();
|
|
558
|
+
const presentedStr = distinct.length === 0 ? "none" : distinct.join(",");
|
|
559
|
+
if (upstream !== null && distinct.length > 0) {
|
|
560
|
+
if (distinct.some((v) => v !== upstream)) {
|
|
561
|
+
if (behindSince === null) behindSince = now().toISOString();
|
|
562
|
+
} else {
|
|
563
|
+
behindSince = null;
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
console.error(
|
|
567
|
+
`${TAG4} op=drift-census presented=${presentedStr} upstream=${upstream ?? reason} behindSince=${behindSince ?? "none"}`
|
|
568
|
+
);
|
|
569
|
+
return `presented=${presentedStr} upstream=${upstream ?? reason}`;
|
|
570
|
+
}
|
|
571
|
+
function startVersionDriftCensus(intervalMs = CENSUS_INTERVAL_MS) {
|
|
572
|
+
return registerLoop({
|
|
573
|
+
name: "whatsapp-version-drift",
|
|
574
|
+
intervalMs,
|
|
575
|
+
firstRunDelayMs: 0,
|
|
576
|
+
run: () => runVersionDriftCensus()
|
|
577
|
+
});
|
|
578
|
+
}
|
|
579
|
+
|
|
474
580
|
// app/lib/whatsapp/session.ts
|
|
475
|
-
var
|
|
581
|
+
var TAG5 = "[whatsapp:session]";
|
|
476
582
|
var WIRE_TAG = "[whatsapp:wire]";
|
|
477
583
|
var baileysPkgVersion = (() => {
|
|
478
584
|
try {
|
|
@@ -482,7 +588,7 @@ var baileysPkgVersion = (() => {
|
|
|
482
588
|
return "unknown";
|
|
483
589
|
}
|
|
484
590
|
})();
|
|
485
|
-
console.error(`${
|
|
591
|
+
console.error(`${TAG5} baileys package loaded version=${baileysPkgVersion}`);
|
|
486
592
|
var __BAILEYS_TAG = "[whatsapp:baileys]";
|
|
487
593
|
var INSPECT_OPTS = {
|
|
488
594
|
depth: null,
|
|
@@ -543,7 +649,7 @@ function createBaileysLogger(bindings = {}, authDir) {
|
|
|
543
649
|
}
|
|
544
650
|
var credsSaveQueue = Promise.resolve();
|
|
545
651
|
async function drainCredsSaveQueue(timeoutMs = 5e3) {
|
|
546
|
-
console.error(`${
|
|
652
|
+
console.error(`${TAG5} draining credential save queue\u2026`);
|
|
547
653
|
const timer = new Promise(
|
|
548
654
|
(resolve6) => setTimeout(() => resolve6("timeout"), timeoutMs)
|
|
549
655
|
);
|
|
@@ -552,9 +658,9 @@ async function drainCredsSaveQueue(timeoutMs = 5e3) {
|
|
|
552
658
|
timer
|
|
553
659
|
]);
|
|
554
660
|
if (result === "timeout") {
|
|
555
|
-
console.error(`${
|
|
661
|
+
console.error(`${TAG5} credential save queue drain timed out after ${timeoutMs}ms`);
|
|
556
662
|
} else {
|
|
557
|
-
console.error(`${
|
|
663
|
+
console.error(`${TAG5} credential save queue drained`);
|
|
558
664
|
}
|
|
559
665
|
}
|
|
560
666
|
function readCredsJsonRaw2(filePath) {
|
|
@@ -577,11 +683,11 @@ async function safeSaveCreds(authDir, saveCreds) {
|
|
|
577
683
|
JSON.parse(raw);
|
|
578
684
|
fsSync2.copyFileSync(credsPath, backupPath);
|
|
579
685
|
} catch (err) {
|
|
580
|
-
console.warn(`${
|
|
686
|
+
console.warn(`${TAG5} backup pre-copy failed (parse or write) authDir=${authDir}: ${String(err)}`);
|
|
581
687
|
}
|
|
582
688
|
}
|
|
583
689
|
} catch (err) {
|
|
584
|
-
console.warn(`${
|
|
690
|
+
console.warn(`${TAG5} backup preparation failed authDir=${authDir}: ${String(err)}`);
|
|
585
691
|
}
|
|
586
692
|
try {
|
|
587
693
|
await Promise.resolve(saveCreds());
|
|
@@ -589,39 +695,39 @@ async function safeSaveCreds(authDir, saveCreds) {
|
|
|
589
695
|
try {
|
|
590
696
|
const stats = await fs3.stat(credsPath);
|
|
591
697
|
if (stats.size <= 1) {
|
|
592
|
-
console.error(`${
|
|
698
|
+
console.error(`${TAG5} WARNING: creds file empty/truncated after save path=${credsPath} size=${stats.size}`);
|
|
593
699
|
} else {
|
|
594
|
-
console.error(`${
|
|
700
|
+
console.error(`${TAG5} creds saved and verified path=${credsPath} size=${stats.size}`);
|
|
595
701
|
}
|
|
596
702
|
} catch (statErr) {
|
|
597
|
-
console.error(`${
|
|
703
|
+
console.error(`${TAG5} creds NOT found on disk after save path=${credsPath}: ${String(statErr)}`);
|
|
598
704
|
}
|
|
599
705
|
} catch (err) {
|
|
600
|
-
console.error(`${
|
|
706
|
+
console.error(`${TAG5} failed saving creds to ${authDir}: ${String(err)}`);
|
|
601
707
|
}
|
|
602
708
|
}
|
|
603
709
|
function enqueueSaveCreds(authDir, saveCreds) {
|
|
604
710
|
credsSaveQueue = credsSaveQueue.then(() => safeSaveCreds(authDir, saveCreds)).catch((err) => {
|
|
605
|
-
console.error(`${
|
|
711
|
+
console.error(`${TAG5} creds save queue error: ${String(err)}`);
|
|
606
712
|
});
|
|
607
713
|
}
|
|
608
714
|
async function createWaSocket(opts) {
|
|
609
|
-
const { authDir, onQr, onConnectionUpdate, silent, correlationId, account, getCodeIssuedTs } = opts;
|
|
715
|
+
const { authDir, onQr, onConnectionUpdate, silent, correlationId, account, getCodeIssuedTs, onCompanionReg } = opts;
|
|
610
716
|
const cid = correlationId ?? "none";
|
|
611
717
|
const acct = account ?? "unknown";
|
|
612
718
|
await fs3.mkdir(authDir, { recursive: true });
|
|
613
719
|
maybeRestoreCredsFromBackup(authDir);
|
|
614
720
|
const { state, saveCreds } = await useMultiFileAuthState(authDir);
|
|
615
|
-
const { version } = await
|
|
721
|
+
const { version } = await resolveWaVersion({ account: acct });
|
|
616
722
|
if (!silent) {
|
|
617
723
|
const preCensus = credsCensus(authDir);
|
|
618
724
|
console.error(
|
|
619
|
-
`${
|
|
725
|
+
`${TAG5} op=creds-pre cid=${cid} account=${acct} credsPresent=${preCensus.credsPresent} registered=${preCensus.registered} hasAccount=${preCensus.hasAccountSignature}`
|
|
620
726
|
);
|
|
621
727
|
}
|
|
622
728
|
const openTs = Date.now();
|
|
623
729
|
console.error(
|
|
624
|
-
`${
|
|
730
|
+
`${TAG5} op=socket-open cid=${cid} account=${acct} baileysVersion=${baileysPkgVersion} waProtocolVersion=${version.join(".")} openTs=${openTs}`
|
|
625
731
|
);
|
|
626
732
|
const baileysLogger = createBaileysLogger({}, authDir);
|
|
627
733
|
const sock = makeWASocket({
|
|
@@ -638,7 +744,7 @@ async function createWaSocket(opts) {
|
|
|
638
744
|
fireInitQueries: false
|
|
639
745
|
});
|
|
640
746
|
sock.ev.on("creds.update", () => {
|
|
641
|
-
console.error(`${
|
|
747
|
+
console.error(`${TAG5} creds.update received \u2014 saving to ${authDir}`);
|
|
642
748
|
enqueueSaveCreds(authDir, saveCreds);
|
|
643
749
|
});
|
|
644
750
|
let qrSequence = 0;
|
|
@@ -658,14 +764,14 @@ async function createWaSocket(opts) {
|
|
|
658
764
|
parts.push(`disconnectReason=${formatError(update.lastDisconnect.error)}`);
|
|
659
765
|
}
|
|
660
766
|
if (!silent && parts.length > 0) {
|
|
661
|
-
console.error(`${
|
|
767
|
+
console.error(`${TAG5} connection.update ${parts.join(" ")}`);
|
|
662
768
|
}
|
|
663
769
|
if (update.qr && !silent) {
|
|
664
770
|
const now = Date.now();
|
|
665
771
|
const sinceLastRefMs = lastQrTs ? now - lastQrTs : null;
|
|
666
772
|
lastQrTs = now;
|
|
667
773
|
console.error(
|
|
668
|
-
`${
|
|
774
|
+
`${TAG5} op=qr cid=${cid} qr=#${qrSequence} sinceOpenMs=${now - openTs} sinceLastRefMs=${sinceLastRefMs ?? "n/a"}`
|
|
669
775
|
);
|
|
670
776
|
}
|
|
671
777
|
if (update.isNewLogin === true && !pairSuccessLogged && !silent) {
|
|
@@ -674,7 +780,7 @@ async function createWaSocket(opts) {
|
|
|
674
780
|
const sinceCodeIssuedMs = codeIssuedTs ? Date.now() - codeIssuedTs : null;
|
|
675
781
|
const afterCensus = credsCensus(authDir);
|
|
676
782
|
console.error(
|
|
677
|
-
`${
|
|
783
|
+
`${TAG5} op=pair-success cid=${cid} account=${acct} sinceCodeIssuedMs=${sinceCodeIssuedMs ?? "n/a"} hasAccountAfter=${afterCensus.hasAccountSignature}`
|
|
678
784
|
);
|
|
679
785
|
}
|
|
680
786
|
if (update.qr && onQr) {
|
|
@@ -684,25 +790,25 @@ async function createWaSocket(opts) {
|
|
|
684
790
|
const status = getStatusCode(update.lastDisconnect?.error);
|
|
685
791
|
if (!silent) {
|
|
686
792
|
console.error(
|
|
687
|
-
`${
|
|
793
|
+
`${TAG5} op=close cid=${cid} account=${acct} status=${status ?? "unknown"} reason="${formatError(update.lastDisconnect?.error)}" socketLifeMs=${Date.now() - openTs} kind=${closeKind(status, everOpen)}`
|
|
688
794
|
);
|
|
689
795
|
}
|
|
690
796
|
if (status === DisconnectReason.loggedOut && !silent) {
|
|
691
|
-
console.error(`${
|
|
797
|
+
console.error(`${TAG5} session logged out (401) \u2014 re-link required`);
|
|
692
798
|
}
|
|
693
799
|
}
|
|
694
800
|
if (update.connection === "open") {
|
|
695
801
|
everOpen = true;
|
|
696
|
-
if (!silent) console.error(`${
|
|
802
|
+
if (!silent) console.error(`${TAG5} connected`);
|
|
697
803
|
}
|
|
698
804
|
onConnectionUpdate?.(update);
|
|
699
805
|
} catch (err) {
|
|
700
|
-
console.error(`${
|
|
806
|
+
console.error(`${TAG5} connection.update handler error: ${String(err)}`);
|
|
701
807
|
}
|
|
702
808
|
});
|
|
703
809
|
if (sock.ws && typeof sock.ws.on === "function") {
|
|
704
810
|
sock.ws.on("error", (err) => {
|
|
705
|
-
console.error(`${
|
|
811
|
+
console.error(`${TAG5} WebSocket error: ${String(err)}`);
|
|
706
812
|
});
|
|
707
813
|
sock.ws.on("CB:message", (node) => {
|
|
708
814
|
const attrs = node?.attrs ?? {};
|
|
@@ -718,6 +824,21 @@ async function createWaSocket(opts) {
|
|
|
718
824
|
});
|
|
719
825
|
}
|
|
720
826
|
});
|
|
827
|
+
if (!silent) {
|
|
828
|
+
sock.ws.on("CB:notification,type:link_code_companion_reg", (node) => {
|
|
829
|
+
try {
|
|
830
|
+
const census = classifyCompanionReg(node);
|
|
831
|
+
console.error(
|
|
832
|
+
`${TAG5} op=companion-reg cid=${cid} account=${acct} stage=${census.stage} children=${census.children.join(",") || "none"} parseable=${census.parseable}`
|
|
833
|
+
);
|
|
834
|
+
onCompanionReg?.(census);
|
|
835
|
+
} catch (err) {
|
|
836
|
+
console.error(
|
|
837
|
+
`${TAG5} op=companion-reg-observer-error cid=${cid} account=${acct} err=${String(err)}`
|
|
838
|
+
);
|
|
839
|
+
}
|
|
840
|
+
});
|
|
841
|
+
}
|
|
721
842
|
}
|
|
722
843
|
return sock;
|
|
723
844
|
}
|
|
@@ -826,6 +947,26 @@ function computeBackoff(attempt, config = {}) {
|
|
|
826
947
|
return Math.round(jitter);
|
|
827
948
|
}
|
|
828
949
|
|
|
950
|
+
// app/lib/whatsapp/link-census.ts
|
|
951
|
+
var HEALTH_TAG = "[whatsapp-health]";
|
|
952
|
+
var LINK_CENSUS_INTERVAL_MS = 15 * 6e4;
|
|
953
|
+
function downMsFor(row, now) {
|
|
954
|
+
if (row.connected) return "0";
|
|
955
|
+
if (row.lastConnectedAt === void 0) return "unknown";
|
|
956
|
+
return String(now - row.lastConnectedAt);
|
|
957
|
+
}
|
|
958
|
+
function linkCensusLines(rows, now) {
|
|
959
|
+
if (rows.length === 0) return [`${HEALTH_TAG} op=link-census accounts=0`];
|
|
960
|
+
return rows.map(
|
|
961
|
+
(row) => `${HEALTH_TAG} op=link-census account=${row.accountId} connected=${row.connected} stuck=${Boolean(row.sessionStuckReason)} downMs=${downMsFor(row, now)} lastError=${row.lastError ?? "none"}`
|
|
962
|
+
);
|
|
963
|
+
}
|
|
964
|
+
function runLinkCensus(read, now) {
|
|
965
|
+
const rows = read();
|
|
966
|
+
for (const line of linkCensusLines(rows, now)) console.error(line);
|
|
967
|
+
return rows.length;
|
|
968
|
+
}
|
|
969
|
+
|
|
829
970
|
// app/lib/whatsapp/init-queries.ts
|
|
830
971
|
import { inspect as inspect2 } from "util";
|
|
831
972
|
|
|
@@ -855,7 +996,7 @@ function withTimeout(label, promise, timeoutMs) {
|
|
|
855
996
|
}
|
|
856
997
|
|
|
857
998
|
// app/lib/whatsapp/init-queries.ts
|
|
858
|
-
var
|
|
999
|
+
var TAG6 = "[whatsapp:init]";
|
|
859
1000
|
var DEFAULT_QUERY_TIMEOUT_MS = 15e3;
|
|
860
1001
|
var INSPECT_OPTS2 = {
|
|
861
1002
|
depth: null,
|
|
@@ -883,20 +1024,20 @@ async function runInitQueries(sock, ctx) {
|
|
|
883
1024
|
elapsedMs: 0
|
|
884
1025
|
};
|
|
885
1026
|
console.error(
|
|
886
|
-
`${
|
|
1027
|
+
`${TAG6} runInitQueries start account=${ctx.accountId} attempt=${ctx.attempt} selfPhone=${ctx.selfPhone ?? "unknown"} authDir=${ctx.authDir} queryTimeoutMs=${queryTimeoutMs}`
|
|
887
1028
|
);
|
|
888
1029
|
console.error(
|
|
889
|
-
`${
|
|
1030
|
+
`${TAG6} fetchProps SKIPPED account=${ctx.accountId} reason=no-platform-consumer`
|
|
890
1031
|
);
|
|
891
1032
|
if (ctx.signal?.aborted) {
|
|
892
1033
|
result.aborted = true;
|
|
893
1034
|
result.elapsedMs = Date.now() - startedAt;
|
|
894
1035
|
console.error(
|
|
895
|
-
`${
|
|
1036
|
+
`${TAG6} runInitQueries aborted before start account=${ctx.accountId}`
|
|
896
1037
|
);
|
|
897
1038
|
return result;
|
|
898
1039
|
}
|
|
899
|
-
console.error(`${
|
|
1040
|
+
console.error(`${TAG6} fetchBlocklist start account=${ctx.accountId}`);
|
|
900
1041
|
try {
|
|
901
1042
|
const blocklist = await withTimeout(
|
|
902
1043
|
"fetchBlocklist",
|
|
@@ -906,24 +1047,24 @@ async function runInitQueries(sock, ctx) {
|
|
|
906
1047
|
const count = Array.isArray(blocklist) ? blocklist.length : 0;
|
|
907
1048
|
result.blocklistOk = true;
|
|
908
1049
|
console.error(
|
|
909
|
-
`${
|
|
1050
|
+
`${TAG6} fetchBlocklist ok account=${ctx.accountId} count=${count}`
|
|
910
1051
|
);
|
|
911
1052
|
} catch (err) {
|
|
912
1053
|
const formatted = formatErr(err);
|
|
913
1054
|
result.errors.blocklist = formatted;
|
|
914
1055
|
console.error(
|
|
915
|
-
`${
|
|
1056
|
+
`${TAG6} fetchBlocklist FAILED account=${ctx.accountId}: ${formatted}`
|
|
916
1057
|
);
|
|
917
1058
|
}
|
|
918
1059
|
if (ctx.signal?.aborted) {
|
|
919
1060
|
result.aborted = true;
|
|
920
1061
|
result.elapsedMs = Date.now() - startedAt;
|
|
921
1062
|
console.error(
|
|
922
|
-
`${
|
|
1063
|
+
`${TAG6} runInitQueries aborted mid-sequence account=${ctx.accountId} after=fetchBlocklist`
|
|
923
1064
|
);
|
|
924
1065
|
return result;
|
|
925
1066
|
}
|
|
926
|
-
console.error(`${
|
|
1067
|
+
console.error(`${TAG6} fetchPrivacySettings start account=${ctx.accountId}`);
|
|
927
1068
|
try {
|
|
928
1069
|
const settings = await withTimeout(
|
|
929
1070
|
"fetchPrivacySettings",
|
|
@@ -933,18 +1074,18 @@ async function runInitQueries(sock, ctx) {
|
|
|
933
1074
|
);
|
|
934
1075
|
result.privacySettingsOk = true;
|
|
935
1076
|
console.error(
|
|
936
|
-
`${
|
|
1077
|
+
`${TAG6} fetchPrivacySettings ok account=${ctx.accountId} keys=${settings ? Object.keys(settings).join(",") : ""}`
|
|
937
1078
|
);
|
|
938
1079
|
} catch (err) {
|
|
939
1080
|
const formatted = formatErr(err);
|
|
940
1081
|
result.errors.privacySettings = formatted;
|
|
941
1082
|
console.error(
|
|
942
|
-
`${
|
|
1083
|
+
`${TAG6} fetchPrivacySettings FAILED account=${ctx.accountId}: ${formatted}`
|
|
943
1084
|
);
|
|
944
1085
|
}
|
|
945
1086
|
result.elapsedMs = Date.now() - startedAt;
|
|
946
1087
|
console.error(
|
|
947
|
-
`${
|
|
1088
|
+
`${TAG6} runInitQueries done account=${ctx.accountId} blocklistOk=${result.blocklistOk} privacySettingsOk=${result.privacySettingsOk} elapsedMs=${result.elapsedMs}`
|
|
948
1089
|
);
|
|
949
1090
|
return result;
|
|
950
1091
|
}
|
|
@@ -1218,16 +1359,16 @@ function toMillis(v) {
|
|
|
1218
1359
|
if (typeof v.low === "number") return (v.low >>> 0) + (v.high ?? 0) * 4294967296;
|
|
1219
1360
|
return null;
|
|
1220
1361
|
}
|
|
1221
|
-
function readReactionEntry(
|
|
1222
|
-
const targetMsgKeyId =
|
|
1362
|
+
function readReactionEntry(entry2) {
|
|
1363
|
+
const targetMsgKeyId = entry2.key?.id ?? void 0;
|
|
1223
1364
|
if (!targetMsgKeyId) return null;
|
|
1224
|
-
const chatJid =
|
|
1365
|
+
const chatJid = entry2.key?.remoteJid ?? entry2.reaction?.key?.remoteJid ?? void 0;
|
|
1225
1366
|
if (!chatJid) return null;
|
|
1226
1367
|
const isGroup = isGroupJid(chatJid);
|
|
1227
|
-
const reactorKey =
|
|
1368
|
+
const reactorKey = entry2.reaction?.key;
|
|
1228
1369
|
const reactorJid = isGroup ? reactorKey?.participant ?? void 0 : reactorKey?.remoteJid ?? chatJid;
|
|
1229
1370
|
if (!reactorJid) return null;
|
|
1230
|
-
const emoji =
|
|
1371
|
+
const emoji = entry2.reaction?.text ?? "";
|
|
1231
1372
|
return {
|
|
1232
1373
|
targetMsgKeyId,
|
|
1233
1374
|
chatJid,
|
|
@@ -1236,7 +1377,7 @@ function readReactionEntry(entry) {
|
|
|
1236
1377
|
emoji,
|
|
1237
1378
|
removed: !emoji,
|
|
1238
1379
|
fromMe: !!reactorKey?.fromMe,
|
|
1239
|
-
timestampMs: toMillis(
|
|
1380
|
+
timestampMs: toMillis(entry2.reaction?.senderTimestampMs)
|
|
1240
1381
|
};
|
|
1241
1382
|
}
|
|
1242
1383
|
|
|
@@ -1381,7 +1522,7 @@ function summariseRecords(records) {
|
|
|
1381
1522
|
return {
|
|
1382
1523
|
first: sorted[0],
|
|
1383
1524
|
last: sorted[sorted.length - 1],
|
|
1384
|
-
firstNamedSender: sorted.find((r) => r.senderName)?.senderName ?? null
|
|
1525
|
+
firstNamedSender: sorted.find((r) => !r.fromMe && r.senderName)?.senderName ?? null
|
|
1385
1526
|
};
|
|
1386
1527
|
}
|
|
1387
1528
|
function readConversationSummaries(platformAccountId) {
|
|
@@ -1393,10 +1534,10 @@ function readConversationSummaries(platformAccountId) {
|
|
|
1393
1534
|
let bytesRead = 0;
|
|
1394
1535
|
try {
|
|
1395
1536
|
if (!existsSync3(dir)) return out;
|
|
1396
|
-
for (const
|
|
1397
|
-
if (!
|
|
1398
|
-
const remoteJid =
|
|
1399
|
-
const file = join2(dir,
|
|
1537
|
+
for (const entry2 of readdirSync2(dir)) {
|
|
1538
|
+
if (!entry2.endsWith(".jsonl")) continue;
|
|
1539
|
+
const remoteJid = entry2.slice(0, -".jsonl".length);
|
|
1540
|
+
const file = join2(dir, entry2);
|
|
1400
1541
|
seen.add(file);
|
|
1401
1542
|
let size;
|
|
1402
1543
|
let mtimeMs;
|
|
@@ -1445,9 +1586,9 @@ function readAllConversations(platformAccountId) {
|
|
|
1445
1586
|
const out = /* @__PURE__ */ new Map();
|
|
1446
1587
|
try {
|
|
1447
1588
|
if (!existsSync3(dir)) return out;
|
|
1448
|
-
for (const
|
|
1449
|
-
if (!
|
|
1450
|
-
const remoteJid =
|
|
1589
|
+
for (const entry2 of readdirSync2(dir)) {
|
|
1590
|
+
if (!entry2.endsWith(".jsonl")) continue;
|
|
1591
|
+
const remoteJid = entry2.slice(0, -".jsonl".length);
|
|
1451
1592
|
out.set(remoteJid, readConversation(platformAccountId, remoteJid));
|
|
1452
1593
|
}
|
|
1453
1594
|
} catch (err) {
|
|
@@ -1468,16 +1609,16 @@ function normalizeEntry(v) {
|
|
|
1468
1609
|
return typeof v === "string" ? { account: v, mode: "active" } : { account: v.managesAccount, mode: v.mode ?? "active" };
|
|
1469
1610
|
}
|
|
1470
1611
|
function managedAccountFor(accountManagers, phone) {
|
|
1471
|
-
for (const [managerPhone,
|
|
1472
|
-
const { account, mode } = normalizeEntry(
|
|
1612
|
+
for (const [managerPhone, entry2] of Object.entries(accountManagers)) {
|
|
1613
|
+
const { account, mode } = normalizeEntry(entry2);
|
|
1473
1614
|
if (account && phonesMatch(managerPhone, phone)) return { account, mode };
|
|
1474
1615
|
}
|
|
1475
1616
|
return null;
|
|
1476
1617
|
}
|
|
1477
1618
|
function managerPhonesForAccount(accountManagers, subAccountId) {
|
|
1478
1619
|
const phones = [];
|
|
1479
|
-
for (const [managerPhone,
|
|
1480
|
-
const { account } = normalizeEntry(
|
|
1620
|
+
for (const [managerPhone, entry2] of Object.entries(accountManagers)) {
|
|
1621
|
+
const { account } = normalizeEntry(entry2);
|
|
1481
1622
|
if (account && account === subAccountId) phones.push(managerPhone);
|
|
1482
1623
|
}
|
|
1483
1624
|
return phones;
|
|
@@ -1498,7 +1639,7 @@ function resolveAccountConfig(config, accountConfig) {
|
|
|
1498
1639
|
function isAdminPhone(phone, adminPhones) {
|
|
1499
1640
|
const normalized = normalizeE164(phone);
|
|
1500
1641
|
if (!normalized) return false;
|
|
1501
|
-
return adminPhones.some((
|
|
1642
|
+
return adminPhones.some((entry2) => phonesMatch(entry2, normalized));
|
|
1502
1643
|
}
|
|
1503
1644
|
function checkDmAccess(params) {
|
|
1504
1645
|
const { senderPhone, selfPhone, accountId } = params;
|
|
@@ -1520,9 +1661,9 @@ function checkDmAccess(params) {
|
|
|
1520
1661
|
case "open":
|
|
1521
1662
|
return { allowed: true, reason: "dm-policy-open", agentType: "public", effectiveAccountId: accountId };
|
|
1522
1663
|
case "allowlist": {
|
|
1523
|
-
const inList = cfg.allowFrom.some((
|
|
1524
|
-
if (
|
|
1525
|
-
return phonesMatch(
|
|
1664
|
+
const inList = cfg.allowFrom.some((entry2) => {
|
|
1665
|
+
if (entry2 === "*") return true;
|
|
1666
|
+
return phonesMatch(entry2, senderPhone);
|
|
1526
1667
|
});
|
|
1527
1668
|
if (inList) {
|
|
1528
1669
|
return { allowed: true, reason: "allowlist-match", agentType: "public", effectiveAccountId: accountId };
|
|
@@ -1597,8 +1738,8 @@ var NOTIFY_MAX = 5e3;
|
|
|
1597
1738
|
var notifyCache = /* @__PURE__ */ new Map();
|
|
1598
1739
|
function isNotifyDuplicate(msgKeyId) {
|
|
1599
1740
|
const now = Date.now();
|
|
1600
|
-
const
|
|
1601
|
-
if (
|
|
1741
|
+
const entry2 = notifyCache.get(msgKeyId);
|
|
1742
|
+
if (entry2 && now - entry2.ts <= NOTIFY_TTL_MS) return true;
|
|
1602
1743
|
for (const [key, e] of notifyCache) {
|
|
1603
1744
|
if (now - e.ts > NOTIFY_TTL_MS) notifyCache.delete(key);
|
|
1604
1745
|
}
|
|
@@ -1675,15 +1816,15 @@ var ACCESS_LOCKOUT_MS = 15 * 60 * 1e3;
|
|
|
1675
1816
|
var ACCESS_WINDOW_MS = 15 * 60 * 1e3;
|
|
1676
1817
|
function checkAccessRateLimit(ip, agentSlug) {
|
|
1677
1818
|
const key = `${ip}:${agentSlug}`;
|
|
1678
|
-
const
|
|
1679
|
-
if (!
|
|
1819
|
+
const entry2 = rateLimitMap.get(key);
|
|
1820
|
+
if (!entry2) return null;
|
|
1680
1821
|
const now = Date.now();
|
|
1681
|
-
if (
|
|
1682
|
-
const remainingMs =
|
|
1822
|
+
if (entry2.lockedUntil && now < entry2.lockedUntil) {
|
|
1823
|
+
const remainingMs = entry2.lockedUntil - now;
|
|
1683
1824
|
const remainingS = Math.ceil(remainingMs / 1e3);
|
|
1684
1825
|
return `Too many attempts. Try again in ${remainingS}s`;
|
|
1685
1826
|
}
|
|
1686
|
-
if (now -
|
|
1827
|
+
if (now - entry2.firstAttempt > ACCESS_WINDOW_MS) {
|
|
1687
1828
|
rateLimitMap.delete(key);
|
|
1688
1829
|
return null;
|
|
1689
1830
|
}
|
|
@@ -1692,14 +1833,14 @@ function checkAccessRateLimit(ip, agentSlug) {
|
|
|
1692
1833
|
function recordAccessFailedAttempt(ip, agentSlug) {
|
|
1693
1834
|
const key = `${ip}:${agentSlug}`;
|
|
1694
1835
|
const now = Date.now();
|
|
1695
|
-
const
|
|
1696
|
-
if (!
|
|
1836
|
+
const entry2 = rateLimitMap.get(key);
|
|
1837
|
+
if (!entry2 || now - entry2.firstAttempt > ACCESS_WINDOW_MS) {
|
|
1697
1838
|
rateLimitMap.set(key, { attempts: 1, firstAttempt: now });
|
|
1698
1839
|
return;
|
|
1699
1840
|
}
|
|
1700
|
-
|
|
1701
|
-
if (
|
|
1702
|
-
|
|
1841
|
+
entry2.attempts++;
|
|
1842
|
+
if (entry2.attempts >= ACCESS_MAX_ATTEMPTS) {
|
|
1843
|
+
entry2.lockedUntil = now + ACCESS_LOCKOUT_MS;
|
|
1703
1844
|
}
|
|
1704
1845
|
}
|
|
1705
1846
|
function clearAccessRateLimit(ip, agentSlug) {
|
|
@@ -1709,15 +1850,15 @@ var contactRateMap = /* @__PURE__ */ new Map();
|
|
|
1709
1850
|
var REQUEST_LINK_MAX = 3;
|
|
1710
1851
|
var REQUEST_LINK_WINDOW_MS = 60 * 60 * 1e3;
|
|
1711
1852
|
function checkRequestLinkRateLimit(contactValue) {
|
|
1712
|
-
const
|
|
1713
|
-
if (!
|
|
1853
|
+
const entry2 = contactRateMap.get(contactValue);
|
|
1854
|
+
if (!entry2) return null;
|
|
1714
1855
|
const now = Date.now();
|
|
1715
|
-
if (now -
|
|
1856
|
+
if (now - entry2.windowStart > REQUEST_LINK_WINDOW_MS) {
|
|
1716
1857
|
contactRateMap.delete(contactValue);
|
|
1717
1858
|
return null;
|
|
1718
1859
|
}
|
|
1719
|
-
if (
|
|
1720
|
-
const remainingMs = REQUEST_LINK_WINDOW_MS - (now -
|
|
1860
|
+
if (entry2.count >= REQUEST_LINK_MAX) {
|
|
1861
|
+
const remainingMs = REQUEST_LINK_WINDOW_MS - (now - entry2.windowStart);
|
|
1721
1862
|
const remainingM = Math.ceil(remainingMs / 6e4);
|
|
1722
1863
|
return `Too many requests. Try again in ${remainingM} minute(s)`;
|
|
1723
1864
|
}
|
|
@@ -1725,12 +1866,12 @@ function checkRequestLinkRateLimit(contactValue) {
|
|
|
1725
1866
|
}
|
|
1726
1867
|
function recordRequestLinkAttempt(contactValue) {
|
|
1727
1868
|
const now = Date.now();
|
|
1728
|
-
const
|
|
1729
|
-
if (!
|
|
1869
|
+
const entry2 = contactRateMap.get(contactValue);
|
|
1870
|
+
if (!entry2 || now - entry2.windowStart > REQUEST_LINK_WINDOW_MS) {
|
|
1730
1871
|
contactRateMap.set(contactValue, { count: 1, windowStart: now });
|
|
1731
1872
|
return;
|
|
1732
1873
|
}
|
|
1733
|
-
|
|
1874
|
+
entry2.count++;
|
|
1734
1875
|
}
|
|
1735
1876
|
function maskContact(value) {
|
|
1736
1877
|
if (value.includes("@")) {
|
|
@@ -1919,8 +2060,8 @@ var agentSentCache = /* @__PURE__ */ new Map();
|
|
|
1919
2060
|
var inboundCache = /* @__PURE__ */ new Map();
|
|
1920
2061
|
function pruneCache(cache3, ttl, max) {
|
|
1921
2062
|
const now = Date.now();
|
|
1922
|
-
for (const [key,
|
|
1923
|
-
if (now -
|
|
2063
|
+
for (const [key, entry2] of cache3) {
|
|
2064
|
+
if (now - entry2.ts > ttl) cache3.delete(key);
|
|
1924
2065
|
}
|
|
1925
2066
|
if (cache3.size > max) {
|
|
1926
2067
|
const excess = cache3.size - max;
|
|
@@ -1937,22 +2078,22 @@ function trackAgentSentMessage(messageId) {
|
|
|
1937
2078
|
agentSentCache.set(messageId, { ts: Date.now() });
|
|
1938
2079
|
}
|
|
1939
2080
|
function isAgentSentMessage(messageId) {
|
|
1940
|
-
const
|
|
1941
|
-
if (!
|
|
1942
|
-
if (Date.now() -
|
|
2081
|
+
const entry2 = agentSentCache.get(messageId);
|
|
2082
|
+
if (!entry2) return false;
|
|
2083
|
+
if (Date.now() - entry2.ts > AGENT_SENT_TTL_MS) {
|
|
1943
2084
|
agentSentCache.delete(messageId);
|
|
1944
2085
|
return false;
|
|
1945
2086
|
}
|
|
1946
2087
|
return true;
|
|
1947
2088
|
}
|
|
1948
2089
|
function isDuplicateInbound(key) {
|
|
1949
|
-
const
|
|
1950
|
-
if (!
|
|
2090
|
+
const entry2 = inboundCache.get(key);
|
|
2091
|
+
if (!entry2) {
|
|
1951
2092
|
pruneCache(inboundCache, INBOUND_TTL_MS, INBOUND_MAX);
|
|
1952
2093
|
inboundCache.set(key, { ts: Date.now() });
|
|
1953
2094
|
return false;
|
|
1954
2095
|
}
|
|
1955
|
-
if (Date.now() -
|
|
2096
|
+
if (Date.now() - entry2.ts > INBOUND_TTL_MS) {
|
|
1956
2097
|
inboundCache.delete(key);
|
|
1957
2098
|
inboundCache.set(key, { ts: Date.now() });
|
|
1958
2099
|
return false;
|
|
@@ -1961,7 +2102,7 @@ function isDuplicateInbound(key) {
|
|
|
1961
2102
|
}
|
|
1962
2103
|
|
|
1963
2104
|
// app/lib/whatsapp/inbound/self-chat.ts
|
|
1964
|
-
var
|
|
2105
|
+
var TAG7 = "[whatsapp:self-chat]";
|
|
1965
2106
|
function normalizeJid(jid) {
|
|
1966
2107
|
const m = jid.match(/^(\d+)(?::\d+)?@(s\.whatsapp\.net|lid)$/i);
|
|
1967
2108
|
return m ? `${m[1]}@${m[2].toLowerCase()}` : jid;
|
|
@@ -1981,10 +2122,10 @@ function evaluateSelfChatCommand(input) {
|
|
|
1981
2122
|
const connAtMs = input.lastConnectedAtMs ?? 0;
|
|
1982
2123
|
const msgId = input.msgId ?? "?";
|
|
1983
2124
|
console.error(
|
|
1984
|
-
`${
|
|
2125
|
+
`${TAG7} op=seen account=${input.accountId} msgId=${msgId} tsMs=${tsMs} connAtMs=${connAtMs} fromMe=true`
|
|
1985
2126
|
);
|
|
1986
2127
|
const skip = (reason) => {
|
|
1987
|
-
console.error(`${
|
|
2128
|
+
console.error(`${TAG7} op=skip account=${input.accountId} msgId=${msgId} reason=${reason}`);
|
|
1988
2129
|
return { kind: "skip", reason };
|
|
1989
2130
|
};
|
|
1990
2131
|
if (input.msgId && isAgentSentMessage(input.msgId)) return skip("echo");
|
|
@@ -1992,13 +2133,13 @@ function evaluateSelfChatCommand(input) {
|
|
|
1992
2133
|
if (isDuplicateInbound(`${input.accountId}:${input.remoteJid}:${input.msgId}`)) return skip("duplicate");
|
|
1993
2134
|
if (!input.text) return skip("no-text");
|
|
1994
2135
|
console.error(
|
|
1995
|
-
`${
|
|
2136
|
+
`${TAG7} op=dispatch account=${input.accountId} msgId=${msgId} bytes=${Buffer.byteLength(input.text, "utf8")} agentType=admin`
|
|
1996
2137
|
);
|
|
1997
2138
|
return { kind: "dispatch", text: input.text };
|
|
1998
2139
|
}
|
|
1999
2140
|
|
|
2000
2141
|
// app/lib/whatsapp/activity.ts
|
|
2001
|
-
var
|
|
2142
|
+
var TAG8 = "[whatsapp:activity]";
|
|
2002
2143
|
var RECENT_EVENTS_MAX = 200;
|
|
2003
2144
|
var counters = /* @__PURE__ */ new Map();
|
|
2004
2145
|
var recentEvents = [];
|
|
@@ -2013,7 +2154,7 @@ function recordActivity(event) {
|
|
|
2013
2154
|
}
|
|
2014
2155
|
acct[direction]++;
|
|
2015
2156
|
acct.lastEventAt = now;
|
|
2016
|
-
const
|
|
2157
|
+
const entry2 = {
|
|
2017
2158
|
channel: "whatsapp",
|
|
2018
2159
|
accountId,
|
|
2019
2160
|
direction,
|
|
@@ -2021,15 +2162,15 @@ function recordActivity(event) {
|
|
|
2021
2162
|
timestamp: now,
|
|
2022
2163
|
messageType
|
|
2023
2164
|
};
|
|
2024
|
-
recentEvents.push(
|
|
2165
|
+
recentEvents.push(entry2);
|
|
2025
2166
|
if (recentEvents.length > RECENT_EVENTS_MAX) {
|
|
2026
2167
|
recentEvents.splice(0, recentEvents.length - RECENT_EVENTS_MAX);
|
|
2027
2168
|
}
|
|
2028
2169
|
console.error(
|
|
2029
|
-
`${
|
|
2170
|
+
`${TAG8} channel-activity: direction=${direction} account=${accountId} jid=${jid}` + (messageType ? ` type=${messageType}` : "")
|
|
2030
2171
|
);
|
|
2031
2172
|
} catch (err) {
|
|
2032
|
-
console.error(`${
|
|
2173
|
+
console.error(`${TAG8} recording failed: ${String(err)}`);
|
|
2033
2174
|
}
|
|
2034
2175
|
}
|
|
2035
2176
|
function getChannelActivity(accountId) {
|
|
@@ -2050,7 +2191,7 @@ function getChannelActivity(accountId) {
|
|
|
2050
2191
|
}
|
|
2051
2192
|
|
|
2052
2193
|
// app/lib/whatsapp/outbound/send.ts
|
|
2053
|
-
var
|
|
2194
|
+
var TAG9 = "[whatsapp:outbound]";
|
|
2054
2195
|
function buildQuoted(q) {
|
|
2055
2196
|
return {
|
|
2056
2197
|
key: {
|
|
@@ -2071,14 +2212,14 @@ async function sendTextMessage(sock, to, text, opts) {
|
|
|
2071
2212
|
const messageId = result?.key?.id;
|
|
2072
2213
|
if (messageId) {
|
|
2073
2214
|
trackAgentSentMessage(messageId);
|
|
2074
|
-
console.error(`${
|
|
2215
|
+
console.error(`${TAG9} sent text to=${jid} id=${messageId}`);
|
|
2075
2216
|
}
|
|
2076
2217
|
if (opts?.accountId) {
|
|
2077
2218
|
recordActivity({ accountId: opts.accountId, direction: "outbound", jid, messageType: "text" });
|
|
2078
2219
|
}
|
|
2079
2220
|
return { success: true, messageId: messageId ?? void 0 };
|
|
2080
2221
|
} catch (err) {
|
|
2081
|
-
console.error(`${
|
|
2222
|
+
console.error(`${TAG9} send failed to=${to}: ${String(err)}`);
|
|
2082
2223
|
return { success: false, error: String(err) };
|
|
2083
2224
|
}
|
|
2084
2225
|
}
|
|
@@ -2155,20 +2296,20 @@ async function sendMediaMessage(sock, to, media, opts) {
|
|
|
2155
2296
|
const messageId = result?.key?.id;
|
|
2156
2297
|
if (messageId) {
|
|
2157
2298
|
trackAgentSentMessage(messageId);
|
|
2158
|
-
console.error(`${
|
|
2299
|
+
console.error(`${TAG9} sent ${media.type} to=${jid} id=${messageId}`);
|
|
2159
2300
|
}
|
|
2160
2301
|
if (opts?.accountId) {
|
|
2161
2302
|
recordActivity({ accountId: opts.accountId, direction: "outbound", jid, messageType: media.type });
|
|
2162
2303
|
}
|
|
2163
2304
|
return { success: true, messageId: messageId ?? void 0 };
|
|
2164
2305
|
} catch (err) {
|
|
2165
|
-
console.error(`${
|
|
2306
|
+
console.error(`${TAG9} send media failed to=${to}: ${String(err)}`);
|
|
2166
2307
|
return { success: false, error: String(err) };
|
|
2167
2308
|
}
|
|
2168
2309
|
}
|
|
2169
2310
|
|
|
2170
2311
|
// app/lib/whatsapp/persist-message.ts
|
|
2171
|
-
var
|
|
2312
|
+
var TAG10 = "[whatsapp-persist]";
|
|
2172
2313
|
var sessionWriteLocks = /* @__PURE__ */ new Map();
|
|
2173
2314
|
async function persistWhatsAppMessage(input) {
|
|
2174
2315
|
if (!input.body && !input.attachmentId || !input.msgKeyId) return null;
|
|
@@ -2219,14 +2360,14 @@ async function persistWhatsAppMessage(input) {
|
|
|
2219
2360
|
};
|
|
2220
2361
|
const result = await session.run(cypher, params);
|
|
2221
2362
|
if (result.records.length === 0) {
|
|
2222
|
-
console.error(`${
|
|
2363
|
+
console.error(`${TAG10} skip reason=conversation-not-found accountId=${input.platformAccountId} cacheKey=${input.cacheKey} messageId=${messageId}`);
|
|
2223
2364
|
return null;
|
|
2224
2365
|
}
|
|
2225
2366
|
const rec = result.records[0];
|
|
2226
2367
|
const senderElementId = rec.get("senderElementId");
|
|
2227
2368
|
const senderReused = rec.get("senderReused") === true;
|
|
2228
2369
|
console.error(
|
|
2229
|
-
`${
|
|
2370
|
+
`${TAG10} sender-resolved telephone=${senderTelephone} nodeKind=Person elementId=${senderElementId} reused=${senderReused}`
|
|
2230
2371
|
);
|
|
2231
2372
|
const diskRecord = {
|
|
2232
2373
|
messageId,
|
|
@@ -2252,18 +2393,18 @@ async function persistWhatsAppMessage(input) {
|
|
|
2252
2393
|
const { created } = appendMessage(input.platformAccountId, diskRecord, altDedupeRemoteJids);
|
|
2253
2394
|
const ms = Date.now() - t0;
|
|
2254
2395
|
if (!created) {
|
|
2255
|
-
console.error(`${
|
|
2396
|
+
console.error(`${TAG10} noop reason=existing-message messageId=${messageId}`);
|
|
2256
2397
|
return { messageId, created: false, senderElementId };
|
|
2257
2398
|
}
|
|
2258
2399
|
console.error(
|
|
2259
|
-
`${
|
|
2400
|
+
`${TAG10} write messageId=${messageId} accountId=${input.platformAccountId} cacheKey=${input.cacheKey} fromMe=${input.fromMe} dateSent=${dateSentIso} bodyBytes=${Buffer.byteLength(input.body, "utf8")} attachment=${input.attachmentId ?? "none"} ms=${ms}`
|
|
2260
2401
|
);
|
|
2261
2402
|
return { messageId, created: true, senderElementId };
|
|
2262
2403
|
} catch (err) {
|
|
2263
2404
|
const ms = Date.now() - t0;
|
|
2264
2405
|
const reason = sanitizeReason(err);
|
|
2265
2406
|
console.error(
|
|
2266
|
-
`${
|
|
2407
|
+
`${TAG10} FAIL accountId=${input.platformAccountId} waname=${input.accountId} remoteJid=${input.remoteJid} msgKey=${input.msgKeyId} reason=${reason} ms=${ms}`
|
|
2267
2408
|
);
|
|
2268
2409
|
return null;
|
|
2269
2410
|
} finally {
|
|
@@ -2295,8 +2436,218 @@ function sanitizeReason(err) {
|
|
|
2295
2436
|
return `${err.name}:${msg}`;
|
|
2296
2437
|
}
|
|
2297
2438
|
|
|
2439
|
+
// app/lib/whatsapp/chat-metadata-store.ts
|
|
2440
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync3, readFileSync as readFileSync5, writeFileSync as writeFileSync3, renameSync, statSync as statSync2 } from "fs";
|
|
2441
|
+
import { dirname as dirname2, join as join4 } from "path";
|
|
2442
|
+
|
|
2443
|
+
// app/lib/data-path.ts
|
|
2444
|
+
import { realpathSync } from "fs";
|
|
2445
|
+
import { resolve as resolve2, normalize, sep, relative, basename, join as join3 } from "path";
|
|
2446
|
+
var PLATFORM_ROOT2 = process.env.MAXY_PLATFORM_ROOT ?? resolve2(process.cwd(), "../platform");
|
|
2447
|
+
var DATA_ROOT = resolve2(PLATFORM_ROOT2, "..", "data");
|
|
2448
|
+
var CLAUDE_UPLOADS_ROOT = process.env.CLAUDE_CONFIG_DIR ? resolve2(process.env.CLAUDE_CONFIG_DIR, "uploads") : null;
|
|
2449
|
+
var ACCOUNT_PARTITION_DIRS = /* @__PURE__ */ new Set(["accounts"]);
|
|
2450
|
+
var ACCOUNT_UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
2451
|
+
function crossesForeignAccountPartition(relPath, accountId) {
|
|
2452
|
+
const segs = relPath.split("/").filter(Boolean);
|
|
2453
|
+
if (segs.length < 2) return false;
|
|
2454
|
+
if (!ACCOUNT_PARTITION_DIRS.has(segs[0])) return false;
|
|
2455
|
+
const owner = segs[1];
|
|
2456
|
+
if (!ACCOUNT_UUID_RE.test(owner)) return false;
|
|
2457
|
+
return owner !== accountId;
|
|
2458
|
+
}
|
|
2459
|
+
function isWithinOwnAccountPartition(relPath, accountId) {
|
|
2460
|
+
const segs = relPath.split("/").filter(Boolean);
|
|
2461
|
+
return segs.length >= 2 && segs[0] === "accounts" && segs[1] === accountId;
|
|
2462
|
+
}
|
|
2463
|
+
function resolveUnderRoot(raw, rootAbs, rootLabel) {
|
|
2464
|
+
const cleaned = normalize("/" + (raw ?? "").replace(/\\/g, "/")).replace(/^\/+/, "");
|
|
2465
|
+
const absolute = resolve2(rootAbs, cleaned);
|
|
2466
|
+
let rootReal;
|
|
2467
|
+
try {
|
|
2468
|
+
rootReal = realpathSync(rootAbs);
|
|
2469
|
+
} catch (err) {
|
|
2470
|
+
return {
|
|
2471
|
+
ok: false,
|
|
2472
|
+
status: 500,
|
|
2473
|
+
error: `${rootLabel} not accessible: ${err instanceof Error ? err.message : String(err)}`
|
|
2474
|
+
};
|
|
2475
|
+
}
|
|
2476
|
+
let resolvedReal;
|
|
2477
|
+
try {
|
|
2478
|
+
resolvedReal = realpathSync(absolute);
|
|
2479
|
+
} catch (err) {
|
|
2480
|
+
const code = err.code;
|
|
2481
|
+
if (code === "ENOENT") {
|
|
2482
|
+
if (absolute !== rootReal && !absolute.startsWith(rootReal + sep)) {
|
|
2483
|
+
return { ok: false, status: 403, error: `Path escapes ${rootLabel}`, resolved: absolute };
|
|
2484
|
+
}
|
|
2485
|
+
return { ok: false, status: 404, error: "Not found" };
|
|
2486
|
+
}
|
|
2487
|
+
return {
|
|
2488
|
+
ok: false,
|
|
2489
|
+
status: 500,
|
|
2490
|
+
error: err instanceof Error ? err.message : String(err)
|
|
2491
|
+
};
|
|
2492
|
+
}
|
|
2493
|
+
if (resolvedReal !== rootReal && !resolvedReal.startsWith(rootReal + sep)) {
|
|
2494
|
+
return { ok: false, status: 403, error: `Path escapes ${rootLabel}`, resolved: resolvedReal };
|
|
2495
|
+
}
|
|
2496
|
+
const relPath = relative(rootReal, resolvedReal) || ".";
|
|
2497
|
+
return { ok: true, absolute: resolvedReal, dataRootReal: rootReal, relative: relPath };
|
|
2498
|
+
}
|
|
2499
|
+
function resolveDataPath(raw) {
|
|
2500
|
+
return resolveUnderRoot(raw, DATA_ROOT, "DATA_ROOT");
|
|
2501
|
+
}
|
|
2502
|
+
function resolveClaudeUploadsPath(raw) {
|
|
2503
|
+
if (!CLAUDE_UPLOADS_ROOT) {
|
|
2504
|
+
return { ok: false, status: 404, error: "Not found" };
|
|
2505
|
+
}
|
|
2506
|
+
return resolveUnderRoot(raw, CLAUDE_UPLOADS_ROOT, "CLAUDE_UPLOADS_ROOT");
|
|
2507
|
+
}
|
|
2508
|
+
function scratchpadTmpBase() {
|
|
2509
|
+
return process.env.MAXY_SCRATCHPAD_BASE ?? "/tmp";
|
|
2510
|
+
}
|
|
2511
|
+
function sessionScratchpadRoot(projectDir, sessionId) {
|
|
2512
|
+
const uid = typeof process.geteuid === "function" ? process.geteuid() : 0;
|
|
2513
|
+
return join3(scratchpadTmpBase(), `claude-${uid}`, basename(projectDir), sessionId, "scratchpad");
|
|
2514
|
+
}
|
|
2515
|
+
function resolveScratchpadPath(raw, scratchpadRootAbs) {
|
|
2516
|
+
return resolveUnderRoot(raw, scratchpadRootAbs, "scratchpad");
|
|
2517
|
+
}
|
|
2518
|
+
|
|
2519
|
+
// app/lib/whatsapp/chat-metadata-store.ts
|
|
2520
|
+
var SOURCE_RANK = { contact: 3, group: 2, chat: 1 };
|
|
2521
|
+
function isSource(v) {
|
|
2522
|
+
return v === "contact" || v === "group" || v === "chat";
|
|
2523
|
+
}
|
|
2524
|
+
function preferEntry(existing, incoming) {
|
|
2525
|
+
if (existing === void 0) return incoming;
|
|
2526
|
+
const a = SOURCE_RANK[existing.source];
|
|
2527
|
+
const b = SOURCE_RANK[incoming.source];
|
|
2528
|
+
if (b > a) return incoming;
|
|
2529
|
+
if (b < a) return existing;
|
|
2530
|
+
return incoming.updatedAt > existing.updatedAt ? incoming : existing;
|
|
2531
|
+
}
|
|
2532
|
+
function parseChatMetadata(raw) {
|
|
2533
|
+
const out = /* @__PURE__ */ new Map();
|
|
2534
|
+
let parsed;
|
|
2535
|
+
try {
|
|
2536
|
+
parsed = JSON.parse(raw);
|
|
2537
|
+
} catch {
|
|
2538
|
+
return out;
|
|
2539
|
+
}
|
|
2540
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) return out;
|
|
2541
|
+
for (const [jid, value] of Object.entries(parsed)) {
|
|
2542
|
+
if (value === null || typeof value !== "object") continue;
|
|
2543
|
+
const v = value;
|
|
2544
|
+
if (typeof v.name !== "string" || v.name.length === 0) continue;
|
|
2545
|
+
if (!isSource(v.source)) continue;
|
|
2546
|
+
if (typeof v.updatedAt !== "string") continue;
|
|
2547
|
+
out.set(jid, {
|
|
2548
|
+
jid,
|
|
2549
|
+
name: v.name,
|
|
2550
|
+
source: v.source,
|
|
2551
|
+
isGroup: v.isGroup === true,
|
|
2552
|
+
updatedAt: v.updatedAt
|
|
2553
|
+
});
|
|
2554
|
+
}
|
|
2555
|
+
return out;
|
|
2556
|
+
}
|
|
2557
|
+
function serializeChatMetadata(map) {
|
|
2558
|
+
const obj = {};
|
|
2559
|
+
for (const [jid, entry2] of map) obj[jid] = entry2;
|
|
2560
|
+
return JSON.stringify(obj);
|
|
2561
|
+
}
|
|
2562
|
+
function chatMetadataPath(accountId) {
|
|
2563
|
+
return join4(DATA_ROOT, "accounts", accountId, "chat-metadata.json");
|
|
2564
|
+
}
|
|
2565
|
+
var readCache = /* @__PURE__ */ new Map();
|
|
2566
|
+
function readChatMetadata(accountId) {
|
|
2567
|
+
const path3 = chatMetadataPath(accountId);
|
|
2568
|
+
try {
|
|
2569
|
+
if (!existsSync4(path3)) return /* @__PURE__ */ new Map();
|
|
2570
|
+
const st = statSync2(path3);
|
|
2571
|
+
const cached = readCache.get(path3);
|
|
2572
|
+
if (cached && cached.size === st.size && cached.mtimeMs === st.mtimeMs) return cached.map;
|
|
2573
|
+
const map = parseChatMetadata(readFileSync5(path3, "utf8"));
|
|
2574
|
+
readCache.set(path3, { size: st.size, mtimeMs: st.mtimeMs, map });
|
|
2575
|
+
return map;
|
|
2576
|
+
} catch (err) {
|
|
2577
|
+
console.error(
|
|
2578
|
+
`[wa-chatmeta] op=read outcome=fail account=${accountId.slice(0, 8)} reason=${(err instanceof Error ? err.message : String(err)).slice(0, 100)}`
|
|
2579
|
+
);
|
|
2580
|
+
return /* @__PURE__ */ new Map();
|
|
2581
|
+
}
|
|
2582
|
+
}
|
|
2583
|
+
function writeChatMetadataMerged(accountId, updates) {
|
|
2584
|
+
if (updates.length === 0) return -1;
|
|
2585
|
+
const path3 = chatMetadataPath(accountId);
|
|
2586
|
+
try {
|
|
2587
|
+
mkdirSync3(dirname2(path3), { recursive: true });
|
|
2588
|
+
let merged = /* @__PURE__ */ new Map();
|
|
2589
|
+
if (existsSync4(path3)) merged = parseChatMetadata(readFileSync5(path3, "utf8"));
|
|
2590
|
+
for (const entry2 of updates) merged.set(entry2.jid, preferEntry(merged.get(entry2.jid), entry2));
|
|
2591
|
+
const tmp = `${path3}.tmp`;
|
|
2592
|
+
writeFileSync3(tmp, serializeChatMetadata(merged), "utf8");
|
|
2593
|
+
renameSync(tmp, path3);
|
|
2594
|
+
return parseChatMetadata(readFileSync5(path3, "utf8")).size;
|
|
2595
|
+
} catch (err) {
|
|
2596
|
+
console.error(
|
|
2597
|
+
`[wa-chatmeta] op=persist outcome=fail account=${accountId.slice(0, 8)} reason=${(err instanceof Error ? err.message : String(err)).slice(0, 100)}`
|
|
2598
|
+
);
|
|
2599
|
+
return -1;
|
|
2600
|
+
}
|
|
2601
|
+
}
|
|
2602
|
+
|
|
2603
|
+
// app/lib/whatsapp/chat-metadata-events.ts
|
|
2604
|
+
var NON_CHAT_JIDS = /* @__PURE__ */ new Set(["status@broadcast"]);
|
|
2605
|
+
function isGroupJid2(jid) {
|
|
2606
|
+
return jid.endsWith("@g.us");
|
|
2607
|
+
}
|
|
2608
|
+
function entry(jid, name, source, nowIso) {
|
|
2609
|
+
return { jid, name, source, isGroup: isGroupJid2(jid), updatedAt: nowIso };
|
|
2610
|
+
}
|
|
2611
|
+
function conversationKey(jid, phoneNumber) {
|
|
2612
|
+
if (!jid.endsWith("@lid")) return jid;
|
|
2613
|
+
if (typeof phoneNumber !== "string") return jid;
|
|
2614
|
+
const trimmed = phoneNumber.trim();
|
|
2615
|
+
return trimmed.endsWith("@s.whatsapp.net") ? trimmed : jid;
|
|
2616
|
+
}
|
|
2617
|
+
function usable(jid, name, phoneNumber) {
|
|
2618
|
+
if (typeof jid !== "string" || jid.length === 0) return null;
|
|
2619
|
+
if (NON_CHAT_JIDS.has(jid)) return null;
|
|
2620
|
+
const trimmed = typeof name === "string" ? name.trim() : "";
|
|
2621
|
+
if (trimmed.length === 0) return null;
|
|
2622
|
+
return { jid: conversationKey(jid, phoneNumber), name: trimmed };
|
|
2623
|
+
}
|
|
2624
|
+
function chatEntriesFromChats(chats, nowIso) {
|
|
2625
|
+
const out = [];
|
|
2626
|
+
for (const c of chats) {
|
|
2627
|
+
const ok = usable(c.id, c.name, c.phoneNumber);
|
|
2628
|
+
if (ok) out.push(entry(ok.jid, ok.name, "chat", nowIso));
|
|
2629
|
+
}
|
|
2630
|
+
return out;
|
|
2631
|
+
}
|
|
2632
|
+
function chatEntriesFromContacts(contacts, nowIso) {
|
|
2633
|
+
const out = [];
|
|
2634
|
+
for (const c of contacts) {
|
|
2635
|
+
const ok = usable(c.id, c.name, c.phoneNumber);
|
|
2636
|
+
if (ok) out.push(entry(ok.jid, ok.name, isGroupJid2(ok.jid) ? "chat" : "contact", nowIso));
|
|
2637
|
+
}
|
|
2638
|
+
return out;
|
|
2639
|
+
}
|
|
2640
|
+
function groupEntriesFromUpdates(updates, nowIso) {
|
|
2641
|
+
const out = [];
|
|
2642
|
+
for (const g of updates) {
|
|
2643
|
+
const ok = usable(g.id, g.subject);
|
|
2644
|
+
if (ok) out.push(entry(ok.jid, ok.name, "group", nowIso));
|
|
2645
|
+
}
|
|
2646
|
+
return out;
|
|
2647
|
+
}
|
|
2648
|
+
|
|
2298
2649
|
// app/lib/whatsapp/ensure-conversation.ts
|
|
2299
|
-
var
|
|
2650
|
+
var TAG11 = "[whatsapp-persist]";
|
|
2300
2651
|
async function ensureWhatsAppConversation(input) {
|
|
2301
2652
|
const t0 = Date.now();
|
|
2302
2653
|
try {
|
|
@@ -2308,27 +2659,27 @@ async function ensureWhatsAppConversation(input) {
|
|
|
2308
2659
|
const ms = Date.now() - t0;
|
|
2309
2660
|
if (!result.sessionId) {
|
|
2310
2661
|
console.error(
|
|
2311
|
-
`${
|
|
2662
|
+
`${TAG11} conversation-merged FAIL cacheKey=${input.cacheKey} reason=null-sessionId ms=${ms}`
|
|
2312
2663
|
);
|
|
2313
2664
|
return null;
|
|
2314
2665
|
}
|
|
2315
2666
|
console.error(
|
|
2316
|
-
`${
|
|
2667
|
+
`${TAG11} conversation-merged cacheKey=${input.cacheKey} agentType=${input.agentType} channel=whatsapp accountId=${input.platformAccountId} connectionAccountId=${input.connectionAccountId ?? input.platformAccountId} created=${result.created} ms=${ms}`
|
|
2317
2668
|
);
|
|
2318
2669
|
return { sessionId: result.sessionId, created: result.created };
|
|
2319
2670
|
} catch (err) {
|
|
2320
2671
|
const ms = Date.now() - t0;
|
|
2321
2672
|
const reason = err instanceof Error ? `${err.name}:${err.message.slice(0, 200)}` : String(err).slice(0, 200);
|
|
2322
2673
|
console.error(
|
|
2323
|
-
`${
|
|
2674
|
+
`${TAG11} conversation-merged FAIL cacheKey=${input.cacheKey} reason=${reason} ms=${ms}`
|
|
2324
2675
|
);
|
|
2325
2676
|
return null;
|
|
2326
2677
|
}
|
|
2327
2678
|
}
|
|
2328
2679
|
|
|
2329
2680
|
// ../lib/account-enumeration/src/index.ts
|
|
2330
|
-
import { readdirSync as readdirSync3, readFileSync as
|
|
2331
|
-
import { resolve as
|
|
2681
|
+
import { readdirSync as readdirSync3, readFileSync as readFileSync6 } from "fs";
|
|
2682
|
+
import { resolve as resolve3 } from "path";
|
|
2332
2683
|
var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
2333
2684
|
var cache2 = /* @__PURE__ */ new Map();
|
|
2334
2685
|
function enumerateValidAccountIds(accountsDir) {
|
|
@@ -2347,9 +2698,9 @@ function enumerateValidAccountIds(accountsDir) {
|
|
|
2347
2698
|
const valid = [];
|
|
2348
2699
|
for (const name of names) {
|
|
2349
2700
|
if (!UUID_RE.test(name)) continue;
|
|
2350
|
-
const configPath =
|
|
2701
|
+
const configPath = resolve3(accountsDir, name, "account.json");
|
|
2351
2702
|
try {
|
|
2352
|
-
JSON.parse(
|
|
2703
|
+
JSON.parse(readFileSync6(configPath, "utf-8"));
|
|
2353
2704
|
valid.push(name);
|
|
2354
2705
|
} catch (err) {
|
|
2355
2706
|
const code = err.code;
|
|
@@ -2363,9 +2714,9 @@ function resolveHouseOrSoleAccountId(accountsDir) {
|
|
|
2363
2714
|
const valid = enumerateValidAccountIds(accountsDir);
|
|
2364
2715
|
const houses = [];
|
|
2365
2716
|
for (const id of valid) {
|
|
2366
|
-
const configPath =
|
|
2717
|
+
const configPath = resolve3(accountsDir, id, "account.json");
|
|
2367
2718
|
try {
|
|
2368
|
-
const cfg = JSON.parse(
|
|
2719
|
+
const cfg = JSON.parse(readFileSync6(configPath, "utf-8"));
|
|
2369
2720
|
if (cfg.role === "house") houses.push(id);
|
|
2370
2721
|
} catch {
|
|
2371
2722
|
}
|
|
@@ -2383,7 +2734,7 @@ function getAccountsDirFromEnv() {
|
|
|
2383
2734
|
"[graph-write] MAXY_PLATFORM_ROOT not set \u2014 cannot enforce accountId gate. Set MAXY_PLATFORM_ROOT in the spawning process or pass `accountsDir` explicitly."
|
|
2384
2735
|
);
|
|
2385
2736
|
}
|
|
2386
|
-
return
|
|
2737
|
+
return resolve3(root, "..", "data/accounts");
|
|
2387
2738
|
}
|
|
2388
2739
|
function validateAccountIdEnv(envValue, diskIds) {
|
|
2389
2740
|
if (!envValue) {
|
|
@@ -2399,8 +2750,8 @@ function validateAccountIdEnv(envValue, diskIds) {
|
|
|
2399
2750
|
}
|
|
2400
2751
|
|
|
2401
2752
|
// app/lib/whatsapp/platform-account-id.ts
|
|
2402
|
-
import { readFileSync as
|
|
2403
|
-
import { resolve as
|
|
2753
|
+
import { readFileSync as readFileSync7 } from "fs";
|
|
2754
|
+
import { resolve as resolve4 } from "path";
|
|
2404
2755
|
var cachedHouseId = null;
|
|
2405
2756
|
var cachedHouseAccountsDir = null;
|
|
2406
2757
|
function resolveHouseId(accountsDir) {
|
|
@@ -2411,10 +2762,10 @@ function resolveHouseId(accountsDir) {
|
|
|
2411
2762
|
return cachedHouseId;
|
|
2412
2763
|
}
|
|
2413
2764
|
function readChannelRoutingOverride(accountsDir, houseId) {
|
|
2414
|
-
const configPath =
|
|
2765
|
+
const configPath = resolve4(accountsDir, houseId, "account.json");
|
|
2415
2766
|
let raw;
|
|
2416
2767
|
try {
|
|
2417
|
-
raw =
|
|
2768
|
+
raw = readFileSync7(configPath, "utf-8");
|
|
2418
2769
|
} catch {
|
|
2419
2770
|
return null;
|
|
2420
2771
|
}
|
|
@@ -2455,8 +2806,8 @@ function warnNonUuidAccount(caller, accountId) {
|
|
|
2455
2806
|
}
|
|
2456
2807
|
|
|
2457
2808
|
// app/lib/managed-service.ts
|
|
2458
|
-
import { join as
|
|
2459
|
-
import { readFileSync as
|
|
2809
|
+
import { join as join5 } from "path";
|
|
2810
|
+
import { readFileSync as readFileSync8 } from "fs";
|
|
2460
2811
|
function isHouseManagedService(accountsDir = ACCOUNTS_DIR) {
|
|
2461
2812
|
let houseId;
|
|
2462
2813
|
try {
|
|
@@ -2466,7 +2817,7 @@ function isHouseManagedService(accountsDir = ACCOUNTS_DIR) {
|
|
|
2466
2817
|
}
|
|
2467
2818
|
try {
|
|
2468
2819
|
const cfg = JSON.parse(
|
|
2469
|
-
|
|
2820
|
+
readFileSync8(join5(accountsDir, houseId, "account.json"), "utf-8")
|
|
2470
2821
|
);
|
|
2471
2822
|
return cfg.managedService === true;
|
|
2472
2823
|
} catch {
|
|
@@ -2480,7 +2831,7 @@ function isCustomerTurnSuppressed(role, managedService) {
|
|
|
2480
2831
|
// app/lib/whatsapp/inbound/media.ts
|
|
2481
2832
|
import { randomUUID as randomUUID4, createHash } from "crypto";
|
|
2482
2833
|
import { writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
2483
|
-
import { join as
|
|
2834
|
+
import { join as join6 } from "path";
|
|
2484
2835
|
import {
|
|
2485
2836
|
downloadMediaMessage,
|
|
2486
2837
|
downloadContentFromMessage,
|
|
@@ -2490,85 +2841,9 @@ import {
|
|
|
2490
2841
|
// app/lib/attachments.ts
|
|
2491
2842
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
2492
2843
|
import { mkdir, writeFile } from "fs/promises";
|
|
2493
|
-
import { realpathSync as realpathSync2, existsSync as
|
|
2844
|
+
import { realpathSync as realpathSync2, existsSync as existsSync5, readFileSync as readFileSync9, readdirSync as readdirSync4 } from "fs";
|
|
2494
2845
|
import { resolve as resolve5, extname } from "path";
|
|
2495
2846
|
|
|
2496
|
-
// app/lib/data-path.ts
|
|
2497
|
-
import { realpathSync } from "fs";
|
|
2498
|
-
import { resolve as resolve4, normalize, sep, relative, basename, join as join4 } from "path";
|
|
2499
|
-
var PLATFORM_ROOT2 = process.env.MAXY_PLATFORM_ROOT ?? resolve4(process.cwd(), "../platform");
|
|
2500
|
-
var DATA_ROOT = resolve4(PLATFORM_ROOT2, "..", "data");
|
|
2501
|
-
var CLAUDE_UPLOADS_ROOT = process.env.CLAUDE_CONFIG_DIR ? resolve4(process.env.CLAUDE_CONFIG_DIR, "uploads") : null;
|
|
2502
|
-
var ACCOUNT_PARTITION_DIRS = /* @__PURE__ */ new Set(["accounts"]);
|
|
2503
|
-
var ACCOUNT_UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
2504
|
-
function crossesForeignAccountPartition(relPath, accountId) {
|
|
2505
|
-
const segs = relPath.split("/").filter(Boolean);
|
|
2506
|
-
if (segs.length < 2) return false;
|
|
2507
|
-
if (!ACCOUNT_PARTITION_DIRS.has(segs[0])) return false;
|
|
2508
|
-
const owner = segs[1];
|
|
2509
|
-
if (!ACCOUNT_UUID_RE.test(owner)) return false;
|
|
2510
|
-
return owner !== accountId;
|
|
2511
|
-
}
|
|
2512
|
-
function isWithinOwnAccountPartition(relPath, accountId) {
|
|
2513
|
-
const segs = relPath.split("/").filter(Boolean);
|
|
2514
|
-
return segs.length >= 2 && segs[0] === "accounts" && segs[1] === accountId;
|
|
2515
|
-
}
|
|
2516
|
-
function resolveUnderRoot(raw, rootAbs, rootLabel) {
|
|
2517
|
-
const cleaned = normalize("/" + (raw ?? "").replace(/\\/g, "/")).replace(/^\/+/, "");
|
|
2518
|
-
const absolute = resolve4(rootAbs, cleaned);
|
|
2519
|
-
let rootReal;
|
|
2520
|
-
try {
|
|
2521
|
-
rootReal = realpathSync(rootAbs);
|
|
2522
|
-
} catch (err) {
|
|
2523
|
-
return {
|
|
2524
|
-
ok: false,
|
|
2525
|
-
status: 500,
|
|
2526
|
-
error: `${rootLabel} not accessible: ${err instanceof Error ? err.message : String(err)}`
|
|
2527
|
-
};
|
|
2528
|
-
}
|
|
2529
|
-
let resolvedReal;
|
|
2530
|
-
try {
|
|
2531
|
-
resolvedReal = realpathSync(absolute);
|
|
2532
|
-
} catch (err) {
|
|
2533
|
-
const code = err.code;
|
|
2534
|
-
if (code === "ENOENT") {
|
|
2535
|
-
if (absolute !== rootReal && !absolute.startsWith(rootReal + sep)) {
|
|
2536
|
-
return { ok: false, status: 403, error: `Path escapes ${rootLabel}`, resolved: absolute };
|
|
2537
|
-
}
|
|
2538
|
-
return { ok: false, status: 404, error: "Not found" };
|
|
2539
|
-
}
|
|
2540
|
-
return {
|
|
2541
|
-
ok: false,
|
|
2542
|
-
status: 500,
|
|
2543
|
-
error: err instanceof Error ? err.message : String(err)
|
|
2544
|
-
};
|
|
2545
|
-
}
|
|
2546
|
-
if (resolvedReal !== rootReal && !resolvedReal.startsWith(rootReal + sep)) {
|
|
2547
|
-
return { ok: false, status: 403, error: `Path escapes ${rootLabel}`, resolved: resolvedReal };
|
|
2548
|
-
}
|
|
2549
|
-
const relPath = relative(rootReal, resolvedReal) || ".";
|
|
2550
|
-
return { ok: true, absolute: resolvedReal, dataRootReal: rootReal, relative: relPath };
|
|
2551
|
-
}
|
|
2552
|
-
function resolveDataPath(raw) {
|
|
2553
|
-
return resolveUnderRoot(raw, DATA_ROOT, "DATA_ROOT");
|
|
2554
|
-
}
|
|
2555
|
-
function resolveClaudeUploadsPath(raw) {
|
|
2556
|
-
if (!CLAUDE_UPLOADS_ROOT) {
|
|
2557
|
-
return { ok: false, status: 404, error: "Not found" };
|
|
2558
|
-
}
|
|
2559
|
-
return resolveUnderRoot(raw, CLAUDE_UPLOADS_ROOT, "CLAUDE_UPLOADS_ROOT");
|
|
2560
|
-
}
|
|
2561
|
-
function scratchpadTmpBase() {
|
|
2562
|
-
return process.env.MAXY_SCRATCHPAD_BASE ?? "/tmp";
|
|
2563
|
-
}
|
|
2564
|
-
function sessionScratchpadRoot(projectDir, sessionId) {
|
|
2565
|
-
const uid = typeof process.geteuid === "function" ? process.geteuid() : 0;
|
|
2566
|
-
return join4(scratchpadTmpBase(), `claude-${uid}`, basename(projectDir), sessionId, "scratchpad");
|
|
2567
|
-
}
|
|
2568
|
-
function resolveScratchpadPath(raw, scratchpadRootAbs) {
|
|
2569
|
-
return resolveUnderRoot(raw, scratchpadRootAbs, "scratchpad");
|
|
2570
|
-
}
|
|
2571
|
-
|
|
2572
2847
|
// app/lib/doc-to-text.ts
|
|
2573
2848
|
import { unzipSync, strFromU8 } from "fflate";
|
|
2574
2849
|
var DOCX = "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
|
|
@@ -2589,10 +2864,10 @@ function bareMime(mimeType) {
|
|
|
2589
2864
|
function unescapeXml(s) {
|
|
2590
2865
|
return s.replace(/</g, "<").replace(/>/g, ">").replace(/"/g, '"').replace(/'/g, "'").replace(/&/g, "&");
|
|
2591
2866
|
}
|
|
2592
|
-
function unzipEntry(buffer,
|
|
2867
|
+
function unzipEntry(buffer, entry2) {
|
|
2593
2868
|
const files = unzipSync(new Uint8Array(buffer));
|
|
2594
|
-
const bytes = files[
|
|
2595
|
-
if (!bytes) throw new Error(`zip entry not found: ${
|
|
2869
|
+
const bytes = files[entry2];
|
|
2870
|
+
if (!bytes) throw new Error(`zip entry not found: ${entry2}`);
|
|
2596
2871
|
return strFromU8(bytes);
|
|
2597
2872
|
}
|
|
2598
2873
|
function docxToText(buffer) {
|
|
@@ -2814,14 +3089,14 @@ async function storeMediaBuffer(accountId, attachmentId, filename, mimeType, buf
|
|
|
2814
3089
|
return writeAttachment(accountId, "", filename, mimeType, buffer.byteLength, buffer, attachmentId);
|
|
2815
3090
|
}
|
|
2816
3091
|
function attachmentExists(accountId, attachmentId) {
|
|
2817
|
-
return
|
|
3092
|
+
return existsSync5(resolve5(uploadsDirFor(accountId, "", attachmentId), `${attachmentId}.meta.json`));
|
|
2818
3093
|
}
|
|
2819
3094
|
function resolveStoredAttachment(accountId, attachmentId) {
|
|
2820
3095
|
if (!ATTACHMENT_ID_RE.test(attachmentId)) return null;
|
|
2821
3096
|
const dir = uploadsDirFor(accountId, "", attachmentId);
|
|
2822
3097
|
let meta;
|
|
2823
3098
|
try {
|
|
2824
|
-
meta = JSON.parse(
|
|
3099
|
+
meta = JSON.parse(readFileSync9(resolve5(dir, `${attachmentId}.meta.json`), "utf8"));
|
|
2825
3100
|
} catch {
|
|
2826
3101
|
return null;
|
|
2827
3102
|
}
|
|
@@ -2969,7 +3244,7 @@ async function storeComponentArtefact(accountId, attachmentId, mimeType, content
|
|
|
2969
3244
|
}
|
|
2970
3245
|
|
|
2971
3246
|
// app/lib/whatsapp/inbound/media-download-guard.ts
|
|
2972
|
-
var
|
|
3247
|
+
var TAG12 = "[media-guard]";
|
|
2973
3248
|
var inFlight = 0;
|
|
2974
3249
|
var containedCount = 0;
|
|
2975
3250
|
function containedSinceBoot() {
|
|
@@ -2999,13 +3274,13 @@ function handleEscapedMediaError(err, channel) {
|
|
|
2999
3274
|
if (undiciFrame && inFlight > 0) {
|
|
3000
3275
|
containedCount += 1;
|
|
3001
3276
|
console.error(
|
|
3002
|
-
`${
|
|
3277
|
+
`${TAG12} op=contained channel=${channel} inFlight=${inFlight} err=${err instanceof Error ? err.message : String(err)} cause=${causeCode(err)}
|
|
3003
3278
|
${stack}`
|
|
3004
3279
|
);
|
|
3005
3280
|
return true;
|
|
3006
3281
|
}
|
|
3007
3282
|
console.error(
|
|
3008
|
-
`${
|
|
3283
|
+
`${TAG12} op=escaped channel=${channel} inFlight=${inFlight} undiciFrame=${undiciFrame} err=${err instanceof Error ? err.message : String(err)} cause=${causeCode(err)} contained=false`
|
|
3009
3284
|
);
|
|
3010
3285
|
return false;
|
|
3011
3286
|
}
|
|
@@ -3030,11 +3305,11 @@ function installMediaDownloadGuard() {
|
|
|
3030
3305
|
);
|
|
3031
3306
|
process.exit(1);
|
|
3032
3307
|
});
|
|
3033
|
-
console.error(`${
|
|
3308
|
+
console.error(`${TAG12} op=installed`);
|
|
3034
3309
|
}
|
|
3035
3310
|
|
|
3036
3311
|
// app/lib/whatsapp/inbound/media.ts
|
|
3037
|
-
var
|
|
3312
|
+
var TAG13 = "[whatsapp:media]";
|
|
3038
3313
|
var MEDIA_DIR = "/tmp/maxy-media";
|
|
3039
3314
|
var DOWNLOAD_TIMEOUT_MS = 45e3;
|
|
3040
3315
|
var DETACHED_GRACE_MS = 3e5;
|
|
@@ -3124,25 +3399,25 @@ async function downloadInboundMediaBuffer(msg, sock, opts) {
|
|
|
3124
3399
|
}
|
|
3125
3400
|
);
|
|
3126
3401
|
if (!buffer || buffer.length === 0) {
|
|
3127
|
-
console.error(`${
|
|
3402
|
+
console.error(`${TAG13} primary download returned empty, trying direct fallback`);
|
|
3128
3403
|
const downloadable = getDownloadableContent(content);
|
|
3129
3404
|
if (downloadable) {
|
|
3130
3405
|
try {
|
|
3131
3406
|
const stream = await downloadContentFromMessage(downloadable.downloadable, downloadable.mediaType);
|
|
3132
3407
|
buffer = await streamToBuffer(stream);
|
|
3133
3408
|
} catch (fallbackErr) {
|
|
3134
|
-
console.error(`${
|
|
3409
|
+
console.error(`${TAG13} direct download fallback failed: ${String(fallbackErr)}`);
|
|
3135
3410
|
}
|
|
3136
3411
|
}
|
|
3137
3412
|
}
|
|
3138
3413
|
if (!buffer || buffer.length === 0) {
|
|
3139
|
-
console.error(`${
|
|
3414
|
+
console.error(`${TAG13} download failed: empty buffer for ${mimetype ?? "unknown"}`);
|
|
3140
3415
|
return void 0;
|
|
3141
3416
|
}
|
|
3142
3417
|
if (buffer.length > maxBytes) {
|
|
3143
3418
|
const sizeMB = (buffer.length / (1024 * 1024)).toFixed(1);
|
|
3144
3419
|
const limitMB = (maxBytes / (1024 * 1024)).toFixed(0);
|
|
3145
|
-
console.error(`${
|
|
3420
|
+
console.error(`${TAG13} media too large type=${mimetype ?? "unknown"} size=${sizeMB}MB limit=${limitMB}MB`);
|
|
3146
3421
|
return void 0;
|
|
3147
3422
|
}
|
|
3148
3423
|
return {
|
|
@@ -3156,7 +3431,7 @@ async function downloadInboundMediaBuffer(msg, sock, opts) {
|
|
|
3156
3431
|
endMediaDownload();
|
|
3157
3432
|
return settled;
|
|
3158
3433
|
} catch (err) {
|
|
3159
|
-
console.error(`${
|
|
3434
|
+
console.error(`${TAG13} media download failed type=${mimetype ?? "unknown"} error=${String(err)}`);
|
|
3160
3435
|
if (isTimeoutError(err)) {
|
|
3161
3436
|
const t = setTimeout(endMediaDownload, DETACHED_GRACE_MS);
|
|
3162
3437
|
t.unref?.();
|
|
@@ -3171,10 +3446,10 @@ async function downloadInboundMedia(msg, sock, opts) {
|
|
|
3171
3446
|
if (!res) return void 0;
|
|
3172
3447
|
await mkdir2(MEDIA_DIR, { recursive: true });
|
|
3173
3448
|
const ext = mimeToExt(res.mimetype);
|
|
3174
|
-
const filePath =
|
|
3449
|
+
const filePath = join6(MEDIA_DIR, `${randomUUID4()}.${ext}`);
|
|
3175
3450
|
await writeFile2(filePath, res.buffer);
|
|
3176
3451
|
const sizeKB = (res.buffer.length / 1024).toFixed(0);
|
|
3177
|
-
console.error(`${
|
|
3452
|
+
console.error(`${TAG13} media downloaded type=${res.mimetype} size=${sizeKB}KB path=${filePath}`);
|
|
3178
3453
|
return { path: filePath, mimetype: res.mimetype, size: res.buffer.length };
|
|
3179
3454
|
}
|
|
3180
3455
|
async function storeInboundMediaServable(msg, sock, accountId, seed, opts) {
|
|
@@ -3183,15 +3458,15 @@ async function storeInboundMediaServable(msg, sock, accountId, seed, opts) {
|
|
|
3183
3458
|
if (attachmentExists(accountId, attachmentId)) return attachmentId;
|
|
3184
3459
|
const res = await downloadInboundMediaBufferCached(msg, sock, opts);
|
|
3185
3460
|
if (!res) {
|
|
3186
|
-
console.error(`${
|
|
3461
|
+
console.error(`${TAG13} servable-store skip reason=no-buffer id=${attachmentId}`);
|
|
3187
3462
|
return null;
|
|
3188
3463
|
}
|
|
3189
3464
|
const filename = res.filename ?? `${attachmentId}.${mimeToExt(res.mimetype)}`;
|
|
3190
3465
|
await storeMediaBuffer(accountId, attachmentId, filename, res.mimetype, res.buffer);
|
|
3191
|
-
console.error(`${
|
|
3466
|
+
console.error(`${TAG13} servable-store ok id=${attachmentId} mime=${res.mimetype} bytes=${res.buffer.length}`);
|
|
3192
3467
|
return attachmentId;
|
|
3193
3468
|
} catch (err) {
|
|
3194
|
-
console.error(`${
|
|
3469
|
+
console.error(`${TAG13} servable-store FAIL id=${attachmentId} reason=${String(err).slice(0, 120)}`);
|
|
3195
3470
|
return null;
|
|
3196
3471
|
}
|
|
3197
3472
|
}
|
|
@@ -3239,7 +3514,7 @@ function createBoundedQueue(limit, opts) {
|
|
|
3239
3514
|
}
|
|
3240
3515
|
|
|
3241
3516
|
// app/lib/whatsapp/inbound/debounce.ts
|
|
3242
|
-
var
|
|
3517
|
+
var TAG14 = "[whatsapp:debounce]";
|
|
3243
3518
|
var STT_TAG = "[whatsapp:stt-await]";
|
|
3244
3519
|
function createInboundDebouncer(opts) {
|
|
3245
3520
|
const { debounceMs, buildKey, onFlush, onError } = opts;
|
|
@@ -3270,7 +3545,7 @@ function createInboundDebouncer(opts) {
|
|
|
3270
3545
|
pending.delete(key);
|
|
3271
3546
|
const batchSize = batch.entries.length;
|
|
3272
3547
|
try {
|
|
3273
|
-
console.error(`${
|
|
3548
|
+
console.error(`${TAG14} debounce flush key=${key} batchSize=${batchSize}`);
|
|
3274
3549
|
const result = onFlush(batch.entries);
|
|
3275
3550
|
if (result && typeof result.catch === "function") {
|
|
3276
3551
|
result.catch(onError);
|
|
@@ -3350,7 +3625,7 @@ function combineInboundBatch(entries) {
|
|
|
3350
3625
|
}
|
|
3351
3626
|
|
|
3352
3627
|
// app/lib/whatsapp/opening-hours.ts
|
|
3353
|
-
var
|
|
3628
|
+
var TAG15 = "[whatsapp:hours]";
|
|
3354
3629
|
async function isBusinessOpen(accountId) {
|
|
3355
3630
|
try {
|
|
3356
3631
|
const timezone = await resolveTimezone(accountId);
|
|
@@ -3368,7 +3643,7 @@ async function isBusinessOpen(accountId) {
|
|
|
3368
3643
|
{ accountId, dayOfWeek, previousDayOfWeek }
|
|
3369
3644
|
);
|
|
3370
3645
|
if (result.records.length === 0) {
|
|
3371
|
-
console.error(`${
|
|
3646
|
+
console.error(`${TAG15} [${accountId}] business hours check: no opening hours configured, treating as open`);
|
|
3372
3647
|
return { open: true, reason: "no opening hours configured" };
|
|
3373
3648
|
}
|
|
3374
3649
|
const specs = result.records.filter((r) => r.get("opens") != null && r.get("closes") != null).map((r) => ({
|
|
@@ -3377,7 +3652,7 @@ async function isBusinessOpen(accountId) {
|
|
|
3377
3652
|
closes: String(r.get("closes")).trim()
|
|
3378
3653
|
}));
|
|
3379
3654
|
if (specs.length === 0) {
|
|
3380
|
-
console.error(`${
|
|
3655
|
+
console.error(`${TAG15} [${accountId}] business hours check: no opening hours configured, treating as open`);
|
|
3381
3656
|
return { open: true, reason: "no opening hours configured" };
|
|
3382
3657
|
}
|
|
3383
3658
|
for (const spec of specs) {
|
|
@@ -3387,7 +3662,7 @@ async function isBusinessOpen(accountId) {
|
|
|
3387
3662
|
if (spec.opens > spec.closes && currentTime < spec.closes) {
|
|
3388
3663
|
const hoursStr = `${spec.opens}-${spec.closes} (${spec.day})`;
|
|
3389
3664
|
console.error(
|
|
3390
|
-
`${
|
|
3665
|
+
`${TAG15} [${accountId}] business hours check: open (day=${dayOfWeek}, time=${currentTime}, hours=${hoursStr})`
|
|
3391
3666
|
);
|
|
3392
3667
|
return {
|
|
3393
3668
|
open: true,
|
|
@@ -3400,7 +3675,7 @@ async function isBusinessOpen(accountId) {
|
|
|
3400
3675
|
} else if (isTimeInRange(currentTime, spec.opens, spec.closes)) {
|
|
3401
3676
|
const hoursStr = `${spec.opens}-${spec.closes}`;
|
|
3402
3677
|
console.error(
|
|
3403
|
-
`${
|
|
3678
|
+
`${TAG15} [${accountId}] business hours check: open (day=${dayOfWeek}, time=${currentTime}, hours=${hoursStr})`
|
|
3404
3679
|
);
|
|
3405
3680
|
return {
|
|
3406
3681
|
open: true,
|
|
@@ -3414,7 +3689,7 @@ async function isBusinessOpen(accountId) {
|
|
|
3414
3689
|
const todaySpecs = specs.filter((s) => s.day === dayOfWeek);
|
|
3415
3690
|
const allHours = todaySpecs.map((s) => `${s.opens}-${s.closes}`).join(", ") || "none today";
|
|
3416
3691
|
console.error(
|
|
3417
|
-
`${
|
|
3692
|
+
`${TAG15} [${accountId}] business hours check: closed (day=${dayOfWeek}, time=${currentTime}, hours=${allHours})`
|
|
3418
3693
|
);
|
|
3419
3694
|
return {
|
|
3420
3695
|
open: false,
|
|
@@ -3428,7 +3703,7 @@ async function isBusinessOpen(accountId) {
|
|
|
3428
3703
|
}
|
|
3429
3704
|
} catch (err) {
|
|
3430
3705
|
console.error(
|
|
3431
|
-
`${
|
|
3706
|
+
`${TAG15} [${accountId}] business hours check failed, treating as open: ${err instanceof Error ? err.message : String(err)}`
|
|
3432
3707
|
);
|
|
3433
3708
|
return { open: true, reason: "hours check failed (treating as open)" };
|
|
3434
3709
|
}
|
|
@@ -3474,7 +3749,7 @@ import { execFile } from "child_process";
|
|
|
3474
3749
|
import { unlink, stat } from "fs/promises";
|
|
3475
3750
|
import { promisify } from "util";
|
|
3476
3751
|
var execFileAsync = promisify(execFile);
|
|
3477
|
-
var
|
|
3752
|
+
var TAG16 = "[stt]";
|
|
3478
3753
|
var WHISPER_BINARY = process.env.WHISPER_BINARY ?? "/opt/whisper.cpp/build/bin/whisper-cli";
|
|
3479
3754
|
var WHISPER_MODEL = process.env.WHISPER_MODEL ?? "/opt/whisper.cpp/models/ggml-base.bin";
|
|
3480
3755
|
var WHISPER_TIMEOUT_MS = 20 * 60 * 1e3;
|
|
@@ -3493,11 +3768,11 @@ async function transcribe(audioPath, mimetype) {
|
|
|
3493
3768
|
const s = await stat(audioPath);
|
|
3494
3769
|
audioBytes = s.size;
|
|
3495
3770
|
} catch {
|
|
3496
|
-
console.error(`${
|
|
3771
|
+
console.error(`${TAG16} failed: file not readable path=${audioPath}`);
|
|
3497
3772
|
return void 0;
|
|
3498
3773
|
}
|
|
3499
3774
|
console.error(
|
|
3500
|
-
`${
|
|
3775
|
+
`${TAG16} start provider=whisper-local audio_bytes=${audioBytes} mimetype=${mimetype} path=${audioPath}`
|
|
3501
3776
|
);
|
|
3502
3777
|
const wavPath = audioPath.replace(/\.[^.]+$/, "") + ".wav";
|
|
3503
3778
|
try {
|
|
@@ -3516,7 +3791,7 @@ async function transcribe(audioPath, mimetype) {
|
|
|
3516
3791
|
], { timeout: 3e4 });
|
|
3517
3792
|
} catch (err) {
|
|
3518
3793
|
const reason = err instanceof Error ? err.message : String(err);
|
|
3519
|
-
console.error(`${
|
|
3794
|
+
console.error(`${TAG16} failed: ffmpeg conversion error=${reason}`);
|
|
3520
3795
|
return void 0;
|
|
3521
3796
|
}
|
|
3522
3797
|
try {
|
|
@@ -3532,20 +3807,20 @@ async function transcribe(audioPath, mimetype) {
|
|
|
3532
3807
|
const durationMs = Date.now() - startMs;
|
|
3533
3808
|
const { text, count: stripped } = stripSpecialTokens(stdout.trim());
|
|
3534
3809
|
if (!text) {
|
|
3535
|
-
console.error(`${
|
|
3810
|
+
console.error(`${TAG16} failed: whisper returned empty output duration_ms=${durationMs}`);
|
|
3536
3811
|
return void 0;
|
|
3537
3812
|
}
|
|
3538
3813
|
const langMatch = stderr.match(/auto-detected language:\s*(\w+)/);
|
|
3539
3814
|
const language = langMatch?.[1] ?? "unknown";
|
|
3540
3815
|
const words = text.split(/\s+/).filter(Boolean).length;
|
|
3541
3816
|
console.error(
|
|
3542
|
-
`${
|
|
3817
|
+
`${TAG16} done provider=whisper-local duration_ms=${durationMs} words=${words} stripped=${stripped} lang=${language}`
|
|
3543
3818
|
);
|
|
3544
3819
|
return { text, language, durationMs };
|
|
3545
3820
|
} catch (err) {
|
|
3546
3821
|
const durationMs = Date.now() - startMs;
|
|
3547
3822
|
const reason = err instanceof Error ? err.message : String(err);
|
|
3548
|
-
console.error(`${
|
|
3823
|
+
console.error(`${TAG16} failed provider=whisper-local duration_ms=${durationMs} error=${reason}`);
|
|
3549
3824
|
return void 0;
|
|
3550
3825
|
} finally {
|
|
3551
3826
|
unlink(wavPath).catch(() => {
|
|
@@ -3564,8 +3839,9 @@ var WaReplyError = class extends Error {
|
|
|
3564
3839
|
};
|
|
3565
3840
|
|
|
3566
3841
|
// app/lib/whatsapp/manager.ts
|
|
3567
|
-
var
|
|
3842
|
+
var TAG17 = "[whatsapp:manager]";
|
|
3568
3843
|
var MAX_RECONNECT_ATTEMPTS = 10;
|
|
3844
|
+
var REARM_INTERVAL_MS = 15 * 6e4;
|
|
3569
3845
|
var MESSAGE_STORE_MAX = 500;
|
|
3570
3846
|
var APPEND_MEDIA_CONCURRENCY = 3;
|
|
3571
3847
|
var APPEND_MEDIA_MAX_PENDING = 24;
|
|
@@ -3607,17 +3883,17 @@ var messageStore = /* @__PURE__ */ new Map();
|
|
|
3607
3883
|
function messageStoreKey(accountId, remoteJid) {
|
|
3608
3884
|
return `${accountId}:${remoteJid}`;
|
|
3609
3885
|
}
|
|
3610
|
-
function storeMessage(storeKey,
|
|
3886
|
+
function storeMessage(storeKey, entry2) {
|
|
3611
3887
|
let entries = messageStore.get(storeKey);
|
|
3612
3888
|
if (!entries) {
|
|
3613
3889
|
entries = [];
|
|
3614
3890
|
messageStore.set(storeKey, entries);
|
|
3615
3891
|
}
|
|
3616
|
-
entries.push(
|
|
3892
|
+
entries.push(entry2);
|
|
3617
3893
|
if (entries.length > MESSAGE_STORE_MAX) {
|
|
3618
3894
|
const trimmed = entries.length - MESSAGE_STORE_MAX;
|
|
3619
3895
|
entries.splice(0, trimmed);
|
|
3620
|
-
console.error(`${
|
|
3896
|
+
console.error(`${TAG17} message store trimmed for ${storeKey}: ${trimmed} oldest messages removed`);
|
|
3621
3897
|
}
|
|
3622
3898
|
}
|
|
3623
3899
|
function deriveCacheKey(input) {
|
|
@@ -3661,7 +3937,7 @@ function buildSelfChatDispatch(input) {
|
|
|
3661
3937
|
}
|
|
3662
3938
|
async function init(opts) {
|
|
3663
3939
|
if (initialized) {
|
|
3664
|
-
console.error(`${
|
|
3940
|
+
console.error(`${TAG17} already initialized`);
|
|
3665
3941
|
return;
|
|
3666
3942
|
}
|
|
3667
3943
|
configDir = opts.configDir;
|
|
@@ -3671,20 +3947,20 @@ async function init(opts) {
|
|
|
3671
3947
|
loadConfig(opts.accountConfig);
|
|
3672
3948
|
const accountIds = listCredentialAccountIds(configDir);
|
|
3673
3949
|
if (accountIds.length === 0) {
|
|
3674
|
-
console.error(`${
|
|
3950
|
+
console.error(`${TAG17} init: no stored WhatsApp credentials found`);
|
|
3675
3951
|
initialized = true;
|
|
3676
3952
|
return;
|
|
3677
3953
|
}
|
|
3678
|
-
console.error(`${
|
|
3954
|
+
console.error(`${TAG17} init: found ${accountIds.length} credentialed account(s): ${accountIds.join(", ")}`);
|
|
3679
3955
|
initialized = true;
|
|
3680
3956
|
for (const accountId of accountIds) {
|
|
3681
3957
|
const accountCfg = whatsAppConfig.accounts?.[accountId];
|
|
3682
3958
|
if (accountCfg?.enabled === false) {
|
|
3683
|
-
console.error(`${
|
|
3959
|
+
console.error(`${TAG17} skipping disabled account=${accountId}`);
|
|
3684
3960
|
continue;
|
|
3685
3961
|
}
|
|
3686
3962
|
startConnection(accountId).catch((err) => {
|
|
3687
|
-
console.error(`${
|
|
3963
|
+
console.error(`${TAG17} failed to auto-start account=${accountId}: ${formatError(err)}`);
|
|
3688
3964
|
});
|
|
3689
3965
|
}
|
|
3690
3966
|
}
|
|
@@ -3693,7 +3969,7 @@ async function startConnection(accountId) {
|
|
|
3693
3969
|
const authDir = resolveAuthDir(configDir, accountId);
|
|
3694
3970
|
const hasAuth = await authExists(authDir);
|
|
3695
3971
|
if (!hasAuth) {
|
|
3696
|
-
console.error(`${
|
|
3972
|
+
console.error(`${TAG17} no credentials for account=${accountId}`);
|
|
3697
3973
|
return;
|
|
3698
3974
|
}
|
|
3699
3975
|
let platformAccountId;
|
|
@@ -3724,6 +4000,7 @@ async function startConnection(accountId) {
|
|
|
3724
4000
|
selfJid: null,
|
|
3725
4001
|
selfLid: null,
|
|
3726
4002
|
reconnectAttempts: 0,
|
|
4003
|
+
rearmAttempts: 0,
|
|
3727
4004
|
abortController: new AbortController(),
|
|
3728
4005
|
debouncer: null,
|
|
3729
4006
|
lidMapping: null
|
|
@@ -3747,11 +4024,11 @@ async function stopConnection(accountId) {
|
|
|
3747
4024
|
conn.sock.ev.removeAllListeners("creds.update");
|
|
3748
4025
|
conn.sock.ws?.close?.();
|
|
3749
4026
|
} catch (err) {
|
|
3750
|
-
console.warn(`${
|
|
4027
|
+
console.warn(`${TAG17} socket cleanup error during stop account=${accountId}: ${String(err)}`);
|
|
3751
4028
|
}
|
|
3752
4029
|
}
|
|
3753
4030
|
connections.delete(accountId);
|
|
3754
|
-
console.error(`${
|
|
4031
|
+
console.error(`${TAG17} stopped account=${accountId}`);
|
|
3755
4032
|
}
|
|
3756
4033
|
function getStatus() {
|
|
3757
4034
|
return Array.from(connections.values()).map((conn) => ({
|
|
@@ -3862,6 +4139,7 @@ async function registerLoginSocket(accountId, sock, authDir) {
|
|
|
3862
4139
|
selfJid: null,
|
|
3863
4140
|
selfLid: null,
|
|
3864
4141
|
reconnectAttempts: 0,
|
|
4142
|
+
rearmAttempts: 0,
|
|
3865
4143
|
lastConnectedAt: Date.now(),
|
|
3866
4144
|
abortController: new AbortController(),
|
|
3867
4145
|
debouncer: null,
|
|
@@ -3870,11 +4148,12 @@ async function registerLoginSocket(accountId, sock, authDir) {
|
|
|
3870
4148
|
applySelfIdentity(conn, sock, "register");
|
|
3871
4149
|
connections.set(accountId, conn);
|
|
3872
4150
|
monitorInbound(conn);
|
|
4151
|
+
monitorChatMetadata(conn);
|
|
3873
4152
|
try {
|
|
3874
4153
|
await sock.sendPresenceUpdate("available");
|
|
3875
|
-
console.error(`${
|
|
4154
|
+
console.error(`${TAG17} presence set to available account=${accountId}`);
|
|
3876
4155
|
} catch (err) {
|
|
3877
|
-
console.error(`${
|
|
4156
|
+
console.error(`${TAG17} presence update failed account=${accountId}: ${String(err)}`);
|
|
3878
4157
|
}
|
|
3879
4158
|
await runInitQueries(sock, {
|
|
3880
4159
|
accountId,
|
|
@@ -3885,7 +4164,7 @@ async function registerLoginSocket(accountId, sock, authDir) {
|
|
|
3885
4164
|
});
|
|
3886
4165
|
watchForDisconnect(conn);
|
|
3887
4166
|
attachSelfIdRefreshOnCredsUpdate(conn);
|
|
3888
|
-
console.error(`${
|
|
4167
|
+
console.error(`${TAG17} registered login socket for account=${accountId} phone=${conn.selfPhone ?? "unknown"}`);
|
|
3889
4168
|
}
|
|
3890
4169
|
function attachSelfIdRefreshOnCredsUpdate(conn) {
|
|
3891
4170
|
if (!conn.sock) return;
|
|
@@ -3944,7 +4223,7 @@ async function shutdown() {
|
|
|
3944
4223
|
const ids = Array.from(connections.keys());
|
|
3945
4224
|
await Promise.all(ids.map((id) => stopConnection(id)));
|
|
3946
4225
|
initialized = false;
|
|
3947
|
-
console.error(`${
|
|
4226
|
+
console.error(`${TAG17} shutdown complete`);
|
|
3948
4227
|
}
|
|
3949
4228
|
function loadConfig(accountConfig) {
|
|
3950
4229
|
try {
|
|
@@ -3955,30 +4234,43 @@ function loadConfig(accountConfig) {
|
|
|
3955
4234
|
if (result.ok) {
|
|
3956
4235
|
whatsAppConfig = result.data;
|
|
3957
4236
|
if (result.droppedKeys.length > 0) {
|
|
3958
|
-
console.error(`${
|
|
4237
|
+
console.error(`${TAG17} config: dropped unknown keys=[${result.droppedKeys.join(",")}] preserved known config`);
|
|
3959
4238
|
}
|
|
3960
4239
|
} else {
|
|
3961
|
-
console.error(`${
|
|
4240
|
+
console.error(`${TAG17} config validation failed: ${result.error.message}`);
|
|
3962
4241
|
whatsAppConfig = {};
|
|
3963
4242
|
}
|
|
3964
4243
|
const diskAdmin = Array.isArray(wa.adminPhones) ? wa.adminPhones : [];
|
|
3965
4244
|
if (diskAdmin.length > 0 && (whatsAppConfig.adminPhones?.length ?? 0) === 0) {
|
|
3966
|
-
console.error(`${
|
|
4245
|
+
console.error(`${TAG17} WARN adminPhones present on disk but empty after load`);
|
|
3967
4246
|
}
|
|
3968
4247
|
}
|
|
3969
4248
|
} catch (err) {
|
|
3970
|
-
console.error(`${
|
|
4249
|
+
console.error(`${TAG17} config load error: ${String(err)}`);
|
|
3971
4250
|
whatsAppConfig = {};
|
|
3972
4251
|
}
|
|
3973
4252
|
}
|
|
4253
|
+
function sinceStuckMs(conn) {
|
|
4254
|
+
return conn.stuckSinceMs === void 0 ? 0 : Date.now() - conn.stuckSinceMs;
|
|
4255
|
+
}
|
|
4256
|
+
function clearStuck(conn) {
|
|
4257
|
+
if (!conn.sessionStuckReason) return;
|
|
4258
|
+
console.error(
|
|
4259
|
+
`${HEALTH_TAG} op=rearm account=${conn.accountId} attempt=${conn.rearmAttempts} sinceStuckMs=${sinceStuckMs(conn)} outcome=connected`
|
|
4260
|
+
);
|
|
4261
|
+
conn.sessionStuckReason = void 0;
|
|
4262
|
+
conn.stuckSinceMs = void 0;
|
|
4263
|
+
conn.rearmAttempts = 0;
|
|
4264
|
+
}
|
|
3974
4265
|
async function connectWithReconnect(conn) {
|
|
3975
4266
|
const maxAttempts = MAX_RECONNECT_ATTEMPTS;
|
|
3976
4267
|
while (!conn.abortController.signal.aborted) {
|
|
3977
4268
|
let uptimeMs = 0;
|
|
3978
4269
|
let cycleError = null;
|
|
3979
4270
|
let connectedAt;
|
|
4271
|
+
let heldTimer;
|
|
3980
4272
|
try {
|
|
3981
|
-
console.error(`${
|
|
4273
|
+
console.error(`${TAG17} connecting account=${conn.accountId} attempt=${conn.reconnectAttempts}`);
|
|
3982
4274
|
if (conn.debouncer) {
|
|
3983
4275
|
await conn.debouncer.destroy();
|
|
3984
4276
|
conn.debouncer = null;
|
|
@@ -3991,7 +4283,8 @@ async function connectWithReconnect(conn) {
|
|
|
3991
4283
|
conn.sock = sock;
|
|
3992
4284
|
applySelfIdentity(conn, sock, "pre-listener");
|
|
3993
4285
|
monitorInbound(conn);
|
|
3994
|
-
|
|
4286
|
+
monitorChatMetadata(conn);
|
|
4287
|
+
console.error(`${TAG17} socket created account=${conn.accountId} \u2014 waiting for connection`);
|
|
3995
4288
|
await waitForConnection(sock);
|
|
3996
4289
|
connectedAt = Date.now();
|
|
3997
4290
|
conn.connected = true;
|
|
@@ -3999,12 +4292,19 @@ async function connectWithReconnect(conn) {
|
|
|
3999
4292
|
conn.lastError = void 0;
|
|
4000
4293
|
conn.terminalReason = void 0;
|
|
4001
4294
|
applySelfIdentity(conn, sock, "post-connect");
|
|
4002
|
-
console.error(`${
|
|
4295
|
+
console.error(`${TAG17} connected account=${conn.accountId} phone=${conn.selfPhone ?? "unknown"}`);
|
|
4296
|
+
if (conn.sessionStuckReason) {
|
|
4297
|
+
heldTimer = setTimeout(() => {
|
|
4298
|
+
if (!conn.connected || connections.get(conn.accountId) !== conn) return;
|
|
4299
|
+
clearStuck(conn);
|
|
4300
|
+
}, STABLE_UPTIME_MS);
|
|
4301
|
+
if (typeof heldTimer.unref === "function") heldTimer.unref();
|
|
4302
|
+
}
|
|
4003
4303
|
try {
|
|
4004
4304
|
await sock.sendPresenceUpdate("available");
|
|
4005
|
-
console.error(`${
|
|
4305
|
+
console.error(`${TAG17} presence set to available account=${conn.accountId}`);
|
|
4006
4306
|
} catch (err) {
|
|
4007
|
-
console.error(`${
|
|
4307
|
+
console.error(`${TAG17} presence update failed account=${conn.accountId}: ${String(err)}`);
|
|
4008
4308
|
}
|
|
4009
4309
|
await runInitQueries(sock, {
|
|
4010
4310
|
accountId: conn.accountId,
|
|
@@ -4027,13 +4327,20 @@ async function connectWithReconnect(conn) {
|
|
|
4027
4327
|
}
|
|
4028
4328
|
const classification = classifyDisconnect(err);
|
|
4029
4329
|
conn.lastError = classification.message;
|
|
4030
|
-
console.error(`${
|
|
4330
|
+
console.error(`${TAG17} disconnect account=${conn.accountId}: ${classification.kind} \u2014 ${classification.message}`);
|
|
4031
4331
|
if (!classification.shouldRetry) {
|
|
4032
4332
|
conn.terminalReason = classification.message;
|
|
4033
|
-
|
|
4333
|
+
if (conn.sessionStuckReason) {
|
|
4334
|
+
console.error(
|
|
4335
|
+
`${HEALTH_TAG} op=rearm account=${conn.accountId} attempt=${conn.rearmAttempts} sinceStuckMs=${sinceStuckMs(conn)} outcome=skipped-terminal`
|
|
4336
|
+
);
|
|
4337
|
+
}
|
|
4338
|
+
console.error(`${TAG17} terminal disconnect for account=${conn.accountId}, stopping reconnection`);
|
|
4339
|
+
if (heldTimer !== void 0) clearTimeout(heldTimer);
|
|
4034
4340
|
return;
|
|
4035
4341
|
}
|
|
4036
4342
|
}
|
|
4343
|
+
if (heldTimer !== void 0) clearTimeout(heldTimer);
|
|
4037
4344
|
const decision = computeReconnectNextState({
|
|
4038
4345
|
reconnectAttempts: conn.reconnectAttempts,
|
|
4039
4346
|
uptimeMs,
|
|
@@ -4041,28 +4348,44 @@ async function connectWithReconnect(conn) {
|
|
|
4041
4348
|
maxAttempts
|
|
4042
4349
|
});
|
|
4043
4350
|
if (decision.action === "abort") {
|
|
4044
|
-
const stuckReason = `
|
|
4351
|
+
const stuckReason = `RETRYING SLOWLY account=${conn.accountId} attempts=${decision.finalAttempts}/${maxAttempts} rearmIntervalMs=${REARM_INTERVAL_MS} uptimeMsLast=${uptimeMs} stableThresholdMs=${STABLE_UPTIME_MS} lastError=${conn.lastError ?? "(none)"}`;
|
|
4352
|
+
if (conn.rearmAttempts > 0) {
|
|
4353
|
+
console.error(
|
|
4354
|
+
`${HEALTH_TAG} op=rearm account=${conn.accountId} attempt=${conn.rearmAttempts} sinceStuckMs=${sinceStuckMs(conn)} outcome=failed`
|
|
4355
|
+
);
|
|
4356
|
+
}
|
|
4045
4357
|
console.error(
|
|
4046
|
-
`${
|
|
4358
|
+
`${TAG17} ${stuckReason} \u2014 re-arming in ${REARM_INTERVAL_MS}ms and every ${REARM_INTERVAL_MS}ms after; a re-pair via QR ends the wait sooner`
|
|
4047
4359
|
);
|
|
4048
4360
|
conn.sessionStuckReason = stuckReason;
|
|
4049
4361
|
conn.lastError = stuckReason;
|
|
4050
|
-
|
|
4362
|
+
if (conn.stuckSinceMs === void 0) conn.stuckSinceMs = Date.now();
|
|
4363
|
+
await new Promise((resolve6) => {
|
|
4364
|
+
const timer = setTimeout(resolve6, REARM_INTERVAL_MS);
|
|
4365
|
+
conn.abortController.signal.addEventListener("abort", () => {
|
|
4366
|
+
clearTimeout(timer);
|
|
4367
|
+
resolve6();
|
|
4368
|
+
}, { once: true });
|
|
4369
|
+
});
|
|
4370
|
+
if (conn.abortController.signal.aborted) return;
|
|
4371
|
+
conn.rearmAttempts += 1;
|
|
4372
|
+
continue;
|
|
4051
4373
|
}
|
|
4052
4374
|
conn.reconnectAttempts = decision.nextAttempts;
|
|
4053
4375
|
if (decision.reason === "short-lived") {
|
|
4054
4376
|
console.error(
|
|
4055
|
-
`${
|
|
4377
|
+
`${TAG17} short-lived session account=${conn.accountId} uptimeMs=${uptimeMs} attempt=${decision.nextAttempts}/${maxAttempts} lastError=${conn.lastError ?? "(clean disconnect)"}`
|
|
4056
4378
|
);
|
|
4057
4379
|
} else {
|
|
4380
|
+
clearStuck(conn);
|
|
4058
4381
|
console.error(
|
|
4059
|
-
`${
|
|
4382
|
+
`${TAG17} session stable account=${conn.accountId} uptimeMs=${uptimeMs} \u2014 reconnect counter reset`
|
|
4060
4383
|
);
|
|
4061
4384
|
}
|
|
4062
4385
|
if (decision.reason === "short-lived" || cycleError) {
|
|
4063
4386
|
const delay = computeBackoff(Math.max(1, decision.nextAttempts));
|
|
4064
4387
|
console.error(
|
|
4065
|
-
`${
|
|
4388
|
+
`${TAG17} reconnecting account=${conn.accountId} in ${delay}ms (attempt ${decision.nextAttempts}/${maxAttempts})`
|
|
4066
4389
|
);
|
|
4067
4390
|
await new Promise((resolve6) => {
|
|
4068
4391
|
const timer = setTimeout(resolve6, delay);
|
|
@@ -4096,11 +4419,11 @@ function watchForDisconnect(conn) {
|
|
|
4096
4419
|
conn.sock.ev.on("connection.update", (update) => {
|
|
4097
4420
|
if (update.connection === "close") {
|
|
4098
4421
|
if (connections.get(conn.accountId) !== conn) return;
|
|
4099
|
-
console.error(`${
|
|
4422
|
+
console.error(`${TAG17} socket disconnected for account=${conn.accountId}`);
|
|
4100
4423
|
conn.connected = false;
|
|
4101
4424
|
conn.sock = null;
|
|
4102
4425
|
connectWithReconnect(conn).catch((err) => {
|
|
4103
|
-
console.error(`${
|
|
4426
|
+
console.error(`${TAG17} reconnection failed for account=${conn.accountId}: ${formatError(err)}`);
|
|
4104
4427
|
});
|
|
4105
4428
|
}
|
|
4106
4429
|
});
|
|
@@ -4122,26 +4445,115 @@ function applySelfIdentity(conn, sock, phase) {
|
|
|
4122
4445
|
);
|
|
4123
4446
|
}
|
|
4124
4447
|
}
|
|
4448
|
+
function monitorChatMetadata(conn) {
|
|
4449
|
+
if (!conn.sock) return;
|
|
4450
|
+
const sock = conn.sock;
|
|
4451
|
+
const account = conn.platformAccountId;
|
|
4452
|
+
const canonicalise = async (entries) => {
|
|
4453
|
+
const out = [];
|
|
4454
|
+
for (const e of entries) {
|
|
4455
|
+
if (!e.jid.endsWith("@lid")) {
|
|
4456
|
+
out.push(e);
|
|
4457
|
+
continue;
|
|
4458
|
+
}
|
|
4459
|
+
const { canonical, resolved } = await resolveCanonicalConversationJid(e.jid, conn.lidMapping);
|
|
4460
|
+
if (resolved === "lid2pn") {
|
|
4461
|
+
console.error(`${TAG17} [wa-chatmeta] op=canonical raw=${e.jid} canonical=${canonical}`);
|
|
4462
|
+
out.push({ ...e, jid: canonical });
|
|
4463
|
+
} else {
|
|
4464
|
+
out.push(e);
|
|
4465
|
+
}
|
|
4466
|
+
}
|
|
4467
|
+
return out;
|
|
4468
|
+
};
|
|
4469
|
+
const persist = (kind, entries, perJid) => {
|
|
4470
|
+
if (entries.length === 0) return;
|
|
4471
|
+
if (perJid) {
|
|
4472
|
+
for (const e of entries) {
|
|
4473
|
+
console.error(
|
|
4474
|
+
`${TAG17} [wa-chatmeta] op=event kind=${kind} jid=${e.jid} name=${e.name.length} source=${e.source}`
|
|
4475
|
+
);
|
|
4476
|
+
}
|
|
4477
|
+
}
|
|
4478
|
+
const total = writeChatMetadataMerged(account, entries);
|
|
4479
|
+
console.error(
|
|
4480
|
+
`${TAG17} [wa-chatmeta] op=persist kind=${kind} account=${account.slice(0, 8)} updates=${entries.length} outcome=${total < 0 ? "fail" : "ok"} entries=${total}`
|
|
4481
|
+
);
|
|
4482
|
+
};
|
|
4483
|
+
sock.ev.on("messaging-history.set", ({ chats, contacts }) => {
|
|
4484
|
+
const nowIso = (/* @__PURE__ */ new Date()).toISOString();
|
|
4485
|
+
const chatEntries = chatEntriesFromChats(chats ?? [], nowIso);
|
|
4486
|
+
const contactEntries = chatEntriesFromContacts(contacts ?? [], nowIso);
|
|
4487
|
+
console.error(
|
|
4488
|
+
`${TAG17} [wa-chatmeta] op=history-set account=${account.slice(0, 8)} chats=${chats?.length ?? 0} contacts=${contacts?.length ?? 0} groups=${chatEntries.filter((e) => e.isGroup).length} named=${chatEntries.length + contactEntries.length}`
|
|
4489
|
+
);
|
|
4490
|
+
void canonicalise([...chatEntries, ...contactEntries]).then((e) => persist("history-set", e, false));
|
|
4491
|
+
});
|
|
4492
|
+
sock.ev.on("chats.upsert", (chats) => {
|
|
4493
|
+
void canonicalise(chatEntriesFromChats(chats ?? [], (/* @__PURE__ */ new Date()).toISOString())).then((e) => persist("chats.upsert", e, true));
|
|
4494
|
+
});
|
|
4495
|
+
sock.ev.on("chats.update", (chats) => {
|
|
4496
|
+
void canonicalise(chatEntriesFromChats(chats ?? [], (/* @__PURE__ */ new Date()).toISOString())).then((e) => persist("chats.update", e, true));
|
|
4497
|
+
});
|
|
4498
|
+
sock.ev.on("contacts.upsert", (contacts) => {
|
|
4499
|
+
void canonicalise(chatEntriesFromContacts(contacts ?? [], (/* @__PURE__ */ new Date()).toISOString())).then((e) => persist("contacts.upsert", e, true));
|
|
4500
|
+
});
|
|
4501
|
+
sock.ev.on("contacts.update", (contacts) => {
|
|
4502
|
+
void canonicalise(chatEntriesFromContacts(contacts ?? [], (/* @__PURE__ */ new Date()).toISOString())).then((e) => persist("contacts.update", e, true));
|
|
4503
|
+
});
|
|
4504
|
+
sock.ev.on("groups.update", (updates) => {
|
|
4505
|
+
persist("groups.update", groupEntriesFromUpdates(updates ?? [], (/* @__PURE__ */ new Date()).toISOString()), true);
|
|
4506
|
+
});
|
|
4507
|
+
const groupFetchAttempted = /* @__PURE__ */ new Set();
|
|
4508
|
+
sock.ev.on("messages.upsert", async (upsert) => {
|
|
4509
|
+
for (const msg of upsert.messages ?? []) {
|
|
4510
|
+
const jid = msg.key?.remoteJid;
|
|
4511
|
+
if (!jid || !jid.endsWith("@g.us")) continue;
|
|
4512
|
+
if (groupFetchAttempted.has(jid)) continue;
|
|
4513
|
+
if (readChatMetadata(account).get(jid)?.source === "group") continue;
|
|
4514
|
+
groupFetchAttempted.add(jid);
|
|
4515
|
+
const startedAt = Date.now();
|
|
4516
|
+
try {
|
|
4517
|
+
const meta = await sock.groupMetadata(jid);
|
|
4518
|
+
console.error(
|
|
4519
|
+
`${TAG17} [wa-chatmeta] op=group-fetch jid=${jid} ms=${Date.now() - startedAt} outcome=ok`
|
|
4520
|
+
);
|
|
4521
|
+
persist(
|
|
4522
|
+
"group-fetch",
|
|
4523
|
+
groupEntriesFromUpdates([{ id: jid, subject: meta.subject }], (/* @__PURE__ */ new Date()).toISOString()),
|
|
4524
|
+
true
|
|
4525
|
+
);
|
|
4526
|
+
} catch (err) {
|
|
4527
|
+
console.error(
|
|
4528
|
+
`${TAG17} [wa-chatmeta] op=group-fetch jid=${jid} ms=${Date.now() - startedAt} outcome=fail reason=${(err instanceof Error ? err.message : String(err)).slice(0, 100)}`
|
|
4529
|
+
);
|
|
4530
|
+
}
|
|
4531
|
+
}
|
|
4532
|
+
});
|
|
4533
|
+
}
|
|
4534
|
+
function listConnections() {
|
|
4535
|
+
return [...connections.values()];
|
|
4536
|
+
}
|
|
4125
4537
|
function monitorInbound(conn) {
|
|
4126
4538
|
if (!conn.sock || !onInboundMessage) return;
|
|
4127
4539
|
const sock = conn.sock;
|
|
4128
4540
|
const debounceMs = whatsAppConfig.accounts?.[conn.accountId]?.debounceMs ?? whatsAppConfig.debounceMs ?? 0;
|
|
4129
|
-
console.error(`${
|
|
4541
|
+
console.error(`${TAG17} monitorInbound started account=${conn.accountId} debounceMs=${debounceMs}`);
|
|
4130
4542
|
conn.debouncer = createInboundDebouncer({
|
|
4131
4543
|
debounceMs,
|
|
4132
4544
|
buildKey: (payload) => {
|
|
4133
|
-
const
|
|
4134
|
-
return `${payload.accountId}:${
|
|
4545
|
+
const conversationKey2 = payload.isGroup ? payload.groupJid : payload.senderPhone;
|
|
4546
|
+
return `${payload.accountId}:${conversationKey2 ?? payload.senderPhone}:${payload.senderPhone}`;
|
|
4135
4547
|
},
|
|
4136
4548
|
onFlush: (entries) => {
|
|
4137
4549
|
if (!onInboundMessage) return;
|
|
4138
4550
|
if (entries.length > 1) {
|
|
4139
|
-
console.error(`${
|
|
4551
|
+
console.error(`${TAG17} debounce: combining ${entries.length} messages account=${conn.accountId} from=${entries[0].senderPhone}`);
|
|
4140
4552
|
}
|
|
4141
4553
|
dispatchInboundPayload(conn, combineInboundBatch(entries));
|
|
4142
4554
|
},
|
|
4143
4555
|
onError: (err) => {
|
|
4144
|
-
console.error(`${
|
|
4556
|
+
console.error(`${TAG17} debounce flush error account=${conn.accountId}: ${String(err)}`);
|
|
4145
4557
|
}
|
|
4146
4558
|
});
|
|
4147
4559
|
sock.ev.on("messages.upsert", async (upsert) => {
|
|
@@ -4254,7 +4666,7 @@ function monitorInbound(conn) {
|
|
|
4254
4666
|
}
|
|
4255
4667
|
const entries = messageStore.get(storeKey);
|
|
4256
4668
|
console.error(
|
|
4257
|
-
`${
|
|
4669
|
+
`${TAG17} stored message ${msg.key.id ?? "?"} for ${remoteJid} (type: ${upsert.type}, store size: ${entries?.length ?? 0}/${MESSAGE_STORE_MAX}) storeAccount=${persistAccountId} socketOwner=${conn.platformAccountId}`
|
|
4258
4670
|
);
|
|
4259
4671
|
recordActivity({
|
|
4260
4672
|
accountId: conn.accountId,
|
|
@@ -4308,7 +4720,7 @@ function monitorInbound(conn) {
|
|
|
4308
4720
|
const wantsServableMedia = mediaServable && !!conn.sock && (!fromMe || !alreadyRecorded);
|
|
4309
4721
|
if (fromMe && mediaServable && !!conn.sock) {
|
|
4310
4722
|
console.error(
|
|
4311
|
-
`${
|
|
4723
|
+
`${TAG17} fromMe-media-echo msgKey=${msg.key.id} decision=${alreadyRecorded ? "skip" : "store"} reason=${alreadyRecorded ? "already-recorded" : "no-prior-record"}`
|
|
4312
4724
|
);
|
|
4313
4725
|
}
|
|
4314
4726
|
const mediaSeed = `whatsapp-live:${conn.accountId}:${canonicalRemoteJid}:${msg.key.id}`;
|
|
@@ -4391,7 +4803,7 @@ function monitorInbound(conn) {
|
|
|
4391
4803
|
receiptDeferredToWorker = true;
|
|
4392
4804
|
if (!accepted) {
|
|
4393
4805
|
console.error(
|
|
4394
|
-
`${
|
|
4806
|
+
`${TAG17} append-media overflow account=${conn.platformAccountId} msgKey=${msgKeyId ?? "?"} \u2014 pending cap ${APPEND_MEDIA_MAX_PENDING} reached, text-fallback persist, inline media shed`
|
|
4395
4807
|
);
|
|
4396
4808
|
const persisted = await persistWhatsAppMessage({
|
|
4397
4809
|
...basePersist,
|
|
@@ -4468,21 +4880,21 @@ function monitorInbound(conn) {
|
|
|
4468
4880
|
const skippedSenderJid = isGroupJid(remoteJid) ? msg.key.participant ?? remoteJid : remoteJid;
|
|
4469
4881
|
const skippedSenderPhone = await resolveJidToE164(skippedSenderJid, conn.lidMapping) ?? skippedSenderJid;
|
|
4470
4882
|
console.error(
|
|
4471
|
-
`${
|
|
4883
|
+
`${TAG17} append-type message ${msg.key.id ?? "?"} dispatch skipped from=${skippedSenderPhone}${msg.pushName ? ` name=${msg.pushName}` : ""} account=${conn.accountId} (persistence not confirmed here)`
|
|
4472
4884
|
);
|
|
4473
4885
|
continue;
|
|
4474
4886
|
}
|
|
4475
4887
|
await handleInboundMessage(conn, msg);
|
|
4476
4888
|
} catch (err) {
|
|
4477
|
-
console.error(`${
|
|
4889
|
+
console.error(`${TAG17} inbound handler error account=${conn.accountId}: ${String(err)}`);
|
|
4478
4890
|
}
|
|
4479
4891
|
}
|
|
4480
4892
|
});
|
|
4481
4893
|
console.error(`[whatsapp-reaction] op=subscribed account=${conn.platformAccountId}`);
|
|
4482
4894
|
sock.ev.on("messages.reaction", async (reactions) => {
|
|
4483
|
-
for (const
|
|
4895
|
+
for (const entry2 of reactions) {
|
|
4484
4896
|
try {
|
|
4485
|
-
const sig = readReactionEntry(
|
|
4897
|
+
const sig = readReactionEntry(entry2);
|
|
4486
4898
|
if (!sig) continue;
|
|
4487
4899
|
if (sig.fromMe) continue;
|
|
4488
4900
|
const reactorTelephone = await resolveJidToE164(sig.reactorJid, conn.lidMapping) ?? sig.reactorJid;
|
|
@@ -4530,31 +4942,31 @@ async function handleInboundMessage(conn, msg) {
|
|
|
4530
4942
|
const remoteJid = msg.key.remoteJid;
|
|
4531
4943
|
if (!remoteJid) return;
|
|
4532
4944
|
if (remoteJid === "status@broadcast") {
|
|
4533
|
-
console.error(`${
|
|
4945
|
+
console.error(`${TAG17} drop: status broadcast account=${conn.accountId}`);
|
|
4534
4946
|
return;
|
|
4535
4947
|
}
|
|
4536
4948
|
if (!msg.message) {
|
|
4537
|
-
console.error(`${
|
|
4949
|
+
console.error(`${TAG17} drop: empty message account=${conn.accountId} from=${remoteJid}`);
|
|
4538
4950
|
return;
|
|
4539
4951
|
}
|
|
4540
4952
|
const dedupKey = `${conn.accountId}:${remoteJid}:${msg.key.id}`;
|
|
4541
4953
|
if (isDuplicateInbound(dedupKey)) {
|
|
4542
|
-
console.error(`${
|
|
4954
|
+
console.error(`${TAG17} drop: duplicate account=${conn.accountId} key=${dedupKey}`);
|
|
4543
4955
|
return;
|
|
4544
4956
|
}
|
|
4545
4957
|
if (msg.key.fromMe) {
|
|
4546
4958
|
if (msg.key.id && isAgentSentMessage(msg.key.id)) {
|
|
4547
|
-
console.error(`${
|
|
4959
|
+
console.error(`${TAG17} drop: echo suppression account=${conn.accountId} msgId=${msg.key.id}`);
|
|
4548
4960
|
return;
|
|
4549
4961
|
}
|
|
4550
4962
|
const extracted2 = extractMessage(msg);
|
|
4551
4963
|
if (!extracted2.text) {
|
|
4552
|
-
console.error(`${
|
|
4964
|
+
console.error(`${TAG17} owner reply skipped \u2014 no text content account=${conn.accountId}`);
|
|
4553
4965
|
return;
|
|
4554
4966
|
}
|
|
4555
4967
|
const isGroup2 = isGroupJid(remoteJid);
|
|
4556
4968
|
const senderPhone2 = conn.selfPhone ?? "owner";
|
|
4557
|
-
console.error(`${
|
|
4969
|
+
console.error(`${TAG17} owner reply mirrored to session from=${senderPhone2} account=${conn.accountId}`);
|
|
4558
4970
|
const reply2 = async (text) => {
|
|
4559
4971
|
const currentSock = conn.sock;
|
|
4560
4972
|
if (!currentSock) throw new Error("WhatsApp disconnected \u2014 cannot reply");
|
|
@@ -4588,7 +5000,7 @@ async function handleInboundMessage(conn, msg) {
|
|
|
4588
5000
|
}
|
|
4589
5001
|
const extracted = extractMessage(msg);
|
|
4590
5002
|
if (!extracted.text && !extracted.mediaType) {
|
|
4591
|
-
console.error(`${
|
|
5003
|
+
console.error(`${TAG17} drop: no text or media account=${conn.accountId} from=${remoteJid}`);
|
|
4592
5004
|
return;
|
|
4593
5005
|
}
|
|
4594
5006
|
const isGroup = isGroupJid(remoteJid);
|
|
@@ -4612,7 +5024,7 @@ async function handleInboundMessage(conn, msg) {
|
|
|
4612
5024
|
});
|
|
4613
5025
|
}
|
|
4614
5026
|
console.error(
|
|
4615
|
-
`${
|
|
5027
|
+
`${TAG17} inbound account=${conn.accountId} from=${senderPhone} group=${isGroup} access=${accessResult.allowed ? "allowed" : "blocked"}(${accessResult.reason}) agent=${accessResult.agentType}` + (extracted.mediaType ? ` media=${extracted.mediaType}` : "") + (extracted.quotedMessage ? ` replyTo=${extracted.quotedMessage.id}` : "")
|
|
4616
5028
|
);
|
|
4617
5029
|
if (!accessResult.allowed && accessResult.reason === "account-manager-unresolved") {
|
|
4618
5030
|
console.error(
|
|
@@ -4649,12 +5061,12 @@ async function handleInboundMessage(conn, msg) {
|
|
|
4649
5061
|
maxBytes: maxMb * 1024 * 1024
|
|
4650
5062
|
});
|
|
4651
5063
|
if (!mediaResult) {
|
|
4652
|
-
console.error(`${
|
|
5064
|
+
console.error(`${TAG17} media download returned undefined account=${conn.accountId} type=${extracted.mediaType} from=${remoteJid}`);
|
|
4653
5065
|
}
|
|
4654
5066
|
}
|
|
4655
5067
|
if (extracted.mediaType === "audio" && mediaResult?.path) {
|
|
4656
|
-
const
|
|
4657
|
-
const debounceKey = `${conn.accountId}:${
|
|
5068
|
+
const conversationKey2 = isGroup ? remoteJid : senderPhone;
|
|
5069
|
+
const debounceKey = `${conn.accountId}:${conversationKey2}:${senderPhone}`;
|
|
4658
5070
|
let resolvePending;
|
|
4659
5071
|
const sttPending = new Promise((resolve6) => {
|
|
4660
5072
|
resolvePending = resolve6;
|
|
@@ -4671,7 +5083,7 @@ async function handleInboundMessage(conn, msg) {
|
|
|
4671
5083
|
}
|
|
4672
5084
|
const reply = async (text) => {
|
|
4673
5085
|
if (isGroupJid(remoteJid)) {
|
|
4674
|
-
console.error(`${
|
|
5086
|
+
console.error(`${TAG17} op=group-reply-blocked jid=${remoteJid}`);
|
|
4675
5087
|
throw new WaReplyError("group reply blocked \u2014 observe-only", false);
|
|
4676
5088
|
}
|
|
4677
5089
|
const currentSock = conn.sock;
|
|
@@ -4718,15 +5130,15 @@ async function handleInboundMessage(conn, msg) {
|
|
|
4718
5130
|
if (accessResult.agentType === "public") {
|
|
4719
5131
|
const hoursResult = await isBusinessOpen(conn.accountId);
|
|
4720
5132
|
if (!hoursResult.open) {
|
|
4721
|
-
console.error(`${
|
|
5133
|
+
console.error(`${TAG17} [${conn.accountId}] dispatch skipped: business closed`);
|
|
4722
5134
|
const afterHoursMessage = whatsAppConfig.accounts?.[conn.accountId]?.afterHoursMessage ?? whatsAppConfig.afterHoursMessage;
|
|
4723
5135
|
if (afterHoursMessage) {
|
|
4724
5136
|
try {
|
|
4725
5137
|
await reply(afterHoursMessage);
|
|
4726
|
-
console.error(`${
|
|
5138
|
+
console.error(`${TAG17} [${conn.accountId}] after-hours auto-reply sent to ${remoteJid}`);
|
|
4727
5139
|
} catch (err) {
|
|
4728
5140
|
console.error(
|
|
4729
|
-
`${
|
|
5141
|
+
`${TAG17} [${conn.accountId}] after-hours auto-reply failed: ${err instanceof Error ? err.message : String(err)}`
|
|
4730
5142
|
);
|
|
4731
5143
|
}
|
|
4732
5144
|
}
|
|
@@ -4775,6 +5187,7 @@ export {
|
|
|
4775
5187
|
getAuthAgeMs,
|
|
4776
5188
|
clearAuth,
|
|
4777
5189
|
discardAuthDirSync,
|
|
5190
|
+
startVersionDriftCensus,
|
|
4778
5191
|
drainCredsSaveQueue,
|
|
4779
5192
|
createWaSocket,
|
|
4780
5193
|
waitForConnection,
|
|
@@ -4782,6 +5195,8 @@ export {
|
|
|
4782
5195
|
formatError,
|
|
4783
5196
|
closeKind,
|
|
4784
5197
|
classifyDisconnect,
|
|
5198
|
+
LINK_CENSUS_INTERVAL_MS,
|
|
5199
|
+
runLinkCensus,
|
|
4785
5200
|
normalizeE164,
|
|
4786
5201
|
toWhatsappJid,
|
|
4787
5202
|
jidToE164,
|
|
@@ -4795,6 +5210,7 @@ export {
|
|
|
4795
5210
|
managerPhonesForAccount,
|
|
4796
5211
|
isAdminPhone,
|
|
4797
5212
|
shouldDispatchInbound,
|
|
5213
|
+
formatSenderPhone,
|
|
4798
5214
|
checkAccessRateLimit,
|
|
4799
5215
|
recordAccessFailedAttempt,
|
|
4800
5216
|
clearAccessRateLimit,
|
|
@@ -4813,10 +5229,6 @@ export {
|
|
|
4813
5229
|
sendComposing,
|
|
4814
5230
|
sendPaused,
|
|
4815
5231
|
sendMediaMessage,
|
|
4816
|
-
resolvePlatformAccountId,
|
|
4817
|
-
warnNonUuidAccount,
|
|
4818
|
-
isHouseManagedService,
|
|
4819
|
-
isCustomerTurnSuppressed,
|
|
4820
5232
|
DATA_ROOT,
|
|
4821
5233
|
crossesForeignAccountPartition,
|
|
4822
5234
|
isWithinOwnAccountPartition,
|
|
@@ -4824,6 +5236,13 @@ export {
|
|
|
4824
5236
|
resolveClaudeUploadsPath,
|
|
4825
5237
|
sessionScratchpadRoot,
|
|
4826
5238
|
resolveScratchpadPath,
|
|
5239
|
+
readChatMetadata,
|
|
5240
|
+
writeChatMetadataMerged,
|
|
5241
|
+
groupEntriesFromUpdates,
|
|
5242
|
+
resolvePlatformAccountId,
|
|
5243
|
+
warnNonUuidAccount,
|
|
5244
|
+
isHouseManagedService,
|
|
5245
|
+
isCustomerTurnSuppressed,
|
|
4827
5246
|
SUPPORTED_MIME_TYPES,
|
|
4828
5247
|
MAX_FILE_SIZE_BYTES,
|
|
4829
5248
|
MAX_FILES_PER_MESSAGE,
|
|
@@ -4866,5 +5285,6 @@ export {
|
|
|
4866
5285
|
getMessages,
|
|
4867
5286
|
listConversationJids,
|
|
4868
5287
|
listStoreAccountKeys,
|
|
4869
|
-
shutdown
|
|
5288
|
+
shutdown,
|
|
5289
|
+
listConnections
|
|
4870
5290
|
};
|