@rubytech/create-maxy-code 0.1.477 → 0.1.479
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/payload/platform/docs/superpowers/plans/2026-07-20-task-1704-data-portal-standing-audit.md +673 -0
- package/payload/platform/docs/superpowers/plans/2026-07-20-task-1789-reseat-channel-row-fork.md +1244 -0
- package/payload/platform/docs/superpowers/plans/2026-07-20-task-1818-loop-gate-app-routes.md +462 -0
- package/payload/platform/docs/superpowers/plans/2026-07-20-task-1819-top-level-label-allowlist.md +321 -0
- package/payload/platform/docs/superpowers/plans/2026-07-20-task-1831-data-portal-class-derived-allowlist.md +2177 -0
- package/payload/platform/docs/superpowers/specs/2026-07-20-task-1704-data-portal-standing-audit-design.md +177 -0
- package/payload/platform/docs/superpowers/specs/2026-07-20-task-1789-reseat-channel-row-fork-design.md +201 -0
- package/payload/platform/docs/superpowers/specs/2026-07-20-task-1818-loop-gate-app-routes-design.md +217 -0
- package/payload/platform/docs/superpowers/specs/2026-07-20-task-1819-top-level-label-allowlist-design.md +80 -0
- package/payload/platform/docs/superpowers/specs/2026-07-20-task-1820-cpu-triage-admin-tools-design.md +97 -0
- package/payload/platform/docs/superpowers/specs/2026-07-20-task-1831-data-portal-class-derived-allowlist-design.md +163 -0
- package/payload/platform/lib/graph-style/dist/index.d.ts.map +1 -1
- package/payload/platform/lib/graph-style/dist/index.js +22 -0
- package/payload/platform/lib/graph-style/dist/index.js.map +1 -1
- package/payload/platform/lib/graph-style/src/__tests__/parity.test.ts +6 -0
- package/payload/platform/lib/graph-style/src/index.ts +20 -0
- package/payload/platform/lib/storage-broker/dist/__tests__/audit.test.js +44 -0
- package/payload/platform/lib/storage-broker/dist/__tests__/audit.test.js.map +1 -1
- package/payload/platform/lib/storage-broker/dist/audit.d.ts +56 -0
- package/payload/platform/lib/storage-broker/dist/audit.d.ts.map +1 -1
- package/payload/platform/lib/storage-broker/dist/audit.js +34 -0
- package/payload/platform/lib/storage-broker/dist/audit.js.map +1 -1
- package/payload/platform/lib/storage-broker/src/__tests__/audit.test.ts +70 -1
- package/payload/platform/lib/storage-broker/src/audit.ts +111 -0
- package/payload/platform/plugins/admin/.claude-plugin/plugin.json +1 -1
- package/payload/platform/plugins/admin/PLUGIN.md +7 -1
- package/payload/platform/plugins/admin/mcp/dist/index.js +72 -0
- package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/admin/mcp/dist/tools/cpu-triage.d.ts +94 -0
- package/payload/platform/plugins/admin/mcp/dist/tools/cpu-triage.d.ts.map +1 -0
- package/payload/platform/plugins/admin/mcp/dist/tools/cpu-triage.js +229 -0
- package/payload/platform/plugins/admin/mcp/dist/tools/cpu-triage.js.map +1 -0
- package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +14 -2
- package/payload/platform/plugins/cloudflare/PLUGIN.md +26 -1
- package/payload/platform/plugins/cloudflare/bin/d1-http.mjs +80 -0
- package/payload/platform/plugins/cloudflare/bin/portal-enrol.mjs +17 -2
- package/payload/platform/plugins/cloudflare/bin/portal-index-push.mjs +357 -0
- package/payload/platform/plugins/cloudflare/bin/schema-exposed-dirs.mjs +103 -0
- package/payload/platform/plugins/cloudflare/mcp/__tests__/authorize.test.ts +2 -2
- package/payload/platform/plugins/cloudflare/mcp/__tests__/d1-http.test.ts +96 -0
- package/payload/platform/plugins/cloudflare/mcp/__tests__/portal-directory-listing.test.ts +120 -0
- package/payload/platform/plugins/cloudflare/mcp/__tests__/portal-enrol.test.ts +59 -4
- package/payload/platform/plugins/cloudflare/mcp/__tests__/portal-index-push.test.ts +176 -0
- package/payload/platform/plugins/cloudflare/mcp/__tests__/portal-index-state-contract.test.ts +75 -0
- package/payload/platform/plugins/cloudflare/mcp/__tests__/portal-indexed-download.test.ts +131 -0
- package/payload/platform/plugins/cloudflare/mcp/__tests__/portal-sign.test.ts +68 -0
- package/payload/platform/plugins/cloudflare/mcp/__tests__/schema-exposed-dirs.test.ts +88 -0
- package/payload/platform/plugins/cloudflare/mcp/__tests__/template-config.test.ts +39 -0
- package/payload/platform/plugins/cloudflare/skills/data-portal/SKILL.md +68 -1
- package/payload/platform/plugins/cloudflare/skills/data-portal/schema.sql +30 -1
- package/payload/platform/plugins/cloudflare/skills/data-portal/template/functions/api/_lib/portal-sign.mjs +87 -0
- package/payload/platform/plugins/cloudflare/skills/data-portal/template/functions/api/_lib/session.ts +8 -4
- package/payload/platform/plugins/cloudflare/skills/data-portal/template/functions/api/_lib/types.ts +10 -0
- package/payload/platform/plugins/cloudflare/skills/data-portal/template/functions/api/download.ts +107 -1
- package/payload/platform/plugins/cloudflare/skills/data-portal/template/functions/api/files.ts +82 -2
- package/payload/platform/plugins/cloudflare/skills/data-portal/template/index.html +9 -0
- package/payload/platform/plugins/cloudflare/skills/data-portal/template/portal.css +56 -0
- package/payload/platform/plugins/cloudflare/skills/data-portal/template/portal.js +98 -2
- package/payload/platform/plugins/cloudflare/skills/data-portal/template/wrangler.toml +12 -0
- package/payload/platform/plugins/docs/references/admin-ui.md +1 -1
- package/payload/platform/plugins/docs/references/cloudflare.md +12 -0
- package/payload/platform/plugins/email/PLUGIN.md +2 -1
- package/payload/platform/plugins/email/mcp/dist/__tests__/confirm-sent-copy.test.d.ts +2 -0
- package/payload/platform/plugins/email/mcp/dist/__tests__/confirm-sent-copy.test.d.ts.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/__tests__/confirm-sent-copy.test.js +92 -0
- package/payload/platform/plugins/email/mcp/dist/__tests__/confirm-sent-copy.test.js.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/__tests__/email-draft-send.test.js +112 -12
- package/payload/platform/plugins/email/mcp/dist/__tests__/email-draft-send.test.js.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/__tests__/email-send-sent-copy.test.d.ts +2 -0
- package/payload/platform/plugins/email/mcp/dist/__tests__/email-send-sent-copy.test.d.ts.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/__tests__/email-send-sent-copy.test.js +54 -0
- package/payload/platform/plugins/email/mcp/dist/__tests__/email-send-sent-copy.test.js.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/__tests__/imap-drafts.test.js +170 -1
- package/payload/platform/plugins/email/mcp/dist/__tests__/imap-drafts.test.js.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/__tests__/sent-copy-note.test.d.ts +2 -0
- package/payload/platform/plugins/email/mcp/dist/__tests__/sent-copy-note.test.d.ts.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/__tests__/sent-copy-note.test.js +86 -0
- package/payload/platform/plugins/email/mcp/dist/__tests__/sent-copy-note.test.js.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/__tests__/sent-copy-sweep.test.d.ts +2 -0
- package/payload/platform/plugins/email/mcp/dist/__tests__/sent-copy-sweep.test.d.ts.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/__tests__/sent-copy-sweep.test.js +164 -0
- package/payload/platform/plugins/email/mcp/dist/__tests__/sent-copy-sweep.test.js.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/__tests__/sent-ledger.test.d.ts +2 -0
- package/payload/platform/plugins/email/mcp/dist/__tests__/sent-ledger.test.d.ts.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/__tests__/sent-ledger.test.js +82 -0
- package/payload/platform/plugins/email/mcp/dist/__tests__/sent-ledger.test.js.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/lib/imap.d.ts +95 -0
- package/payload/platform/plugins/email/mcp/dist/lib/imap.d.ts.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/lib/imap.js +223 -0
- package/payload/platform/plugins/email/mcp/dist/lib/imap.js.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/lib/sent-copy-note.d.ts +18 -0
- package/payload/platform/plugins/email/mcp/dist/lib/sent-copy-note.d.ts.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/lib/sent-copy-note.js +59 -0
- package/payload/platform/plugins/email/mcp/dist/lib/sent-copy-note.js.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/lib/sent-copy-sweep.d.ts +48 -0
- package/payload/platform/plugins/email/mcp/dist/lib/sent-copy-sweep.d.ts.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/lib/sent-copy-sweep.js +79 -0
- package/payload/platform/plugins/email/mcp/dist/lib/sent-copy-sweep.js.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/lib/sent-ledger.d.ts +44 -0
- package/payload/platform/plugins/email/mcp/dist/lib/sent-ledger.d.ts.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/lib/sent-ledger.js +63 -0
- package/payload/platform/plugins/email/mcp/dist/lib/sent-ledger.js.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/scripts/sent-copy-sweep.d.ts +2 -0
- package/payload/platform/plugins/email/mcp/dist/scripts/sent-copy-sweep.d.ts.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/scripts/sent-copy-sweep.js +55 -0
- package/payload/platform/plugins/email/mcp/dist/scripts/sent-copy-sweep.js.map +1 -0
- package/payload/platform/plugins/email/mcp/dist/tools/email-draft-send.d.ts.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/tools/email-draft-send.js +64 -13
- package/payload/platform/plugins/email/mcp/dist/tools/email-draft-send.js.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/tools/email-reply.d.ts.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/tools/email-reply.js +11 -1
- package/payload/platform/plugins/email/mcp/dist/tools/email-reply.js.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/tools/email-send.d.ts.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/tools/email-send.js +16 -3
- package/payload/platform/plugins/email/mcp/dist/tools/email-send.js.map +1 -1
- package/payload/platform/plugins/email/references/email-reference.md +36 -0
- package/payload/platform/plugins/scheduling/mcp/dist/__tests__/portal-index-audit.test.d.ts +2 -0
- package/payload/platform/plugins/scheduling/mcp/dist/__tests__/portal-index-audit.test.d.ts.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/__tests__/portal-index-audit.test.js +66 -0
- package/payload/platform/plugins/scheduling/mcp/dist/__tests__/portal-index-audit.test.js.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/__tests__/sent-sweep-throttle.test.d.ts +2 -0
- package/payload/platform/plugins/scheduling/mcp/dist/__tests__/sent-sweep-throttle.test.d.ts.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/__tests__/sent-sweep-throttle.test.js +18 -0
- package/payload/platform/plugins/scheduling/mcp/dist/__tests__/sent-sweep-throttle.test.js.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/portal-index-audit.d.ts +41 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/portal-index-audit.d.ts.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/portal-index-audit.js +75 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/portal-index-audit.js.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/sent-sweep-throttle.d.ts +13 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/sent-sweep-throttle.d.ts.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/sent-sweep-throttle.js +10 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/sent-sweep-throttle.js.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/check-due-events.js +175 -0
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/check-due-events.js.map +1 -1
- package/payload/platform/scripts/cpu-triage-run.sh +117 -0
- package/payload/platform/scripts/cpu-triage.sh +23 -4
- package/payload/platform/services/claude-session-manager/dist/activity-range.d.ts +40 -0
- package/payload/platform/services/claude-session-manager/dist/activity-range.d.ts.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/activity-range.js +68 -0
- package/payload/platform/services/claude-session-manager/dist/activity-range.js.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/activity-rows.d.ts +40 -8
- package/payload/platform/services/claude-session-manager/dist/activity-rows.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/activity-rows.js +74 -7
- package/payload/platform/services/claude-session-manager/dist/activity-rows.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js +2 -0
- package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/http-server.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/http-server.js +9 -0
- package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -1
- package/payload/server/{chunk-56WJMBQQ.js → chunk-3XLLTG6R.js} +14 -0
- package/payload/server/{chunk-JECAP3Z2.js → chunk-XGNRSM57.js} +3 -0
- package/payload/server/maxy-edge.js +1 -1
- package/payload/server/public/activity.html +8 -6
- package/payload/server/public/assets/{AdminLoginScreens-mEKP4pDi.js → AdminLoginScreens-DkxQZnvB.js} +1 -1
- package/payload/server/public/assets/AdminShell-Cv_nIFGe.js +2 -0
- package/payload/server/public/assets/Checkbox-CsTWK8mm.js +1 -0
- package/payload/server/public/assets/activity-BbnTiVBT.js +1 -0
- package/payload/server/public/assets/{admin-BVhgfgs1.js → admin-BZY3i_Ff.js} +1 -1
- package/payload/server/public/assets/{browser-D-HMmHcM.js → browser-BbIiSFKr.js} +1 -1
- package/payload/server/public/assets/{calendar-tGWvFyFn.js → calendar-BvxeTHvQ.js} +1 -1
- package/payload/server/public/assets/chat-DUKxufRk.js +1 -0
- package/payload/server/public/assets/chevron-left-BtfJD6LL.js +1 -0
- package/payload/server/public/assets/data-hl1wa-Ax.js +1 -0
- package/payload/server/public/assets/{graph-zEw610xK.js → graph-CDSGRoeb.js} +1 -1
- package/payload/server/public/assets/graph-labels-C5qLwnWA.js +1 -0
- package/payload/server/public/assets/{maximize-2-BlTjXT_Y.js → maximize-2-CAzH8dED.js} +1 -1
- package/payload/server/public/assets/{operator-C7oIw2PG.js → operator-BK7r8DAB.js} +1 -1
- package/payload/server/public/assets/{page-CTP7OFZa.js → page-B5YngOrK.js} +1 -1
- package/payload/server/public/assets/{page-DL6Zsdvk.js → page-BsCS0Dmc.js} +1 -1
- package/payload/server/public/assets/{public-r1A9dqR_.js → public-C1Pz-IBs.js} +1 -1
- package/payload/server/public/assets/{rotate-ccw-CzkkKx4-.js → rotate-ccw-UDln4Tou.js} +1 -1
- package/payload/server/public/assets/tasks-B-cLsn3p.js +1 -0
- package/payload/server/public/assets/{time-entry-format-j669DgXj.js → time-entry-format-DPOBIE18.js} +1 -1
- package/payload/server/public/assets/{triangle-alert-BPgCgIef.js → triangle-alert-Dz-CZKo5.js} +1 -1
- package/payload/server/public/assets/{useCopyFeedback-BmLa2aes.js → useCopyFeedback-wtUHGohk.js} +1 -1
- package/payload/server/public/assets/{useSelectionMode-DnnM7A8-.js → useSelectionMode-sph7AQT5.js} +1 -1
- package/payload/server/public/assets/useSubAccountSwitcher-BRIluIKf.css +1 -0
- package/payload/server/public/assets/{useVoiceRecorder-u2dwZfen.js → useVoiceRecorder-DN4jYQ0A.js} +1 -1
- package/payload/server/public/brand-defaults.css +1 -0
- package/payload/server/public/browser.html +5 -5
- package/payload/server/public/calendar.html +6 -6
- package/payload/server/public/chat.html +12 -12
- package/payload/server/public/data.html +11 -11
- package/payload/server/public/graph.html +11 -11
- package/payload/server/public/index.html +14 -14
- package/payload/server/public/operator.html +14 -14
- package/payload/server/public/privacy.html +1 -0
- package/payload/server/public/public.html +12 -12
- package/payload/server/public/tasks.html +6 -5
- package/payload/server/public/vnc-popout.html +1 -0
- package/payload/server/server.js +894 -384
- package/payload/server/{src-JYPKMWJR.js → src-XQ63FPRE.js} +3 -1
- package/payload/server/public/assets/AdminShell-BwSaZ88d.js +0 -2
- package/payload/server/public/assets/Checkbox-CN4uQ80w.js +0 -1
- package/payload/server/public/assets/activity-CIQ8ozNZ.js +0 -1
- package/payload/server/public/assets/chat-DkttVxAz.js +0 -1
- package/payload/server/public/assets/chevron-left-CZ4ez9G5.js +0 -1
- package/payload/server/public/assets/data-P-mcmnNi.js +0 -1
- package/payload/server/public/assets/graph-labels-qnBleOE6.js +0 -1
- package/payload/server/public/assets/tasks-fIJwYFWG.js +0 -1
- package/payload/server/public/assets/useSubAccountSwitcher-C_E8h07P.css +0 -1
- /package/payload/server/public/assets/{useSubAccountSwitcher-CFa6ZAT1.js → useSubAccountSwitcher-v8LyBzZN.js} +0 -0
package/payload/server/server.js
CHANGED
|
@@ -117,7 +117,7 @@ import {
|
|
|
117
117
|
vncLog,
|
|
118
118
|
walkPremiumBundles,
|
|
119
119
|
writeAdminUserAndPerson
|
|
120
|
-
} from "./chunk-
|
|
120
|
+
} from "./chunk-XGNRSM57.js";
|
|
121
121
|
import {
|
|
122
122
|
D1_MAX_SQL_STATEMENT_BYTES,
|
|
123
123
|
D1_QUERY_MAX_BODY_BYTES,
|
|
@@ -129,10 +129,11 @@ import {
|
|
|
129
129
|
listResources,
|
|
130
130
|
makeHouseCfExec,
|
|
131
131
|
makeHousePagesExec,
|
|
132
|
+
reconcileDataPortal,
|
|
132
133
|
registerResource,
|
|
133
134
|
resolveOwner,
|
|
134
135
|
tooLargeMessage
|
|
135
|
-
} from "./chunk-
|
|
136
|
+
} from "./chunk-3XLLTG6R.js";
|
|
136
137
|
import {
|
|
137
138
|
__commonJS,
|
|
138
139
|
__toESM
|
|
@@ -651,7 +652,17 @@ var require_dist3 = __commonJS({
|
|
|
651
652
|
Risk: "#C15B57",
|
|
652
653
|
Source: "#2F8FA6",
|
|
653
654
|
Finding: "#4FA389",
|
|
654
|
-
Hypothesis: "#9A8C3E"
|
|
655
|
+
Hypothesis: "#9A8C3E",
|
|
656
|
+
// Ledger cash movement (schema-base.md, added by 7b225f814). One movement
|
|
657
|
+
// of money attached to no invoice — operator-entry with no parent to drill
|
|
658
|
+
// down from, so it is a top-level chip. Burnished olive-gold sits in the
|
|
659
|
+
// money register beside Invoice's brown without colliding with it.
|
|
660
|
+
//
|
|
661
|
+
// Deliberately appended at the END rather than beside the ledger family:
|
|
662
|
+
// SHAPE_BY_LABEL assigns shapes by index position in ALL_GRAPH_LABELS
|
|
663
|
+
// (this object's key order), so a mid-table insert would silently change
|
|
664
|
+
// the rendered shape of every label after it.
|
|
665
|
+
CashEntry: "#7D5A2B"
|
|
655
666
|
};
|
|
656
667
|
exports.FALLBACK_LABEL_COLOUR = "#94A3B8";
|
|
657
668
|
exports.ALL_GRAPH_LABELS = Object.freeze(Object.keys(exports.GRAPH_LABEL_COLOURS));
|
|
@@ -748,7 +759,8 @@ var require_dist3 = __commonJS({
|
|
|
748
759
|
Risk: '<path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3"></path><path d="M12 9v4"></path><path d="M12 17h.01"></path>',
|
|
749
760
|
Source: '<path d="M12 7v14"></path><path d="M3 18a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h5a4 4 0 0 1 4 4 4 4 0 0 1 4-4h5a1 1 0 0 1 1 1v13a1 1 0 0 1-1 1h-6a3 3 0 0 0-3 3 3 3 0 0 0-3-3z"></path>',
|
|
750
761
|
Finding: '<path d="M15 14c.2-1 .7-1.7 1.5-2.5 1-.9 1.5-2.2 1.5-3.5A6 6 0 0 0 6 8c0 1 .2 2.2 1.5 3.5.7.7 1.3 1.5 1.5 2.5"></path><path d="M9 18h6"></path><path d="M10 22h4"></path>',
|
|
751
|
-
Hypothesis: '<path d="M14 2v6a2 2 0 0 0 .245.96l5.51 10.08A2 2 0 0 1 18 22H6a2 2 0 0 1-1.755-2.96l5.51-10.08A2 2 0 0 0 10 8V2"></path><path d="M6.453 15h11.094"></path><path d="M8.5 2h7"></path>'
|
|
762
|
+
Hypothesis: '<path d="M14 2v6a2 2 0 0 0 .245.96l5.51 10.08A2 2 0 0 1 18 22H6a2 2 0 0 1-1.755-2.96l5.51-10.08A2 2 0 0 0 10 8V2"></path><path d="M6.453 15h11.094"></path><path d="M8.5 2h7"></path>',
|
|
763
|
+
CashEntry: '<rect width="20" height="12" x="2" y="6" rx="2"></rect><circle cx="12" cy="12" r="2"></circle><path d="M6 12h.01"></path><path d="M18 12h.01"></path>'
|
|
752
764
|
};
|
|
753
765
|
function iconForLabel2(label) {
|
|
754
766
|
return Object.prototype.hasOwnProperty.call(LABEL_ICONS, label) ? LABEL_ICONS[label] : null;
|
|
@@ -844,6 +856,11 @@ var require_dist3 = __commonJS({
|
|
|
844
856
|
if (clientName.length > 0) {
|
|
845
857
|
return clientName.length > 24 ? clientName.slice(0, 24) + "\u2026" : clientName;
|
|
846
858
|
}
|
|
859
|
+
} else if (primaryLabel2 === "CashEntry") {
|
|
860
|
+
const desc = typeof props.description === "string" ? props.description : "";
|
|
861
|
+
if (desc.length > 0) {
|
|
862
|
+
return desc.length > 24 ? desc.slice(0, 24) + "\u2026" : desc;
|
|
863
|
+
}
|
|
847
864
|
} else if (primaryLabel2 === "Message") {
|
|
848
865
|
const role = typeof props.role === "string" ? props.role : "";
|
|
849
866
|
const roleShort = role === "user" ? "user" : role === "assistant" ? "asst" : role === "system" ? "sys" : role === "tool" ? "tool" : null;
|
|
@@ -917,6 +934,10 @@ var require_dist3 = __commonJS({
|
|
|
917
934
|
const clientName = typeof props.clientName === "string" ? props.clientName : "";
|
|
918
935
|
if (clientName.length > 0)
|
|
919
936
|
return clientName;
|
|
937
|
+
} else if (primaryLabel2 === "CashEntry") {
|
|
938
|
+
const desc = typeof props.description === "string" ? props.description : "";
|
|
939
|
+
if (desc.length > 0)
|
|
940
|
+
return desc;
|
|
920
941
|
}
|
|
921
942
|
for (const k of ["name", "title", "summary", "givenName", "subject", "text"]) {
|
|
922
943
|
const v = props[k];
|
|
@@ -1290,8 +1311,8 @@ var require_dist4 = __commonJS({
|
|
|
1290
1311
|
let edgesCreated = 0;
|
|
1291
1312
|
for (const rel of relationships) {
|
|
1292
1313
|
const type = rel.type.replace(/`/g, "");
|
|
1293
|
-
const
|
|
1294
|
-
const r = await tx.run(
|
|
1314
|
+
const q2 = rel.direction === "outgoing" ? `MATCH (a), (b) WHERE elementId(a) = $from AND elementId(b) = $to CREATE (a)-[:\`${type}\`]->(b)` : `MATCH (a), (b) WHERE elementId(a) = $from AND elementId(b) = $to CREATE (b)-[:\`${type}\`]->(a)`;
|
|
1315
|
+
const r = await tx.run(q2, { from: nodeId, to: rel.targetNodeId });
|
|
1295
1316
|
const created = r.summary.counters.updates().relationshipsCreated;
|
|
1296
1317
|
if (created === 0) {
|
|
1297
1318
|
process.stderr.write(`[graph-write] reject reason=unresolved-target-on-create labels=${labelCsv} agent=${agentLabel} relType=${rel.type} targetId=${rel.targetNodeId}
|
|
@@ -1539,10 +1560,10 @@ var serveStatic = (options = { root: "" }) => {
|
|
|
1539
1560
|
};
|
|
1540
1561
|
|
|
1541
1562
|
// server/index.ts
|
|
1542
|
-
import { readFileSync as
|
|
1563
|
+
import { readFileSync as readFileSync42, existsSync as existsSync38, watchFile, readdirSync as readdirSync26, statSync as statSync17 } from "fs";
|
|
1543
1564
|
import { spawn as spawn3 } from "child_process";
|
|
1544
1565
|
import { createHash as createHash7 } from "crypto";
|
|
1545
|
-
import { resolve as
|
|
1566
|
+
import { resolve as resolve37, join as join44, basename as basename12 } from "path";
|
|
1546
1567
|
import { homedir as homedir4 } from "os";
|
|
1547
1568
|
import { monitorEventLoopDelay } from "perf_hooks";
|
|
1548
1569
|
|
|
@@ -1957,14 +1978,14 @@ function readAllConversationsCached(accountId) {
|
|
|
1957
1978
|
for (const file of dir) {
|
|
1958
1979
|
if (!file.endsWith(".jsonl")) continue;
|
|
1959
1980
|
const path3 = join4(STORE_ROOT(), accountId, file);
|
|
1960
|
-
let
|
|
1981
|
+
let stat9;
|
|
1961
1982
|
try {
|
|
1962
|
-
|
|
1983
|
+
stat9 = statSync3(path3);
|
|
1963
1984
|
} catch {
|
|
1964
1985
|
continue;
|
|
1965
1986
|
}
|
|
1966
1987
|
const hit = idCache.get(path3);
|
|
1967
|
-
if (hit && hit.size ===
|
|
1988
|
+
if (hit && hit.size === stat9.size && hit.mtimeMs === stat9.mtimeMs) {
|
|
1968
1989
|
out.set(file, hit.ids);
|
|
1969
1990
|
continue;
|
|
1970
1991
|
}
|
|
@@ -1981,7 +2002,7 @@ function readAllConversationsCached(accountId) {
|
|
|
1981
2002
|
} catch {
|
|
1982
2003
|
continue;
|
|
1983
2004
|
}
|
|
1984
|
-
idCache.set(path3, { size:
|
|
2005
|
+
idCache.set(path3, { size: stat9.size, mtimeMs: stat9.mtimeMs, ids: parsed });
|
|
1985
2006
|
out.set(file, parsed);
|
|
1986
2007
|
}
|
|
1987
2008
|
return out;
|
|
@@ -2815,7 +2836,7 @@ var credsSaveQueue = Promise.resolve();
|
|
|
2815
2836
|
async function drainCredsSaveQueue(timeoutMs = 5e3) {
|
|
2816
2837
|
console.error(`${TAG6} draining credential save queue\u2026`);
|
|
2817
2838
|
const timer = new Promise(
|
|
2818
|
-
(
|
|
2839
|
+
(resolve38) => setTimeout(() => resolve38("timeout"), timeoutMs)
|
|
2819
2840
|
);
|
|
2820
2841
|
const result = await Promise.race([
|
|
2821
2842
|
credsSaveQueue.then(() => "drained"),
|
|
@@ -2992,11 +3013,11 @@ async function createWaSocket(opts) {
|
|
|
2992
3013
|
return sock;
|
|
2993
3014
|
}
|
|
2994
3015
|
async function waitForConnection(sock) {
|
|
2995
|
-
return new Promise((
|
|
3016
|
+
return new Promise((resolve38, reject) => {
|
|
2996
3017
|
const handler = (update) => {
|
|
2997
3018
|
if (update.connection === "open") {
|
|
2998
3019
|
sock.ev.off("connection.update", handler);
|
|
2999
|
-
|
|
3020
|
+
resolve38();
|
|
3000
3021
|
}
|
|
3001
3022
|
if (update.connection === "close") {
|
|
3002
3023
|
sock.ev.off("connection.update", handler);
|
|
@@ -3104,7 +3125,7 @@ function isTimeoutError(err) {
|
|
|
3104
3125
|
return Boolean(err?.__maxyTimeout);
|
|
3105
3126
|
}
|
|
3106
3127
|
function withTimeout(label, promise, timeoutMs) {
|
|
3107
|
-
return new Promise((
|
|
3128
|
+
return new Promise((resolve38, reject) => {
|
|
3108
3129
|
const timer = setTimeout(() => {
|
|
3109
3130
|
const err = new Error(`${label} timed out after ${timeoutMs}ms`);
|
|
3110
3131
|
err.__maxyTimeout = true;
|
|
@@ -3114,7 +3135,7 @@ function withTimeout(label, promise, timeoutMs) {
|
|
|
3114
3135
|
promise.then(
|
|
3115
3136
|
(value) => {
|
|
3116
3137
|
clearTimeout(timer);
|
|
3117
|
-
|
|
3138
|
+
resolve38(value);
|
|
3118
3139
|
},
|
|
3119
3140
|
(err) => {
|
|
3120
3141
|
clearTimeout(timer);
|
|
@@ -4028,15 +4049,15 @@ function getChannelActivity(accountId) {
|
|
|
4028
4049
|
|
|
4029
4050
|
// app/lib/whatsapp/outbound/send.ts
|
|
4030
4051
|
var TAG10 = "[whatsapp:outbound]";
|
|
4031
|
-
function buildQuoted(
|
|
4052
|
+
function buildQuoted(q2) {
|
|
4032
4053
|
return {
|
|
4033
4054
|
key: {
|
|
4034
|
-
remoteJid:
|
|
4035
|
-
id:
|
|
4036
|
-
fromMe:
|
|
4037
|
-
...
|
|
4055
|
+
remoteJid: q2.remoteJid,
|
|
4056
|
+
id: q2.id,
|
|
4057
|
+
fromMe: q2.fromMe,
|
|
4058
|
+
...q2.participant ? { participant: q2.participant } : {}
|
|
4038
4059
|
},
|
|
4039
|
-
message: { conversation:
|
|
4060
|
+
message: { conversation: q2.body ?? "" }
|
|
4040
4061
|
};
|
|
4041
4062
|
}
|
|
4042
4063
|
async function sendTextMessage(sock, to, text, opts) {
|
|
@@ -4154,8 +4175,8 @@ async function persistWhatsAppMessage(input) {
|
|
|
4154
4175
|
const { givenName, familyName } = splitName(input.pushName);
|
|
4155
4176
|
const prev = sessionWriteLocks.get(input.cacheKey);
|
|
4156
4177
|
let release;
|
|
4157
|
-
const mine = new Promise((
|
|
4158
|
-
release =
|
|
4178
|
+
const mine = new Promise((resolve38) => {
|
|
4179
|
+
release = resolve38;
|
|
4159
4180
|
});
|
|
4160
4181
|
const chained = (prev ?? Promise.resolve()).then(() => mine);
|
|
4161
4182
|
sessionWriteLocks.set(input.cacheKey, chained);
|
|
@@ -5859,11 +5880,11 @@ async function connectWithReconnect(conn) {
|
|
|
5859
5880
|
console.error(
|
|
5860
5881
|
`${TAG17} reconnecting account=${conn.accountId} in ${delay}ms (attempt ${decision.nextAttempts}/${maxAttempts})`
|
|
5861
5882
|
);
|
|
5862
|
-
await new Promise((
|
|
5863
|
-
const timer = setTimeout(
|
|
5883
|
+
await new Promise((resolve38) => {
|
|
5884
|
+
const timer = setTimeout(resolve38, delay);
|
|
5864
5885
|
conn.abortController.signal.addEventListener("abort", () => {
|
|
5865
5886
|
clearTimeout(timer);
|
|
5866
|
-
|
|
5887
|
+
resolve38();
|
|
5867
5888
|
}, { once: true });
|
|
5868
5889
|
});
|
|
5869
5890
|
}
|
|
@@ -5871,16 +5892,16 @@ async function connectWithReconnect(conn) {
|
|
|
5871
5892
|
}
|
|
5872
5893
|
}
|
|
5873
5894
|
function waitForDisconnectEvent(conn) {
|
|
5874
|
-
return new Promise((
|
|
5895
|
+
return new Promise((resolve38) => {
|
|
5875
5896
|
if (!conn.sock) {
|
|
5876
|
-
|
|
5897
|
+
resolve38();
|
|
5877
5898
|
return;
|
|
5878
5899
|
}
|
|
5879
5900
|
const sock = conn.sock;
|
|
5880
5901
|
const handler = (update) => {
|
|
5881
5902
|
if (update.connection === "close") {
|
|
5882
5903
|
sock.ev.off("connection.update", handler);
|
|
5883
|
-
|
|
5904
|
+
resolve38();
|
|
5884
5905
|
}
|
|
5885
5906
|
};
|
|
5886
5907
|
sock.ev.on("connection.update", handler);
|
|
@@ -6290,8 +6311,8 @@ async function handleInboundMessage(conn, msg) {
|
|
|
6290
6311
|
const conversationKey = isGroup ? remoteJid : senderPhone;
|
|
6291
6312
|
const debounceKey = `${conn.accountId}:${conversationKey}:${senderPhone}`;
|
|
6292
6313
|
let resolvePending;
|
|
6293
|
-
const sttPending = new Promise((
|
|
6294
|
-
resolvePending =
|
|
6314
|
+
const sttPending = new Promise((resolve38) => {
|
|
6315
|
+
resolvePending = resolve38;
|
|
6295
6316
|
});
|
|
6296
6317
|
if (conn.debouncer) conn.debouncer.registerPending(debounceKey, sttPending);
|
|
6297
6318
|
try {
|
|
@@ -6719,20 +6740,20 @@ function buildX11Env(chromiumWrapperPath, transport = "vnc") {
|
|
|
6719
6740
|
|
|
6720
6741
|
// server/routes/health.ts
|
|
6721
6742
|
function checkPort(port2, timeoutMs = 500) {
|
|
6722
|
-
return new Promise((
|
|
6743
|
+
return new Promise((resolve38) => {
|
|
6723
6744
|
const socket = createConnection2(port2, "127.0.0.1");
|
|
6724
6745
|
socket.setTimeout(timeoutMs);
|
|
6725
6746
|
socket.once("connect", () => {
|
|
6726
6747
|
socket.destroy();
|
|
6727
|
-
|
|
6748
|
+
resolve38(true);
|
|
6728
6749
|
});
|
|
6729
6750
|
socket.once("error", () => {
|
|
6730
6751
|
socket.destroy();
|
|
6731
|
-
|
|
6752
|
+
resolve38(false);
|
|
6732
6753
|
});
|
|
6733
6754
|
socket.once("timeout", () => {
|
|
6734
6755
|
socket.destroy();
|
|
6735
|
-
|
|
6756
|
+
resolve38(false);
|
|
6736
6757
|
});
|
|
6737
6758
|
});
|
|
6738
6759
|
}
|
|
@@ -6987,8 +7008,8 @@ function webchatTurnTimeoutMs() {
|
|
|
6987
7008
|
return Number(process.env.WEBCHAT_TURN_TIMEOUT_MS ?? String(2 * 6e4));
|
|
6988
7009
|
}
|
|
6989
7010
|
function createChatRoutes(deps) {
|
|
6990
|
-
const
|
|
6991
|
-
|
|
7011
|
+
const app65 = new Hono();
|
|
7012
|
+
app65.post("/", async (c) => {
|
|
6992
7013
|
console.log(`[chat-route] entered route=public method=POST`);
|
|
6993
7014
|
const contentType = c.req.header("content-type") ?? "";
|
|
6994
7015
|
const account = resolveAccount();
|
|
@@ -7252,7 +7273,7 @@ ${result.result.text}` : result.result.text;
|
|
|
7252
7273
|
}
|
|
7253
7274
|
});
|
|
7254
7275
|
});
|
|
7255
|
-
return
|
|
7276
|
+
return app65;
|
|
7256
7277
|
}
|
|
7257
7278
|
|
|
7258
7279
|
// app/lib/channel-pty-bridge/admin-session-id.ts
|
|
@@ -7286,8 +7307,8 @@ function isLoopbackAddr(addr) {
|
|
|
7286
7307
|
return addr === "127.0.0.1" || addr === "::1" || addr === "::ffff:127.0.0.1";
|
|
7287
7308
|
}
|
|
7288
7309
|
function createScheduleInjectRoutes(deps) {
|
|
7289
|
-
const
|
|
7290
|
-
|
|
7310
|
+
const app65 = new Hono();
|
|
7311
|
+
app65.post("/", async (c) => {
|
|
7291
7312
|
const env = c.env;
|
|
7292
7313
|
const remoteAddr = env?.incoming?.socket?.remoteAddress ?? "";
|
|
7293
7314
|
if (!isLoopbackAddr(remoteAddr)) {
|
|
@@ -7388,7 +7409,7 @@ function createScheduleInjectRoutes(deps) {
|
|
|
7388
7409
|
console.error(`${TAG20} op=inject-spawn eventId=${eventId} sessionId=${sessionId} result=ok status=-`);
|
|
7389
7410
|
return c.json({ ok: true }, 200);
|
|
7390
7411
|
});
|
|
7391
|
-
return
|
|
7412
|
+
return app65;
|
|
7392
7413
|
}
|
|
7393
7414
|
|
|
7394
7415
|
// server/index.ts
|
|
@@ -8021,8 +8042,8 @@ async function startLogin(opts) {
|
|
|
8021
8042
|
await clearAuth(authDir);
|
|
8022
8043
|
let resolveCode = null;
|
|
8023
8044
|
let rejectCode = null;
|
|
8024
|
-
const codePromise = new Promise((
|
|
8025
|
-
resolveCode =
|
|
8045
|
+
const codePromise = new Promise((resolve38, reject) => {
|
|
8046
|
+
resolveCode = resolve38;
|
|
8026
8047
|
rejectCode = reject;
|
|
8027
8048
|
});
|
|
8028
8049
|
const codeTimer = setTimeout(
|
|
@@ -9637,7 +9658,7 @@ app3.post("/r2/object/delete", envelopeBodyLimit("write"), async (c) => {
|
|
|
9637
9658
|
});
|
|
9638
9659
|
var storage_broker_default = app3;
|
|
9639
9660
|
async function runStorageAudit(root) {
|
|
9640
|
-
const { reconcileStorage } = await import("./src-
|
|
9661
|
+
const { reconcileStorage } = await import("./src-XQ63FPRE.js");
|
|
9641
9662
|
const cf = await makeHouseCfExec(root);
|
|
9642
9663
|
const accountsDir = resolve11(root, "..", "data", "accounts");
|
|
9643
9664
|
const strays = (await reconcileStorage({
|
|
@@ -9677,26 +9698,108 @@ async function runStorageAudit(root) {
|
|
|
9677
9698
|
);
|
|
9678
9699
|
}
|
|
9679
9700
|
async function runPagesAudit(root) {
|
|
9680
|
-
const { reconcilePages } = await import("./src-
|
|
9681
|
-
const
|
|
9682
|
-
|
|
9683
|
-
|
|
9684
|
-
|
|
9685
|
-
|
|
9686
|
-
|
|
9701
|
+
const { reconcilePages } = await import("./src-XQ63FPRE.js");
|
|
9702
|
+
const cf = await makeHousePagesExec(root);
|
|
9703
|
+
const r = await reconcilePages({
|
|
9704
|
+
cfProjects: async () => (await cf.pagesProjectList()).map((p) => p.name),
|
|
9705
|
+
// Its own session per invocation, same rule as runStorageAudit above and
|
|
9706
|
+
// for the same reason. This previously held one session opened in the
|
|
9707
|
+
// enclosing scope, which was safe only because reconcilePages issues
|
|
9708
|
+
// exactly one registry query — a property of the library, not of this
|
|
9709
|
+
// caller. A second read added there would have reproduced 1813 here
|
|
9710
|
+
// verbatim. The old shape also held that session open across
|
|
9711
|
+
// makeHousePagesExec's network I/O, which is a pooled connection parked
|
|
9712
|
+
// for the length of a Cloudflare round trip.
|
|
9713
|
+
registeredProjects: async () => {
|
|
9714
|
+
const session = getSession();
|
|
9715
|
+
try {
|
|
9687
9716
|
const res = await session.run(
|
|
9688
9717
|
"MATCH (r:StorageResource {kind: $kind}) RETURN r.name AS name",
|
|
9689
9718
|
{ kind: "pages" }
|
|
9690
9719
|
);
|
|
9691
9720
|
return res.records.map((rec) => rec.get("name"));
|
|
9721
|
+
} finally {
|
|
9722
|
+
await session.close();
|
|
9692
9723
|
}
|
|
9693
|
-
}
|
|
9724
|
+
}
|
|
9725
|
+
});
|
|
9726
|
+
console.error(
|
|
9727
|
+
`[pages-audit] projects=${r.projects} registered=${r.registered} orphan=${r.orphans.length} phantom=${r.phantoms.length}` + r.orphans.map((n) => ` orphan:${n}`).join("") + r.phantoms.map((n) => ` phantom:${n}`).join("")
|
|
9728
|
+
);
|
|
9729
|
+
}
|
|
9730
|
+
function parseManifestRows(raw) {
|
|
9731
|
+
const envelope = Array.isArray(raw) ? raw[0] : null;
|
|
9732
|
+
const results = envelope?.results;
|
|
9733
|
+
if (!Array.isArray(results)) {
|
|
9734
|
+
throw new Error(`unexpected d1 payload: envelope carries no results array`);
|
|
9735
|
+
}
|
|
9736
|
+
return results.map((r, i) => {
|
|
9737
|
+
const row = r;
|
|
9738
|
+
const bad = ["objectKey", "ingested", "uploadedAt"].find(
|
|
9739
|
+
(f) => f === "ingested" ? typeof row[f] !== "number" : typeof row[f] !== "string"
|
|
9740
|
+
);
|
|
9741
|
+
if (bad) throw new Error(`unexpected d1 row: index=${i} field=${bad} type=${typeof row[bad]}`);
|
|
9742
|
+
return { objectKey: row.objectKey, ingested: row.ingested, uploadedAt: row.uploadedAt };
|
|
9743
|
+
});
|
|
9744
|
+
}
|
|
9745
|
+
function auditErrorText(message) {
|
|
9746
|
+
return message.split("\n")[0].replaceAll('"', "'").slice(0, 160);
|
|
9747
|
+
}
|
|
9748
|
+
function discoverPortalAccounts(accountsDir) {
|
|
9749
|
+
if (!existsSync9(accountsDir)) return [];
|
|
9750
|
+
const found = [];
|
|
9751
|
+
for (const accountId of readdirSync8(accountsDir)) {
|
|
9752
|
+
const configPath2 = join13(accountsDir, accountId, "data-portal.json");
|
|
9753
|
+
if (!existsSync9(configPath2)) continue;
|
|
9754
|
+
try {
|
|
9755
|
+
const cfg = JSON.parse(readFileSync15(configPath2, "utf-8"));
|
|
9756
|
+
if (typeof cfg.portalDbName === "string" && cfg.portalDbName.length > 0 && typeof cfg.bucketName === "string" && cfg.bucketName.length > 0) {
|
|
9757
|
+
found.push({ accountId, portalDbName: cfg.portalDbName, bucketName: cfg.bucketName });
|
|
9758
|
+
}
|
|
9759
|
+
} catch {
|
|
9760
|
+
}
|
|
9761
|
+
}
|
|
9762
|
+
return found;
|
|
9763
|
+
}
|
|
9764
|
+
async function runDataPortalAudit(root) {
|
|
9765
|
+
const portals = discoverPortalAccounts(resolve11(root, "..", "data", "accounts"));
|
|
9766
|
+
if (portals.length === 0) return "portals=0 degraded=0";
|
|
9767
|
+
const cf = await makeHouseCfExec(root);
|
|
9768
|
+
let degraded = 0;
|
|
9769
|
+
for (const portal of portals) {
|
|
9770
|
+
let rows = null;
|
|
9771
|
+
let d1Error = null;
|
|
9772
|
+
try {
|
|
9773
|
+
rows = parseManifestRows(
|
|
9774
|
+
await cf.d1Query(
|
|
9775
|
+
portal.portalDbName,
|
|
9776
|
+
"SELECT objectKey, ingested, uploadedAt FROM manifest"
|
|
9777
|
+
)
|
|
9778
|
+
);
|
|
9779
|
+
} catch (err) {
|
|
9780
|
+
d1Error = err.message;
|
|
9781
|
+
}
|
|
9782
|
+
let objects = null;
|
|
9783
|
+
let r2Error = null;
|
|
9784
|
+
try {
|
|
9785
|
+
objects = await cf.r2ObjectList(portal.bucketName);
|
|
9786
|
+
} catch (err) {
|
|
9787
|
+
r2Error = err.message;
|
|
9788
|
+
}
|
|
9789
|
+
const nowMs = Date.now();
|
|
9790
|
+
const reconciled = rows !== null && objects !== null ? reconcileDataPortal(objects, rows, nowMs) : null;
|
|
9791
|
+
const ageHrs = rows === null ? null : reconcileDataPortal([], rows, nowMs).oldestUningestedHrs;
|
|
9792
|
+
const degradation = [d1Error !== null ? "d1" : null, r2Error !== null ? "r2" : null].filter(Boolean).join("+");
|
|
9793
|
+
if (degradation) degraded += 1;
|
|
9794
|
+
const reasons = [
|
|
9795
|
+
d1Error !== null ? `d1:${auditErrorText(d1Error)}` : null,
|
|
9796
|
+
r2Error !== null ? `r2:${auditErrorText(r2Error)}` : null
|
|
9797
|
+
].filter(Boolean).join(" | ");
|
|
9694
9798
|
console.error(
|
|
9695
|
-
`[
|
|
9799
|
+
`[data-portal-audit] account=${portal.accountId} objects=${objects?.length ?? "na"} rows=${rows?.length ?? "na"} orphanObjects=${reconciled?.orphanObjects.length ?? "na"} phantomRows=${reconciled?.phantomRows.length ?? "na"} oldestUningestedHrs=${ageHrs?.toFixed(1) ?? "na"}` + (degradation ? ` degraded=${degradation} err="${reasons}"` : "")
|
|
9696
9800
|
);
|
|
9697
|
-
} finally {
|
|
9698
|
-
await session.close();
|
|
9699
9801
|
}
|
|
9802
|
+
return `portals=${portals.length} degraded=${degraded}`;
|
|
9700
9803
|
}
|
|
9701
9804
|
|
|
9702
9805
|
// server/google-pending-audit.ts
|
|
@@ -11254,12 +11357,12 @@ app5.get("/store-stream", requireAdminSession, (c) => {
|
|
|
11254
11357
|
...reactions && reactions.length > 0 ? { reactions } : {}
|
|
11255
11358
|
};
|
|
11256
11359
|
if (r.quotedId) {
|
|
11257
|
-
const
|
|
11360
|
+
const q2 = byMsgKeyId.get(r.quotedId);
|
|
11258
11361
|
turn = {
|
|
11259
11362
|
...turn,
|
|
11260
|
-
quoted:
|
|
11363
|
+
quoted: q2 ? { body: q2.body, sender: q2.senderName ?? (q2.fromMe ? null : q2.senderTelephone || null), fromMe: q2.fromMe } : { body: "", sender: r.quotedSender, fromMe: false }
|
|
11261
11364
|
};
|
|
11262
|
-
console.log(`[wa-reader] op=store-quote msgId=${r.msgKeyId} quoted=${!!
|
|
11365
|
+
console.log(`[wa-reader] op=store-quote msgId=${r.msgKeyId} quoted=${!!q2}`);
|
|
11263
11366
|
}
|
|
11264
11367
|
if (r.attachmentId) {
|
|
11265
11368
|
const meta = readAttachmentMeta(join19(uploadsBase, r.attachmentId));
|
|
@@ -12169,10 +12272,7 @@ function locateSidecar(sessionId) {
|
|
|
12169
12272
|
}
|
|
12170
12273
|
for (const slug of slugs) {
|
|
12171
12274
|
const metaPath = join23(projectsRoot, slug, `${sessionId}.meta.json`);
|
|
12172
|
-
if (existsSync15(metaPath))
|
|
12173
|
-
const meta = readSidecarMeta(metaPath);
|
|
12174
|
-
return { channel: meta.channel, adminUserId: meta.adminUserId, accountId: meta.accountId };
|
|
12175
|
-
}
|
|
12275
|
+
if (existsSync15(metaPath)) return readSidecarMeta(metaPath);
|
|
12176
12276
|
}
|
|
12177
12277
|
return null;
|
|
12178
12278
|
}
|
|
@@ -12185,6 +12285,13 @@ function readTargetOwner(sessionId) {
|
|
|
12185
12285
|
}
|
|
12186
12286
|
return { found: false, owner: null };
|
|
12187
12287
|
}
|
|
12288
|
+
function readSourceRowMeta(sessionId) {
|
|
12289
|
+
const sidecar = locateSidecar(sessionId);
|
|
12290
|
+
if (sidecar !== null) return sidecar;
|
|
12291
|
+
const { projectDir } = locateSession(sessionId);
|
|
12292
|
+
if (projectDir !== null) return readSidecarMeta(join23(projectDir, `${sessionId}.meta.json`));
|
|
12293
|
+
return null;
|
|
12294
|
+
}
|
|
12188
12295
|
function ownedByRequester(owner, requesterUserId, primaryUserId) {
|
|
12189
12296
|
if (requesterUserId === null) return false;
|
|
12190
12297
|
return owner === requesterUserId || owner === null && requesterUserId === primaryUserId;
|
|
@@ -12393,8 +12500,8 @@ async function reapplyLeversViaManager() {
|
|
|
12393
12500
|
var WEBCHAT_SEND_TURN_WINDOW_MS = Number(process.env.WEBCHAT_SEND_TURN_WINDOW_MS ?? String(15e3));
|
|
12394
12501
|
var sendSeq = 0;
|
|
12395
12502
|
function createWebchatRoutes(deps) {
|
|
12396
|
-
const
|
|
12397
|
-
|
|
12503
|
+
const app65 = new Hono();
|
|
12504
|
+
app65.post("/send", requireAdminSession, async (c) => {
|
|
12398
12505
|
let accountId;
|
|
12399
12506
|
try {
|
|
12400
12507
|
accountId = resolvePlatformAccountId();
|
|
@@ -12606,7 +12713,7 @@ ${note}` : note;
|
|
|
12606
12713
|
if (turnTimer.unref) turnTimer.unref();
|
|
12607
12714
|
return c.json({ ok: true });
|
|
12608
12715
|
});
|
|
12609
|
-
|
|
12716
|
+
app65.post("/interrupt", requireAdminSession, async (c) => {
|
|
12610
12717
|
const cacheKey = c.get("cacheKey");
|
|
12611
12718
|
const requesterUserId = (cacheKey ? getUserIdForSession(cacheKey) : void 0) ?? null;
|
|
12612
12719
|
const primaryUserId = resolvePrimaryAdminUserId();
|
|
@@ -12634,7 +12741,7 @@ ${note}` : note;
|
|
|
12634
12741
|
}
|
|
12635
12742
|
return c.json({ ok: true, stopped: outcome.stopped, intId: outcome.intId, deadChild: outcome.deadChild }, 200);
|
|
12636
12743
|
});
|
|
12637
|
-
|
|
12744
|
+
app65.post("/settings", requireAdminSession, async (c) => {
|
|
12638
12745
|
const body = await c.req.json().catch(() => null);
|
|
12639
12746
|
const op = body?.op;
|
|
12640
12747
|
const value = body?.value;
|
|
@@ -12674,7 +12781,7 @@ ${note}` : note;
|
|
|
12674
12781
|
console.log(`[webchat:settings] op=${op} outcome=accepted value=${value} settingsApplied=${settingsApplied}`);
|
|
12675
12782
|
return c.json({ ok: true, settingsApplied });
|
|
12676
12783
|
});
|
|
12677
|
-
|
|
12784
|
+
app65.get("/session", requireAdminSession, async (c) => {
|
|
12678
12785
|
let accountId;
|
|
12679
12786
|
try {
|
|
12680
12787
|
accountId = resolvePlatformAccountId();
|
|
@@ -12752,7 +12859,7 @@ ${note}` : note;
|
|
|
12752
12859
|
const indicators = await fetchComposerIndicators(sessionId);
|
|
12753
12860
|
return c.json({ sessionId, projectDir, source, sizeBytes: jsonlSizeBytes(projectDir, sessionId), pendingPermissionPrompt: deps.pendingPromptFor?.(`session:${sessionId}`) ?? null, deliveryFailure: deps.deliveryFailureFor?.(`session:${sessionId}`) ?? null, ...indicators, ...readLevers(account) });
|
|
12754
12861
|
});
|
|
12755
|
-
|
|
12862
|
+
app65.post("/permission-verdict", requireAdminSession, async (c) => {
|
|
12756
12863
|
const body = await c.req.json().catch(() => null);
|
|
12757
12864
|
const sessionId = body?.sessionId;
|
|
12758
12865
|
const requestId = body?.request_id;
|
|
@@ -12769,7 +12876,7 @@ ${note}` : note;
|
|
|
12769
12876
|
const ok = deps.resolvePermissionVerdict?.(`session:${sessionId}`, requestId, behavior) ?? false;
|
|
12770
12877
|
return c.json({ ok });
|
|
12771
12878
|
});
|
|
12772
|
-
return
|
|
12879
|
+
return app65;
|
|
12773
12880
|
}
|
|
12774
12881
|
|
|
12775
12882
|
// server/routes/webchat-greeting.ts
|
|
@@ -14125,15 +14232,15 @@ function operatorRoleFor(config, userId) {
|
|
|
14125
14232
|
async function buildAccountOptionList(accounts, userId) {
|
|
14126
14233
|
const houseAccount = accounts.find((a) => a.config.role === "house");
|
|
14127
14234
|
console.log(`[admin-accounts] op=list count=${accounts.length} house=${houseAccount?.accountId ?? "none"}`);
|
|
14128
|
-
const
|
|
14235
|
+
const resolve38 = async (a) => {
|
|
14129
14236
|
const businessName = await fetchAccountName(a.accountId) || void 0;
|
|
14130
14237
|
return { accountId: a.accountId, businessName, role: operatorRoleFor(a.config, userId), isHouse: a.config.role === "house" };
|
|
14131
14238
|
};
|
|
14132
14239
|
const houseOptions = await Promise.all(
|
|
14133
|
-
accounts.filter((a) => a.config.role === "house").map(
|
|
14240
|
+
accounts.filter((a) => a.config.role === "house").map(resolve38)
|
|
14134
14241
|
);
|
|
14135
14242
|
const nonHouseOptions = await Promise.all(
|
|
14136
|
-
accounts.filter((a) => a.config.role !== "house").map(
|
|
14243
|
+
accounts.filter((a) => a.config.role !== "house").map(resolve38)
|
|
14137
14244
|
);
|
|
14138
14245
|
const sortKey = (o) => (o.businessName || o.accountId).toLowerCase();
|
|
14139
14246
|
nonHouseOptions.sort((x, y) => {
|
|
@@ -15140,9 +15247,9 @@ app18.post("/:id/resume", async (c) => {
|
|
|
15140
15247
|
if (healQueue.length > 0) {
|
|
15141
15248
|
void (async () => {
|
|
15142
15249
|
for (let i = 0; i < healQueue.length; i++) {
|
|
15143
|
-
const
|
|
15250
|
+
const q2 = healQueue[i];
|
|
15144
15251
|
try {
|
|
15145
|
-
const healComponents = await Promise.all(
|
|
15252
|
+
const healComponents = await Promise.all(q2.components.map(async (c2) => {
|
|
15146
15253
|
const base = {
|
|
15147
15254
|
componentId: c2.componentId,
|
|
15148
15255
|
name: c2.name,
|
|
@@ -15180,25 +15287,25 @@ app18.post("/:id/resume", async (c) => {
|
|
|
15180
15287
|
}));
|
|
15181
15288
|
const messageId = await persistMessage(
|
|
15182
15289
|
sessionId,
|
|
15183
|
-
|
|
15184
|
-
|
|
15290
|
+
q2.role,
|
|
15291
|
+
q2.content,
|
|
15185
15292
|
accountId,
|
|
15186
15293
|
void 0,
|
|
15187
|
-
|
|
15294
|
+
q2.createdAt,
|
|
15188
15295
|
void 0,
|
|
15189
15296
|
healComponents,
|
|
15190
15297
|
void 0
|
|
15191
15298
|
);
|
|
15192
15299
|
jsonlHealOk += 1;
|
|
15193
15300
|
try {
|
|
15194
|
-
streamLogPath.write(`[${(/* @__PURE__ */ new Date()).toISOString()}] [admin-persist-heal] convId=${sessionId.slice(0, 8)} turnIndex=${i} role=${
|
|
15301
|
+
streamLogPath.write(`[${(/* @__PURE__ */ new Date()).toISOString()}] [admin-persist-heal] convId=${sessionId.slice(0, 8)} turnIndex=${i} role=${q2.role} outcome=ok messageId=${(messageId ?? "").slice(0, 8)}
|
|
15195
15302
|
`);
|
|
15196
15303
|
} catch {
|
|
15197
15304
|
}
|
|
15198
15305
|
} catch (err) {
|
|
15199
15306
|
jsonlHealFail += 1;
|
|
15200
15307
|
try {
|
|
15201
|
-
streamLogPath.write(`[${(/* @__PURE__ */ new Date()).toISOString()}] [admin-persist-heal] convId=${sessionId.slice(0, 8)} turnIndex=${i} role=${
|
|
15308
|
+
streamLogPath.write(`[${(/* @__PURE__ */ new Date()).toISOString()}] [admin-persist-heal] convId=${sessionId.slice(0, 8)} turnIndex=${i} role=${q2.role} outcome=fail reason=${JSON.stringify((err instanceof Error ? err.message : String(err)).slice(0, 200))}
|
|
15202
15309
|
`);
|
|
15203
15310
|
} catch {
|
|
15204
15311
|
}
|
|
@@ -15523,10 +15630,10 @@ function computeSpecialistDomains(accountId) {
|
|
|
15523
15630
|
const resolveDesc = (qualified) => {
|
|
15524
15631
|
if (!qualified.startsWith("mcp__")) return void 0;
|
|
15525
15632
|
const rest = qualified.slice(5);
|
|
15526
|
-
const
|
|
15527
|
-
if (
|
|
15528
|
-
const plugin = rest.slice(0,
|
|
15529
|
-
const tool = rest.slice(
|
|
15633
|
+
const sep10 = rest.indexOf("__");
|
|
15634
|
+
if (sep10 < 0) return void 0;
|
|
15635
|
+
const plugin = rest.slice(0, sep10);
|
|
15636
|
+
const tool = rest.slice(sep10 + 2);
|
|
15530
15637
|
let map = descByPlugin.get(plugin);
|
|
15531
15638
|
if (!map) {
|
|
15532
15639
|
const dir = pluginDirs.get(plugin);
|
|
@@ -15829,18 +15936,18 @@ var ALLOWED_EVENTS = /* @__PURE__ */ new Set([
|
|
|
15829
15936
|
]);
|
|
15830
15937
|
var app21 = new Hono();
|
|
15831
15938
|
app21.post("/", async (c) => {
|
|
15832
|
-
const
|
|
15939
|
+
const TAG54 = "[admin:events]";
|
|
15833
15940
|
let body;
|
|
15834
15941
|
try {
|
|
15835
15942
|
body = await c.req.json();
|
|
15836
15943
|
} catch (err) {
|
|
15837
15944
|
const detail = err instanceof Error ? err.message : String(err);
|
|
15838
|
-
console.error(`${
|
|
15945
|
+
console.error(`${TAG54} reject reason=body-not-json detail=${detail}`);
|
|
15839
15946
|
return c.json({ ok: false, detail: "Request body was not valid JSON" }, 400);
|
|
15840
15947
|
}
|
|
15841
15948
|
const event = typeof body.event === "string" ? body.event : "";
|
|
15842
15949
|
if (!ALLOWED_EVENTS.has(event)) {
|
|
15843
|
-
console.error(`${
|
|
15950
|
+
console.error(`${TAG54} reject reason=event-not-allowed event=${JSON.stringify(event)}`);
|
|
15844
15951
|
return c.json({ ok: false, detail: `Event "${event}" is not allowed` }, 400);
|
|
15845
15952
|
}
|
|
15846
15953
|
const rawFields = body.fields && typeof body.fields === "object" ? body.fields : {};
|
|
@@ -18481,7 +18588,7 @@ var CONVERSATION_PARENT_LABELS = /* @__PURE__ */ new Set(["AdminConversation", "
|
|
|
18481
18588
|
var app23 = new Hono();
|
|
18482
18589
|
app23.get("/", requireAdminSession, async (c) => {
|
|
18483
18590
|
const cacheKey = c.var.cacheKey;
|
|
18484
|
-
const
|
|
18591
|
+
const q2 = (c.req.query("q") ?? "").trim();
|
|
18485
18592
|
const rawLimit = c.req.query("limit");
|
|
18486
18593
|
const rawLabels = c.req.query("labels");
|
|
18487
18594
|
const rawThreshold = c.req.query("threshold");
|
|
@@ -18490,13 +18597,13 @@ app23.get("/", requireAdminSession, async (c) => {
|
|
|
18490
18597
|
console.error(`[graph-search] auth-rejected endpoint="/api/admin/graph-search" reason="no account for session"`);
|
|
18491
18598
|
return c.json({ error: "Account not found for session" }, 401);
|
|
18492
18599
|
}
|
|
18493
|
-
if (!
|
|
18600
|
+
if (!q2) return c.json({ error: "q (search query) required" }, 400);
|
|
18494
18601
|
const labels = rawLabels ? rawLabels.split(",").map((s) => s.trim()).filter((s) => s.length > 0) : [];
|
|
18495
18602
|
const wildcard = labels.includes("*");
|
|
18496
18603
|
const noChips = labels.length === 0;
|
|
18497
18604
|
const allLabels = wildcard || noChips;
|
|
18498
18605
|
if (noChips && !wildcard) {
|
|
18499
|
-
console.error(`[graph-search] op=labels-default applied=all query="${
|
|
18606
|
+
console.error(`[graph-search] op=labels-default applied=all query="${q2}"`);
|
|
18500
18607
|
}
|
|
18501
18608
|
const parsedLimit = rawLimit ? parseInt(rawLimit, 10) : DEFAULT_LIMIT;
|
|
18502
18609
|
const limit = Number.isFinite(parsedLimit) && parsedLimit > 0 ? Math.min(parsedLimit, MAX_LIMIT) : DEFAULT_LIMIT;
|
|
@@ -18516,7 +18623,7 @@ app23.get("/", requireAdminSession, async (c) => {
|
|
|
18516
18623
|
const session = getSession();
|
|
18517
18624
|
try {
|
|
18518
18625
|
const res = await hybrid(session, embed, {
|
|
18519
|
-
query:
|
|
18626
|
+
query: q2,
|
|
18520
18627
|
accountId,
|
|
18521
18628
|
labels: forwardedLabels,
|
|
18522
18629
|
limit,
|
|
@@ -18526,7 +18633,7 @@ app23.get("/", requireAdminSession, async (c) => {
|
|
|
18526
18633
|
const total = Date.now() - started;
|
|
18527
18634
|
if (res.embedError) {
|
|
18528
18635
|
console.error(`[graph-search] embed-unavailable err="${res.embedError}" \u2014 bm25-only`);
|
|
18529
|
-
console.error(`[graph-search] embed-degraded query="${
|
|
18636
|
+
console.error(`[graph-search] embed-degraded query="${q2}" results=${res.results.length}`);
|
|
18530
18637
|
}
|
|
18531
18638
|
let labelMatchCount = 0;
|
|
18532
18639
|
let bodyFulltextCount = 0;
|
|
@@ -18577,16 +18684,16 @@ app23.get("/", requireAdminSession, async (c) => {
|
|
|
18577
18684
|
}
|
|
18578
18685
|
resolvedResults = Array.from(seen.values()).sort((a, b) => b.score - a.score);
|
|
18579
18686
|
console.error(
|
|
18580
|
-
`[graph-search] label-match query="${
|
|
18687
|
+
`[graph-search] label-match query="${q2}" hits=${labelMatchCount}`
|
|
18581
18688
|
);
|
|
18582
18689
|
console.error(
|
|
18583
|
-
`[graph-search] body-fulltext query="${
|
|
18690
|
+
`[graph-search] body-fulltext query="${q2}" hits=${bodyFulltextCount} ms=${total}`
|
|
18584
18691
|
);
|
|
18585
18692
|
}
|
|
18586
18693
|
const labelsToken = wildcard ? "*" : noChips ? "all" : labels.join(",");
|
|
18587
18694
|
const expandedFlag = !wildcard && wantsBodyFulltext ? 1 : 0;
|
|
18588
18695
|
console.error(
|
|
18589
|
-
`[graph-search] query="${
|
|
18696
|
+
`[graph-search] query="${q2}" labels=${labelsToken} expanded=${expandedFlag} limit=${limit} mode=${res.mode} raw-results=${res.results.length} resolved-results=${resolvedResults.length} expand-ms=${res.expandMs} total-ms=${total}`
|
|
18590
18697
|
);
|
|
18591
18698
|
console.error(
|
|
18592
18699
|
`[graph-search] threshold=${res.threshold ?? "off"} raw-merged=${res.rawMerged} suppressed=${res.suppressed} rendered=${res.results.length} bm25-bypass=${res.bm25Bypass}`
|
|
@@ -18609,7 +18716,7 @@ app23.get("/", requireAdminSession, async (c) => {
|
|
|
18609
18716
|
} catch (err) {
|
|
18610
18717
|
const elapsed = Date.now() - started;
|
|
18611
18718
|
const message = err instanceof Error ? err.message : String(err);
|
|
18612
|
-
console.error(`[graph-search] neo4j-unreachable query="${
|
|
18719
|
+
console.error(`[graph-search] neo4j-unreachable query="${q2}" ms=${elapsed} err="${message}"`);
|
|
18613
18720
|
return c.json({ error: `Graph search unavailable: ${message}` }, 503);
|
|
18614
18721
|
} finally {
|
|
18615
18722
|
await session.close();
|
|
@@ -18676,7 +18783,11 @@ var FILTER_TOP_LEVEL_LABELS = Object.freeze(
|
|
|
18676
18783
|
"Risk",
|
|
18677
18784
|
"Source",
|
|
18678
18785
|
"Finding",
|
|
18679
|
-
"Hypothesis"
|
|
18786
|
+
"Hypothesis",
|
|
18787
|
+
// Base-schema ledger (schema-base.md, Task 1819). A cash movement
|
|
18788
|
+
// attached to no invoice — its only edges are outbound, so nothing
|
|
18789
|
+
// drills down to it and a chip is the sole way to reach it.
|
|
18790
|
+
"CashEntry"
|
|
18680
18791
|
])
|
|
18681
18792
|
);
|
|
18682
18793
|
var AGENT_ACTION_LABELS = Object.freeze(
|
|
@@ -18727,6 +18838,17 @@ var STATIC_TOP_LEVEL_LABELS = Object.freeze(
|
|
|
18727
18838
|
"Review",
|
|
18728
18839
|
"ImageObject",
|
|
18729
18840
|
"Invoice",
|
|
18841
|
+
// Supplier invoice. Declared in schema-base.md's `## Node Types` table
|
|
18842
|
+
// (promoted there from schema-construction.md's top-level table by
|
|
18843
|
+
// 7b225f814), which this module never parses — so it is static, not
|
|
18844
|
+
// derived. Top-level because it is operator-entry with a natural key
|
|
18845
|
+
// (accountId, supplier, confirmationNumber), unlike its InvoiceLine /
|
|
18846
|
+
// InvoicePayment / Credit children.
|
|
18847
|
+
"InboundInvoice",
|
|
18848
|
+
// Ledger cash movement, declared in schema-base.md. Operator-entry with
|
|
18849
|
+
// a natural key (accountId, entryId) and no parent label, so it is
|
|
18850
|
+
// reachable only as a chip.
|
|
18851
|
+
"CashEntry",
|
|
18730
18852
|
"Task",
|
|
18731
18853
|
"Project",
|
|
18732
18854
|
"Event",
|
|
@@ -18777,8 +18899,8 @@ function parseTopLevelLabels(content) {
|
|
|
18777
18899
|
const line = lines[i];
|
|
18778
18900
|
if (line.startsWith("## ")) return [];
|
|
18779
18901
|
if (!line.includes("|")) continue;
|
|
18780
|
-
const
|
|
18781
|
-
if (!/^\s*\|?[\s:|-]+\|?\s*$/.test(
|
|
18902
|
+
const sep10 = lines[i + 1];
|
|
18903
|
+
if (!/^\s*\|?[\s:|-]+\|?\s*$/.test(sep10) || !sep10.includes("-")) continue;
|
|
18782
18904
|
const headers = splitPipeRow(line).map((h) => stripBackticks(h).toLowerCase());
|
|
18783
18905
|
const labelCol = headers.findIndex((h) => h === "neo4j label");
|
|
18784
18906
|
if (labelCol === -1) return [];
|
|
@@ -19029,7 +19151,7 @@ async function handleNeighbourhood(c, accountId) {
|
|
|
19029
19151
|
}
|
|
19030
19152
|
const includeAgentActions = c.req.query("includeAgentActions") === "1";
|
|
19031
19153
|
const agentActionLabels = includeAgentActions ? [] : [...AGENT_ACTION_LABELS];
|
|
19032
|
-
const
|
|
19154
|
+
const q2 = (c.req.query("q") ?? "").trim();
|
|
19033
19155
|
const rawLimit = c.req.query("limit");
|
|
19034
19156
|
const parsedLimit = rawLimit ? parseInt(rawLimit, 10) : NEIGHBOURHOOD_SEARCH_DEFAULT_LIMIT;
|
|
19035
19157
|
const searchLimit = Number.isFinite(parsedLimit) && parsedLimit > 0 ? Math.min(parsedLimit, NEIGHBOURHOOD_SEARCH_MAX_LIMIT) : NEIGHBOURHOOD_SEARCH_DEFAULT_LIMIT;
|
|
@@ -19037,9 +19159,9 @@ async function handleNeighbourhood(c, accountId) {
|
|
|
19037
19159
|
const session = getSession();
|
|
19038
19160
|
try {
|
|
19039
19161
|
let allowedIds = null;
|
|
19040
|
-
if (
|
|
19162
|
+
if (q2) {
|
|
19041
19163
|
const searchRes = await hybrid(session, embed, {
|
|
19042
|
-
query:
|
|
19164
|
+
query: q2,
|
|
19043
19165
|
accountId,
|
|
19044
19166
|
labels: [],
|
|
19045
19167
|
limit: searchLimit,
|
|
@@ -19122,7 +19244,7 @@ async function handleNeighbourhood(c, accountId) {
|
|
|
19122
19244
|
}
|
|
19123
19245
|
if (allowedIds !== null) {
|
|
19124
19246
|
console.error(
|
|
19125
|
-
`[graph-page] neighbourhood-search-intersect q="${
|
|
19247
|
+
`[graph-page] neighbourhood-search-intersect q="${q2}" allowed=${allowedIds.length} root=${elementId} rendered=${nodes.length}`
|
|
19126
19248
|
);
|
|
19127
19249
|
}
|
|
19128
19250
|
return c.json({ nodes, edges });
|
|
@@ -20463,18 +20585,68 @@ var session_rc_spawn_default = app35;
|
|
|
20463
20585
|
|
|
20464
20586
|
// server/routes/admin/session-reseat.ts
|
|
20465
20587
|
import { randomUUID as randomUUID12 } from "crypto";
|
|
20466
|
-
|
|
20588
|
+
|
|
20589
|
+
// server/channel-session-override.ts
|
|
20590
|
+
import { readFileSync as readFileSync34, writeFileSync as writeFileSync12, renameSync as renameSync9 } from "fs";
|
|
20591
|
+
import { join as join35 } from "path";
|
|
20592
|
+
var FILE2 = "channel-session-override.json";
|
|
20593
|
+
var UUID2 = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;
|
|
20594
|
+
function channelOverridePath(accountDir) {
|
|
20595
|
+
return join35(accountDir, FILE2);
|
|
20596
|
+
}
|
|
20597
|
+
function senderKey(channel, senderId) {
|
|
20598
|
+
return `${channel}:${senderId}`;
|
|
20599
|
+
}
|
|
20600
|
+
function readRaw2(accountDir) {
|
|
20601
|
+
try {
|
|
20602
|
+
return JSON.parse(readFileSync34(channelOverridePath(accountDir), "utf8"));
|
|
20603
|
+
} catch {
|
|
20604
|
+
return null;
|
|
20605
|
+
}
|
|
20606
|
+
}
|
|
20607
|
+
function readChannelOverrideId(accountDir, channel, senderId) {
|
|
20608
|
+
const raw = readRaw2(accountDir);
|
|
20609
|
+
if (!raw || !raw.bySender || typeof raw.bySender !== "object") return null;
|
|
20610
|
+
const v = raw.bySender[senderKey(channel, senderId)];
|
|
20611
|
+
return typeof v === "string" && UUID2.test(v) ? v : null;
|
|
20612
|
+
}
|
|
20613
|
+
function writeChannelOverrideId(accountDir, channel, senderId, sessionId) {
|
|
20614
|
+
const raw = readRaw2(accountDir);
|
|
20615
|
+
const bySender = {};
|
|
20616
|
+
if (raw?.bySender && typeof raw.bySender === "object") {
|
|
20617
|
+
for (const [k, v] of Object.entries(raw.bySender)) {
|
|
20618
|
+
if (typeof v === "string") bySender[k] = v;
|
|
20619
|
+
}
|
|
20620
|
+
}
|
|
20621
|
+
bySender[senderKey(channel, senderId)] = sessionId;
|
|
20622
|
+
const path3 = channelOverridePath(accountDir);
|
|
20623
|
+
const tmp = `${path3}.${process.pid}.tmp`;
|
|
20624
|
+
writeFileSync12(tmp, JSON.stringify({ bySender }, null, 2), "utf8");
|
|
20625
|
+
renameSync9(tmp, path3);
|
|
20626
|
+
}
|
|
20627
|
+
|
|
20628
|
+
// server/routes/admin/session-reseat.ts
|
|
20629
|
+
function isAdminChannelSource(source) {
|
|
20630
|
+
return !!source && source.role === "admin" && (source.channel === "whatsapp" || source.channel === "telegram") && typeof source.senderId === "string" && source.senderId.length > 0;
|
|
20631
|
+
}
|
|
20632
|
+
function resolveReseatPlan(body, mintId = randomUUID12, adminUserId, authenticatedName, source) {
|
|
20467
20633
|
const sessionId = mintId();
|
|
20468
20634
|
const key = `session:${sessionId}`;
|
|
20469
20635
|
const payload = {
|
|
20470
20636
|
sessionId,
|
|
20471
20637
|
forkFromSessionId: body.fromSessionId,
|
|
20472
20638
|
model: body.model,
|
|
20473
|
-
role: "admin"
|
|
20474
|
-
channel: "webchat",
|
|
20475
|
-
senderId: key,
|
|
20476
|
-
webchatChannel: webchatChannelDescriptor(key)
|
|
20639
|
+
role: "admin"
|
|
20477
20640
|
};
|
|
20641
|
+
if (isAdminChannelSource(source)) {
|
|
20642
|
+
payload.channel = source.channel;
|
|
20643
|
+
payload.senderId = source.senderId;
|
|
20644
|
+
if (source.accountId) payload.targetAccountId = source.accountId;
|
|
20645
|
+
} else {
|
|
20646
|
+
payload.channel = "webchat";
|
|
20647
|
+
payload.senderId = key;
|
|
20648
|
+
payload.webchatChannel = webchatChannelDescriptor(key);
|
|
20649
|
+
}
|
|
20478
20650
|
if (body.name) payload.name = body.name;
|
|
20479
20651
|
if (body.permissionMode) payload.permissionMode = body.permissionMode;
|
|
20480
20652
|
if (body.effort) payload.effort = body.effort;
|
|
@@ -20515,14 +20687,18 @@ app36.post("/", requireAdminSession, async (c) => {
|
|
|
20515
20687
|
}
|
|
20516
20688
|
const accountId = cacheKey ? getAccountIdForSession(cacheKey) : void 0;
|
|
20517
20689
|
const authenticatedName = accountId ? await resolveAuthenticatedName(accountId, adminUserId) : void 0;
|
|
20690
|
+
const sourceMeta = readSourceRowMeta(fromSessionId);
|
|
20691
|
+
const source = sourceMeta ? { channel: sourceMeta.channel, senderId: sourceMeta.senderId, role: sourceMeta.role, accountId: sourceMeta.accountId } : null;
|
|
20518
20692
|
const plan = resolveReseatPlan(
|
|
20519
20693
|
{ fromSessionId, model, ...permissionMode ? { permissionMode } : {}, ...effort ? { effort } : {} },
|
|
20520
20694
|
randomUUID12,
|
|
20521
20695
|
adminUserId,
|
|
20522
|
-
authenticatedName
|
|
20696
|
+
authenticatedName,
|
|
20697
|
+
source
|
|
20523
20698
|
);
|
|
20699
|
+
const isChannelFork = isAdminChannelSource(source);
|
|
20524
20700
|
const reseatReqId = randomUUID12();
|
|
20525
|
-
console.log(`[chat-reseat] op=request reseatReqId=${reseatReqId} from=${fromSessionId} to-model=${model} to-mode=${permissionMode ?? "none"} to-effort=${effort ?? "none"} new=${plan.sessionId}`);
|
|
20701
|
+
console.log(`[chat-reseat] op=request reseatReqId=${reseatReqId} from=${fromSessionId} from-channel=${source?.channel ?? "none"} from-sender=${source?.senderId ?? "none"} to-model=${model} to-mode=${permissionMode ?? "none"} to-effort=${effort ?? "none"} new=${plan.sessionId}`);
|
|
20526
20702
|
let res;
|
|
20527
20703
|
try {
|
|
20528
20704
|
res = await fetch(`${managerBase("session-reseat:wrapper")}/rc-spawn`, {
|
|
@@ -20562,6 +20738,23 @@ app36.post("/", requireAdminSession, async (c) => {
|
|
|
20562
20738
|
} catch (err) {
|
|
20563
20739
|
console.error(`[session-reseat] canonical-forward-failed err=${err instanceof Error ? err.message : String(err)}`);
|
|
20564
20740
|
}
|
|
20741
|
+
if (isChannelFork) {
|
|
20742
|
+
const channel = plan.payload.channel;
|
|
20743
|
+
const senderId = plan.payload.senderId;
|
|
20744
|
+
try {
|
|
20745
|
+
const provenance = source?.accountId ? "source-account" : "boot-account-fallback";
|
|
20746
|
+
const dir = source?.accountId ? listValidAccounts().find((a) => a.accountId === source.accountId)?.accountDir ?? null : resolveAccount()?.accountDir ?? null;
|
|
20747
|
+
if (dir) {
|
|
20748
|
+
writeChannelOverrideId(dir, channel, senderId, plan.sessionId);
|
|
20749
|
+
console.log(`[chat-reseat] op=channel-forward reseatReqId=${reseatReqId} senderId=${senderId} from=${fromSessionId} to=${plan.sessionId} keyedOn=${provenance}`);
|
|
20750
|
+
} else {
|
|
20751
|
+
console.error(`[session-reseat] channel-forward-failed reason=no-account-dir accountId=${source?.accountId ?? "none"} senderId=${senderId}`);
|
|
20752
|
+
}
|
|
20753
|
+
} catch (err) {
|
|
20754
|
+
console.error(`[session-reseat] channel-forward-failed err=${err instanceof Error ? err.message : String(err)} senderId=${senderId}`);
|
|
20755
|
+
}
|
|
20756
|
+
console.log(`[chat-reseat] op=channel-fork-unbound reseatReqId=${reseatReqId} senderId=${senderId} session=${plan.sessionId} note=binds-on-next-inbound`);
|
|
20757
|
+
}
|
|
20565
20758
|
const target = `/chat?session=${plan.sessionId}`;
|
|
20566
20759
|
console.log(`[chat-reseat] op=acquired reseatReqId=${reseatReqId} pid=${parsed?.spawnedPid ?? "none"} new=${plan.sessionId}`);
|
|
20567
20760
|
console.log(`[chat-reseat] op=navigate reseatReqId=${reseatReqId} target=${target}`);
|
|
@@ -20696,14 +20889,14 @@ app37.get("/", async (c) => {
|
|
|
20696
20889
|
var system_stats_default = app37;
|
|
20697
20890
|
|
|
20698
20891
|
// server/routes/admin/health.ts
|
|
20699
|
-
import { existsSync as existsSync31, readFileSync as
|
|
20700
|
-
import { resolve as resolve26, join as
|
|
20892
|
+
import { existsSync as existsSync31, readFileSync as readFileSync35 } from "fs";
|
|
20893
|
+
import { resolve as resolve26, join as join36 } from "path";
|
|
20701
20894
|
var PLATFORM_ROOT6 = process.env.MAXY_PLATFORM_ROOT ?? resolve26(process.cwd(), "..");
|
|
20702
20895
|
var brandHostname = "maxy";
|
|
20703
|
-
var brandJsonPath =
|
|
20896
|
+
var brandJsonPath = join36(PLATFORM_ROOT6, "config", "brand.json");
|
|
20704
20897
|
if (existsSync31(brandJsonPath)) {
|
|
20705
20898
|
try {
|
|
20706
|
-
const brand = JSON.parse(
|
|
20899
|
+
const brand = JSON.parse(readFileSync35(brandJsonPath, "utf-8"));
|
|
20707
20900
|
if (brand.hostname) brandHostname = brand.hostname;
|
|
20708
20901
|
} catch {
|
|
20709
20902
|
}
|
|
@@ -20713,7 +20906,7 @@ var PROCESS_STARTED_AT = (/* @__PURE__ */ new Date()).toISOString();
|
|
|
20713
20906
|
var PROBE_TIMEOUT_MS = 1e3;
|
|
20714
20907
|
function readVersion() {
|
|
20715
20908
|
if (!existsSync31(VERSION_FILE)) return "unknown";
|
|
20716
|
-
return
|
|
20909
|
+
return readFileSync35(VERSION_FILE, "utf-8").trim() || "unknown";
|
|
20717
20910
|
}
|
|
20718
20911
|
async function probeConversationDb() {
|
|
20719
20912
|
let session;
|
|
@@ -20893,7 +21086,7 @@ function managerLogFollowUrl(sessionId, opts) {
|
|
|
20893
21086
|
|
|
20894
21087
|
// server/routes/admin/linkedin-ingest.ts
|
|
20895
21088
|
var TAG31 = "[linkedin-ingest-route]";
|
|
20896
|
-
var
|
|
21089
|
+
var UUID3 = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;
|
|
20897
21090
|
var ISO = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,3})?(?:Z|[+-]\d{2}:\d{2})$/;
|
|
20898
21091
|
var LINKEDIN_URL = /^https:\/\/www\.linkedin\.com\//;
|
|
20899
21092
|
function rejectSchema(reason, missing = []) {
|
|
@@ -20907,7 +21100,7 @@ function validate(body) {
|
|
|
20907
21100
|
if (b.kind !== "profile" && b.kind !== "thread") missing.push("kind");
|
|
20908
21101
|
if (typeof b.capturedAt !== "string" || !ISO.test(b.capturedAt)) missing.push("capturedAt");
|
|
20909
21102
|
if (typeof b.pageUrl !== "string" || !LINKEDIN_URL.test(b.pageUrl)) missing.push("pageUrl");
|
|
20910
|
-
if (typeof b.dispatchId !== "string" || !
|
|
21103
|
+
if (typeof b.dispatchId !== "string" || !UUID3.test(b.dispatchId)) missing.push("dispatchId");
|
|
20911
21104
|
if (missing.length) return rejectSchema("envelope", missing);
|
|
20912
21105
|
if (b.kind === "profile") {
|
|
20913
21106
|
const p = b.profile;
|
|
@@ -21490,7 +21683,7 @@ var browser_default = app45;
|
|
|
21490
21683
|
|
|
21491
21684
|
// server/routes/admin/calendar.ts
|
|
21492
21685
|
import { existsSync as existsSync32 } from "fs";
|
|
21493
|
-
import { join as
|
|
21686
|
+
import { join as join38 } from "path";
|
|
21494
21687
|
|
|
21495
21688
|
// server/lib/calendar-ics.ts
|
|
21496
21689
|
var CRLF = "\r\n";
|
|
@@ -21589,8 +21782,8 @@ function countVEvents(ics) {
|
|
|
21589
21782
|
}
|
|
21590
21783
|
|
|
21591
21784
|
// server/lib/calendar-availability.ts
|
|
21592
|
-
import { readFileSync as
|
|
21593
|
-
import { join as
|
|
21785
|
+
import { readFileSync as readFileSync36 } from "fs";
|
|
21786
|
+
import { join as join37 } from "path";
|
|
21594
21787
|
var AVAILABILITY_FILENAME = "calendar-availability.json";
|
|
21595
21788
|
var REQUIRED_KEYS = [
|
|
21596
21789
|
"timezone",
|
|
@@ -21599,10 +21792,10 @@ var REQUIRED_KEYS = [
|
|
|
21599
21792
|
"weekly"
|
|
21600
21793
|
];
|
|
21601
21794
|
function readAvailabilityConfig(accountDir) {
|
|
21602
|
-
const path3 =
|
|
21795
|
+
const path3 = join37(accountDir, AVAILABILITY_FILENAME);
|
|
21603
21796
|
let raw;
|
|
21604
21797
|
try {
|
|
21605
|
-
raw =
|
|
21798
|
+
raw = readFileSync36(path3, "utf-8");
|
|
21606
21799
|
} catch {
|
|
21607
21800
|
throw new Error(`availability not configured: ${path3} not found`);
|
|
21608
21801
|
}
|
|
@@ -21961,7 +22154,7 @@ app46.get("/booking-link", requireAdminSession, (c) => {
|
|
|
21961
22154
|
console.error('[calendar] op=booking-link-fail err="no account configured"');
|
|
21962
22155
|
return c.json({ bookingDomain: null });
|
|
21963
22156
|
}
|
|
21964
|
-
if (!existsSync32(
|
|
22157
|
+
if (!existsSync32(join38(account.accountDir, AVAILABILITY_FILENAME))) {
|
|
21965
22158
|
console.log("[calendar] op=booking-link bookingDomain=none source=availability-config");
|
|
21966
22159
|
return c.json({ bookingDomain: null });
|
|
21967
22160
|
}
|
|
@@ -22016,16 +22209,32 @@ app47.get("/", requireAdminSession, async (c) => {
|
|
|
22016
22209
|
WHERE te.endedAt IS NULL
|
|
22017
22210
|
WITH t, te ORDER BY te.startedAt
|
|
22018
22211
|
WITH t, head(collect(te)) AS openTe
|
|
22212
|
+
// OPTIONAL is load-bearing. Unassigned is the common state, so a plain
|
|
22213
|
+
// MATCH would silently drop most of the list with nothing looking wrong.
|
|
22214
|
+
OPTIONAL MATCH (t)-[:ASSIGNED_TO]->(a:AdminUser)
|
|
22215
|
+
// head(collect(...)) for the same reason the TimeEntry match above uses
|
|
22216
|
+
// it: a second ASSIGNED_TO edge would fan the row out, duplicating
|
|
22217
|
+
// rowKey and double-counting the assignee.
|
|
22218
|
+
WITH t, openTe, head(collect(a)) AS assignee
|
|
22019
22219
|
RETURN t.taskId AS taskId, elementId(t) AS rowKey, t.name AS name, t.clientName AS clientName,
|
|
22020
22220
|
t.propertyAddress AS propertyAddress, t.fee AS fee, t.status AS status,
|
|
22021
22221
|
coalesce(t.secondsLogged, 0) AS secondsLogged,
|
|
22022
|
-
openTe.startedAt AS runningStartedAt
|
|
22222
|
+
openTe.startedAt AS runningStartedAt,
|
|
22223
|
+
t.createdAt AS createdAt,
|
|
22224
|
+
assignee.userId AS assigneeUserId, assignee.name AS assigneeName
|
|
22023
22225
|
ORDER BY t.createdAt DESC`,
|
|
22024
22226
|
{ accountId }
|
|
22025
22227
|
);
|
|
22228
|
+
const totalResult = await session.run(
|
|
22229
|
+
`MATCH (t:Task {accountId: $accountId}) RETURN count(t) AS total`,
|
|
22230
|
+
{ accountId }
|
|
22231
|
+
);
|
|
22232
|
+
const graphTotal = totalResult.records.length > 0 ? toNum(totalResult.records[0].get("total")) : 0;
|
|
22026
22233
|
const open = [];
|
|
22027
22234
|
const completed = [];
|
|
22028
22235
|
let missingTaskId = 0;
|
|
22236
|
+
let missingCreatedAt = 0;
|
|
22237
|
+
let assigned = 0;
|
|
22029
22238
|
for (const rec of result.records) {
|
|
22030
22239
|
const status = rec.get("status") ?? "pending";
|
|
22031
22240
|
const runningStartedAt = rec.get("runningStartedAt") ?? null;
|
|
@@ -22033,6 +22242,10 @@ app47.get("/", requireAdminSession, async (c) => {
|
|
|
22033
22242
|
const rawTaskId = rec.get("taskId");
|
|
22034
22243
|
const taskId = typeof rawTaskId === "string" && rawTaskId !== "" ? rawTaskId : null;
|
|
22035
22244
|
if (taskId === null) missingTaskId++;
|
|
22245
|
+
const createdAt = rec.get("createdAt") ?? null;
|
|
22246
|
+
if (createdAt === null) missingCreatedAt++;
|
|
22247
|
+
const assigneeUserId = rec.get("assigneeUserId") ?? null;
|
|
22248
|
+
if (assigneeUserId !== null) assigned++;
|
|
22036
22249
|
const row = {
|
|
22037
22250
|
taskId,
|
|
22038
22251
|
rowKey: rec.get("rowKey"),
|
|
@@ -22043,12 +22256,23 @@ app47.get("/", requireAdminSession, async (c) => {
|
|
|
22043
22256
|
status,
|
|
22044
22257
|
secondsLogged: toNum(rec.get("secondsLogged")),
|
|
22045
22258
|
running: runningStartedAt !== null,
|
|
22046
|
-
runningStartedAt
|
|
22259
|
+
runningStartedAt,
|
|
22260
|
+
createdAt,
|
|
22261
|
+
assigneeUserId,
|
|
22262
|
+
assigneeName: rec.get("assigneeName") ?? null
|
|
22047
22263
|
};
|
|
22048
22264
|
if (COMPLETED.has(status)) completed.push(row);
|
|
22049
22265
|
else if (!HIDDEN_FROM_OPEN.has(status)) open.push(row);
|
|
22050
22266
|
}
|
|
22051
|
-
|
|
22267
|
+
const returned = result.records.length;
|
|
22268
|
+
if (returned < graphTotal) {
|
|
22269
|
+
console.error(
|
|
22270
|
+
`${TAG37} op=list row-count-shortfall account=${accountId} returned=${returned} graphTotal=${graphTotal} missing=${graphTotal - returned}`
|
|
22271
|
+
);
|
|
22272
|
+
}
|
|
22273
|
+
console.log(
|
|
22274
|
+
`${TAG37} op=list account=${accountId} returned=${returned} graphTotal=${graphTotal} open=${open.length} completed=${completed.length} missingTaskId=${missingTaskId} missingCreatedAt=${missingCreatedAt} assigned=${assigned}`
|
|
22275
|
+
);
|
|
22052
22276
|
return c.json({ open, completed });
|
|
22053
22277
|
} catch (err) {
|
|
22054
22278
|
console.error(`${TAG37} op=list error="${err instanceof Error ? err.message : String(err)}"`);
|
|
@@ -22366,7 +22590,13 @@ app52.get("/", requireAdminSession, async (c) => {
|
|
|
22366
22590
|
const start = Date.now();
|
|
22367
22591
|
let res;
|
|
22368
22592
|
try {
|
|
22369
|
-
|
|
22593
|
+
const qs = new URLSearchParams();
|
|
22594
|
+
const from = c.req.query("from");
|
|
22595
|
+
const to = c.req.query("to");
|
|
22596
|
+
if (from) qs.set("from", from);
|
|
22597
|
+
if (to) qs.set("to", to);
|
|
22598
|
+
const suffix = qs.size > 0 ? `?${qs.toString()}` : "";
|
|
22599
|
+
res = await fetch(`${managerBase("activity:wrapper")}/activity${suffix}`, {
|
|
22370
22600
|
signal: AbortSignal.timeout(3e4)
|
|
22371
22601
|
});
|
|
22372
22602
|
} catch (err) {
|
|
@@ -22420,6 +22650,11 @@ app52.get("/", requireAdminSession, async (c) => {
|
|
|
22420
22650
|
rows,
|
|
22421
22651
|
census,
|
|
22422
22652
|
censusAgeMs,
|
|
22653
|
+
// `satisfies` and not a bare literal (Task 1825). LoopsPayload is what the
|
|
22654
|
+
// activity page destructures; without the check, a key renamed or dropped
|
|
22655
|
+
// here still compiles and the page reads undefined, which its own
|
|
22656
|
+
// vocabulary renders as a dash meaning "not measured". The check runs both
|
|
22657
|
+
// ways: a key the page does not know about fails as an excess property.
|
|
22423
22658
|
loops: {
|
|
22424
22659
|
armed: armed2,
|
|
22425
22660
|
rows: loopRows,
|
|
@@ -22722,7 +22957,7 @@ app56.route("/request-magic-link", request_magic_link_default);
|
|
|
22722
22957
|
var access_default = app56;
|
|
22723
22958
|
|
|
22724
22959
|
// server/routes/sites.ts
|
|
22725
|
-
import { existsSync as existsSync33, readFileSync as
|
|
22960
|
+
import { existsSync as existsSync33, readFileSync as readFileSync37, realpathSync as realpathSync7, statSync as statSync14 } from "fs";
|
|
22726
22961
|
import { resolve as resolve28 } from "path";
|
|
22727
22962
|
var SAFE_SEG_RE = /^[a-z0-9_][a-z0-9_.-]{0,99}$/i;
|
|
22728
22963
|
var MIME = {
|
|
@@ -22786,13 +23021,13 @@ app57.get("/:rel{.*}", (c) => {
|
|
|
22786
23021
|
console.error(`[sites] path-traversal-rejected path=${reqPath} reason=escape status=403`);
|
|
22787
23022
|
return c.text("Forbidden", 403);
|
|
22788
23023
|
}
|
|
22789
|
-
let
|
|
23024
|
+
let stat9;
|
|
22790
23025
|
try {
|
|
22791
|
-
|
|
23026
|
+
stat9 = existsSync33(filePath) ? statSync14(filePath) : null;
|
|
22792
23027
|
} catch {
|
|
22793
|
-
|
|
23028
|
+
stat9 = null;
|
|
22794
23029
|
}
|
|
22795
|
-
if (
|
|
23030
|
+
if (stat9?.isDirectory() && !reqPath.endsWith("/")) {
|
|
22796
23031
|
const search = new URL(c.req.url).search;
|
|
22797
23032
|
const target = `${reqPath}/${search}`;
|
|
22798
23033
|
console.log(
|
|
@@ -22800,7 +23035,7 @@ app57.get("/:rel{.*}", (c) => {
|
|
|
22800
23035
|
);
|
|
22801
23036
|
return c.redirect(target, 301);
|
|
22802
23037
|
}
|
|
22803
|
-
if (
|
|
23038
|
+
if (stat9?.isDirectory()) {
|
|
22804
23039
|
filePath = resolve28(filePath, "index.html");
|
|
22805
23040
|
}
|
|
22806
23041
|
if (!filePath.startsWith(rootDir + "/")) {
|
|
@@ -22826,7 +23061,7 @@ app57.get("/:rel{.*}", (c) => {
|
|
|
22826
23061
|
}
|
|
22827
23062
|
let body;
|
|
22828
23063
|
try {
|
|
22829
|
-
body =
|
|
23064
|
+
body = readFileSync37(realPath);
|
|
22830
23065
|
} catch (err) {
|
|
22831
23066
|
const code = err?.code;
|
|
22832
23067
|
if (code === "EISDIR") {
|
|
@@ -22862,7 +23097,7 @@ var sites_default = app57;
|
|
|
22862
23097
|
|
|
22863
23098
|
// app/lib/visitor-token.ts
|
|
22864
23099
|
import { createHmac, randomBytes, timingSafeEqual } from "crypto";
|
|
22865
|
-
import { mkdirSync as mkdirSync8, readFileSync as
|
|
23100
|
+
import { mkdirSync as mkdirSync8, readFileSync as readFileSync38, writeFileSync as writeFileSync13 } from "fs";
|
|
22866
23101
|
import { dirname as dirname12 } from "path";
|
|
22867
23102
|
var TOKEN_PREFIX = "v1.";
|
|
22868
23103
|
var SECRET_BYTES = 32;
|
|
@@ -22871,7 +23106,7 @@ var cachedSecret = null;
|
|
|
22871
23106
|
function getSecret() {
|
|
22872
23107
|
if (cachedSecret) return cachedSecret;
|
|
22873
23108
|
try {
|
|
22874
|
-
const hex2 =
|
|
23109
|
+
const hex2 = readFileSync38(VISITOR_TOKEN_SECRET_FILE, "utf-8").trim();
|
|
22875
23110
|
if (hex2.length === SECRET_BYTES * 2) {
|
|
22876
23111
|
cachedSecret = Buffer.from(hex2, "hex");
|
|
22877
23112
|
return cachedSecret;
|
|
@@ -22881,11 +23116,11 @@ function getSecret() {
|
|
|
22881
23116
|
const fresh = randomBytes(SECRET_BYTES).toString("hex");
|
|
22882
23117
|
try {
|
|
22883
23118
|
mkdirSync8(dirname12(VISITOR_TOKEN_SECRET_FILE), { recursive: true, mode: 448 });
|
|
22884
|
-
|
|
23119
|
+
writeFileSync13(VISITOR_TOKEN_SECRET_FILE, fresh, { mode: 384, flag: "wx" });
|
|
22885
23120
|
console.log(`[visitor-token] secret minted path=${VISITOR_TOKEN_SECRET_FILE}`);
|
|
22886
23121
|
} catch {
|
|
22887
23122
|
}
|
|
22888
|
-
const hex =
|
|
23123
|
+
const hex = readFileSync38(VISITOR_TOKEN_SECRET_FILE, "utf-8").trim();
|
|
22889
23124
|
cachedSecret = Buffer.from(hex, "hex");
|
|
22890
23125
|
return cachedSecret;
|
|
22891
23126
|
}
|
|
@@ -22938,8 +23173,8 @@ var VISITOR_COOKIE_NAME = "mxy_v";
|
|
|
22938
23173
|
var VISITOR_COOKIE_MAX_AGE_SECONDS = Math.floor(DEFAULT_TTL_MS / 1e3);
|
|
22939
23174
|
|
|
22940
23175
|
// app/lib/brand-config.ts
|
|
22941
|
-
import { existsSync as existsSync34, readFileSync as
|
|
22942
|
-
import { join as
|
|
23176
|
+
import { existsSync as existsSync34, readFileSync as readFileSync39 } from "fs";
|
|
23177
|
+
import { join as join39 } from "path";
|
|
22943
23178
|
var cached2 = null;
|
|
22944
23179
|
var cachedAttempted = false;
|
|
22945
23180
|
function readBrandConfig() {
|
|
@@ -22947,10 +23182,10 @@ function readBrandConfig() {
|
|
|
22947
23182
|
cachedAttempted = true;
|
|
22948
23183
|
const platformRoot5 = process.env.MAXY_PLATFORM_ROOT;
|
|
22949
23184
|
if (!platformRoot5) return null;
|
|
22950
|
-
const brandPath =
|
|
23185
|
+
const brandPath = join39(platformRoot5, "config", "brand.json");
|
|
22951
23186
|
if (!existsSync34(brandPath)) return null;
|
|
22952
23187
|
try {
|
|
22953
|
-
cached2 = JSON.parse(
|
|
23188
|
+
cached2 = JSON.parse(readFileSync39(brandPath, "utf-8"));
|
|
22954
23189
|
return cached2;
|
|
22955
23190
|
} catch {
|
|
22956
23191
|
return null;
|
|
@@ -23084,8 +23319,8 @@ function appendConsentParams(pageUrl, token) {
|
|
|
23084
23319
|
const hashIdx = pageUrl.indexOf("#");
|
|
23085
23320
|
const hash = hashIdx >= 0 ? pageUrl.slice(hashIdx) : "";
|
|
23086
23321
|
const base = hashIdx >= 0 ? pageUrl.slice(0, hashIdx) : pageUrl;
|
|
23087
|
-
const
|
|
23088
|
-
return base +
|
|
23322
|
+
const sep10 = base.indexOf("?") >= 0 ? "&" : "?";
|
|
23323
|
+
return base + sep10 + `consent=needed&v=${encodeURIComponent(token)}` + hash;
|
|
23089
23324
|
}
|
|
23090
23325
|
}
|
|
23091
23326
|
var SAFE_SLUG_RE = /^[a-z0-9](?:[a-z0-9-]{0,118}[a-z0-9])?$/;
|
|
@@ -23448,13 +23683,13 @@ var visitor_event_default = app60;
|
|
|
23448
23683
|
|
|
23449
23684
|
// server/routes/session.ts
|
|
23450
23685
|
import { resolve as resolve29 } from "path";
|
|
23451
|
-
import { existsSync as existsSync35, writeFileSync as
|
|
23686
|
+
import { existsSync as existsSync35, writeFileSync as writeFileSync14, mkdirSync as mkdirSync9 } from "fs";
|
|
23452
23687
|
var UUID_RE5 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
23453
23688
|
function writeBrandingCache(accountId, agentSlug, branding) {
|
|
23454
23689
|
try {
|
|
23455
23690
|
const cacheDir = resolve29(MAXY_DIR, "branding-cache", accountId);
|
|
23456
23691
|
mkdirSync9(cacheDir, { recursive: true });
|
|
23457
|
-
|
|
23692
|
+
writeFileSync14(resolve29(cacheDir, `${agentSlug}.json`), JSON.stringify(branding), "utf-8");
|
|
23458
23693
|
} catch (err) {
|
|
23459
23694
|
console.error(`[branding] cache write failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
23460
23695
|
}
|
|
@@ -23779,8 +24014,205 @@ app62.get("/free-busy", async (c) => {
|
|
|
23779
24014
|
});
|
|
23780
24015
|
var calendar_public_default = app62;
|
|
23781
24016
|
|
|
24017
|
+
// server/routes/portal-fetch.ts
|
|
24018
|
+
import { createReadStream as createReadStream3 } from "fs";
|
|
24019
|
+
import { stat as stat6, readFile as readFile6, realpath } from "fs/promises";
|
|
24020
|
+
import { join as join40, resolve as resolve30, sep as sep8 } from "path";
|
|
24021
|
+
import { Readable as Readable3 } from "stream";
|
|
24022
|
+
|
|
24023
|
+
// ../plugins/cloudflare/bin/schema-exposed-dirs.mjs
|
|
24024
|
+
var ONT_START = "<!-- ontology-buckets:start -->";
|
|
24025
|
+
var ONT_END = "<!-- ontology-buckets:end -->";
|
|
24026
|
+
var OWNED_START = "<!-- plugin-owned-dirs:start -->";
|
|
24027
|
+
var OWNED_END = "<!-- plugin-owned-dirs:end -->";
|
|
24028
|
+
var ALWAYS_EXPOSED = "output";
|
|
24029
|
+
var DOMAIN_LINE = /^- `([^`/]+)\/` - one folder per /;
|
|
24030
|
+
var OWNED_LINE = /^- `([^`/]+)\/` — /;
|
|
24031
|
+
function region(text, start, end) {
|
|
24032
|
+
const i = text.indexOf(start);
|
|
24033
|
+
if (i === -1) return null;
|
|
24034
|
+
const j = text.indexOf(end, i);
|
|
24035
|
+
return text.slice(i, j === -1 ? text.length : j);
|
|
24036
|
+
}
|
|
24037
|
+
function allowedBlockOrNull(text) {
|
|
24038
|
+
const lines = text.split("\n");
|
|
24039
|
+
const start = lines.findIndex((l) => l.trim() === "```allowed-top-level");
|
|
24040
|
+
if (start === -1) return null;
|
|
24041
|
+
for (let j = start + 1; j < lines.length; j++) {
|
|
24042
|
+
if (lines[j].trim() === "```") {
|
|
24043
|
+
return lines.slice(start + 1, j).map((l) => l.trim()).filter(Boolean);
|
|
24044
|
+
}
|
|
24045
|
+
}
|
|
24046
|
+
return null;
|
|
24047
|
+
}
|
|
24048
|
+
function resolveExposedDirs(schemaText) {
|
|
24049
|
+
const empty = {
|
|
24050
|
+
schemaPresent: false,
|
|
24051
|
+
allowed: (
|
|
24052
|
+
/** @type {string[]} */
|
|
24053
|
+
[]
|
|
24054
|
+
),
|
|
24055
|
+
domainBuckets: (
|
|
24056
|
+
/** @type {string[]} */
|
|
24057
|
+
[]
|
|
24058
|
+
),
|
|
24059
|
+
pluginOwned: (
|
|
24060
|
+
/** @type {string[]} */
|
|
24061
|
+
[]
|
|
24062
|
+
),
|
|
24063
|
+
collisions: (
|
|
24064
|
+
/** @type {string[]} */
|
|
24065
|
+
[]
|
|
24066
|
+
),
|
|
24067
|
+
exposed: (
|
|
24068
|
+
/** @type {string[]} */
|
|
24069
|
+
[]
|
|
24070
|
+
)
|
|
24071
|
+
};
|
|
24072
|
+
if (typeof schemaText !== "string" || schemaText.length === 0) return empty;
|
|
24073
|
+
const allowed = allowedBlockOrNull(schemaText);
|
|
24074
|
+
if (allowed === null) return { ...empty, schemaPresent: true };
|
|
24075
|
+
const ont = region(schemaText, ONT_START, ONT_END);
|
|
24076
|
+
const owned = region(schemaText, OWNED_START, OWNED_END);
|
|
24077
|
+
const dirsIn = (text, re) => {
|
|
24078
|
+
if (!text) return [];
|
|
24079
|
+
const out = [];
|
|
24080
|
+
for (const line of text.split("\n")) {
|
|
24081
|
+
const m = re.exec(line.trim());
|
|
24082
|
+
if (m && !out.includes(m[1])) out.push(m[1]);
|
|
24083
|
+
}
|
|
24084
|
+
return out;
|
|
24085
|
+
};
|
|
24086
|
+
const domainBuckets = dirsIn(ont, DOMAIN_LINE);
|
|
24087
|
+
const pluginOwned = dirsIn(owned, OWNED_LINE);
|
|
24088
|
+
const collisions = domainBuckets.filter((d) => pluginOwned.includes(d));
|
|
24089
|
+
const candidates = [ALWAYS_EXPOSED, ...domainBuckets.filter((d) => !collisions.includes(d))];
|
|
24090
|
+
const exposed = [...new Set(candidates)].filter((d) => allowed.includes(d)).sort();
|
|
24091
|
+
return { schemaPresent: true, allowed, domainBuckets, pluginOwned, collisions, exposed };
|
|
24092
|
+
}
|
|
24093
|
+
|
|
24094
|
+
// server/routes/portal-fetch.ts
|
|
24095
|
+
var app63 = new Hono();
|
|
24096
|
+
var TAG45 = "[portal-fetch]";
|
|
24097
|
+
async function verifySignature(secret, accountId, relPath, expiresAtMs, signature, nowMs) {
|
|
24098
|
+
if (!secret || !signature) return false;
|
|
24099
|
+
if (!Number.isFinite(expiresAtMs) || expiresAtMs <= nowMs) return false;
|
|
24100
|
+
const key = await crypto.subtle.importKey(
|
|
24101
|
+
"raw",
|
|
24102
|
+
new TextEncoder().encode(secret),
|
|
24103
|
+
{ name: "HMAC", hash: "SHA-256" },
|
|
24104
|
+
false,
|
|
24105
|
+
["sign"]
|
|
24106
|
+
);
|
|
24107
|
+
const mac = await crypto.subtle.sign(
|
|
24108
|
+
"HMAC",
|
|
24109
|
+
key,
|
|
24110
|
+
new TextEncoder().encode(`${accountId}
|
|
24111
|
+
${relPath}
|
|
24112
|
+
${expiresAtMs}`)
|
|
24113
|
+
);
|
|
24114
|
+
const expected = [...new Uint8Array(mac)].map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
24115
|
+
if (expected.length !== signature.length) return false;
|
|
24116
|
+
let diff = 0;
|
|
24117
|
+
for (let i = 0; i < expected.length; i++) diff |= expected.charCodeAt(i) ^ signature.charCodeAt(i);
|
|
24118
|
+
return diff === 0;
|
|
24119
|
+
}
|
|
24120
|
+
async function readAccountSecret(accountId) {
|
|
24121
|
+
const path3 = resolve30(DATA_ROOT, "accounts", accountId, "secrets", "data-portal.env");
|
|
24122
|
+
let text;
|
|
24123
|
+
try {
|
|
24124
|
+
text = await readFile6(path3, "utf8");
|
|
24125
|
+
} catch {
|
|
24126
|
+
console.error(`${TAG45} op=no-secret account="${accountId}" path="${path3}"`);
|
|
24127
|
+
return "";
|
|
24128
|
+
}
|
|
24129
|
+
for (const line of text.split("\n")) {
|
|
24130
|
+
const m = line.match(/^PORTAL_FETCH_SECRET=(.*)$/);
|
|
24131
|
+
if (m) return m[1].trim();
|
|
24132
|
+
}
|
|
24133
|
+
console.error(`${TAG45} op=no-secret account="${accountId}" reason=key-absent path="${path3}"`);
|
|
24134
|
+
return "";
|
|
24135
|
+
}
|
|
24136
|
+
function isValidAccountId2(value) {
|
|
24137
|
+
return /^[A-Za-z0-9-]+$/.test(value);
|
|
24138
|
+
}
|
|
24139
|
+
async function resolveFetchTarget(accountDir, relPath) {
|
|
24140
|
+
let schemaText = null;
|
|
24141
|
+
try {
|
|
24142
|
+
schemaText = await readFile6(join40(accountDir, "SCHEMA.md"), "utf8");
|
|
24143
|
+
} catch {
|
|
24144
|
+
schemaText = null;
|
|
24145
|
+
}
|
|
24146
|
+
const { exposed } = resolveExposedDirs(schemaText);
|
|
24147
|
+
if (exposed.length === 0) return { ok: false, reason: "not-exposed" };
|
|
24148
|
+
const lexical = resolve30(accountDir, relPath);
|
|
24149
|
+
let root;
|
|
24150
|
+
let absolute;
|
|
24151
|
+
try {
|
|
24152
|
+
root = await realpath(accountDir);
|
|
24153
|
+
absolute = await realpath(lexical);
|
|
24154
|
+
} catch {
|
|
24155
|
+
return { ok: false, reason: "enoent" };
|
|
24156
|
+
}
|
|
24157
|
+
if (absolute === root || !absolute.startsWith(root + sep8)) return { ok: false, reason: "not-exposed" };
|
|
24158
|
+
const top = absolute.slice(root.length + 1).split(sep8)[0];
|
|
24159
|
+
if (!exposed.includes(top)) return { ok: false, reason: "not-exposed" };
|
|
24160
|
+
try {
|
|
24161
|
+
const st = await stat6(absolute);
|
|
24162
|
+
if (!st.isFile()) return { ok: false, reason: "enoent" };
|
|
24163
|
+
return { ok: true, absolute, sizeBytes: st.size };
|
|
24164
|
+
} catch {
|
|
24165
|
+
return { ok: false, reason: "enoent" };
|
|
24166
|
+
}
|
|
24167
|
+
}
|
|
24168
|
+
function q(value) {
|
|
24169
|
+
return `"${value.replace(/[\\"]/g, "\\$&").replace(/[\r\n\t]/g, " ")}"`;
|
|
24170
|
+
}
|
|
24171
|
+
async function handle(c, headOnly) {
|
|
24172
|
+
const accountId = c.req.query("accountId") ?? "";
|
|
24173
|
+
const relPath = c.req.query("path") ?? "";
|
|
24174
|
+
const exp = Number(c.req.query("exp") ?? "0");
|
|
24175
|
+
const sig = c.req.query("sig") ?? "";
|
|
24176
|
+
console.log(`${TAG45} op=request account=${q(accountId)} path=${q(relPath)} head=${headOnly}`);
|
|
24177
|
+
if (!isValidAccountId2(accountId)) {
|
|
24178
|
+
console.error(`${TAG45} op=denied account=${q(accountId)} reason=account-id`);
|
|
24179
|
+
return c.json({ ok: false, error: "denied" }, 401);
|
|
24180
|
+
}
|
|
24181
|
+
const secret = await readAccountSecret(accountId);
|
|
24182
|
+
if (!await verifySignature(secret, accountId, relPath, exp, sig, Date.now())) {
|
|
24183
|
+
console.error(`${TAG45} op=denied account=${q(accountId)} reason=secret`);
|
|
24184
|
+
return c.json({ ok: false, error: "denied" }, 401);
|
|
24185
|
+
}
|
|
24186
|
+
const target = await resolveFetchTarget(resolve30(DATA_ROOT, "accounts", accountId), relPath);
|
|
24187
|
+
if (!target.ok) {
|
|
24188
|
+
const op = target.reason === "enoent" ? "miss" : "denied";
|
|
24189
|
+
console.error(`${TAG45} op=${op} account=${accountId} reason=${target.reason}`);
|
|
24190
|
+
return c.json({ ok: false, error: "not found" }, 404);
|
|
24191
|
+
}
|
|
24192
|
+
if (headOnly) {
|
|
24193
|
+
console.log(`${TAG45} op=served account=${accountId} bytes=${target.sizeBytes} head=true`);
|
|
24194
|
+
return c.body(null, 200);
|
|
24195
|
+
}
|
|
24196
|
+
console.log(`${TAG45} op=served account=${accountId} bytes=${target.sizeBytes}`);
|
|
24197
|
+
return new Response(Readable3.toWeb(createReadStream3(target.absolute)), {
|
|
24198
|
+
status: 200,
|
|
24199
|
+
headers: {
|
|
24200
|
+
"content-type": detectMimeType(target.absolute),
|
|
24201
|
+
// Percent-encoded per RFC 6266, so a name carrying a quote cannot break
|
|
24202
|
+
// out of the form and CR/LF cannot reach the Headers constructor.
|
|
24203
|
+
"content-disposition": `attachment; filename*=UTF-8''${encodeURIComponent(relPath.split("/").pop() ?? "file")}`,
|
|
24204
|
+
"x-content-type-options": "nosniff",
|
|
24205
|
+
// Private client documents: never let a browser or proxy cache one.
|
|
24206
|
+
"cache-control": "private, no-store"
|
|
24207
|
+
}
|
|
24208
|
+
});
|
|
24209
|
+
}
|
|
24210
|
+
app63.get("/fetch", (c) => handle(c, false));
|
|
24211
|
+
app63.on("HEAD", "/fetch", (c) => handle(c, true));
|
|
24212
|
+
var portal_fetch_default = app63;
|
|
24213
|
+
|
|
23782
24214
|
// app/lib/timeentry-census.ts
|
|
23783
|
-
var
|
|
24215
|
+
var TAG46 = "[timeentry-census]";
|
|
23784
24216
|
function reconcileTimeEntries(rows, now, horizonMs) {
|
|
23785
24217
|
const openEntries = rows.openEntries.length;
|
|
23786
24218
|
let oldestOpenAgeMs = 0;
|
|
@@ -23820,12 +24252,12 @@ async function runTimeEntryCensusOnce(session, now, horizonMs) {
|
|
|
23820
24252
|
sumAdjustments: toNum(r.get("sumAdjustments"))
|
|
23821
24253
|
}));
|
|
23822
24254
|
const finding = reconcileTimeEntries({ openEntries, taskSums }, now, horizonMs);
|
|
23823
|
-
const line = `${
|
|
24255
|
+
const line = `${TAG46} openEntries=${finding.openEntries} oldestOpenAgeMin=${finding.oldestOpenAgeMin} maxOpenPerAccount=${finding.maxOpenPerAccount} driftTasks=${finding.driftTasks}`;
|
|
23824
24256
|
if (finding.alarm) console.error(`${line} alarm=true`);
|
|
23825
24257
|
else console.log(line);
|
|
23826
24258
|
return finding;
|
|
23827
24259
|
} catch (err) {
|
|
23828
|
-
console.error(`${
|
|
24260
|
+
console.error(`${TAG46} error="${err instanceof Error ? err.message : String(err)}"`);
|
|
23829
24261
|
return null;
|
|
23830
24262
|
}
|
|
23831
24263
|
}
|
|
@@ -23839,13 +24271,13 @@ function startTimeEntryCensus(openSession, opts = {}) {
|
|
|
23839
24271
|
try {
|
|
23840
24272
|
session = openSession();
|
|
23841
24273
|
} catch (err) {
|
|
23842
|
-
console.error(`${
|
|
24274
|
+
console.error(`${TAG46} open-session-failed error="${err instanceof Error ? err.message : String(err)}"`);
|
|
23843
24275
|
return;
|
|
23844
24276
|
}
|
|
23845
24277
|
try {
|
|
23846
24278
|
await runTimeEntryCensusOnce(session, Date.now(), horizonMs);
|
|
23847
24279
|
} catch (err) {
|
|
23848
|
-
console.error(`${
|
|
24280
|
+
console.error(`${TAG46} tick-failed error="${err instanceof Error ? err.message : String(err)}"`);
|
|
23849
24281
|
} finally {
|
|
23850
24282
|
try {
|
|
23851
24283
|
await session.close();
|
|
@@ -23862,8 +24294,8 @@ function startTimeEntryCensus(openSession, opts = {}) {
|
|
|
23862
24294
|
}
|
|
23863
24295
|
|
|
23864
24296
|
// app/lib/ledger-census.ts
|
|
23865
|
-
import { readdirSync as readdirSync25, readFileSync as
|
|
23866
|
-
import { join as
|
|
24297
|
+
import { readdirSync as readdirSync25, readFileSync as readFileSync40, statSync as statSync15 } from "fs";
|
|
24298
|
+
import { join as join41 } from "path";
|
|
23867
24299
|
|
|
23868
24300
|
// ../lib/ledger-core/dist/reconcile.js
|
|
23869
24301
|
var LEDGER_INDEX_NAMES = [
|
|
@@ -23906,7 +24338,7 @@ function formatCensusLine(f) {
|
|
|
23906
24338
|
}
|
|
23907
24339
|
|
|
23908
24340
|
// app/lib/ledger-census.ts
|
|
23909
|
-
var
|
|
24341
|
+
var TAG47 = "[ledger-census]";
|
|
23910
24342
|
var DAY_MS = 864e5;
|
|
23911
24343
|
function countWriteRejects24h(logDir, now) {
|
|
23912
24344
|
let entries;
|
|
@@ -23917,10 +24349,10 @@ function countWriteRejects24h(logDir, now) {
|
|
|
23917
24349
|
}
|
|
23918
24350
|
let total = 0;
|
|
23919
24351
|
for (const name of entries) {
|
|
23920
|
-
const path3 =
|
|
24352
|
+
const path3 = join41(logDir, name);
|
|
23921
24353
|
try {
|
|
23922
24354
|
if (now - statSync15(path3).mtimeMs > DAY_MS) continue;
|
|
23923
|
-
for (const line of
|
|
24355
|
+
for (const line of readFileSync40(path3, "utf8").split("\n")) {
|
|
23924
24356
|
if (line.includes("[graph-write] reject")) total += 1;
|
|
23925
24357
|
}
|
|
23926
24358
|
} catch {
|
|
@@ -23982,7 +24414,7 @@ async function runLedgerCensusOnce(session, now) {
|
|
|
23982
24414
|
else console.log(line);
|
|
23983
24415
|
return finding;
|
|
23984
24416
|
} catch (err) {
|
|
23985
|
-
console.error(`${
|
|
24417
|
+
console.error(`${TAG47} error="${err instanceof Error ? err.message : String(err)}"`);
|
|
23986
24418
|
return null;
|
|
23987
24419
|
}
|
|
23988
24420
|
}
|
|
@@ -23995,7 +24427,7 @@ function startLedgerCensus(openSession, opts = {}) {
|
|
|
23995
24427
|
session = openSession();
|
|
23996
24428
|
} catch (err) {
|
|
23997
24429
|
console.error(
|
|
23998
|
-
`${
|
|
24430
|
+
`${TAG47} open-session-failed error="${err instanceof Error ? err.message : String(err)}"`
|
|
23999
24431
|
);
|
|
24000
24432
|
return;
|
|
24001
24433
|
}
|
|
@@ -24003,7 +24435,7 @@ function startLedgerCensus(openSession, opts = {}) {
|
|
|
24003
24435
|
await runLedgerCensusOnce(session, Date.now());
|
|
24004
24436
|
} catch (err) {
|
|
24005
24437
|
console.error(
|
|
24006
|
-
`${
|
|
24438
|
+
`${TAG47} tick-failed error="${err instanceof Error ? err.message : String(err)}"`
|
|
24007
24439
|
);
|
|
24008
24440
|
} finally {
|
|
24009
24441
|
try {
|
|
@@ -24021,7 +24453,7 @@ function startLedgerCensus(openSession, opts = {}) {
|
|
|
24021
24453
|
}
|
|
24022
24454
|
|
|
24023
24455
|
// app/lib/conversation-audit.ts
|
|
24024
|
-
var
|
|
24456
|
+
var TAG48 = "[conversation-audit]";
|
|
24025
24457
|
async function runConversationAudit() {
|
|
24026
24458
|
const session = getSession();
|
|
24027
24459
|
try {
|
|
@@ -24047,12 +24479,12 @@ async function runConversationAudit() {
|
|
|
24047
24479
|
unpairable: Number(rec?.get("unpairable") ?? 0)
|
|
24048
24480
|
};
|
|
24049
24481
|
console.error(
|
|
24050
|
-
`${
|
|
24482
|
+
`${TAG48} channel=whatsapp total=${result.total} unreconciled=${result.unreconciled} unpairable=${result.unpairable}`
|
|
24051
24483
|
);
|
|
24052
24484
|
return result;
|
|
24053
24485
|
} catch (err) {
|
|
24054
24486
|
const reason = err instanceof Error ? `${err.name}:${err.message.slice(0, 200)}` : String(err).slice(0, 200);
|
|
24055
|
-
console.error(`${
|
|
24487
|
+
console.error(`${TAG48} op=failed reason=${reason}`);
|
|
24056
24488
|
return null;
|
|
24057
24489
|
} finally {
|
|
24058
24490
|
await session.close();
|
|
@@ -24178,13 +24610,13 @@ function startGraphHealthTimer() {
|
|
|
24178
24610
|
|
|
24179
24611
|
// app/lib/file-watcher.ts
|
|
24180
24612
|
import * as fsp2 from "fs/promises";
|
|
24181
|
-
import { resolve as
|
|
24613
|
+
import { resolve as resolve31, sep as sep9 } from "path";
|
|
24182
24614
|
var ACCOUNT_UUID_RE4 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
24183
24615
|
var DEFAULT_COALESCE_MS = 500;
|
|
24184
24616
|
var ROOTS = ["accounts"];
|
|
24185
24617
|
function _routeEvent(rootName, filename) {
|
|
24186
24618
|
if (!filename) return null;
|
|
24187
|
-
const segs = filename.split(
|
|
24619
|
+
const segs = filename.split(sep9).filter(Boolean);
|
|
24188
24620
|
if (segs.length < 2) return null;
|
|
24189
24621
|
const accountId = segs[0];
|
|
24190
24622
|
if (!ACCOUNT_UUID_RE4.test(accountId)) return null;
|
|
@@ -24197,7 +24629,7 @@ async function startFileWatcher(opts = {}) {
|
|
|
24197
24629
|
const dropFn = opts.drop ?? dropFileIndex;
|
|
24198
24630
|
for (const r of ROOTS) {
|
|
24199
24631
|
try {
|
|
24200
|
-
await fsp2.mkdir(
|
|
24632
|
+
await fsp2.mkdir(resolve31(dataRoot, r), { recursive: true });
|
|
24201
24633
|
} catch (err) {
|
|
24202
24634
|
console.error(
|
|
24203
24635
|
`[file-watcher] start-failed root="${r}" err="${err.message}" \u2014 index will be maintained by the 5-min reconcile backstop only`
|
|
@@ -24218,7 +24650,7 @@ async function startFileWatcher(opts = {}) {
|
|
|
24218
24650
|
timers.set(relativePath, t);
|
|
24219
24651
|
}
|
|
24220
24652
|
async function runHook(relativePath, accountId) {
|
|
24221
|
-
const absolute =
|
|
24653
|
+
const absolute = resolve31(dataRoot, relativePath);
|
|
24222
24654
|
let exists = false;
|
|
24223
24655
|
try {
|
|
24224
24656
|
const st = await fsp2.stat(absolute);
|
|
@@ -24242,7 +24674,7 @@ async function startFileWatcher(opts = {}) {
|
|
|
24242
24674
|
}
|
|
24243
24675
|
}
|
|
24244
24676
|
async function watchRoot(rootName) {
|
|
24245
|
-
const absRoot =
|
|
24677
|
+
const absRoot = resolve31(dataRoot, rootName);
|
|
24246
24678
|
try {
|
|
24247
24679
|
const iter = fsp2.watch(absRoot, { recursive: true, signal: controller.signal });
|
|
24248
24680
|
for await (const event of iter) {
|
|
@@ -24281,7 +24713,7 @@ async function startFileWatcher(opts = {}) {
|
|
|
24281
24713
|
|
|
24282
24714
|
// app/lib/migrate-uploads.ts
|
|
24283
24715
|
import { mkdir as mkdir5, readdir as readdir5, rename as rename2, rm as rm3 } from "fs/promises";
|
|
24284
|
-
import { dirname as dirname13, relative as relative6, resolve as
|
|
24716
|
+
import { dirname as dirname13, relative as relative6, resolve as resolve32 } from "path";
|
|
24285
24717
|
var ACCOUNT_UUID_RE5 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
24286
24718
|
async function walkFiles(dir) {
|
|
24287
24719
|
const out = [];
|
|
@@ -24292,7 +24724,7 @@ async function walkFiles(dir) {
|
|
|
24292
24724
|
return out;
|
|
24293
24725
|
}
|
|
24294
24726
|
for (const e of entries) {
|
|
24295
|
-
const abs =
|
|
24727
|
+
const abs = resolve32(dir, e.name);
|
|
24296
24728
|
if (e.isDirectory()) {
|
|
24297
24729
|
out.push(...await walkFiles(abs));
|
|
24298
24730
|
} else if (e.isFile()) {
|
|
@@ -24312,7 +24744,7 @@ async function relocateTree(srcDir, destDir, dataRoot, accountId, session) {
|
|
|
24312
24744
|
let moved = 0;
|
|
24313
24745
|
for (const oldAbs of files) {
|
|
24314
24746
|
const suffix = relative6(srcDir, oldAbs);
|
|
24315
|
-
const newAbs =
|
|
24747
|
+
const newAbs = resolve32(destDir, suffix);
|
|
24316
24748
|
await mkdir5(dirname13(newAbs), { recursive: true });
|
|
24317
24749
|
await rename2(oldAbs, newAbs);
|
|
24318
24750
|
moved++;
|
|
@@ -24339,7 +24771,7 @@ async function relocateTree(srcDir, destDir, dataRoot, accountId, session) {
|
|
|
24339
24771
|
}
|
|
24340
24772
|
async function migrateUploads(opts = {}) {
|
|
24341
24773
|
const dataRoot = opts.dataRoot ?? DATA_ROOT;
|
|
24342
|
-
const oldRoot =
|
|
24774
|
+
const oldRoot = resolve32(dataRoot, "uploads");
|
|
24343
24775
|
let topEntries;
|
|
24344
24776
|
try {
|
|
24345
24777
|
topEntries = await readdir5(oldRoot, { withFileTypes: true });
|
|
@@ -24360,8 +24792,8 @@ async function migrateUploads(opts = {}) {
|
|
|
24360
24792
|
const name = entry.name;
|
|
24361
24793
|
if (ACCOUNT_UUID_RE5.test(name)) {
|
|
24362
24794
|
moved += await relocateTree(
|
|
24363
|
-
|
|
24364
|
-
|
|
24795
|
+
resolve32(oldRoot, name),
|
|
24796
|
+
resolve32(dataRoot, "accounts", name, "uploads"),
|
|
24365
24797
|
dataRoot,
|
|
24366
24798
|
name,
|
|
24367
24799
|
session
|
|
@@ -24373,8 +24805,8 @@ async function migrateUploads(opts = {}) {
|
|
|
24373
24805
|
continue;
|
|
24374
24806
|
}
|
|
24375
24807
|
moved += await relocateTree(
|
|
24376
|
-
|
|
24377
|
-
|
|
24808
|
+
resolve32(oldRoot, "public"),
|
|
24809
|
+
resolve32(dataRoot, "accounts", installAccountId, "uploads", "public"),
|
|
24378
24810
|
dataRoot,
|
|
24379
24811
|
null,
|
|
24380
24812
|
// public uploads carry no graph nodes
|
|
@@ -24403,9 +24835,9 @@ async function migrateUploads(opts = {}) {
|
|
|
24403
24835
|
}
|
|
24404
24836
|
|
|
24405
24837
|
// app/lib/migrate-admin-webchat-sidecars.ts
|
|
24406
|
-
import { readdir as readdir6, readFile as
|
|
24838
|
+
import { readdir as readdir6, readFile as readFile7, rename as rename3, writeFile as writeFile4, unlink as unlink3 } from "fs/promises";
|
|
24407
24839
|
import { existsSync as existsSync36 } from "fs";
|
|
24408
|
-
import { join as
|
|
24840
|
+
import { join as join42 } from "path";
|
|
24409
24841
|
var ADMIN_ROLE2 = "admin";
|
|
24410
24842
|
var WEBCHAT_CHANNEL2 = "webchat";
|
|
24411
24843
|
var SESSION_META_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\.meta\.json$/i;
|
|
@@ -24413,10 +24845,10 @@ var defaultResolveName = async (_accountId, userId) => {
|
|
|
24413
24845
|
const res = await loadAdminUserName(userId);
|
|
24414
24846
|
return res.source === "neo4j" && res.joined.length > 0 ? res.joined : null;
|
|
24415
24847
|
};
|
|
24416
|
-
async function
|
|
24848
|
+
async function readRaw3(path3) {
|
|
24417
24849
|
let raw;
|
|
24418
24850
|
try {
|
|
24419
|
-
raw = await
|
|
24851
|
+
raw = await readFile7(path3, "utf8");
|
|
24420
24852
|
} catch {
|
|
24421
24853
|
return null;
|
|
24422
24854
|
}
|
|
@@ -24450,7 +24882,7 @@ async function collectLiveSidecars(projectsRoot) {
|
|
|
24450
24882
|
}
|
|
24451
24883
|
for (const slug of slugs) {
|
|
24452
24884
|
if (!slug.isDirectory()) continue;
|
|
24453
|
-
const slugDir =
|
|
24885
|
+
const slugDir = join42(projectsRoot, slug.name);
|
|
24454
24886
|
let entries;
|
|
24455
24887
|
try {
|
|
24456
24888
|
entries = await readdir6(slugDir, { withFileTypes: true });
|
|
@@ -24459,9 +24891,9 @@ async function collectLiveSidecars(projectsRoot) {
|
|
|
24459
24891
|
}
|
|
24460
24892
|
for (const entry of entries) {
|
|
24461
24893
|
if (entry.isFile() && SESSION_META_RE.test(entry.name)) {
|
|
24462
|
-
out.push(
|
|
24894
|
+
out.push(join42(slugDir, entry.name));
|
|
24463
24895
|
} else if (entry.isDirectory() && entry.name === "subagents") {
|
|
24464
|
-
const subDir =
|
|
24896
|
+
const subDir = join42(slugDir, entry.name);
|
|
24465
24897
|
let subs;
|
|
24466
24898
|
try {
|
|
24467
24899
|
subs = await readdir6(subDir, { withFileTypes: true });
|
|
@@ -24469,7 +24901,7 @@ async function collectLiveSidecars(projectsRoot) {
|
|
|
24469
24901
|
continue;
|
|
24470
24902
|
}
|
|
24471
24903
|
for (const s of subs) {
|
|
24472
|
-
if (s.isFile() && SESSION_META_RE.test(s.name)) out.push(
|
|
24904
|
+
if (s.isFile() && SESSION_META_RE.test(s.name)) out.push(join42(subDir, s.name));
|
|
24473
24905
|
}
|
|
24474
24906
|
}
|
|
24475
24907
|
}
|
|
@@ -24481,7 +24913,7 @@ function shortId(path3) {
|
|
|
24481
24913
|
return base.slice(0, 8);
|
|
24482
24914
|
}
|
|
24483
24915
|
async function migrateAdminWebchatSidecars(opts = {}) {
|
|
24484
|
-
const projectsRoot = opts.projectsRoot ?? (process.env.CLAUDE_CONFIG_DIR ?
|
|
24916
|
+
const projectsRoot = opts.projectsRoot ?? (process.env.CLAUDE_CONFIG_DIR ? join42(process.env.CLAUDE_CONFIG_DIR, "projects") : null) ?? "";
|
|
24485
24917
|
const usersFile = opts.usersFile ?? USERS_FILE;
|
|
24486
24918
|
const accountId = opts.accountId ?? resolveAccount()?.accountId ?? null;
|
|
24487
24919
|
const resolveName = opts.resolveName ?? defaultResolveName;
|
|
@@ -24521,7 +24953,7 @@ async function migrateAdminWebchatSidecars(opts = {}) {
|
|
|
24521
24953
|
};
|
|
24522
24954
|
const sidecars = await collectLiveSidecars(projectsRoot);
|
|
24523
24955
|
for (const path3 of sidecars) {
|
|
24524
|
-
const raw = await
|
|
24956
|
+
const raw = await readRaw3(path3);
|
|
24525
24957
|
if (!raw) continue;
|
|
24526
24958
|
if (raw.role !== ADMIN_ROLE2 || raw.channel !== WEBCHAT_CHANNEL2) continue;
|
|
24527
24959
|
result.scanned++;
|
|
@@ -24539,7 +24971,7 @@ async function migrateAdminWebchatSidecars(opts = {}) {
|
|
|
24539
24971
|
await writeRaw(path3, raw);
|
|
24540
24972
|
result.backfilled++;
|
|
24541
24973
|
await resolveAdminName();
|
|
24542
|
-
const back = await
|
|
24974
|
+
const back = await readRaw3(path3);
|
|
24543
24975
|
const wrote = back && back.adminUserId === soleAdminUserId;
|
|
24544
24976
|
if (wrote && resolvedNameForAdmin) {
|
|
24545
24977
|
result.resolvedName++;
|
|
@@ -24826,8 +25258,8 @@ var streamSSE = (c, cb, onError) => {
|
|
|
24826
25258
|
|
|
24827
25259
|
// app/lib/whatsapp/gateway/routes.ts
|
|
24828
25260
|
function createWaChannelRoutes(deps) {
|
|
24829
|
-
const
|
|
24830
|
-
|
|
25261
|
+
const app65 = new Hono();
|
|
25262
|
+
app65.get("/wa-channel/inbound", (c) => {
|
|
24831
25263
|
const senderId = c.req.query("senderId");
|
|
24832
25264
|
if (!senderId) return c.json({ error: "senderId required" }, 400);
|
|
24833
25265
|
return streamSSE(c, async (stream2) => {
|
|
@@ -24835,9 +25267,11 @@ function createWaChannelRoutes(deps) {
|
|
|
24835
25267
|
void stream2.writeSSE({ data: JSON.stringify(payload) });
|
|
24836
25268
|
});
|
|
24837
25269
|
console.error(`[whatsapp-native] op=channel-attached senderId=${senderId}`);
|
|
25270
|
+
deps.onAttached?.(senderId);
|
|
24838
25271
|
stream2.onAbort(() => {
|
|
24839
25272
|
deps.hub.detach(senderId);
|
|
24840
25273
|
console.error(`[whatsapp-native] op=channel-detached senderId=${senderId}`);
|
|
25274
|
+
deps.onDetached?.(senderId);
|
|
24841
25275
|
});
|
|
24842
25276
|
while (!stream2.aborted) {
|
|
24843
25277
|
await stream2.sleep(15e3);
|
|
@@ -24845,7 +25279,7 @@ function createWaChannelRoutes(deps) {
|
|
|
24845
25279
|
}
|
|
24846
25280
|
});
|
|
24847
25281
|
});
|
|
24848
|
-
|
|
25282
|
+
app65.post("/wa-channel/reply", async (c) => {
|
|
24849
25283
|
const body = await c.req.json().catch(() => null);
|
|
24850
25284
|
const senderId = body?.senderId;
|
|
24851
25285
|
const text = body?.text;
|
|
@@ -24866,7 +25300,7 @@ function createWaChannelRoutes(deps) {
|
|
|
24866
25300
|
console.error(`[whatsapp-native] op=reply-dispatch senderId=${senderId} bytes=${bytes}`);
|
|
24867
25301
|
return c.json({ ok: true });
|
|
24868
25302
|
});
|
|
24869
|
-
|
|
25303
|
+
app65.post("/wa-channel/reply-document", async (c) => {
|
|
24870
25304
|
const body = await c.req.json().catch(() => null);
|
|
24871
25305
|
const senderId = body?.senderId;
|
|
24872
25306
|
const files = body?.files;
|
|
@@ -24905,7 +25339,7 @@ function createWaChannelRoutes(deps) {
|
|
|
24905
25339
|
}
|
|
24906
25340
|
return c.json({ ok: true, results });
|
|
24907
25341
|
});
|
|
24908
|
-
|
|
25342
|
+
app65.post("/wa-channel/ready", async (c) => {
|
|
24909
25343
|
const body = await c.req.json().catch(() => null);
|
|
24910
25344
|
const senderId = body?.senderId;
|
|
24911
25345
|
if (typeof senderId !== "string") {
|
|
@@ -24914,7 +25348,7 @@ function createWaChannelRoutes(deps) {
|
|
|
24914
25348
|
deps.onReady?.(senderId);
|
|
24915
25349
|
return c.json({ ok: true });
|
|
24916
25350
|
});
|
|
24917
|
-
|
|
25351
|
+
app65.post("/wa-channel/received", async (c) => {
|
|
24918
25352
|
const body = await c.req.json().catch(() => null);
|
|
24919
25353
|
const senderId = body?.senderId;
|
|
24920
25354
|
const waMessageId = body?.waMessageId;
|
|
@@ -24924,7 +25358,7 @@ function createWaChannelRoutes(deps) {
|
|
|
24924
25358
|
deps.onReceived?.(senderId, waMessageId);
|
|
24925
25359
|
return c.json({ ok: true });
|
|
24926
25360
|
});
|
|
24927
|
-
|
|
25361
|
+
app65.post("/wa-channel/turn-end", async (c) => {
|
|
24928
25362
|
const body = await c.req.json().catch(() => null);
|
|
24929
25363
|
const senderId = body?.senderId;
|
|
24930
25364
|
const sessionId = body?.sessionId;
|
|
@@ -24957,7 +25391,7 @@ function createWaChannelRoutes(deps) {
|
|
|
24957
25391
|
console.error(`[whatsapp-native] op=turn-end sessionId=${sid} replied=no delivered=none`);
|
|
24958
25392
|
return c.json({ ok: true, delivered: "none" });
|
|
24959
25393
|
});
|
|
24960
|
-
return
|
|
25394
|
+
return app65;
|
|
24961
25395
|
}
|
|
24962
25396
|
|
|
24963
25397
|
// app/lib/whatsapp/gateway/wa-gateway.ts
|
|
@@ -24979,9 +25413,30 @@ var WaGateway = class {
|
|
|
24979
25413
|
spawning = /* @__PURE__ */ new Set();
|
|
24980
25414
|
seq = 0;
|
|
24981
25415
|
/** Hono sub-app exposing the loopback channel routes; mount on the UI app. */
|
|
25416
|
+
/** Task 1789 — senderId -> the ms at which its channel server last detached,
|
|
25417
|
+
* cleared on attach. The standing audit reads this; nothing else does. */
|
|
25418
|
+
detachedAt = /* @__PURE__ */ new Map();
|
|
25419
|
+
/** Seat lost. `atMs` is injected so the audit's accounting is testable. */
|
|
25420
|
+
recordDetached(senderId, atMs) {
|
|
25421
|
+
this.detachedAt.set(senderId, atMs);
|
|
25422
|
+
}
|
|
25423
|
+
/** Seat regained — the sender is no longer unseated. */
|
|
25424
|
+
recordAttached(senderId) {
|
|
25425
|
+
this.detachedAt.delete(senderId);
|
|
25426
|
+
}
|
|
25427
|
+
/** Senders with no channel server for longer than `olderThanMs`. */
|
|
25428
|
+
unseatedSenders(nowMs, olderThanMs) {
|
|
25429
|
+
const out = [];
|
|
25430
|
+
for (const [senderId, sinceMs] of this.detachedAt) {
|
|
25431
|
+
if (nowMs - sinceMs > olderThanMs) out.push({ senderId, sinceMs });
|
|
25432
|
+
}
|
|
25433
|
+
return out.sort((a, b) => a.senderId.localeCompare(b.senderId));
|
|
25434
|
+
}
|
|
24982
25435
|
routes() {
|
|
24983
25436
|
return createWaChannelRoutes({
|
|
24984
25437
|
hub: this.hub,
|
|
25438
|
+
onAttached: (senderId) => this.recordAttached(senderId),
|
|
25439
|
+
onDetached: (senderId) => this.recordDetached(senderId, Date.now()),
|
|
24985
25440
|
sendOutbound: (senderId, text) => this.sendOutbound(senderId, text),
|
|
24986
25441
|
sendDocument: (senderId, filePath, caption) => this.sendDocument(senderId, filePath, caption),
|
|
24987
25442
|
onReady: (senderId) => {
|
|
@@ -25092,7 +25547,7 @@ var WaGateway = class {
|
|
|
25092
25547
|
},
|
|
25093
25548
|
Date.now()
|
|
25094
25549
|
);
|
|
25095
|
-
const resolvedSessionId = adminSessionIdFor(
|
|
25550
|
+
const resolvedSessionId = (input.role === "admin" ? this.deps.resolveSessionOverride?.(input.effectiveAccountId, senderId) : null) || adminSessionIdFor(
|
|
25096
25551
|
input.effectiveAccountId,
|
|
25097
25552
|
senderId,
|
|
25098
25553
|
input.role === "public" && input.personId ? input.personId : void 0
|
|
@@ -25160,17 +25615,28 @@ function logDuplicateSenderGroups(groups) {
|
|
|
25160
25615
|
);
|
|
25161
25616
|
}
|
|
25162
25617
|
}
|
|
25618
|
+
function logUnseatedSenders(entries, nowMs) {
|
|
25619
|
+
for (const e of entries) {
|
|
25620
|
+
const secs = Math.round((nowMs - e.sinceMs) / 1e3);
|
|
25621
|
+
console.error(`[whatsapp-native] op=sender-unseated senderId=${e.senderId} since=${new Date(e.sinceMs).toISOString()} forSeconds=${secs}`);
|
|
25622
|
+
}
|
|
25623
|
+
}
|
|
25163
25624
|
|
|
25164
25625
|
// app/lib/whatsapp/gateway/spawn-request.ts
|
|
25165
25626
|
function buildWaSpawnRequest(input) {
|
|
25166
25627
|
const personId = input.role === "public" && input.personId ? input.personId : void 0;
|
|
25167
25628
|
return {
|
|
25168
|
-
// Task 1746 — the deterministic per-sender id is the
|
|
25169
|
-
// session
|
|
25170
|
-
//
|
|
25171
|
-
//
|
|
25172
|
-
//
|
|
25173
|
-
|
|
25629
|
+
// Task 1746 — the deterministic per-sender id is the DEFAULT id a sender's
|
|
25630
|
+
// session has. Task 1526's per-firing seat override used to replace it on a
|
|
25631
|
+
// scheduled cold start, which spawned a second session per firing; that
|
|
25632
|
+
// session then attached under the sender's own hub key and swallowed their
|
|
25633
|
+
// real messages until it detached, splitting the conversation in two.
|
|
25634
|
+
//
|
|
25635
|
+
// Task 1789's override is not that. It is a stable per-sender forward
|
|
25636
|
+
// pointer written once by an operator re-seat and resolving identically for
|
|
25637
|
+
// every subsequent inbound, so it can never yield a second id for a sender.
|
|
25638
|
+
// Do not remove it without reading server/channel-session-override.ts.
|
|
25639
|
+
sessionId: input.overrideSessionId || adminSessionIdFor(input.accountId, input.senderId, personId),
|
|
25174
25640
|
role: input.role,
|
|
25175
25641
|
channel: "whatsapp",
|
|
25176
25642
|
personId,
|
|
@@ -25354,8 +25820,8 @@ var import_dist7 = __toESM(require_dist(), 1);
|
|
|
25354
25820
|
|
|
25355
25821
|
// app/lib/webchat/gateway/routes.ts
|
|
25356
25822
|
function createWebchatChannelRoutes(deps) {
|
|
25357
|
-
const
|
|
25358
|
-
|
|
25823
|
+
const app65 = new Hono();
|
|
25824
|
+
app65.get("/webchat-channel/inbound", (c) => {
|
|
25359
25825
|
const key = c.req.query("key");
|
|
25360
25826
|
if (!key) return c.json({ error: "key required" }, 400);
|
|
25361
25827
|
return streamSSE(c, async (stream2) => {
|
|
@@ -25373,7 +25839,7 @@ function createWebchatChannelRoutes(deps) {
|
|
|
25373
25839
|
}
|
|
25374
25840
|
});
|
|
25375
25841
|
});
|
|
25376
|
-
|
|
25842
|
+
app65.post("/webchat-channel/reply", async (c) => {
|
|
25377
25843
|
const body = await c.req.json().catch(() => null);
|
|
25378
25844
|
const key = body?.key;
|
|
25379
25845
|
const text = body?.text;
|
|
@@ -25383,7 +25849,7 @@ function createWebchatChannelRoutes(deps) {
|
|
|
25383
25849
|
deps.onReply?.(key, text);
|
|
25384
25850
|
return c.json({ ok: true });
|
|
25385
25851
|
});
|
|
25386
|
-
|
|
25852
|
+
app65.post("/webchat-channel/connector-auth", async (c) => {
|
|
25387
25853
|
const body = await c.req.json().catch(() => null);
|
|
25388
25854
|
const key = body?.key;
|
|
25389
25855
|
const sessionId = body?.sessionId;
|
|
@@ -25396,7 +25862,7 @@ function createWebchatChannelRoutes(deps) {
|
|
|
25396
25862
|
const result = await deps.onConnectorAuth({ key, sessionId, name, completed });
|
|
25397
25863
|
return c.json(result);
|
|
25398
25864
|
});
|
|
25399
|
-
|
|
25865
|
+
app65.post("/webchat-channel/ready", async (c) => {
|
|
25400
25866
|
const body = await c.req.json().catch(() => null);
|
|
25401
25867
|
const key = body?.key;
|
|
25402
25868
|
if (typeof key !== "string") {
|
|
@@ -25405,7 +25871,7 @@ function createWebchatChannelRoutes(deps) {
|
|
|
25405
25871
|
deps.onReady?.(key);
|
|
25406
25872
|
return c.json({ ok: true });
|
|
25407
25873
|
});
|
|
25408
|
-
|
|
25874
|
+
app65.post("/webchat-channel/permission-request", async (c) => {
|
|
25409
25875
|
const body = await c.req.json().catch(() => null);
|
|
25410
25876
|
const key = body?.key;
|
|
25411
25877
|
const requestId = body?.request_id;
|
|
@@ -25419,7 +25885,7 @@ function createWebchatChannelRoutes(deps) {
|
|
|
25419
25885
|
const verdict = await deps.awaitPermissionVerdict({ key, requestId, toolName, description, inputPreview });
|
|
25420
25886
|
return c.json(verdict);
|
|
25421
25887
|
});
|
|
25422
|
-
|
|
25888
|
+
app65.post("/webchat-channel/received", async (c) => {
|
|
25423
25889
|
const body = await c.req.json().catch(() => null);
|
|
25424
25890
|
const key = body?.key;
|
|
25425
25891
|
const messageId = body?.messageId;
|
|
@@ -25429,7 +25895,7 @@ function createWebchatChannelRoutes(deps) {
|
|
|
25429
25895
|
deps.onReceived?.(key, messageId);
|
|
25430
25896
|
return c.json({ ok: true });
|
|
25431
25897
|
});
|
|
25432
|
-
|
|
25898
|
+
app65.post("/webchat-channel/turn-end", async (c) => {
|
|
25433
25899
|
const body = await c.req.json().catch(() => null);
|
|
25434
25900
|
const key = body?.key;
|
|
25435
25901
|
const sessionId = body?.sessionId;
|
|
@@ -25452,7 +25918,7 @@ function createWebchatChannelRoutes(deps) {
|
|
|
25452
25918
|
console.error(`[webchat-native] op=turn-end channel=webchat key=${k} sessionId=${sid} replied=no delivered=none`);
|
|
25453
25919
|
return c.json({ ok: true, delivered: "none" });
|
|
25454
25920
|
});
|
|
25455
|
-
return
|
|
25921
|
+
return app65;
|
|
25456
25922
|
}
|
|
25457
25923
|
|
|
25458
25924
|
// app/lib/webchat/gateway/webchat-gateway.ts
|
|
@@ -25510,16 +25976,16 @@ var WebchatGateway = class _WebchatGateway {
|
|
|
25510
25976
|
* The public /api/chat route awaits this to return the reply on the same SSE
|
|
25511
25977
|
* response, preserving the pre-756 POST→single-blob browser contract. */
|
|
25512
25978
|
awaitReply(key, timeoutMs) {
|
|
25513
|
-
return new Promise((
|
|
25979
|
+
return new Promise((resolve38) => {
|
|
25514
25980
|
const timer = setTimeout(() => {
|
|
25515
25981
|
this.replyAwaiters.delete(key);
|
|
25516
|
-
|
|
25982
|
+
resolve38({ timeout: true });
|
|
25517
25983
|
}, timeoutMs);
|
|
25518
25984
|
if (timer.unref) timer.unref();
|
|
25519
25985
|
this.replyAwaiters.set(key, (r) => {
|
|
25520
25986
|
clearTimeout(timer);
|
|
25521
25987
|
this.replyAwaiters.delete(key);
|
|
25522
|
-
|
|
25988
|
+
resolve38(r);
|
|
25523
25989
|
});
|
|
25524
25990
|
});
|
|
25525
25991
|
}
|
|
@@ -25530,11 +25996,11 @@ var WebchatGateway = class _WebchatGateway {
|
|
|
25530
25996
|
* terminal dialog stays answerable). */
|
|
25531
25997
|
awaitPermissionVerdict(p, timeoutMs) {
|
|
25532
25998
|
const k = _WebchatGateway.promptKey(p.key, p.requestId);
|
|
25533
|
-
return new Promise((
|
|
25999
|
+
return new Promise((resolve38) => {
|
|
25534
26000
|
this.pendingPrompts.get(k)?.resolve({ timeout: true });
|
|
25535
26001
|
const timer = setTimeout(() => {
|
|
25536
26002
|
this.pendingPrompts.delete(k);
|
|
25537
|
-
|
|
26003
|
+
resolve38({ timeout: true });
|
|
25538
26004
|
}, timeoutMs);
|
|
25539
26005
|
if (timer.unref) timer.unref();
|
|
25540
26006
|
this.pendingPrompts.set(k, {
|
|
@@ -25543,7 +26009,7 @@ var WebchatGateway = class _WebchatGateway {
|
|
|
25543
26009
|
resolve: (r) => {
|
|
25544
26010
|
clearTimeout(timer);
|
|
25545
26011
|
this.pendingPrompts.delete(k);
|
|
25546
|
-
|
|
26012
|
+
resolve38(r);
|
|
25547
26013
|
}
|
|
25548
26014
|
});
|
|
25549
26015
|
console.error(`[webchat:perm] op=open key=${keyDisplay(p.key)} id=${p.requestId} tool=${p.toolName}`);
|
|
@@ -26108,7 +26574,7 @@ async function fanOut(subscribers, text, onError, tag) {
|
|
|
26108
26574
|
|
|
26109
26575
|
// app/lib/webchat/file-delivery.ts
|
|
26110
26576
|
import { realpathSync as realpathSync8 } from "fs";
|
|
26111
|
-
import { resolve as
|
|
26577
|
+
import { resolve as resolve33 } from "path";
|
|
26112
26578
|
|
|
26113
26579
|
// app/lib/channel-pty-bridge/file-delivery.ts
|
|
26114
26580
|
var SEND_USER_FILE = "SendUserFile";
|
|
@@ -26199,29 +26665,29 @@ function makeFileDelivery(opts) {
|
|
|
26199
26665
|
}
|
|
26200
26666
|
|
|
26201
26667
|
// app/lib/webchat/file-delivery.ts
|
|
26202
|
-
var
|
|
26668
|
+
var TAG49 = "[webchat-adaptor]";
|
|
26203
26669
|
function platformRoot2() {
|
|
26204
26670
|
return process.env.MAXY_PLATFORM_ROOT || "";
|
|
26205
26671
|
}
|
|
26206
26672
|
function makeWebchatSendFile(entry) {
|
|
26207
26673
|
return async (filePath) => {
|
|
26208
26674
|
if (!entry.accountId) {
|
|
26209
|
-
console.error(`${
|
|
26675
|
+
console.error(`${TAG49} file-delivery reject reason=no-account sender=${entry.senderId}`);
|
|
26210
26676
|
return { ok: false, error: "no-account" };
|
|
26211
26677
|
}
|
|
26212
|
-
const accountDir =
|
|
26678
|
+
const accountDir = resolve33(platformRoot2(), "..", "data/accounts", entry.accountId);
|
|
26213
26679
|
try {
|
|
26214
26680
|
const resolved = realpathSync8(filePath);
|
|
26215
26681
|
const accountResolved = realpathSync8(accountDir);
|
|
26216
26682
|
if (!resolved.startsWith(accountResolved + "/")) {
|
|
26217
|
-
console.error(`${
|
|
26683
|
+
console.error(`${TAG49} file-delivery reject reason=outside_account_directory sender=${entry.senderId}`);
|
|
26218
26684
|
return { ok: false, error: "outside-account" };
|
|
26219
26685
|
}
|
|
26220
26686
|
return { ok: true };
|
|
26221
26687
|
} catch (err) {
|
|
26222
26688
|
const code = err.code;
|
|
26223
26689
|
console.error(
|
|
26224
|
-
`${
|
|
26690
|
+
`${TAG49} file-delivery reject reason=${code === "ENOENT" ? "not-found" : "path-error"} sender=${entry.senderId}`
|
|
26225
26691
|
);
|
|
26226
26692
|
return { ok: false, error: code === "ENOENT" ? "not-found" : "path-error" };
|
|
26227
26693
|
}
|
|
@@ -26230,7 +26696,7 @@ function makeWebchatSendFile(entry) {
|
|
|
26230
26696
|
function makeWebchatFileDelivery(entry) {
|
|
26231
26697
|
return makeFileDelivery({
|
|
26232
26698
|
entry,
|
|
26233
|
-
tag:
|
|
26699
|
+
tag: TAG49,
|
|
26234
26700
|
channel: "webchat",
|
|
26235
26701
|
sendFile: makeWebchatSendFile(entry),
|
|
26236
26702
|
deferUntilVerdict: true
|
|
@@ -26292,7 +26758,7 @@ function buildPublicWebchatSpawnRequest(input) {
|
|
|
26292
26758
|
}
|
|
26293
26759
|
|
|
26294
26760
|
// app/lib/whatsapp/inbound/file-delivery-bridge.ts
|
|
26295
|
-
var
|
|
26761
|
+
var TAG50 = "[whatsapp-adaptor]";
|
|
26296
26762
|
var SEND_USER_FILE2 = "SendUserFile";
|
|
26297
26763
|
var WHATSAPP_SEND_DOCUMENT = "whatsapp-send-document";
|
|
26298
26764
|
function platformRoot3() {
|
|
@@ -26310,7 +26776,7 @@ function makeWhatsAppSendFile(entry, maxyAccountId) {
|
|
|
26310
26776
|
});
|
|
26311
26777
|
if (result.ok) return { ok: true };
|
|
26312
26778
|
console.error(
|
|
26313
|
-
`${
|
|
26779
|
+
`${TAG50} file-delivery reject reason=send-failed sender=${entry.senderId} status=${result.status} message=${result.error}`
|
|
26314
26780
|
);
|
|
26315
26781
|
return { ok: false, error: result.error };
|
|
26316
26782
|
};
|
|
@@ -26318,7 +26784,7 @@ function makeWhatsAppSendFile(entry, maxyAccountId) {
|
|
|
26318
26784
|
function makeWhatsAppFileDelivery(entry, maxyAccountId) {
|
|
26319
26785
|
const shared = makeFileDelivery({
|
|
26320
26786
|
entry,
|
|
26321
|
-
tag:
|
|
26787
|
+
tag: TAG50,
|
|
26322
26788
|
channel: "whatsapp",
|
|
26323
26789
|
sendFile: makeWhatsAppSendFile(entry, maxyAccountId)
|
|
26324
26790
|
});
|
|
@@ -26353,7 +26819,7 @@ function makeWhatsAppFileDelivery(entry, maxyAccountId) {
|
|
|
26353
26819
|
if (!delivered) {
|
|
26354
26820
|
const fileField = call.filePath !== void 0 ? ` file=${call.filePath}` : "";
|
|
26355
26821
|
console.error(
|
|
26356
|
-
`${
|
|
26822
|
+
`${TAG50} file-delivery-unreconciled sender=${entry.senderId} sessionId=${sid} tool=${WHATSAPP_SEND_DOCUMENT}${fileField}`
|
|
26357
26823
|
);
|
|
26358
26824
|
}
|
|
26359
26825
|
}
|
|
@@ -26539,8 +27005,8 @@ var import_dist8 = __toESM(require_dist(), 1);
|
|
|
26539
27005
|
|
|
26540
27006
|
// app/lib/telegram/gateway/routes.ts
|
|
26541
27007
|
function createTelegramChannelRoutes(deps) {
|
|
26542
|
-
const
|
|
26543
|
-
|
|
27008
|
+
const app65 = new Hono();
|
|
27009
|
+
app65.get("/tg-channel/inbound", (c) => {
|
|
26544
27010
|
const key = c.req.query("key");
|
|
26545
27011
|
if (!key) return c.json({ error: "key required" }, 400);
|
|
26546
27012
|
return streamSSE(c, async (stream2) => {
|
|
@@ -26558,7 +27024,7 @@ function createTelegramChannelRoutes(deps) {
|
|
|
26558
27024
|
}
|
|
26559
27025
|
});
|
|
26560
27026
|
});
|
|
26561
|
-
|
|
27027
|
+
app65.post("/tg-channel/reply", async (c) => {
|
|
26562
27028
|
const body = await c.req.json().catch(() => null);
|
|
26563
27029
|
const key = body?.key;
|
|
26564
27030
|
const text = body?.text;
|
|
@@ -26574,7 +27040,7 @@ function createTelegramChannelRoutes(deps) {
|
|
|
26574
27040
|
console.error(`[telegram-native] op=reply key=${key} bytes=${Buffer.byteLength(text, "utf8")}`);
|
|
26575
27041
|
return c.json({ ok: true });
|
|
26576
27042
|
});
|
|
26577
|
-
|
|
27043
|
+
app65.post("/tg-channel/ready", async (c) => {
|
|
26578
27044
|
const body = await c.req.json().catch(() => null);
|
|
26579
27045
|
const key = body?.key;
|
|
26580
27046
|
if (typeof key !== "string") {
|
|
@@ -26583,7 +27049,7 @@ function createTelegramChannelRoutes(deps) {
|
|
|
26583
27049
|
deps.onReady?.(key);
|
|
26584
27050
|
return c.json({ ok: true });
|
|
26585
27051
|
});
|
|
26586
|
-
|
|
27052
|
+
app65.post("/tg-channel/received", async (c) => {
|
|
26587
27053
|
const body = await c.req.json().catch(() => null);
|
|
26588
27054
|
const key = body?.key;
|
|
26589
27055
|
const messageId = body?.messageId;
|
|
@@ -26593,7 +27059,7 @@ function createTelegramChannelRoutes(deps) {
|
|
|
26593
27059
|
deps.onReceived?.(key, messageId);
|
|
26594
27060
|
return c.json({ ok: true });
|
|
26595
27061
|
});
|
|
26596
|
-
|
|
27062
|
+
app65.post("/tg-channel/turn-end", async (c) => {
|
|
26597
27063
|
const body = await c.req.json().catch(() => null);
|
|
26598
27064
|
const key = body?.key;
|
|
26599
27065
|
const sessionId = body?.sessionId;
|
|
@@ -26626,7 +27092,7 @@ function createTelegramChannelRoutes(deps) {
|
|
|
26626
27092
|
console.error(`[telegram-native] op=turn-end key=${key} sessionId=${sid} replied=no delivered=none`);
|
|
26627
27093
|
return c.json({ ok: true, delivered: "none" });
|
|
26628
27094
|
});
|
|
26629
|
-
return
|
|
27095
|
+
return app65;
|
|
26630
27096
|
}
|
|
26631
27097
|
|
|
26632
27098
|
// app/lib/telegram/gateway/telegram-gateway.ts
|
|
@@ -26717,7 +27183,7 @@ var TelegramGateway = class {
|
|
|
26717
27183
|
{ key: input.senderId, text: input.text, messageId: `tg-${++this.seq}`, source, standingRules, scheduleProvenance: input.scheduleProvenance },
|
|
26718
27184
|
Date.now()
|
|
26719
27185
|
);
|
|
26720
|
-
const resolvedSessionId = adminSessionIdFor(
|
|
27186
|
+
const resolvedSessionId = (input.role === "admin" ? this.deps.resolveSessionOverride?.(input.accountId, input.senderId) : null) || adminSessionIdFor(
|
|
26721
27187
|
input.accountId,
|
|
26722
27188
|
input.senderId,
|
|
26723
27189
|
input.role === "public" && input.personId ? input.personId : void 0
|
|
@@ -26754,10 +27220,11 @@ var TelegramGateway = class {
|
|
|
26754
27220
|
function buildTelegramSpawnRequest(input) {
|
|
26755
27221
|
const personId = input.role === "public" && input.personId ? input.personId : void 0;
|
|
26756
27222
|
return {
|
|
26757
|
-
// Task 1746 — the deterministic per-sender id is the
|
|
26758
|
-
//
|
|
26759
|
-
//
|
|
26760
|
-
|
|
27223
|
+
// Task 1746 / 1789 — the deterministic per-sender id is the DEFAULT; an
|
|
27224
|
+
// operator re-seat sets a stable per-sender forward pointer that wins here.
|
|
27225
|
+
// Mirror of the WhatsApp builder; see its comment for the full distinction
|
|
27226
|
+
// between this pointer and Task 1526's removed per-firing seat.
|
|
27227
|
+
sessionId: input.overrideSessionId || adminSessionIdFor(input.accountId, input.senderId, personId),
|
|
26761
27228
|
role: input.role,
|
|
26762
27229
|
channel: "telegram",
|
|
26763
27230
|
personId,
|
|
@@ -26774,9 +27241,9 @@ function buildTelegramSpawnRequest(input) {
|
|
|
26774
27241
|
|
|
26775
27242
|
// app/lib/telegram/outbound/send-document.ts
|
|
26776
27243
|
import { realpathSync as realpathSync9 } from "fs";
|
|
26777
|
-
import { readFile as
|
|
26778
|
-
import { resolve as
|
|
26779
|
-
var
|
|
27244
|
+
import { readFile as readFile8, stat as stat8 } from "fs/promises";
|
|
27245
|
+
import { resolve as resolve34, basename as basename11 } from "path";
|
|
27246
|
+
var TAG51 = "[telegram:outbound]";
|
|
26780
27247
|
var TELEGRAM_DOCUMENT_MAX_BYTES = 50 * 1024 * 1024;
|
|
26781
27248
|
async function sendTelegramDocument(input) {
|
|
26782
27249
|
const { botToken, chatId, filePath, caption, maxyAccountId, platformRoot: platformRoot5 } = input;
|
|
@@ -26786,25 +27253,25 @@ async function sendTelegramDocument(input) {
|
|
|
26786
27253
|
if (!maxyAccountId || !platformRoot5) {
|
|
26787
27254
|
return { ok: false, status: 400, error: "Cannot validate file path: missing account or platform context" };
|
|
26788
27255
|
}
|
|
26789
|
-
const accountDir =
|
|
27256
|
+
const accountDir = resolve34(platformRoot5, "..", "data/accounts", maxyAccountId);
|
|
26790
27257
|
let resolvedPath;
|
|
26791
27258
|
try {
|
|
26792
27259
|
resolvedPath = realpathSync9(filePath);
|
|
26793
27260
|
const accountResolved = realpathSync9(accountDir);
|
|
26794
27261
|
if (!resolvedPath.startsWith(accountResolved + "/")) {
|
|
26795
|
-
console.error(`${
|
|
27262
|
+
console.error(`${TAG51} document REJECTED reason=outside_account_directory maxyAccountId=${maxyAccountId}`);
|
|
26796
27263
|
return { ok: false, status: 403, error: "Access denied: file is outside the account directory" };
|
|
26797
27264
|
}
|
|
26798
27265
|
} catch (err) {
|
|
26799
27266
|
const code = err.code;
|
|
26800
27267
|
if (code === "ENOENT") {
|
|
26801
|
-
console.error(`${
|
|
27268
|
+
console.error(`${TAG51} document ENOENT path=${filePath}`);
|
|
26802
27269
|
return { ok: false, status: 404, error: `File not found: ${filePath}` };
|
|
26803
27270
|
}
|
|
26804
|
-
console.error(`${
|
|
27271
|
+
console.error(`${TAG51} document path error: ${String(err)}`);
|
|
26805
27272
|
return { ok: false, status: 500, error: String(err) };
|
|
26806
27273
|
}
|
|
26807
|
-
const fileStat = await
|
|
27274
|
+
const fileStat = await stat8(resolvedPath);
|
|
26808
27275
|
if (fileStat.size > TELEGRAM_DOCUMENT_MAX_BYTES) {
|
|
26809
27276
|
return {
|
|
26810
27277
|
ok: false,
|
|
@@ -26813,7 +27280,7 @@ async function sendTelegramDocument(input) {
|
|
|
26813
27280
|
};
|
|
26814
27281
|
}
|
|
26815
27282
|
const filename = basename11(resolvedPath);
|
|
26816
|
-
const buffer = Buffer.from(await
|
|
27283
|
+
const buffer = Buffer.from(await readFile8(resolvedPath));
|
|
26817
27284
|
const form = new FormData();
|
|
26818
27285
|
form.append("chat_id", String(chatId));
|
|
26819
27286
|
if (caption) form.append("caption", caption);
|
|
@@ -26834,13 +27301,13 @@ async function sendTelegramDocument(input) {
|
|
|
26834
27301
|
error = e instanceof Error ? e.message : String(e);
|
|
26835
27302
|
}
|
|
26836
27303
|
console.error(
|
|
26837
|
-
`${
|
|
27304
|
+
`${TAG51} sent document to=${chatId} file=${filename} bytes=${fileStat.size} ok=${ok}` + (messageId !== void 0 ? ` id=${messageId}` : "")
|
|
26838
27305
|
);
|
|
26839
27306
|
return ok ? { ok: true, messageId } : { ok: false, status: 500, error };
|
|
26840
27307
|
}
|
|
26841
27308
|
|
|
26842
27309
|
// app/lib/telegram/outbound/file-delivery.ts
|
|
26843
|
-
var
|
|
27310
|
+
var TAG52 = "[telegram:outbound]";
|
|
26844
27311
|
function platformRoot4() {
|
|
26845
27312
|
return process.env.MAXY_PLATFORM_ROOT || "";
|
|
26846
27313
|
}
|
|
@@ -26852,11 +27319,11 @@ function makeTelegramSendFile(entry) {
|
|
|
26852
27319
|
botToken = (entry.role === "admin" ? tg?.adminBotToken : tg?.publicBotToken) ?? null;
|
|
26853
27320
|
}
|
|
26854
27321
|
if (!botToken) {
|
|
26855
|
-
console.error(`${
|
|
27322
|
+
console.error(`${TAG52} file-delivery reject reason=no-bot-token sender=${entry.senderId} role=${entry.role}`);
|
|
26856
27323
|
return { ok: false, error: "no-bot-token" };
|
|
26857
27324
|
}
|
|
26858
27325
|
if (entry.replyTarget == null) {
|
|
26859
|
-
console.error(`${
|
|
27326
|
+
console.error(`${TAG52} file-delivery reject reason=no-reply-target sender=${entry.senderId} role=${entry.role}`);
|
|
26860
27327
|
return { ok: false, error: "no-reply-target" };
|
|
26861
27328
|
}
|
|
26862
27329
|
const result = await sendTelegramDocument({
|
|
@@ -26873,7 +27340,7 @@ function makeTelegramSendFile(entry) {
|
|
|
26873
27340
|
function makeTelegramFileDelivery(entry) {
|
|
26874
27341
|
return makeFileDelivery({
|
|
26875
27342
|
entry,
|
|
26876
|
-
tag:
|
|
27343
|
+
tag: TAG52,
|
|
26877
27344
|
channel: "telegram",
|
|
26878
27345
|
sendFile: makeTelegramSendFile(entry)
|
|
26879
27346
|
});
|
|
@@ -26913,17 +27380,17 @@ function startTelegramNativeFileFollower(input) {
|
|
|
26913
27380
|
}
|
|
26914
27381
|
|
|
26915
27382
|
// server/telegram-descriptor.ts
|
|
26916
|
-
import { resolve as
|
|
27383
|
+
import { resolve as resolve35, join as join43 } from "path";
|
|
26917
27384
|
function telegramGatewayUrl() {
|
|
26918
27385
|
return `http://127.0.0.1:${process.env.MAXY_UI_INTERNAL_PORT ?? ""}`;
|
|
26919
27386
|
}
|
|
26920
27387
|
function telegramServerPath() {
|
|
26921
|
-
return process.env.MAXY_TELEGRAM_CHANNEL_SERVER_PATH ??
|
|
27388
|
+
return process.env.MAXY_TELEGRAM_CHANNEL_SERVER_PATH ?? resolve35(process.env.MAXY_PLATFORM_ROOT ?? join43(__dirname, ".."), "services/telegram-channel/dist/server.js");
|
|
26922
27389
|
}
|
|
26923
27390
|
|
|
26924
27391
|
// app/lib/channel-pty-bridge/public-session-end-review.ts
|
|
26925
27392
|
import { randomUUID as randomUUID16 } from "crypto";
|
|
26926
|
-
var
|
|
27393
|
+
var TAG53 = "[public-session-review]";
|
|
26927
27394
|
var CONSUMED_CAP = 500;
|
|
26928
27395
|
var consumed = /* @__PURE__ */ new Set();
|
|
26929
27396
|
function consumeOnce(sessionId) {
|
|
@@ -26950,7 +27417,7 @@ async function fetchJsonl(sessionId) {
|
|
|
26950
27417
|
return await res.text();
|
|
26951
27418
|
} catch (err) {
|
|
26952
27419
|
console.error(
|
|
26953
|
-
`${
|
|
27420
|
+
`${TAG53} jsonl-fetch-failed sessionId=${sessionId} err="${err instanceof Error ? err.message : String(err)}"`
|
|
26954
27421
|
);
|
|
26955
27422
|
return null;
|
|
26956
27423
|
}
|
|
@@ -26974,7 +27441,7 @@ async function fetchPriorWrites(sliceToken, accountId) {
|
|
|
26974
27441
|
const res = await fetch(url);
|
|
26975
27442
|
if (!res.ok) {
|
|
26976
27443
|
console.error(
|
|
26977
|
-
`${
|
|
27444
|
+
`${TAG53} prior-writes-fetch-failed sliceToken=${sliceToken.slice(0, 8)} status=${res.status}`
|
|
26978
27445
|
);
|
|
26979
27446
|
return [];
|
|
26980
27447
|
}
|
|
@@ -26982,7 +27449,7 @@ async function fetchPriorWrites(sliceToken, accountId) {
|
|
|
26982
27449
|
return Array.isArray(body.writes) ? body.writes : [];
|
|
26983
27450
|
} catch (err) {
|
|
26984
27451
|
console.error(
|
|
26985
|
-
`${
|
|
27452
|
+
`${TAG53} prior-writes-fetch-threw sliceToken=${sliceToken.slice(0, 8)} err="${err instanceof Error ? err.message : String(err)}"`
|
|
26986
27453
|
);
|
|
26987
27454
|
return [];
|
|
26988
27455
|
}
|
|
@@ -27011,7 +27478,7 @@ function composeInitialMessage(input) {
|
|
|
27011
27478
|
}
|
|
27012
27479
|
async function dispatchReviewer(input, initialMessage) {
|
|
27013
27480
|
const sessionId = randomUUID16();
|
|
27014
|
-
console.log(`${
|
|
27481
|
+
console.log(`${TAG53} route target=rc-spawn sessionId=${sessionId.slice(0, 8)} sourceSession=${input.sessionId.slice(0, 8)}`);
|
|
27015
27482
|
const spawned = await managerRcSpawn({
|
|
27016
27483
|
sessionId,
|
|
27017
27484
|
initialMessage,
|
|
@@ -27031,21 +27498,21 @@ async function firePublicSessionEndReview(input) {
|
|
|
27031
27498
|
const dispatchedAt = Date.now();
|
|
27032
27499
|
if (consumed.has(input.sessionId)) {
|
|
27033
27500
|
console.log(
|
|
27034
|
-
`${
|
|
27501
|
+
`${TAG53} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-already-reviewed`
|
|
27035
27502
|
);
|
|
27036
27503
|
return;
|
|
27037
27504
|
}
|
|
27038
27505
|
const jsonl = await fetchJsonl(input.sessionId);
|
|
27039
27506
|
if (!jsonl) {
|
|
27040
27507
|
console.log(
|
|
27041
|
-
`${
|
|
27508
|
+
`${TAG53} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-jsonl-missing`
|
|
27042
27509
|
);
|
|
27043
27510
|
return;
|
|
27044
27511
|
}
|
|
27045
27512
|
const operatorTurns = countOperatorTurns(jsonl);
|
|
27046
27513
|
if (operatorTurns === 0) {
|
|
27047
27514
|
console.log(
|
|
27048
|
-
`${
|
|
27515
|
+
`${TAG53} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-no-turns`
|
|
27049
27516
|
);
|
|
27050
27517
|
return;
|
|
27051
27518
|
}
|
|
@@ -27059,19 +27526,19 @@ async function firePublicSessionEndReview(input) {
|
|
|
27059
27526
|
});
|
|
27060
27527
|
if (!consumeOnce(input.sessionId)) {
|
|
27061
27528
|
console.log(
|
|
27062
|
-
`${
|
|
27529
|
+
`${TAG53} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-already-reviewed`
|
|
27063
27530
|
);
|
|
27064
27531
|
return;
|
|
27065
27532
|
}
|
|
27066
27533
|
const dispatched = await dispatchReviewer(input, initialMessage);
|
|
27067
27534
|
if (!dispatched.ok) {
|
|
27068
27535
|
console.error(
|
|
27069
|
-
`${
|
|
27536
|
+
`${TAG53} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-spawn-failed reason=${dispatched.reason}`
|
|
27070
27537
|
);
|
|
27071
27538
|
return;
|
|
27072
27539
|
}
|
|
27073
27540
|
console.log(
|
|
27074
|
-
`${
|
|
27541
|
+
`${TAG53} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=dispatched managerSessionId=${dispatched.managerSessionId} operatorTurns=${operatorTurns} priorWrites=${priorWrites.length} ms=${Date.now() - dispatchedAt}`
|
|
27075
27542
|
);
|
|
27076
27543
|
}
|
|
27077
27544
|
|
|
@@ -27205,8 +27672,8 @@ function broadcastAdminShutdown(reason) {
|
|
|
27205
27672
|
|
|
27206
27673
|
// ../lib/entitlement/src/index.ts
|
|
27207
27674
|
import { createPublicKey, createHash as createHash6, verify as cryptoVerify } from "crypto";
|
|
27208
|
-
import { existsSync as existsSync37, readFileSync as
|
|
27209
|
-
import { resolve as
|
|
27675
|
+
import { existsSync as existsSync37, readFileSync as readFileSync41, statSync as statSync16 } from "fs";
|
|
27676
|
+
import { resolve as resolve36 } from "path";
|
|
27210
27677
|
|
|
27211
27678
|
// ../lib/entitlement/src/canonicalize.ts
|
|
27212
27679
|
function canonicalize(value) {
|
|
@@ -27241,7 +27708,7 @@ var PUBKEY_SHA256 = "8eee6bcb33545fd13b16d3199a5735ca5db5062834c7b49dfe4f23801d9
|
|
|
27241
27708
|
var GRACE_DAYS = 7;
|
|
27242
27709
|
var GRACE_MS = GRACE_DAYS * 24 * 60 * 60 * 1e3;
|
|
27243
27710
|
function pubkeyPath(brand) {
|
|
27244
|
-
return
|
|
27711
|
+
return resolve36(brand.platformRoot, "lib", "entitlement", "rubytech-pubkey.pem");
|
|
27245
27712
|
}
|
|
27246
27713
|
var memo = null;
|
|
27247
27714
|
function memoKey(mtimeMs, account) {
|
|
@@ -27253,12 +27720,12 @@ function resolveEntitlement(brand, account) {
|
|
|
27253
27720
|
if (brand.commercialMode !== true) {
|
|
27254
27721
|
return logResolved(implicitTrust(account), null);
|
|
27255
27722
|
}
|
|
27256
|
-
const entitlementPath =
|
|
27723
|
+
const entitlementPath = resolve36(brand.configDir, "entitlement.json");
|
|
27257
27724
|
if (!existsSync37(entitlementPath)) {
|
|
27258
27725
|
return logResolved(anonymousFallback("missing"), { reason: "missing" });
|
|
27259
27726
|
}
|
|
27260
|
-
const
|
|
27261
|
-
const key = memoKey(
|
|
27727
|
+
const stat9 = statSync16(entitlementPath);
|
|
27728
|
+
const key = memoKey(stat9.mtimeMs, account);
|
|
27262
27729
|
if (memo && memo.key === key) {
|
|
27263
27730
|
return memo.result;
|
|
27264
27731
|
}
|
|
@@ -27269,7 +27736,7 @@ function resolveEntitlement(brand, account) {
|
|
|
27269
27736
|
function verifyAndResolve(brand, entitlementPath, account) {
|
|
27270
27737
|
let pubkeyPem;
|
|
27271
27738
|
try {
|
|
27272
|
-
pubkeyPem =
|
|
27739
|
+
pubkeyPem = readFileSync41(pubkeyPath(brand), "utf-8");
|
|
27273
27740
|
} catch (err) {
|
|
27274
27741
|
return logResolved(anonymousFallback("pubkey-missing"), {
|
|
27275
27742
|
reason: "pubkey-missing"
|
|
@@ -27283,7 +27750,7 @@ function verifyAndResolve(brand, entitlementPath, account) {
|
|
|
27283
27750
|
}
|
|
27284
27751
|
let envelope;
|
|
27285
27752
|
try {
|
|
27286
|
-
envelope = JSON.parse(
|
|
27753
|
+
envelope = JSON.parse(readFileSync41(entitlementPath, "utf-8"));
|
|
27287
27754
|
} catch {
|
|
27288
27755
|
return logResolved(anonymousFallback("malformed"), { reason: "malformed" });
|
|
27289
27756
|
}
|
|
@@ -27445,14 +27912,14 @@ function clientFrom(c) {
|
|
|
27445
27912
|
}
|
|
27446
27913
|
installMediaDownloadGuard();
|
|
27447
27914
|
var PLATFORM_ROOT8 = process.env.MAXY_PLATFORM_ROOT || "";
|
|
27448
|
-
var BRAND_JSON_PATH = PLATFORM_ROOT8 ?
|
|
27915
|
+
var BRAND_JSON_PATH = PLATFORM_ROOT8 ? join44(PLATFORM_ROOT8, "config", "brand.json") : "";
|
|
27449
27916
|
var BRAND = { productName: "Maxy", hostname: "maxy", configDir: ".maxy", marketingUrl: "https://getmaxy.com" };
|
|
27450
27917
|
if (BRAND_JSON_PATH && !existsSync38(BRAND_JSON_PATH)) {
|
|
27451
27918
|
console.error(`[brand] WARNING: brand.json not found at ${BRAND_JSON_PATH} \u2014 using Maxy defaults`);
|
|
27452
27919
|
}
|
|
27453
27920
|
if (BRAND_JSON_PATH && existsSync38(BRAND_JSON_PATH)) {
|
|
27454
27921
|
try {
|
|
27455
|
-
const parsed = JSON.parse(
|
|
27922
|
+
const parsed = JSON.parse(readFileSync42(BRAND_JSON_PATH, "utf-8"));
|
|
27456
27923
|
BRAND = { ...BRAND, ...parsed };
|
|
27457
27924
|
} catch (err) {
|
|
27458
27925
|
console.error(`[brand] Failed to parse brand.json: ${err.message}`);
|
|
@@ -27484,11 +27951,11 @@ var brandLoginOpts = {
|
|
|
27484
27951
|
appleTouchIconPath: brandAppIcon512Path,
|
|
27485
27952
|
themeColor: BRAND.defaultColors?.primary
|
|
27486
27953
|
};
|
|
27487
|
-
var ALIAS_DOMAINS_PATH =
|
|
27954
|
+
var ALIAS_DOMAINS_PATH = join44(homedir4(), BRAND.configDir, "alias-domains.json");
|
|
27488
27955
|
function loadAliasDomains() {
|
|
27489
27956
|
try {
|
|
27490
27957
|
if (!existsSync38(ALIAS_DOMAINS_PATH)) return null;
|
|
27491
|
-
const parsed = JSON.parse(
|
|
27958
|
+
const parsed = JSON.parse(readFileSync42(ALIAS_DOMAINS_PATH, "utf-8"));
|
|
27492
27959
|
if (!Array.isArray(parsed)) {
|
|
27493
27960
|
console.error("[alias-domains] malformed alias-domains.json \u2014 expected array");
|
|
27494
27961
|
return null;
|
|
@@ -27512,7 +27979,15 @@ watchFile(ALIAS_DOMAINS_PATH, { interval: 2e3 }, () => {
|
|
|
27512
27979
|
function isPublicHost(host) {
|
|
27513
27980
|
return host.startsWith("public.") || aliasDomains.has(host);
|
|
27514
27981
|
}
|
|
27515
|
-
var
|
|
27982
|
+
var app64 = new Hono();
|
|
27983
|
+
function resolveChannelOverride(accountId, channel, senderId) {
|
|
27984
|
+
try {
|
|
27985
|
+
const dir = listValidAccounts().find((a) => a.accountId === accountId)?.accountDir;
|
|
27986
|
+
return dir ? readChannelOverrideId(dir, channel, senderId) : null;
|
|
27987
|
+
} catch {
|
|
27988
|
+
return null;
|
|
27989
|
+
}
|
|
27990
|
+
}
|
|
27516
27991
|
var nativeFileFollowers = /* @__PURE__ */ new Map();
|
|
27517
27992
|
var webchatFileFollowers = /* @__PURE__ */ new Map();
|
|
27518
27993
|
async function fetchAccountStandingRules(accountId) {
|
|
@@ -27551,8 +28026,9 @@ async function recordPassiveDispatch(input) {
|
|
|
27551
28026
|
}
|
|
27552
28027
|
var waGateway = new WaGateway({
|
|
27553
28028
|
fetchStandingRules: fetchAccountStandingRules,
|
|
28029
|
+
resolveSessionOverride: (accountId, senderId) => resolveChannelOverride(accountId, "whatsapp", senderId),
|
|
27554
28030
|
gatewayUrl: `http://127.0.0.1:${process.env.MAXY_UI_INTERNAL_PORT ?? ""}`,
|
|
27555
|
-
serverPath: process.env.MAXY_WA_CHANNEL_SERVER_PATH ??
|
|
28031
|
+
serverPath: process.env.MAXY_WA_CHANNEL_SERVER_PATH ?? resolve37(process.env.MAXY_PLATFORM_ROOT ?? join44(__dirname, ".."), "services/whatsapp-channel/dist/server.js"),
|
|
27556
28032
|
// Task 751 / 1390 — file delivery on the native channel. `maxyAccountId` (the
|
|
27557
28033
|
// path-validation scope) is the sender's effective SESSION account, resolved
|
|
27558
28034
|
// once at the inbound gate and threaded here via the gateway's per-sender doc
|
|
@@ -27567,7 +28043,7 @@ var waGateway = new WaGateway({
|
|
|
27567
28043
|
caption,
|
|
27568
28044
|
accountId,
|
|
27569
28045
|
maxyAccountId,
|
|
27570
|
-
platformRoot:
|
|
28046
|
+
platformRoot: resolve37(process.env.MAXY_PLATFORM_ROOT ?? join44(__dirname, ".."))
|
|
27571
28047
|
});
|
|
27572
28048
|
return result.ok ? { ok: true, messageId: result.messageId } : { ok: false, error: result.error };
|
|
27573
28049
|
},
|
|
@@ -27619,7 +28095,17 @@ var waGateway = new WaGateway({
|
|
|
27619
28095
|
const managesAccount = effectiveAccountId !== accountId ? effectiveAccountId : "none";
|
|
27620
28096
|
console.error(`[whatsapp-native] op=account-manager-route senderId=${senderId} managesAccount=${managesAccount} effectiveAccount=${effectiveAccountId} source=gate`);
|
|
27621
28097
|
}
|
|
27622
|
-
const req = buildWaSpawnRequest({
|
|
28098
|
+
const req = buildWaSpawnRequest({
|
|
28099
|
+
accountId: effectiveAccountId,
|
|
28100
|
+
senderId,
|
|
28101
|
+
role,
|
|
28102
|
+
personId,
|
|
28103
|
+
gatewayUrl,
|
|
28104
|
+
serverPath,
|
|
28105
|
+
// Task 1789 — admin rows only: a public session's id keys on personId as
|
|
28106
|
+
// well as senderId, so the per-sender pointer is not applicable to it.
|
|
28107
|
+
overrideSessionId: role === "admin" ? resolveChannelOverride(effectiveAccountId, "whatsapp", senderId) : null
|
|
28108
|
+
});
|
|
27623
28109
|
await ensureSessionConversation({
|
|
27624
28110
|
accountId: effectiveAccountId,
|
|
27625
28111
|
sessionId: req.sessionId,
|
|
@@ -27665,13 +28151,13 @@ var waGateway = new WaGateway({
|
|
|
27665
28151
|
nativeFileFollowers.set(senderId, ac);
|
|
27666
28152
|
}
|
|
27667
28153
|
});
|
|
27668
|
-
|
|
28154
|
+
app64.route("/", waGateway.routes());
|
|
27669
28155
|
waGateway.startSweeper();
|
|
27670
28156
|
function runDuplicateSenderAudit() {
|
|
27671
28157
|
try {
|
|
27672
28158
|
const configDir2 = process.env.CLAUDE_CONFIG_DIR;
|
|
27673
28159
|
if (!configDir2) return;
|
|
27674
|
-
const projectsRoot =
|
|
28160
|
+
const projectsRoot = join44(configDir2, "projects");
|
|
27675
28161
|
const records = [];
|
|
27676
28162
|
for (const { path: jsonlPath, isSubagent, archived } of enumerateJsonls(projectsRoot)) {
|
|
27677
28163
|
if (isSubagent || archived) continue;
|
|
@@ -27681,6 +28167,8 @@ function runDuplicateSenderAudit() {
|
|
|
27681
28167
|
records.push({ sessionId, channel: meta.channel, senderId: meta.senderId, accountId: meta.accountId });
|
|
27682
28168
|
}
|
|
27683
28169
|
logDuplicateSenderGroups(findDuplicateSenderGroups(records));
|
|
28170
|
+
const now = Date.now();
|
|
28171
|
+
logUnseatedSenders(waGateway.unseatedSenders(now, 15 * 6e4), now);
|
|
27684
28172
|
} catch (err) {
|
|
27685
28173
|
console.error(`[whatsapp-native] op=duplicate-sender-audit-error error=${err instanceof Error ? err.message : String(err)}`);
|
|
27686
28174
|
}
|
|
@@ -27787,16 +28275,26 @@ var webchatGateway = new WebchatGateway({
|
|
|
27787
28275
|
firePublicSessionEndReview: (input) => firePublicSessionEndReview(input)
|
|
27788
28276
|
});
|
|
27789
28277
|
setWebchatGateway(webchatGateway);
|
|
27790
|
-
|
|
28278
|
+
app64.route("/", webchatGateway.routes());
|
|
27791
28279
|
webchatGateway.startSweeper();
|
|
27792
28280
|
webchatGateway.startPublicReaper();
|
|
27793
28281
|
var telegramFileFollowers = /* @__PURE__ */ new Map();
|
|
27794
28282
|
var telegramGateway = new TelegramGateway({
|
|
27795
28283
|
fetchStandingRules: fetchAccountStandingRules,
|
|
28284
|
+
resolveSessionOverride: (accountId, senderId) => resolveChannelOverride(accountId, "telegram", senderId),
|
|
27796
28285
|
gatewayUrl: telegramGatewayUrl(),
|
|
27797
28286
|
serverPath: telegramServerPath(),
|
|
27798
28287
|
ensureChannelSession: async ({ accountId, senderId, role, personId, chatId, gatewayUrl, serverPath }) => {
|
|
27799
|
-
const req = buildTelegramSpawnRequest({
|
|
28288
|
+
const req = buildTelegramSpawnRequest({
|
|
28289
|
+
accountId,
|
|
28290
|
+
senderId,
|
|
28291
|
+
role,
|
|
28292
|
+
personId,
|
|
28293
|
+
gatewayUrl,
|
|
28294
|
+
serverPath,
|
|
28295
|
+
// Task 1789 — admin rows only; see the WhatsApp twin.
|
|
28296
|
+
overrideSessionId: role === "admin" ? resolveChannelOverride(accountId, "telegram", senderId) : null
|
|
28297
|
+
});
|
|
27800
28298
|
const result = await managerRcSpawn(req);
|
|
27801
28299
|
if ("error" in result) {
|
|
27802
28300
|
console.error(`[telegram-native] spawn-failed senderId=${senderId} role=${role} error=${result.error} status=${result.status}`);
|
|
@@ -27821,7 +28319,7 @@ var telegramGateway = new TelegramGateway({
|
|
|
27821
28319
|
}
|
|
27822
28320
|
});
|
|
27823
28321
|
setTelegramGateway(telegramGateway);
|
|
27824
|
-
|
|
28322
|
+
app64.route("/", telegramGateway.routes());
|
|
27825
28323
|
telegramGateway.startSweeper();
|
|
27826
28324
|
var chatRoutes = createChatRoutes({
|
|
27827
28325
|
handleInbound: (input) => webchatGateway.handleInbound(input),
|
|
@@ -27855,19 +28353,19 @@ var scheduleInjectRoutes = createScheduleInjectRoutes({
|
|
|
27855
28353
|
return managed.account;
|
|
27856
28354
|
}
|
|
27857
28355
|
});
|
|
27858
|
-
|
|
27859
|
-
|
|
28356
|
+
app64.route("/api/channel/schedule-inject", scheduleInjectRoutes);
|
|
28357
|
+
app64.use("*", clientIpMiddleware);
|
|
27860
28358
|
function allowsSameOriginFraming(path3) {
|
|
27861
28359
|
return path3 === "/vnc-viewer.html" || path3.startsWith("/api/admin/attachment/") || path3.startsWith("/api/public-reader/attachment/") || path3 === "/api/admin/files/download";
|
|
27862
28360
|
}
|
|
27863
|
-
|
|
28361
|
+
app64.use("*", async (c, next) => {
|
|
27864
28362
|
await next();
|
|
27865
28363
|
c.header("X-Content-Type-Options", "nosniff");
|
|
27866
28364
|
c.header("Referrer-Policy", "strict-origin-when-cross-origin");
|
|
27867
28365
|
c.header("X-Frame-Options", allowsSameOriginFraming(c.req.path) ? "SAMEORIGIN" : "DENY");
|
|
27868
28366
|
});
|
|
27869
28367
|
var HTTP_LOG_PATHS = /* @__PURE__ */ new Set(["/vnc-viewer.html", "/vnc-popout.html"]);
|
|
27870
|
-
|
|
28368
|
+
app64.use("*", async (c, next) => {
|
|
27871
28369
|
if (!HTTP_LOG_PATHS.has(c.req.path)) {
|
|
27872
28370
|
await next();
|
|
27873
28371
|
return;
|
|
@@ -27885,7 +28383,7 @@ app63.use("*", async (c, next) => {
|
|
|
27885
28383
|
});
|
|
27886
28384
|
}
|
|
27887
28385
|
});
|
|
27888
|
-
|
|
28386
|
+
app64.use("*", async (c, next) => {
|
|
27889
28387
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
27890
28388
|
if (isOperatorHost(host, getOperatorDomains()) || !isPublicHost(host)) {
|
|
27891
28389
|
await next();
|
|
@@ -27923,7 +28421,7 @@ function resolveRemoteAuthOpts(c) {
|
|
|
27923
28421
|
return { ...brandLoginOpts, origin };
|
|
27924
28422
|
}
|
|
27925
28423
|
var MAX_LOGIN_BODY = 8 * 1024;
|
|
27926
|
-
|
|
28424
|
+
app64.post("/__remote-auth/login", async (c) => {
|
|
27927
28425
|
const client = clientFrom(c);
|
|
27928
28426
|
const clientIp = client.ip || "unknown";
|
|
27929
28427
|
if (!requestIsTlsTerminated(c)) {
|
|
@@ -27968,7 +28466,7 @@ app63.post("/__remote-auth/login", async (c) => {
|
|
|
27968
28466
|
}
|
|
27969
28467
|
});
|
|
27970
28468
|
});
|
|
27971
|
-
|
|
28469
|
+
app64.get("/__remote-auth/logout", (c) => {
|
|
27972
28470
|
const client = clientFrom(c);
|
|
27973
28471
|
const clientIp = client.ip || "unknown";
|
|
27974
28472
|
console.error(`[remote-auth] logout ip=${clientIp}`);
|
|
@@ -27981,7 +28479,7 @@ app63.get("/__remote-auth/logout", (c) => {
|
|
|
27981
28479
|
}
|
|
27982
28480
|
});
|
|
27983
28481
|
});
|
|
27984
|
-
|
|
28482
|
+
app64.post("/__remote-auth/change-password", async (c) => {
|
|
27985
28483
|
const client = clientFrom(c);
|
|
27986
28484
|
const clientIp = client.ip || "unknown";
|
|
27987
28485
|
const rateLimited = checkRateLimit(client);
|
|
@@ -28040,13 +28538,13 @@ app63.post("/__remote-auth/change-password", async (c) => {
|
|
|
28040
28538
|
return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(c), mode: "change", changeError: "Failed to save password", redirect }), 200);
|
|
28041
28539
|
}
|
|
28042
28540
|
});
|
|
28043
|
-
|
|
28541
|
+
app64.get("/__remote-auth/setup", (c) => {
|
|
28044
28542
|
if (isRemoteAuthConfigured()) {
|
|
28045
28543
|
return c.redirect("/");
|
|
28046
28544
|
}
|
|
28047
28545
|
return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(c), mode: "setup" }), 200);
|
|
28048
28546
|
});
|
|
28049
|
-
|
|
28547
|
+
app64.post("/__remote-auth/set-initial-password", async (c) => {
|
|
28050
28548
|
if (isRemoteAuthConfigured()) {
|
|
28051
28549
|
return c.redirect("/");
|
|
28052
28550
|
}
|
|
@@ -28084,10 +28582,10 @@ app63.post("/__remote-auth/set-initial-password", async (c) => {
|
|
|
28084
28582
|
return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(c), mode: "setup", setupError: "Failed to save password. Please try again." }), 200);
|
|
28085
28583
|
}
|
|
28086
28584
|
});
|
|
28087
|
-
|
|
28585
|
+
app64.get("/api/remote-auth/status", (c) => {
|
|
28088
28586
|
return c.json({ configured: isRemoteAuthConfigured() });
|
|
28089
28587
|
});
|
|
28090
|
-
|
|
28588
|
+
app64.post("/api/remote-auth/set-password", async (c) => {
|
|
28091
28589
|
let body;
|
|
28092
28590
|
try {
|
|
28093
28591
|
body = await c.req.json();
|
|
@@ -28126,10 +28624,10 @@ app63.post("/api/remote-auth/set-password", async (c) => {
|
|
|
28126
28624
|
return c.json({ error: "Failed to save password" }, 500);
|
|
28127
28625
|
}
|
|
28128
28626
|
});
|
|
28129
|
-
|
|
28627
|
+
app64.route("/api/_client-error", client_error_default);
|
|
28130
28628
|
console.log("[client-error-route] mounted");
|
|
28131
28629
|
var PWA_PUBLIC_PATHS = /* @__PURE__ */ new Set(["/sw.js", ...PWA_SURFACES.map((s) => s.manifestPath)]);
|
|
28132
|
-
|
|
28630
|
+
app64.use("*", async (c, next) => {
|
|
28133
28631
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
28134
28632
|
const path3 = c.req.path;
|
|
28135
28633
|
if (path3 === "/favicon.ico" || path3.startsWith("/assets/") || path3.startsWith("/brand/") || // Public free/busy is read by the booking page (a separate Cloudflare Pages
|
|
@@ -28173,27 +28671,28 @@ app63.use("*", async (c, next) => {
|
|
|
28173
28671
|
}
|
|
28174
28672
|
return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(c), redirect: path3 }), 200);
|
|
28175
28673
|
});
|
|
28176
|
-
|
|
28177
|
-
|
|
28178
|
-
|
|
28179
|
-
|
|
28180
|
-
|
|
28181
|
-
|
|
28182
|
-
|
|
28674
|
+
app64.route("/api/health", health_default);
|
|
28675
|
+
app64.route("/api/chat", chatRoutes);
|
|
28676
|
+
app64.route("/api/whatsapp", whatsapp_default);
|
|
28677
|
+
app64.route("/api/storage", storage_broker_default);
|
|
28678
|
+
app64.route("/api/whatsapp-reader", whatsapp_reader_default);
|
|
28679
|
+
app64.route("/api/public-reader", public_reader_default);
|
|
28680
|
+
app64.route("/api/webchat", createWebchatRoutes({
|
|
28183
28681
|
handleInbound: (input) => webchatGateway.handleInbound(input),
|
|
28184
28682
|
// Task 940 — the permission relay's pointer read + verdict write.
|
|
28185
28683
|
pendingPromptFor: (key) => webchatGateway.pendingPromptFor(key),
|
|
28186
28684
|
deliveryFailureFor: (key) => webchatGateway.deliveryFailureFor(key),
|
|
28187
28685
|
resolvePermissionVerdict: (key, requestId, behavior) => webchatGateway.resolvePermissionVerdict(key, requestId, behavior)
|
|
28188
28686
|
}));
|
|
28189
|
-
|
|
28190
|
-
|
|
28191
|
-
|
|
28192
|
-
|
|
28193
|
-
|
|
28194
|
-
|
|
28195
|
-
|
|
28196
|
-
|
|
28687
|
+
app64.route("/api/webchat/greeting", webchat_greeting_default);
|
|
28688
|
+
app64.route("/api/telegram", telegram_default);
|
|
28689
|
+
app64.route("/api/quickbooks", quickbooks_default);
|
|
28690
|
+
app64.route("/api/onboarding", onboarding_default);
|
|
28691
|
+
app64.route("/api/admin", admin_default);
|
|
28692
|
+
app64.route("/api/access", access_default);
|
|
28693
|
+
app64.route("/api/session", session_default2);
|
|
28694
|
+
app64.route("/api/calendar", calendar_public_default);
|
|
28695
|
+
app64.route("/api/portal", portal_fetch_default);
|
|
28197
28696
|
var SAFE_SLUG_RE2 = /^[a-z][a-z0-9-]{2,49}$/;
|
|
28198
28697
|
var SAFE_FILENAME_RE = /^[a-z0-9_][a-z0-9_.-]{0,99}$/i;
|
|
28199
28698
|
var IMAGE_MIME = {
|
|
@@ -28205,7 +28704,7 @@ var IMAGE_MIME = {
|
|
|
28205
28704
|
".svg": "image/svg+xml",
|
|
28206
28705
|
".ico": "image/x-icon"
|
|
28207
28706
|
};
|
|
28208
|
-
|
|
28707
|
+
app64.get("/agent-assets/:slug/:filename", (c) => {
|
|
28209
28708
|
const slug = c.req.param("slug");
|
|
28210
28709
|
const filename = c.req.param("filename");
|
|
28211
28710
|
if (!SAFE_SLUG_RE2.test(slug)) {
|
|
@@ -28221,8 +28720,8 @@ app63.get("/agent-assets/:slug/:filename", (c) => {
|
|
|
28221
28720
|
console.error(`[agent-assets] no-account slug=${slug} file=${filename}`);
|
|
28222
28721
|
return c.text("Not found", 404);
|
|
28223
28722
|
}
|
|
28224
|
-
const filePath =
|
|
28225
|
-
const expectedDir =
|
|
28723
|
+
const filePath = resolve37(account.accountDir, "agents", slug, "assets", filename);
|
|
28724
|
+
const expectedDir = resolve37(account.accountDir, "agents", slug, "assets");
|
|
28226
28725
|
if (!filePath.startsWith(expectedDir + "/")) {
|
|
28227
28726
|
console.error(`[agent-assets] path-traversal-rejected slug=${slug} file=${filename}`);
|
|
28228
28727
|
return c.text("Forbidden", 403);
|
|
@@ -28234,13 +28733,13 @@ app63.get("/agent-assets/:slug/:filename", (c) => {
|
|
|
28234
28733
|
const ext = "." + filename.split(".").pop()?.toLowerCase();
|
|
28235
28734
|
const contentType = IMAGE_MIME[ext] || "application/octet-stream";
|
|
28236
28735
|
console.log(`[agent-assets] serve slug=${slug} file=${filename} status=200`);
|
|
28237
|
-
const body =
|
|
28736
|
+
const body = readFileSync42(filePath);
|
|
28238
28737
|
return c.body(body, 200, {
|
|
28239
28738
|
"Content-Type": contentType,
|
|
28240
28739
|
"Cache-Control": "public, max-age=3600"
|
|
28241
28740
|
});
|
|
28242
28741
|
});
|
|
28243
|
-
|
|
28742
|
+
app64.get("/generated/:filename", (c) => {
|
|
28244
28743
|
const filename = c.req.param("filename");
|
|
28245
28744
|
if (!SAFE_FILENAME_RE.test(filename) || filename.includes("..")) {
|
|
28246
28745
|
console.error(`[generated] serve file=${filename} status=403`);
|
|
@@ -28251,8 +28750,8 @@ app63.get("/generated/:filename", (c) => {
|
|
|
28251
28750
|
console.error(`[generated] serve file=${filename} status=404`);
|
|
28252
28751
|
return c.text("Not found", 404);
|
|
28253
28752
|
}
|
|
28254
|
-
const filePath =
|
|
28255
|
-
const expectedDir =
|
|
28753
|
+
const filePath = resolve37(account.accountDir, "generated", filename);
|
|
28754
|
+
const expectedDir = resolve37(account.accountDir, "generated");
|
|
28256
28755
|
if (!filePath.startsWith(expectedDir + "/")) {
|
|
28257
28756
|
console.error(`[generated] serve file=${filename} status=403`);
|
|
28258
28757
|
return c.text("Forbidden", 403);
|
|
@@ -28264,22 +28763,22 @@ app63.get("/generated/:filename", (c) => {
|
|
|
28264
28763
|
const ext = "." + filename.split(".").pop()?.toLowerCase();
|
|
28265
28764
|
const contentType = IMAGE_MIME[ext] || "application/octet-stream";
|
|
28266
28765
|
console.log(`[generated] serve file=${filename} status=200`);
|
|
28267
|
-
const body =
|
|
28766
|
+
const body = readFileSync42(filePath);
|
|
28268
28767
|
return c.body(body, 200, {
|
|
28269
28768
|
"Content-Type": contentType,
|
|
28270
28769
|
"Cache-Control": "public, max-age=86400"
|
|
28271
28770
|
});
|
|
28272
28771
|
});
|
|
28273
|
-
|
|
28274
|
-
|
|
28275
|
-
|
|
28276
|
-
|
|
28772
|
+
app64.route("/sites", sites_default);
|
|
28773
|
+
app64.route("/listings", listings_default);
|
|
28774
|
+
app64.route("/v", visitor_event_default);
|
|
28775
|
+
app64.route("/v", visitor_consent_default);
|
|
28277
28776
|
var htmlCache = /* @__PURE__ */ new Map();
|
|
28278
28777
|
var brandLogoPath = "/brand/maxy-monochrome.png";
|
|
28279
28778
|
var brandIconPath = "/brand/maxy-monochrome.png";
|
|
28280
28779
|
if (BRAND_JSON_PATH && existsSync38(BRAND_JSON_PATH)) {
|
|
28281
28780
|
try {
|
|
28282
|
-
const fullBrand = JSON.parse(
|
|
28781
|
+
const fullBrand = JSON.parse(readFileSync42(BRAND_JSON_PATH, "utf-8"));
|
|
28283
28782
|
if (fullBrand.assets?.logo) brandLogoPath = `/brand/${fullBrand.assets.logo}`;
|
|
28284
28783
|
brandIconPath = fullBrand.assets?.icon ? `/brand/${fullBrand.assets.icon}` : brandLogoPath;
|
|
28285
28784
|
} catch {
|
|
@@ -28306,11 +28805,11 @@ var brandScript = `<script>window.__BRAND__=${JSON.stringify({
|
|
|
28306
28805
|
var brandThemeColor = BRAND.defaultColors?.primary ?? "#000000";
|
|
28307
28806
|
var brandBackgroundColor = BRAND.defaultColors?.background ?? "#ffffff";
|
|
28308
28807
|
var brandAppIconsExist = [brandAppIcon192Path, brandAppIcon512Path, brandMaskableIconPath].every(
|
|
28309
|
-
(p) => existsSync38(
|
|
28808
|
+
(p) => existsSync38(resolve37(process.cwd(), "public", p.replace(/^\//, "")))
|
|
28310
28809
|
);
|
|
28311
28810
|
var SW_SOURCE = (() => {
|
|
28312
28811
|
try {
|
|
28313
|
-
return
|
|
28812
|
+
return readFileSync42(resolve37(process.cwd(), "public", "sw.js"), "utf-8");
|
|
28314
28813
|
} catch {
|
|
28315
28814
|
return null;
|
|
28316
28815
|
}
|
|
@@ -28318,9 +28817,9 @@ var SW_SOURCE = (() => {
|
|
|
28318
28817
|
function readInstalledVersion() {
|
|
28319
28818
|
try {
|
|
28320
28819
|
if (!PLATFORM_ROOT8) return "unknown";
|
|
28321
|
-
const versionFile =
|
|
28820
|
+
const versionFile = join44(PLATFORM_ROOT8, "config", `.${BRAND.hostname}-version`);
|
|
28322
28821
|
if (!existsSync38(versionFile)) return "unknown";
|
|
28323
|
-
const content =
|
|
28822
|
+
const content = readFileSync42(versionFile, "utf-8").trim();
|
|
28324
28823
|
return content || "unknown";
|
|
28325
28824
|
} catch {
|
|
28326
28825
|
return "unknown";
|
|
@@ -28361,7 +28860,7 @@ var clientErrorReporterScript = `<script>
|
|
|
28361
28860
|
function cachedHtml(file) {
|
|
28362
28861
|
let html = htmlCache.get(file);
|
|
28363
28862
|
if (!html) {
|
|
28364
|
-
html =
|
|
28863
|
+
html = readFileSync42(resolve37(process.cwd(), "public", file), "utf-8");
|
|
28365
28864
|
const productNameEsc = escapeHtml(BRAND.productName);
|
|
28366
28865
|
html = html.replace(/<title>([^<]*)<\/title>/, (_match, inner) => `<title>${escapeHtml(inner).replace(/Maxy/g, productNameEsc)}</title>`);
|
|
28367
28866
|
html = html.replace('href="/favicon.ico"', `href="${escapeHtml(brandFaviconPath)}"`);
|
|
@@ -28378,13 +28877,13 @@ ${clientErrorReporterScript}
|
|
|
28378
28877
|
return html;
|
|
28379
28878
|
}
|
|
28380
28879
|
function loadBrandingCache(agentSlug) {
|
|
28381
|
-
const configDir2 =
|
|
28880
|
+
const configDir2 = join44(homedir4(), BRAND.configDir);
|
|
28382
28881
|
try {
|
|
28383
28882
|
const accountId = getDefaultAccountId();
|
|
28384
28883
|
if (!accountId) return null;
|
|
28385
|
-
const cachePath =
|
|
28884
|
+
const cachePath = join44(configDir2, "branding-cache", accountId, `${agentSlug}.json`);
|
|
28386
28885
|
if (!existsSync38(cachePath)) return null;
|
|
28387
|
-
return JSON.parse(
|
|
28886
|
+
return JSON.parse(readFileSync42(cachePath, "utf-8"));
|
|
28388
28887
|
} catch {
|
|
28389
28888
|
return null;
|
|
28390
28889
|
}
|
|
@@ -28428,7 +28927,7 @@ function brandedPublicHtml(agentSlug) {
|
|
|
28428
28927
|
function agentUnavailableHtml() {
|
|
28429
28928
|
return `<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>${escapeHtml(BRAND.productName)}</title></head><body style="font-family:system-ui,sans-serif;max-width:32rem;margin:4rem auto;padding:0 1.5rem;color:#222"><h1 style="font-size:1.25rem">Agent unavailable</h1><p>This agent isn't available right now. If you reached this page from a saved link, the agent may have been turned off.</p></body></html>`;
|
|
28430
28929
|
}
|
|
28431
|
-
|
|
28930
|
+
app64.get("/", (c) => {
|
|
28432
28931
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
28433
28932
|
const klass = classifyHost(host, getOperatorDomains(), isPublicHost);
|
|
28434
28933
|
if (klass === "operator") {
|
|
@@ -28450,12 +28949,12 @@ app63.get("/", (c) => {
|
|
|
28450
28949
|
console.log(`[host-class] host=${host} class=admin served=index.html`);
|
|
28451
28950
|
return c.html(cachedHtml("index.html"));
|
|
28452
28951
|
});
|
|
28453
|
-
|
|
28952
|
+
app64.get("/public", (c) => {
|
|
28454
28953
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
28455
28954
|
if (isPublicHost(host)) return c.text("Not found", 404);
|
|
28456
28955
|
return c.html(cachedHtml("public.html"));
|
|
28457
28956
|
});
|
|
28458
|
-
|
|
28957
|
+
app64.get("/public-chat", (c) => {
|
|
28459
28958
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
28460
28959
|
if (isPublicHost(host)) return c.text("Not found", 404);
|
|
28461
28960
|
return c.html(cachedHtml("public.html"));
|
|
@@ -28474,12 +28973,12 @@ async function logViewerFetch(c, next) {
|
|
|
28474
28973
|
duration_ms: Date.now() - start
|
|
28475
28974
|
});
|
|
28476
28975
|
}
|
|
28477
|
-
|
|
28478
|
-
|
|
28479
|
-
|
|
28976
|
+
app64.use("/vnc-viewer.html", logViewerFetch);
|
|
28977
|
+
app64.use("/vnc-popout.html", logViewerFetch);
|
|
28978
|
+
app64.get("/vnc-popout.html", (c) => {
|
|
28480
28979
|
let html = htmlCache.get("vnc-popout.html");
|
|
28481
28980
|
if (!html) {
|
|
28482
|
-
html =
|
|
28981
|
+
html = readFileSync42(resolve37(process.cwd(), "public", "vnc-popout.html"), "utf-8");
|
|
28483
28982
|
const name = escapeHtml(BRAND.productName);
|
|
28484
28983
|
html = html.replace("<title>Browser \u2014 Maxy</title>", `<title>${name}</title>`);
|
|
28485
28984
|
html = html.replace("</head>", ` ${brandScript}
|
|
@@ -28489,7 +28988,7 @@ app63.get("/vnc-popout.html", (c) => {
|
|
|
28489
28988
|
}
|
|
28490
28989
|
return c.html(html);
|
|
28491
28990
|
});
|
|
28492
|
-
|
|
28991
|
+
app64.post("/api/vnc/client-event", async (c) => {
|
|
28493
28992
|
let body;
|
|
28494
28993
|
try {
|
|
28495
28994
|
body = await c.req.json();
|
|
@@ -28510,11 +29009,11 @@ app63.post("/api/vnc/client-event", async (c) => {
|
|
|
28510
29009
|
});
|
|
28511
29010
|
return c.json({ ok: true });
|
|
28512
29011
|
});
|
|
28513
|
-
|
|
29012
|
+
app64.get("/g/:slug", (c) => {
|
|
28514
29013
|
return c.html(brandedPublicHtml(resolveDefaultSlug() ?? void 0));
|
|
28515
29014
|
});
|
|
28516
29015
|
for (const pwa of PWA_SURFACES) {
|
|
28517
|
-
|
|
29016
|
+
app64.get(pwa.manifestPath, (c) => {
|
|
28518
29017
|
const manifest = buildManifest(pwa, {
|
|
28519
29018
|
productName: BRAND.productName,
|
|
28520
29019
|
appIcon192: brandAppIcon192Path,
|
|
@@ -28530,7 +29029,7 @@ for (const pwa of PWA_SURFACES) {
|
|
|
28530
29029
|
return c.body(JSON.stringify(manifest));
|
|
28531
29030
|
});
|
|
28532
29031
|
}
|
|
28533
|
-
|
|
29032
|
+
app64.get("/sw.js", (c) => {
|
|
28534
29033
|
if (SW_SOURCE == null) {
|
|
28535
29034
|
console.error("[pwa] op=sw status=500 reason=sw-source-missing");
|
|
28536
29035
|
return c.text("Service worker unavailable", 500);
|
|
@@ -28539,12 +29038,12 @@ app63.get("/sw.js", (c) => {
|
|
|
28539
29038
|
console.log(`[pwa] op=sw status=200 ct=${SW_CONTENT_TYPE}`);
|
|
28540
29039
|
return c.body(SW_SOURCE);
|
|
28541
29040
|
});
|
|
28542
|
-
|
|
29041
|
+
app64.get("/graph", (c) => {
|
|
28543
29042
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
28544
29043
|
if (isPublicHost(host) || isOperatorHost(host, getOperatorDomains())) return c.text("Not found", 404);
|
|
28545
29044
|
return c.html(cachedHtml("graph.html"));
|
|
28546
29045
|
});
|
|
28547
|
-
|
|
29046
|
+
app64.get("/chat", (c) => {
|
|
28548
29047
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
28549
29048
|
if (isOperatorHost(host, getOperatorDomains())) {
|
|
28550
29049
|
console.log(`[host-class] host=${host} class=operator served=operator.html`);
|
|
@@ -28553,32 +29052,32 @@ app63.get("/chat", (c) => {
|
|
|
28553
29052
|
if (isPublicHost(host)) return c.text("Not found", 404);
|
|
28554
29053
|
return c.html(cachedHtml("chat.html"));
|
|
28555
29054
|
});
|
|
28556
|
-
|
|
29055
|
+
app64.get("/data", (c) => {
|
|
28557
29056
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
28558
29057
|
if (isPublicHost(host)) return c.text("Not found", 404);
|
|
28559
29058
|
return c.html(cachedHtml("data.html"));
|
|
28560
29059
|
});
|
|
28561
|
-
|
|
29060
|
+
app64.get("/tasks", (c) => {
|
|
28562
29061
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
28563
29062
|
if (isPublicHost(host)) return c.text("Not found", 404);
|
|
28564
29063
|
return c.html(cachedHtml("tasks.html"));
|
|
28565
29064
|
});
|
|
28566
|
-
|
|
29065
|
+
app64.get("/activity", (c) => {
|
|
28567
29066
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
28568
29067
|
if (isPublicHost(host)) return c.text("Not found", 404);
|
|
28569
29068
|
return c.html(cachedHtml("activity.html"));
|
|
28570
29069
|
});
|
|
28571
|
-
|
|
29070
|
+
app64.get("/calendar", (c) => {
|
|
28572
29071
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
28573
29072
|
if (isPublicHost(host)) return c.text("Not found", 404);
|
|
28574
29073
|
return c.html(cachedHtml("calendar.html"));
|
|
28575
29074
|
});
|
|
28576
|
-
|
|
29075
|
+
app64.get("/browser", (c) => {
|
|
28577
29076
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
28578
29077
|
if (isPublicHost(host) || isOperatorHost(host, getOperatorDomains())) return c.text("Not found", 404);
|
|
28579
29078
|
return c.html(cachedHtml("browser.html"));
|
|
28580
29079
|
});
|
|
28581
|
-
|
|
29080
|
+
app64.get("/:slug", async (c, next) => {
|
|
28582
29081
|
const slug = c.req.param("slug");
|
|
28583
29082
|
if (AGENT_SLUG_PATTERN.test(`/${slug}`)) {
|
|
28584
29083
|
const account = resolveAccount();
|
|
@@ -28593,13 +29092,13 @@ app63.get("/:slug", async (c, next) => {
|
|
|
28593
29092
|
await next();
|
|
28594
29093
|
});
|
|
28595
29094
|
if (brandFaviconPath !== "/favicon.ico") {
|
|
28596
|
-
|
|
29095
|
+
app64.get("/favicon.ico", (c) => {
|
|
28597
29096
|
c.header("Cache-Control", "public, max-age=300");
|
|
28598
29097
|
return c.redirect(brandFaviconPath, 302);
|
|
28599
29098
|
});
|
|
28600
29099
|
}
|
|
28601
|
-
|
|
28602
|
-
|
|
29100
|
+
app64.use("/*", serveStatic({ root: "./public" }));
|
|
29101
|
+
app64.all("*", (c) => {
|
|
28603
29102
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
28604
29103
|
const path3 = c.req.path;
|
|
28605
29104
|
if (isPublicHost(host)) {
|
|
@@ -28613,11 +29112,11 @@ app63.all("*", (c) => {
|
|
|
28613
29112
|
});
|
|
28614
29113
|
var port = requirePortEnv("MAXY_UI_INTERNAL_PORT", { tag: "ui-server" });
|
|
28615
29114
|
var hostname = process.env.HOSTNAME ?? "127.0.0.1";
|
|
28616
|
-
var httpServer = serve({ fetch:
|
|
29115
|
+
var httpServer = serve({ fetch: app64.fetch, port, hostname });
|
|
28617
29116
|
console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
28618
29117
|
{
|
|
28619
|
-
const reconcilePlatformRoot = process.env.MAXY_PLATFORM_ROOT ??
|
|
28620
|
-
const reconcileScript =
|
|
29118
|
+
const reconcilePlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join44(__dirname, "..");
|
|
29119
|
+
const reconcileScript = resolve37(reconcilePlatformRoot, "plugins/scheduling/mcp/dist/scripts/reconcile-bookings.js");
|
|
28621
29120
|
const RECONCILE_INTERVAL_MS = 12e4;
|
|
28622
29121
|
const runReconcile = (ctx) => {
|
|
28623
29122
|
if (!existsSync38(reconcileScript)) return;
|
|
@@ -28642,8 +29141,8 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
28642
29141
|
});
|
|
28643
29142
|
}
|
|
28644
29143
|
{
|
|
28645
|
-
const outlookPlatformRoot = process.env.MAXY_PLATFORM_ROOT ??
|
|
28646
|
-
const outlookScript =
|
|
29144
|
+
const outlookPlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join44(__dirname, "..");
|
|
29145
|
+
const outlookScript = resolve37(outlookPlatformRoot, "plugins/outlook/mcp/dist/scripts/complete-registration.js");
|
|
28647
29146
|
const OUTLOOK_COMPLETE_INTERVAL_MS = 3e4;
|
|
28648
29147
|
const runOutlookComplete = (ctx) => {
|
|
28649
29148
|
if (!existsSync38(outlookScript)) return;
|
|
@@ -28668,8 +29167,8 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
28668
29167
|
});
|
|
28669
29168
|
}
|
|
28670
29169
|
{
|
|
28671
|
-
const auditRoot = process.env.MAXY_PLATFORM_ROOT ??
|
|
28672
|
-
const strandedAccountsDir =
|
|
29170
|
+
const auditRoot = process.env.MAXY_PLATFORM_ROOT ?? join44(__dirname, "..");
|
|
29171
|
+
const strandedAccountsDir = resolve37(auditRoot, "..", "data/accounts");
|
|
28673
29172
|
const STRANDED_AUDIT_INTERVAL_MS = 3e5;
|
|
28674
29173
|
const STRANDED_AGE_MS = 16 * 6e4;
|
|
28675
29174
|
const STRANDED_UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
@@ -28679,7 +29178,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
28679
29178
|
const now = Date.now();
|
|
28680
29179
|
for (const name of readdirSync26(strandedAccountsDir)) {
|
|
28681
29180
|
if (!STRANDED_UUID_RE.test(name)) continue;
|
|
28682
|
-
const pendingPath2 =
|
|
29181
|
+
const pendingPath2 = resolve37(strandedAccountsDir, name, "secrets/outlook/pending-devicecode.enc");
|
|
28683
29182
|
if (!existsSync38(pendingPath2)) continue;
|
|
28684
29183
|
const ageMs = now - statSync17(pendingPath2).mtimeMs;
|
|
28685
29184
|
if (ageMs > STRANDED_AGE_MS) {
|
|
@@ -28698,8 +29197,8 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
28698
29197
|
});
|
|
28699
29198
|
}
|
|
28700
29199
|
{
|
|
28701
|
-
const googleRoot = process.env.MAXY_PLATFORM_ROOT ??
|
|
28702
|
-
const googleAccountsDir =
|
|
29200
|
+
const googleRoot = process.env.MAXY_PLATFORM_ROOT ?? join44(__dirname, "..");
|
|
29201
|
+
const googleAccountsDir = resolve37(googleRoot, "..", "data/accounts");
|
|
28703
29202
|
const GOOGLE_PENDING_AUDIT_INTERVAL_MS = 3e5;
|
|
28704
29203
|
const runGooglePendingAuditSafe = () => {
|
|
28705
29204
|
try {
|
|
@@ -28714,7 +29213,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
28714
29213
|
firstRunDelayMs: 27e3,
|
|
28715
29214
|
run: runGooglePendingAuditSafe
|
|
28716
29215
|
});
|
|
28717
|
-
const googleAuditScript =
|
|
29216
|
+
const googleAuditScript = resolve37(googleRoot, "plugins/google/mcp/dist/scripts/account-audit.js");
|
|
28718
29217
|
const GOOGLE_ACCOUNT_AUDIT_INTERVAL_MS = 36e5;
|
|
28719
29218
|
const runGoogleAccountAudit = (ctx) => {
|
|
28720
29219
|
if (!existsSync38(googleAuditScript)) return;
|
|
@@ -28742,7 +29241,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
28742
29241
|
});
|
|
28743
29242
|
}
|
|
28744
29243
|
{
|
|
28745
|
-
const auditPlatformRoot = process.env.MAXY_PLATFORM_ROOT ??
|
|
29244
|
+
const auditPlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join44(__dirname, "..");
|
|
28746
29245
|
const STORAGE_AUDIT_INTERVAL_MS = 3e5;
|
|
28747
29246
|
const runStorageAuditSafe = () => runStorageAudit(auditPlatformRoot).catch((err) => {
|
|
28748
29247
|
console.error(`[storage-audit] error="${err.message}"`);
|
|
@@ -28764,10 +29263,20 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
28764
29263
|
firstRunDelayMs: 35e3,
|
|
28765
29264
|
run: runPagesAuditSafe
|
|
28766
29265
|
});
|
|
29266
|
+
const runDataPortalAuditSafe = () => runDataPortalAudit(auditPlatformRoot).catch((err) => {
|
|
29267
|
+
console.error(`[data-portal-audit] error="${err.message}"`);
|
|
29268
|
+
throw err;
|
|
29269
|
+
});
|
|
29270
|
+
registerLoop({
|
|
29271
|
+
name: "data-portal-audit",
|
|
29272
|
+
intervalMs: STORAGE_AUDIT_INTERVAL_MS,
|
|
29273
|
+
firstRunDelayMs: 5e4,
|
|
29274
|
+
run: runDataPortalAuditSafe
|
|
29275
|
+
});
|
|
28767
29276
|
}
|
|
28768
29277
|
{
|
|
28769
|
-
const publishPlatformRoot = process.env.MAXY_PLATFORM_ROOT ??
|
|
28770
|
-
const publishScript =
|
|
29278
|
+
const publishPlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join44(__dirname, "..");
|
|
29279
|
+
const publishScript = resolve37(publishPlatformRoot, "plugins/scheduling/mcp/dist/scripts/publish-availability.js");
|
|
28771
29280
|
const PUBLISH_INTERVAL_MS = 3e5;
|
|
28772
29281
|
const currentAccount = () => {
|
|
28773
29282
|
try {
|
|
@@ -28797,21 +29306,21 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
28797
29306
|
}
|
|
28798
29307
|
};
|
|
28799
29308
|
const auditSnapshotAge = (account) => {
|
|
28800
|
-
const availPath =
|
|
29309
|
+
const availPath = resolve37(account.accountDir, "calendar-availability.json");
|
|
28801
29310
|
let hasBookingSite = false;
|
|
28802
29311
|
try {
|
|
28803
29312
|
if (existsSync38(availPath)) {
|
|
28804
|
-
const cfg = JSON.parse(
|
|
29313
|
+
const cfg = JSON.parse(readFileSync42(availPath, "utf-8"));
|
|
28805
29314
|
hasBookingSite = typeof cfg.bookingDbName === "string" && cfg.bookingDbName.length > 0;
|
|
28806
29315
|
}
|
|
28807
29316
|
} catch {
|
|
28808
29317
|
}
|
|
28809
29318
|
if (!hasBookingSite) return;
|
|
28810
|
-
const statePath =
|
|
29319
|
+
const statePath = resolve37(account.accountDir, "state", "booking-availability", "last-publish.json");
|
|
28811
29320
|
let lastSuccessAt = null;
|
|
28812
29321
|
if (existsSync38(statePath)) {
|
|
28813
29322
|
try {
|
|
28814
|
-
const rec = JSON.parse(
|
|
29323
|
+
const rec = JSON.parse(readFileSync42(statePath, "utf-8"));
|
|
28815
29324
|
lastSuccessAt = rec.lastSuccessAt ?? null;
|
|
28816
29325
|
} catch {
|
|
28817
29326
|
console.error(`[calendar-availability] op=audit accountId=${account.accountId} snapshotAgeMs=na reason=bad-state-file`);
|
|
@@ -28839,8 +29348,8 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
28839
29348
|
startTimeEntryCensus(() => getSession());
|
|
28840
29349
|
startLedgerCensus(() => getSession());
|
|
28841
29350
|
{
|
|
28842
|
-
const auditPlatformRoot = process.env.MAXY_PLATFORM_ROOT ??
|
|
28843
|
-
const auditScript =
|
|
29351
|
+
const auditPlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join44(__dirname, "..");
|
|
29352
|
+
const auditScript = resolve37(auditPlatformRoot, "plugins/connector/mcp/dist/scripts/audit-connectors.js");
|
|
28844
29353
|
const auditLogDir = process.env.LOG_DIR ?? LOG_DIR;
|
|
28845
29354
|
const CONNECTOR_AUDIT_INTERVAL_MS = 3e5;
|
|
28846
29355
|
const runConnectorAudit = (ctx) => {
|
|
@@ -28912,6 +29421,7 @@ var SUBAPP_MANIFEST = [
|
|
|
28912
29421
|
{ prefix: "/api/access", file: "server/routes/access/index.ts", subapp: access_default },
|
|
28913
29422
|
{ prefix: "/api/session", file: "server/routes/session.ts", subapp: session_default2 },
|
|
28914
29423
|
{ prefix: "/api/calendar", file: "server/routes/calendar-public.ts", subapp: calendar_public_default },
|
|
29424
|
+
{ prefix: "/api/portal", file: "server/routes/portal-fetch.ts", subapp: portal_fetch_default },
|
|
28915
29425
|
{ prefix: "/api/_client-error", file: "server/routes/client-error.ts", subapp: client_error_default },
|
|
28916
29426
|
{ prefix: "/listings", file: "server/routes/listings.ts", subapp: listings_default },
|
|
28917
29427
|
{ prefix: "/v", file: "server/routes/visitor-event.ts", subapp: visitor_event_default },
|
|
@@ -28923,7 +29433,7 @@ for (const m of SUBAPP_MANIFEST) {
|
|
|
28923
29433
|
}
|
|
28924
29434
|
try {
|
|
28925
29435
|
const registered = [];
|
|
28926
|
-
for (const r of
|
|
29436
|
+
for (const r of app64.routes ?? []) {
|
|
28927
29437
|
if (typeof r.path !== "string" || r.path.includes(":") || r.path.includes("*")) continue;
|
|
28928
29438
|
if (AGENT_SLUG_PATTERN.test(r.path)) {
|
|
28929
29439
|
registered.push({ method: (r.method ?? "ALL").toUpperCase(), path: r.path });
|
|
@@ -28967,7 +29477,7 @@ async function runAdminUserReconcileTick() {
|
|
|
28967
29477
|
console.error("[adminuser-self-heal] skip reason=no-users-file");
|
|
28968
29478
|
return;
|
|
28969
29479
|
}
|
|
28970
|
-
const usersRaw =
|
|
29480
|
+
const usersRaw = readFileSync42(USERS_FILE, "utf-8").trim();
|
|
28971
29481
|
if (!usersRaw) {
|
|
28972
29482
|
console.error("[adminuser-self-heal] skip reason=empty-users-file");
|
|
28973
29483
|
return;
|
|
@@ -29015,7 +29525,7 @@ registerLoop({
|
|
|
29015
29525
|
var USERS_RECONCILE_INTERVAL_MS = 60 * 60 * 1e3;
|
|
29016
29526
|
function countUsersRows() {
|
|
29017
29527
|
if (!existsSync38(USERS_FILE)) return 0;
|
|
29018
|
-
const raw =
|
|
29528
|
+
const raw = readFileSync42(USERS_FILE, "utf-8").trim();
|
|
29019
29529
|
if (!raw) return 0;
|
|
29020
29530
|
const users = JSON.parse(raw);
|
|
29021
29531
|
return users.filter((u) => typeof u.userId === "string").length;
|
|
@@ -29148,7 +29658,7 @@ if (bootAccountConfig?.whatsapp) {
|
|
|
29148
29658
|
}
|
|
29149
29659
|
init({
|
|
29150
29660
|
configDir: configDirForWhatsApp,
|
|
29151
|
-
platformRoot:
|
|
29661
|
+
platformRoot: resolve37(process.env.MAXY_PLATFORM_ROOT ?? join44(__dirname, "..")),
|
|
29152
29662
|
accountConfig: bootAccountConfig,
|
|
29153
29663
|
onMessage: async (msg) => {
|
|
29154
29664
|
if (msg.isOwnerMirror) {
|