@rubytech/create-maxy-code 0.1.476 → 0.1.478

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.
Files changed (165) hide show
  1. package/package.json +1 -1
  2. package/payload/platform/docs/superpowers/plans/2026-07-20-task-1704-data-portal-standing-audit.md +673 -0
  3. package/payload/platform/docs/superpowers/specs/2026-07-20-task-1704-data-portal-standing-audit-design.md +177 -0
  4. package/payload/platform/docs/superpowers/specs/2026-07-20-task-1820-cpu-triage-admin-tools-design.md +97 -0
  5. package/payload/platform/lib/storage-broker/dist/__tests__/audit.test.js +44 -0
  6. package/payload/platform/lib/storage-broker/dist/__tests__/audit.test.js.map +1 -1
  7. package/payload/platform/lib/storage-broker/dist/audit.d.ts +56 -0
  8. package/payload/platform/lib/storage-broker/dist/audit.d.ts.map +1 -1
  9. package/payload/platform/lib/storage-broker/dist/audit.js +34 -0
  10. package/payload/platform/lib/storage-broker/dist/audit.js.map +1 -1
  11. package/payload/platform/lib/storage-broker/src/__tests__/audit.test.ts +70 -1
  12. package/payload/platform/lib/storage-broker/src/audit.ts +111 -0
  13. package/payload/platform/plugins/admin/.claude-plugin/plugin.json +1 -1
  14. package/payload/platform/plugins/admin/PLUGIN.md +7 -1
  15. package/payload/platform/plugins/admin/mcp/dist/index.js +72 -0
  16. package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -1
  17. package/payload/platform/plugins/admin/mcp/dist/tools/cpu-triage.d.ts +94 -0
  18. package/payload/platform/plugins/admin/mcp/dist/tools/cpu-triage.d.ts.map +1 -0
  19. package/payload/platform/plugins/admin/mcp/dist/tools/cpu-triage.js +229 -0
  20. package/payload/platform/plugins/admin/mcp/dist/tools/cpu-triage.js.map +1 -0
  21. package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +2 -2
  22. package/payload/platform/plugins/docs/references/admin-ui.md +1 -1
  23. package/payload/platform/plugins/email/PLUGIN.md +2 -1
  24. package/payload/platform/plugins/email/mcp/dist/__tests__/confirm-sent-copy.test.d.ts +2 -0
  25. package/payload/platform/plugins/email/mcp/dist/__tests__/confirm-sent-copy.test.d.ts.map +1 -0
  26. package/payload/platform/plugins/email/mcp/dist/__tests__/confirm-sent-copy.test.js +92 -0
  27. package/payload/platform/plugins/email/mcp/dist/__tests__/confirm-sent-copy.test.js.map +1 -0
  28. package/payload/platform/plugins/email/mcp/dist/__tests__/email-draft-send.test.js +112 -12
  29. package/payload/platform/plugins/email/mcp/dist/__tests__/email-draft-send.test.js.map +1 -1
  30. package/payload/platform/plugins/email/mcp/dist/__tests__/email-send-sent-copy.test.d.ts +2 -0
  31. package/payload/platform/plugins/email/mcp/dist/__tests__/email-send-sent-copy.test.d.ts.map +1 -0
  32. package/payload/platform/plugins/email/mcp/dist/__tests__/email-send-sent-copy.test.js +54 -0
  33. package/payload/platform/plugins/email/mcp/dist/__tests__/email-send-sent-copy.test.js.map +1 -0
  34. package/payload/platform/plugins/email/mcp/dist/__tests__/imap-drafts.test.js +170 -1
  35. package/payload/platform/plugins/email/mcp/dist/__tests__/imap-drafts.test.js.map +1 -1
  36. package/payload/platform/plugins/email/mcp/dist/__tests__/sent-copy-note.test.d.ts +2 -0
  37. package/payload/platform/plugins/email/mcp/dist/__tests__/sent-copy-note.test.d.ts.map +1 -0
  38. package/payload/platform/plugins/email/mcp/dist/__tests__/sent-copy-note.test.js +86 -0
  39. package/payload/platform/plugins/email/mcp/dist/__tests__/sent-copy-note.test.js.map +1 -0
  40. package/payload/platform/plugins/email/mcp/dist/__tests__/sent-copy-sweep.test.d.ts +2 -0
  41. package/payload/platform/plugins/email/mcp/dist/__tests__/sent-copy-sweep.test.d.ts.map +1 -0
  42. package/payload/platform/plugins/email/mcp/dist/__tests__/sent-copy-sweep.test.js +164 -0
  43. package/payload/platform/plugins/email/mcp/dist/__tests__/sent-copy-sweep.test.js.map +1 -0
  44. package/payload/platform/plugins/email/mcp/dist/__tests__/sent-ledger.test.d.ts +2 -0
  45. package/payload/platform/plugins/email/mcp/dist/__tests__/sent-ledger.test.d.ts.map +1 -0
  46. package/payload/platform/plugins/email/mcp/dist/__tests__/sent-ledger.test.js +82 -0
  47. package/payload/platform/plugins/email/mcp/dist/__tests__/sent-ledger.test.js.map +1 -0
  48. package/payload/platform/plugins/email/mcp/dist/lib/imap.d.ts +95 -0
  49. package/payload/platform/plugins/email/mcp/dist/lib/imap.d.ts.map +1 -1
  50. package/payload/platform/plugins/email/mcp/dist/lib/imap.js +223 -0
  51. package/payload/platform/plugins/email/mcp/dist/lib/imap.js.map +1 -1
  52. package/payload/platform/plugins/email/mcp/dist/lib/sent-copy-note.d.ts +18 -0
  53. package/payload/platform/plugins/email/mcp/dist/lib/sent-copy-note.d.ts.map +1 -0
  54. package/payload/platform/plugins/email/mcp/dist/lib/sent-copy-note.js +59 -0
  55. package/payload/platform/plugins/email/mcp/dist/lib/sent-copy-note.js.map +1 -0
  56. package/payload/platform/plugins/email/mcp/dist/lib/sent-copy-sweep.d.ts +48 -0
  57. package/payload/platform/plugins/email/mcp/dist/lib/sent-copy-sweep.d.ts.map +1 -0
  58. package/payload/platform/plugins/email/mcp/dist/lib/sent-copy-sweep.js +79 -0
  59. package/payload/platform/plugins/email/mcp/dist/lib/sent-copy-sweep.js.map +1 -0
  60. package/payload/platform/plugins/email/mcp/dist/lib/sent-ledger.d.ts +44 -0
  61. package/payload/platform/plugins/email/mcp/dist/lib/sent-ledger.d.ts.map +1 -0
  62. package/payload/platform/plugins/email/mcp/dist/lib/sent-ledger.js +63 -0
  63. package/payload/platform/plugins/email/mcp/dist/lib/sent-ledger.js.map +1 -0
  64. package/payload/platform/plugins/email/mcp/dist/scripts/sent-copy-sweep.d.ts +2 -0
  65. package/payload/platform/plugins/email/mcp/dist/scripts/sent-copy-sweep.d.ts.map +1 -0
  66. package/payload/platform/plugins/email/mcp/dist/scripts/sent-copy-sweep.js +55 -0
  67. package/payload/platform/plugins/email/mcp/dist/scripts/sent-copy-sweep.js.map +1 -0
  68. package/payload/platform/plugins/email/mcp/dist/tools/email-draft-send.d.ts.map +1 -1
  69. package/payload/platform/plugins/email/mcp/dist/tools/email-draft-send.js +64 -13
  70. package/payload/platform/plugins/email/mcp/dist/tools/email-draft-send.js.map +1 -1
  71. package/payload/platform/plugins/email/mcp/dist/tools/email-reply.d.ts.map +1 -1
  72. package/payload/platform/plugins/email/mcp/dist/tools/email-reply.js +11 -1
  73. package/payload/platform/plugins/email/mcp/dist/tools/email-reply.js.map +1 -1
  74. package/payload/platform/plugins/email/mcp/dist/tools/email-send.d.ts.map +1 -1
  75. package/payload/platform/plugins/email/mcp/dist/tools/email-send.js +16 -3
  76. package/payload/platform/plugins/email/mcp/dist/tools/email-send.js.map +1 -1
  77. package/payload/platform/plugins/email/references/email-reference.md +36 -0
  78. package/payload/platform/plugins/scheduling/mcp/dist/__tests__/sent-sweep-throttle.test.d.ts +2 -0
  79. package/payload/platform/plugins/scheduling/mcp/dist/__tests__/sent-sweep-throttle.test.d.ts.map +1 -0
  80. package/payload/platform/plugins/scheduling/mcp/dist/__tests__/sent-sweep-throttle.test.js +18 -0
  81. package/payload/platform/plugins/scheduling/mcp/dist/__tests__/sent-sweep-throttle.test.js.map +1 -0
  82. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/booking-block-sqlite.test.d.ts +2 -0
  83. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/booking-block-sqlite.test.d.ts.map +1 -0
  84. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/booking-block-sqlite.test.js +150 -0
  85. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/booking-block-sqlite.test.js.map +1 -0
  86. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/booking-block.test.d.ts +2 -0
  87. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/booking-block.test.d.ts.map +1 -0
  88. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/booking-block.test.js +248 -0
  89. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/booking-block.test.js.map +1 -0
  90. package/payload/platform/plugins/scheduling/mcp/dist/lib/booking-reconcile.d.ts +90 -0
  91. package/payload/platform/plugins/scheduling/mcp/dist/lib/booking-reconcile.d.ts.map +1 -1
  92. package/payload/platform/plugins/scheduling/mcp/dist/lib/booking-reconcile.js +105 -0
  93. package/payload/platform/plugins/scheduling/mcp/dist/lib/booking-reconcile.js.map +1 -1
  94. package/payload/platform/plugins/scheduling/mcp/dist/lib/sent-sweep-throttle.d.ts +13 -0
  95. package/payload/platform/plugins/scheduling/mcp/dist/lib/sent-sweep-throttle.d.ts.map +1 -0
  96. package/payload/platform/plugins/scheduling/mcp/dist/lib/sent-sweep-throttle.js +10 -0
  97. package/payload/platform/plugins/scheduling/mcp/dist/lib/sent-sweep-throttle.js.map +1 -0
  98. package/payload/platform/plugins/scheduling/mcp/dist/scripts/check-due-events.js +53 -0
  99. package/payload/platform/plugins/scheduling/mcp/dist/scripts/check-due-events.js.map +1 -1
  100. package/payload/platform/plugins/scheduling/mcp/dist/scripts/reconcile-bookings.js +88 -10
  101. package/payload/platform/plugins/scheduling/mcp/dist/scripts/reconcile-bookings.js.map +1 -1
  102. package/payload/platform/scripts/cpu-triage-run.sh +117 -0
  103. package/payload/platform/scripts/cpu-triage.sh +332 -0
  104. package/payload/platform/services/claude-session-manager/dist/activity-range.d.ts +40 -0
  105. package/payload/platform/services/claude-session-manager/dist/activity-range.d.ts.map +1 -0
  106. package/payload/platform/services/claude-session-manager/dist/activity-range.js +68 -0
  107. package/payload/platform/services/claude-session-manager/dist/activity-range.js.map +1 -0
  108. package/payload/platform/services/claude-session-manager/dist/activity-rows.d.ts +40 -8
  109. package/payload/platform/services/claude-session-manager/dist/activity-rows.d.ts.map +1 -1
  110. package/payload/platform/services/claude-session-manager/dist/activity-rows.js +74 -7
  111. package/payload/platform/services/claude-session-manager/dist/activity-rows.js.map +1 -1
  112. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.d.ts.map +1 -1
  113. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js +2 -0
  114. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js.map +1 -1
  115. package/payload/platform/services/claude-session-manager/dist/http-server.d.ts.map +1 -1
  116. package/payload/platform/services/claude-session-manager/dist/http-server.js +9 -0
  117. package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -1
  118. package/payload/server/{chunk-56WJMBQQ.js → chunk-3XLLTG6R.js} +14 -0
  119. package/payload/server/{chunk-LBCMFD4O.js → chunk-XGNRSM57.js} +129 -37
  120. package/payload/server/maxy-edge.js +1 -1
  121. package/payload/server/public/activity.html +6 -6
  122. package/payload/server/public/assets/{AdminLoginScreens-DximwPlS.js → AdminLoginScreens-DR5eLF-a.js} +1 -1
  123. package/payload/server/public/assets/AdminShell-D9zaUxPd.js +2 -0
  124. package/payload/server/public/assets/{Checkbox-DM-eHqOS.js → Checkbox-DcbbAIe5.js} +1 -1
  125. package/payload/server/public/assets/activity-C9P5NjEZ.js +1 -0
  126. package/payload/server/public/assets/{admin-CAkDnGdk.js → admin-D2KLZnQW.js} +1 -1
  127. package/payload/server/public/assets/{browser-DXl8hOoi.js → browser-CDWqEiCD.js} +1 -1
  128. package/payload/server/public/assets/{calendar-DP6hn4-6.js → calendar-BG7fOpbB.js} +1 -1
  129. package/payload/server/public/assets/chat-RyH-WYQC.js +1 -0
  130. package/payload/server/public/assets/chevron-left-DmXCr6nB.js +1 -0
  131. package/payload/server/public/assets/data-BaY_LkLR.js +1 -0
  132. package/payload/server/public/assets/{graph-CHcYoEJ5.js → graph-DRloNic4.js} +1 -1
  133. package/payload/server/public/assets/{graph-labels-CZykslZM.js → graph-labels-DD-GqS4v.js} +1 -1
  134. package/payload/server/public/assets/{maximize-2-31AZEcMS.js → maximize-2-CzDaBugs.js} +1 -1
  135. package/payload/server/public/assets/{operator-BGGpdsO_.js → operator-BZeeyait.js} +1 -1
  136. package/payload/server/public/assets/{page-BloC6ygA.js → page-B0obcg3b.js} +1 -1
  137. package/payload/server/public/assets/{page-DOeiiqbR.js → page-ef-6rRPL.js} +1 -1
  138. package/payload/server/public/assets/{public-CGgO6IZv.js → public-9D3MMOkC.js} +1 -1
  139. package/payload/server/public/assets/{rotate-ccw-BlAi6iFg.js → rotate-ccw-DXEKUhXP.js} +1 -1
  140. package/payload/server/public/assets/tasks-BB0L7e5a.js +1 -0
  141. package/payload/server/public/assets/{time-entry-format-CiUUQdq7.js → time-entry-format-CsYAjplS.js} +1 -1
  142. package/payload/server/public/assets/{triangle-alert-CVqKuLoa.js → triangle-alert-0lSBIUg-.js} +1 -1
  143. package/payload/server/public/assets/{useCopyFeedback-ksyUe_g1.js → useCopyFeedback-jTBQ2h0V.js} +1 -1
  144. package/payload/server/public/assets/{useSelectionMode-BPrBkdTd.js → useSelectionMode-BooArySZ.js} +1 -1
  145. package/payload/server/public/assets/useSubAccountSwitcher-D9knHOKq.css +1 -0
  146. package/payload/server/public/assets/{useVoiceRecorder-CTXP8oTW.js → useVoiceRecorder-PUT8q_sm.js} +1 -1
  147. package/payload/server/public/browser.html +5 -5
  148. package/payload/server/public/calendar.html +6 -6
  149. package/payload/server/public/chat.html +12 -12
  150. package/payload/server/public/data.html +11 -11
  151. package/payload/server/public/graph.html +11 -11
  152. package/payload/server/public/index.html +14 -14
  153. package/payload/server/public/operator.html +14 -14
  154. package/payload/server/public/public.html +12 -12
  155. package/payload/server/public/tasks.html +5 -5
  156. package/payload/server/server.js +325 -77
  157. package/payload/server/{src-JYPKMWJR.js → src-XQ63FPRE.js} +3 -1
  158. package/payload/server/public/assets/AdminShell-BehbH-Oa.js +0 -2
  159. package/payload/server/public/assets/activity-hDwUeQd6.js +0 -1
  160. package/payload/server/public/assets/chat-DhOfVGYM.js +0 -1
  161. package/payload/server/public/assets/chevron-left-DYNBoLii.js +0 -1
  162. package/payload/server/public/assets/data-bq8fTo_s.js +0 -1
  163. package/payload/server/public/assets/tasks-C1giFRRL.js +0 -1
  164. package/payload/server/public/assets/useSubAccountSwitcher-K4kYeN6i.css +0 -1
  165. /package/payload/server/public/assets/{useSubAccountSwitcher-DzgzTRRn.js → useSubAccountSwitcher-BZnzc5C3.js} +0 -0
@@ -97,6 +97,8 @@ import {
97
97
  runAdminUserSelfHeal,
98
98
  runUserProfileReconcile,
99
99
  safeJson,
100
+ sampleCpu,
101
+ sampleProcessCpu,
100
102
  sanitizeClientCorrId,
101
103
  serve,
102
104
  setAccessPassword,
@@ -115,7 +117,7 @@ import {
115
117
  vncLog,
116
118
  walkPremiumBundles,
117
119
  writeAdminUserAndPerson
118
- } from "./chunk-LBCMFD4O.js";
120
+ } from "./chunk-XGNRSM57.js";
119
121
  import {
120
122
  D1_MAX_SQL_STATEMENT_BYTES,
121
123
  D1_QUERY_MAX_BODY_BYTES,
@@ -127,10 +129,11 @@ import {
127
129
  listResources,
128
130
  makeHouseCfExec,
129
131
  makeHousePagesExec,
132
+ reconcileDataPortal,
130
133
  registerResource,
131
134
  resolveOwner,
132
135
  tooLargeMessage
133
- } from "./chunk-56WJMBQQ.js";
136
+ } from "./chunk-3XLLTG6R.js";
134
137
  import {
135
138
  __commonJS,
136
139
  __toESM
@@ -9635,59 +9638,148 @@ app3.post("/r2/object/delete", envelopeBodyLimit("write"), async (c) => {
9635
9638
  });
9636
9639
  var storage_broker_default = app3;
9637
9640
  async function runStorageAudit(root) {
9638
- const { reconcileStorage } = await import("./src-JYPKMWJR.js");
9639
- const session = getSession();
9640
- try {
9641
- const cf = await makeHouseCfExec(root);
9642
- const accountsDir = resolve11(root, "..", "data", "accounts");
9643
- const strays = (await reconcileStorage({
9644
- cfD1: async () => (await cf.d1List()).map((d) => d.name),
9645
- cfR2: async () => (await cf.r2List()).map((b) => b.name),
9646
- registeredNames: async (kind) => {
9641
+ const { reconcileStorage } = await import("./src-XQ63FPRE.js");
9642
+ const cf = await makeHouseCfExec(root);
9643
+ const accountsDir = resolve11(root, "..", "data", "accounts");
9644
+ const strays = (await reconcileStorage({
9645
+ cfD1: async () => (await cf.d1List()).map((d) => d.name),
9646
+ cfR2: async () => (await cf.r2List()).map((b) => b.name),
9647
+ // Its own session per invocation, never one captured from the enclosing
9648
+ // scope. reconcileStorage issues the "d1" and "r2" reads through the same
9649
+ // Promise.all, and a Neo4j session admits one query at a time — sharing one
9650
+ // made the second reject, which left this audit reporting nothing for 802
9651
+ // consecutive ticks on maxy-code and 295 on sitedesk-code. Sessions come
9652
+ // from the driver pool, so one per read is cheap; the alternative,
9653
+ // serialising the library's fan-out, would bend it around this caller.
9654
+ registeredNames: async (kind) => {
9655
+ const session = getSession();
9656
+ try {
9647
9657
  const res = await session.run(
9648
9658
  "MATCH (r:StorageResource {kind: $kind}) RETURN r.name AS name",
9649
9659
  { kind }
9650
9660
  );
9651
9661
  return res.records.map((rec) => rec.get("name"));
9652
- },
9653
- accountSecretsFiles: async () => {
9654
- if (!existsSync9(accountsDir)) return [];
9655
- const files = [];
9656
- for (const accountId of readdirSync8(accountsDir)) {
9657
- const p = join13(accountsDir, accountId, "secrets", "cloudflare.env");
9658
- if (existsSync9(p)) files.push({ accountId, path: p, contents: readFileSync15(p, "utf8") });
9659
- }
9660
- return files;
9662
+ } finally {
9663
+ await session.close();
9661
9664
  }
9662
- })).strays;
9663
- console.error(
9664
- `[storage-audit] strays=${strays.length}${strays.map((s) => ` ${s.kind}:${s.detail}`).join("")}`
9665
- );
9666
- } finally {
9667
- await session.close();
9668
- }
9665
+ },
9666
+ accountSecretsFiles: async () => {
9667
+ if (!existsSync9(accountsDir)) return [];
9668
+ const files = [];
9669
+ for (const accountId of readdirSync8(accountsDir)) {
9670
+ const p = join13(accountsDir, accountId, "secrets", "cloudflare.env");
9671
+ if (existsSync9(p)) files.push({ accountId, path: p, contents: readFileSync15(p, "utf8") });
9672
+ }
9673
+ return files;
9674
+ }
9675
+ })).strays;
9676
+ console.error(
9677
+ `[storage-audit] strays=${strays.length}${strays.map((s) => ` ${s.kind}:${s.detail}`).join("")}`
9678
+ );
9669
9679
  }
9670
9680
  async function runPagesAudit(root) {
9671
- const { reconcilePages } = await import("./src-JYPKMWJR.js");
9672
- const session = getSession();
9673
- try {
9674
- const cf = await makeHousePagesExec(root);
9675
- const r = await reconcilePages({
9676
- cfProjects: async () => (await cf.pagesProjectList()).map((p) => p.name),
9677
- registeredProjects: async () => {
9681
+ const { reconcilePages } = await import("./src-XQ63FPRE.js");
9682
+ const cf = await makeHousePagesExec(root);
9683
+ const r = await reconcilePages({
9684
+ cfProjects: async () => (await cf.pagesProjectList()).map((p) => p.name),
9685
+ // Its own session per invocation, same rule as runStorageAudit above and
9686
+ // for the same reason. This previously held one session opened in the
9687
+ // enclosing scope, which was safe only because reconcilePages issues
9688
+ // exactly one registry query — a property of the library, not of this
9689
+ // caller. A second read added there would have reproduced 1813 here
9690
+ // verbatim. The old shape also held that session open across
9691
+ // makeHousePagesExec's network I/O, which is a pooled connection parked
9692
+ // for the length of a Cloudflare round trip.
9693
+ registeredProjects: async () => {
9694
+ const session = getSession();
9695
+ try {
9678
9696
  const res = await session.run(
9679
9697
  "MATCH (r:StorageResource {kind: $kind}) RETURN r.name AS name",
9680
9698
  { kind: "pages" }
9681
9699
  );
9682
9700
  return res.records.map((rec) => rec.get("name"));
9701
+ } finally {
9702
+ await session.close();
9683
9703
  }
9684
- });
9704
+ }
9705
+ });
9706
+ console.error(
9707
+ `[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("")
9708
+ );
9709
+ }
9710
+ function parseManifestRows(raw) {
9711
+ const envelope = Array.isArray(raw) ? raw[0] : null;
9712
+ const results = envelope?.results;
9713
+ if (!Array.isArray(results)) {
9714
+ throw new Error(`unexpected d1 payload: envelope carries no results array`);
9715
+ }
9716
+ return results.map((r, i) => {
9717
+ const row = r;
9718
+ const bad = ["objectKey", "ingested", "uploadedAt"].find(
9719
+ (f) => f === "ingested" ? typeof row[f] !== "number" : typeof row[f] !== "string"
9720
+ );
9721
+ if (bad) throw new Error(`unexpected d1 row: index=${i} field=${bad} type=${typeof row[bad]}`);
9722
+ return { objectKey: row.objectKey, ingested: row.ingested, uploadedAt: row.uploadedAt };
9723
+ });
9724
+ }
9725
+ function auditErrorText(message) {
9726
+ return message.split("\n")[0].replaceAll('"', "'").slice(0, 160);
9727
+ }
9728
+ function discoverPortalAccounts(accountsDir) {
9729
+ if (!existsSync9(accountsDir)) return [];
9730
+ const found = [];
9731
+ for (const accountId of readdirSync8(accountsDir)) {
9732
+ const configPath2 = join13(accountsDir, accountId, "data-portal.json");
9733
+ if (!existsSync9(configPath2)) continue;
9734
+ try {
9735
+ const cfg = JSON.parse(readFileSync15(configPath2, "utf-8"));
9736
+ if (typeof cfg.portalDbName === "string" && cfg.portalDbName.length > 0 && typeof cfg.bucketName === "string" && cfg.bucketName.length > 0) {
9737
+ found.push({ accountId, portalDbName: cfg.portalDbName, bucketName: cfg.bucketName });
9738
+ }
9739
+ } catch {
9740
+ }
9741
+ }
9742
+ return found;
9743
+ }
9744
+ async function runDataPortalAudit(root) {
9745
+ const portals = discoverPortalAccounts(resolve11(root, "..", "data", "accounts"));
9746
+ if (portals.length === 0) return "portals=0 degraded=0";
9747
+ const cf = await makeHouseCfExec(root);
9748
+ let degraded = 0;
9749
+ for (const portal of portals) {
9750
+ let rows = null;
9751
+ let d1Error = null;
9752
+ try {
9753
+ rows = parseManifestRows(
9754
+ await cf.d1Query(
9755
+ portal.portalDbName,
9756
+ "SELECT objectKey, ingested, uploadedAt FROM manifest"
9757
+ )
9758
+ );
9759
+ } catch (err) {
9760
+ d1Error = err.message;
9761
+ }
9762
+ let objects = null;
9763
+ let r2Error = null;
9764
+ try {
9765
+ objects = await cf.r2ObjectList(portal.bucketName);
9766
+ } catch (err) {
9767
+ r2Error = err.message;
9768
+ }
9769
+ const nowMs = Date.now();
9770
+ const reconciled = rows !== null && objects !== null ? reconcileDataPortal(objects, rows, nowMs) : null;
9771
+ const ageHrs = rows === null ? null : reconcileDataPortal([], rows, nowMs).oldestUningestedHrs;
9772
+ const degradation = [d1Error !== null ? "d1" : null, r2Error !== null ? "r2" : null].filter(Boolean).join("+");
9773
+ if (degradation) degraded += 1;
9774
+ const reasons = [
9775
+ d1Error !== null ? `d1:${auditErrorText(d1Error)}` : null,
9776
+ r2Error !== null ? `r2:${auditErrorText(r2Error)}` : null
9777
+ ].filter(Boolean).join(" | ");
9685
9778
  console.error(
9686
- `[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("")
9779
+ `[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}"` : "")
9687
9780
  );
9688
- } finally {
9689
- await session.close();
9690
9781
  }
9782
+ return `portals=${portals.length} degraded=${degraded}`;
9691
9783
  }
9692
9784
 
9693
9785
  // server/google-pending-audit.ts
@@ -13905,12 +13997,21 @@ function checkRateLimit2(ip, cls) {
13905
13997
  }
13906
13998
  return { allow: true, blocked: 0, shouldLog: false };
13907
13999
  }
13908
- setInterval(() => {
13909
- const now = Date.now();
13910
- for (const [ip, rec] of rateHits) {
13911
- if (now - rec.windowStart >= WINDOW_MS * 2) rateHits.delete(ip);
14000
+ registerLoop({
14001
+ name: "client-error-rate-cleanup",
14002
+ intervalMs: WINDOW_MS,
14003
+ run: () => {
14004
+ const now = Date.now();
14005
+ let purged = 0;
14006
+ for (const [key, rec] of rateHits) {
14007
+ if (now - rec.windowStart >= WINDOW_MS * 2) {
14008
+ rateHits.delete(key);
14009
+ purged += 1;
14010
+ }
14011
+ }
14012
+ return `purged ${purged}, ${rateHits.size} live`;
13912
14013
  }
13913
- }, WINDOW_MS).unref();
14014
+ });
13914
14015
  function truncate(value, max) {
13915
14016
  if (typeof value !== "string") return "";
13916
14017
  return value.length > max ? value.slice(0, max) + "\u2026" : value;
@@ -21998,16 +22099,32 @@ app47.get("/", requireAdminSession, async (c) => {
21998
22099
  WHERE te.endedAt IS NULL
21999
22100
  WITH t, te ORDER BY te.startedAt
22000
22101
  WITH t, head(collect(te)) AS openTe
22102
+ // OPTIONAL is load-bearing. Unassigned is the common state, so a plain
22103
+ // MATCH would silently drop most of the list with nothing looking wrong.
22104
+ OPTIONAL MATCH (t)-[:ASSIGNED_TO]->(a:AdminUser)
22105
+ // head(collect(...)) for the same reason the TimeEntry match above uses
22106
+ // it: a second ASSIGNED_TO edge would fan the row out, duplicating
22107
+ // rowKey and double-counting the assignee.
22108
+ WITH t, openTe, head(collect(a)) AS assignee
22001
22109
  RETURN t.taskId AS taskId, elementId(t) AS rowKey, t.name AS name, t.clientName AS clientName,
22002
22110
  t.propertyAddress AS propertyAddress, t.fee AS fee, t.status AS status,
22003
22111
  coalesce(t.secondsLogged, 0) AS secondsLogged,
22004
- openTe.startedAt AS runningStartedAt
22112
+ openTe.startedAt AS runningStartedAt,
22113
+ t.createdAt AS createdAt,
22114
+ assignee.userId AS assigneeUserId, assignee.name AS assigneeName
22005
22115
  ORDER BY t.createdAt DESC`,
22006
22116
  { accountId }
22007
22117
  );
22118
+ const totalResult = await session.run(
22119
+ `MATCH (t:Task {accountId: $accountId}) RETURN count(t) AS total`,
22120
+ { accountId }
22121
+ );
22122
+ const graphTotal = totalResult.records.length > 0 ? toNum(totalResult.records[0].get("total")) : 0;
22008
22123
  const open = [];
22009
22124
  const completed = [];
22010
22125
  let missingTaskId = 0;
22126
+ let missingCreatedAt = 0;
22127
+ let assigned = 0;
22011
22128
  for (const rec of result.records) {
22012
22129
  const status = rec.get("status") ?? "pending";
22013
22130
  const runningStartedAt = rec.get("runningStartedAt") ?? null;
@@ -22015,6 +22132,10 @@ app47.get("/", requireAdminSession, async (c) => {
22015
22132
  const rawTaskId = rec.get("taskId");
22016
22133
  const taskId = typeof rawTaskId === "string" && rawTaskId !== "" ? rawTaskId : null;
22017
22134
  if (taskId === null) missingTaskId++;
22135
+ const createdAt = rec.get("createdAt") ?? null;
22136
+ if (createdAt === null) missingCreatedAt++;
22137
+ const assigneeUserId = rec.get("assigneeUserId") ?? null;
22138
+ if (assigneeUserId !== null) assigned++;
22018
22139
  const row = {
22019
22140
  taskId,
22020
22141
  rowKey: rec.get("rowKey"),
@@ -22025,12 +22146,23 @@ app47.get("/", requireAdminSession, async (c) => {
22025
22146
  status,
22026
22147
  secondsLogged: toNum(rec.get("secondsLogged")),
22027
22148
  running: runningStartedAt !== null,
22028
- runningStartedAt
22149
+ runningStartedAt,
22150
+ createdAt,
22151
+ assigneeUserId,
22152
+ assigneeName: rec.get("assigneeName") ?? null
22029
22153
  };
22030
22154
  if (COMPLETED.has(status)) completed.push(row);
22031
22155
  else if (!HIDDEN_FROM_OPEN.has(status)) open.push(row);
22032
22156
  }
22033
- console.log(`${TAG37} op=list account=${accountId} open=${open.length} completed=${completed.length} missingTaskId=${missingTaskId}`);
22157
+ const returned = result.records.length;
22158
+ if (returned < graphTotal) {
22159
+ console.error(
22160
+ `${TAG37} op=list row-count-shortfall account=${accountId} returned=${returned} graphTotal=${graphTotal} missing=${graphTotal - returned}`
22161
+ );
22162
+ }
22163
+ console.log(
22164
+ `${TAG37} op=list account=${accountId} returned=${returned} graphTotal=${graphTotal} open=${open.length} completed=${completed.length} missingTaskId=${missingTaskId} missingCreatedAt=${missingCreatedAt} assigned=${assigned}`
22165
+ );
22034
22166
  return c.json({ open, completed });
22035
22167
  } catch (err) {
22036
22168
  console.error(`${TAG37} op=list error="${err instanceof Error ? err.message : String(err)}"`);
@@ -22348,7 +22480,13 @@ app52.get("/", requireAdminSession, async (c) => {
22348
22480
  const start = Date.now();
22349
22481
  let res;
22350
22482
  try {
22351
- res = await fetch(`${managerBase("activity:wrapper")}/activity`, {
22483
+ const qs = new URLSearchParams();
22484
+ const from = c.req.query("from");
22485
+ const to = c.req.query("to");
22486
+ if (from) qs.set("from", from);
22487
+ if (to) qs.set("to", to);
22488
+ const suffix = qs.size > 0 ? `?${qs.toString()}` : "";
22489
+ res = await fetch(`${managerBase("activity:wrapper")}/activity${suffix}`, {
22352
22490
  signal: AbortSignal.timeout(3e4)
22353
22491
  });
22354
22492
  } catch (err) {
@@ -22386,6 +22524,9 @@ app52.get("/", requireAdminSession, async (c) => {
22386
22524
  if (name) names[a.accountId] = name;
22387
22525
  })
22388
22526
  );
22527
+ const sampledAt = Date.now();
22528
+ sampleCpu(sampledAt);
22529
+ const cpu = sampleProcessCpu(sampledAt);
22389
22530
  const loopRows = listLoops();
22390
22531
  const armed2 = armedCount();
22391
22532
  const byState = loopRows.reduce((acc, r) => {
@@ -22393,13 +22534,27 @@ app52.get("/", requireAdminSession, async (c) => {
22393
22534
  return acc;
22394
22535
  }, {});
22395
22536
  console.log(
22396
- `${TAG42} op=loops req=${req} armed=${armed2} rendered=${loopRows.length} ` + Object.entries(byState).map(([s, n]) => `${s}=${n}`).join(" ")
22537
+ `${TAG42} op=loops req=${req} armed=${armed2} rendered=${loopRows.length} procPct=${cpu.processPct?.toFixed(1) ?? "null"} attributedPct=${cpu.attributedPct?.toFixed(1) ?? "null"} ` + Object.entries(byState).map(([s, n]) => `${s}=${n}`).join(" ")
22397
22538
  );
22398
22539
  return c.json({
22399
22540
  rows,
22400
22541
  census,
22401
22542
  censusAgeMs,
22402
- loops: { armed: armed2, rows: loopRows },
22543
+ // `satisfies` and not a bare literal (Task 1825). LoopsPayload is what the
22544
+ // activity page destructures; without the check, a key renamed or dropped
22545
+ // here still compiles and the page reads undefined, which its own
22546
+ // vocabulary renders as a dash meaning "not measured". The check runs both
22547
+ // ways: a key the page does not know about fails as an excess property.
22548
+ loops: {
22549
+ armed: armed2,
22550
+ rows: loopRows,
22551
+ // The server's own CPU beside what the registry can explain. The GAP is
22552
+ // the point: loops summing to 2% against a process at 60% says the cost
22553
+ // is somewhere else, which small numbers alone would never tell you.
22554
+ processPct: cpu.processPct,
22555
+ attributedPct: cpu.attributedPct,
22556
+ unattributedPct: cpu.unattributedPct
22557
+ },
22403
22558
  accounts: {
22404
22559
  onDisk,
22405
22560
  valid: valid.length,
@@ -23143,12 +23298,21 @@ function rateLimit(ip, now = Date.now()) {
23143
23298
  if (b.count > RATE_LIMIT) return { ok: false, resetIn: b.resetAt - now };
23144
23299
  return { ok: true, resetIn: b.resetAt - now };
23145
23300
  }
23146
- setInterval(() => {
23147
- const now = Date.now();
23148
- for (const [ip, b] of buckets) {
23149
- if (now >= b.resetAt) buckets.delete(ip);
23301
+ registerLoop({
23302
+ name: "visitor-event-rate-cleanup",
23303
+ intervalMs: 5 * RATE_WINDOW_MS,
23304
+ run: () => {
23305
+ const now = Date.now();
23306
+ let purged = 0;
23307
+ for (const [ip, b] of buckets) {
23308
+ if (now >= b.resetAt) {
23309
+ buckets.delete(ip);
23310
+ purged += 1;
23311
+ }
23312
+ }
23313
+ return `purged ${purged}, ${buckets.size} live`;
23150
23314
  }
23151
- }, 5 * RATE_WINDOW_MS).unref?.();
23315
+ });
23152
23316
  var VALID_TYPES = /* @__PURE__ */ new Set(["pageview", "click", "scroll", "dwell"]);
23153
23317
  var ID_RE = /^[A-Za-z0-9_-]{6,128}$/;
23154
23318
  function parseEvent(body) {
@@ -24976,10 +25140,28 @@ var WaGateway = class {
24976
25140
  }
24977
25141
  return events;
24978
25142
  }
24979
- /** Start the periodic reconciliation sweep in the long-lived UI process.
24980
- * Returns the interval handle. */
25143
+ /** Arm the periodic reconciliation sweep in the long-lived UI process.
25144
+ *
25145
+ * Task 1814 — armed through the registry, so it appears on /activity. This
25146
+ * gateway is constructed once at module scope in server/index.ts and reached
25147
+ * everywhere else through the singleton accessor in ./instance, so the sweep
25148
+ * is per-install, not per-instance. Returns the registry's disarm function
25149
+ * rather than the interval handle; no caller reads it.
25150
+ *
25151
+ * Two semantics move with it. The timer is now unref'd, where the bare
25152
+ * setInterval was not: a loop is a passenger of the process, never what
25153
+ * keeps it alive. And a throwing sweep is recorded as lastError instead of
25154
+ * surfacing as an uncaught timer exception, so a sweep that fails every
25155
+ * tick now reads as a failing row on /activity rather than a crash. */
24981
25156
  startSweeper(intervalMs = SWEEP_INTERVAL_MS) {
24982
- return setInterval(() => this.reconcile(Date.now()), intervalMs);
25157
+ return registerLoop({
25158
+ name: "whatsapp-native-sweep",
25159
+ intervalMs,
25160
+ run: () => {
25161
+ const events = this.reconcile(Date.now());
25162
+ return events.length > 0 ? `${events.length} terminal events` : void 0;
25163
+ }
25164
+ });
24983
25165
  }
24984
25166
  /** Handle one inbound admin WhatsApp message. */
24985
25167
  async handleInbound(input) {
@@ -25552,11 +25734,22 @@ var WebchatGateway = class _WebchatGateway {
25552
25734
  void this.deps.deleteSession?.(rec.sessionId);
25553
25735
  }
25554
25736
  }
25555
- /** Start the periodic public idle reaper in the long-lived UI process. */
25737
+ /** Arm the periodic public idle reaper in the long-lived UI process.
25738
+ *
25739
+ * Task 1814 — armed through the registry, so it appears on /activity. Like
25740
+ * the sweeper below, this is per-install: one gateway per server, reached
25741
+ * through the singleton accessor in ./instance. Returns the registry's
25742
+ * disarm function rather than the interval handle; no caller reads it.
25743
+ *
25744
+ * This one already unref'd its handle, so only the error semantics move: a
25745
+ * throwing reap is recorded as lastError rather than surfacing as an
25746
+ * uncaught timer exception. */
25556
25747
  startPublicReaper(intervalMs = 6e4) {
25557
- const handle = setInterval(() => this.reapPublic(Date.now()), intervalMs);
25558
- if (handle.unref) handle.unref();
25559
- return handle;
25748
+ return registerLoop({
25749
+ name: "webchat-public-reaper",
25750
+ intervalMs,
25751
+ run: () => this.reapPublic(Date.now())
25752
+ });
25560
25753
  }
25561
25754
  /** A gated public PTY exited explicitly on the manager side (operator stop,
25562
25755
  * claude crash, manager DELETE), not via the idle reaper. The manager POSTs
@@ -25638,15 +25831,36 @@ var WebchatGateway = class _WebchatGateway {
25638
25831
  }
25639
25832
  return events;
25640
25833
  }
25641
- /** Start the periodic reconciliation sweep in the long-lived UI process. Each
25834
+ /** Arm the periodic reconciliation sweep in the long-lived UI process. Each
25642
25835
  * tick runs the sync sweep (drain-/inbound-undelivered) AND the async
25643
- * recovery pass for queued-no-subscriber messages (Task-962). */
25836
+ * recovery pass for queued-no-subscriber messages (Task-962).
25837
+ *
25838
+ * Task 1814 — armed through the registry, so it appears on /activity. This
25839
+ * gateway is constructed once at module scope in server/index.ts and reached
25840
+ * everywhere else through the singleton accessor in ./instance, so the sweep
25841
+ * is per-install, not per-instance. Returns the registry's disarm function
25842
+ * rather than the interval handle; no caller reads it.
25843
+ *
25844
+ * Two semantics move with it. The timer is now unref'd, where the bare
25845
+ * setInterval was not: a loop is a passenger of the process, never what
25846
+ * keeps it alive. And a throwing sweep is recorded as lastError instead of
25847
+ * surfacing as an uncaught timer exception, so a sweep that fails every
25848
+ * tick now reads as a failing row on /activity rather than a crash.
25849
+ *
25850
+ * recoverStalled stays fire-and-forget, exactly as it was: run() returns the
25851
+ * sync note, so the registry's duration covers the sweep and not the
25852
+ * recovery pass. */
25644
25853
  startSweeper(intervalMs = SWEEP_INTERVAL_MS2) {
25645
- return setInterval(() => {
25646
- const now = Date.now();
25647
- this.reconcile(now);
25648
- void this.recoverStalled(now);
25649
- }, intervalMs);
25854
+ return registerLoop({
25855
+ name: "webchat-inbound-sweep",
25856
+ intervalMs,
25857
+ run: () => {
25858
+ const now = Date.now();
25859
+ const events = this.reconcile(now);
25860
+ void this.recoverStalled(now);
25861
+ return events.length > 0 ? `${events.length} terminal events` : void 0;
25862
+ }
25863
+ });
25650
25864
  }
25651
25865
  /** Task-962 — the per-session delivery-failure marker for the /session pointer.
25652
25866
  * Non-null only when redelivery was exhausted for `key`; cleared on the next
@@ -26584,9 +26798,28 @@ var TelegramGateway = class {
26584
26798
  }
26585
26799
  return events;
26586
26800
  }
26587
- /** Start the periodic reconciliation sweep in the long-lived UI process. */
26801
+ /** Arm the periodic reconciliation sweep in the long-lived UI process.
26802
+ *
26803
+ * Task 1814 — armed through the registry, so it appears on /activity. This
26804
+ * gateway is constructed once at module scope in server/index.ts and reached
26805
+ * everywhere else through the singleton accessor in ./instance, so the sweep
26806
+ * is per-install, not per-instance. Returns the registry's disarm function
26807
+ * rather than the interval handle; no caller reads it.
26808
+ *
26809
+ * Two semantics move with it. The timer is now unref'd, where the bare
26810
+ * setInterval was not: a loop is a passenger of the process, never what
26811
+ * keeps it alive. And a throwing sweep is recorded as lastError instead of
26812
+ * surfacing as an uncaught timer exception, so a sweep that fails every
26813
+ * tick now reads as a failing row on /activity rather than a crash. */
26588
26814
  startSweeper(intervalMs = SWEEP_INTERVAL_MS3) {
26589
- return setInterval(() => this.reconcile(Date.now()), intervalMs);
26815
+ return registerLoop({
26816
+ name: "telegram-native-sweep",
26817
+ intervalMs,
26818
+ run: () => {
26819
+ const events = this.reconcile(Date.now());
26820
+ return events.length > 0 ? `${events.length} terminal events` : void 0;
26821
+ }
26822
+ });
26590
26823
  }
26591
26824
  /** Handle one inbound telegram message. */
26592
26825
  async handleInbound(input) {
@@ -28511,7 +28744,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
28511
28744
  const reconcilePlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join42(__dirname, "..");
28512
28745
  const reconcileScript = resolve36(reconcilePlatformRoot, "plugins/scheduling/mcp/dist/scripts/reconcile-bookings.js");
28513
28746
  const RECONCILE_INTERVAL_MS = 12e4;
28514
- const runReconcile = () => {
28747
+ const runReconcile = (ctx) => {
28515
28748
  if (!existsSync38(reconcileScript)) return;
28516
28749
  try {
28517
28750
  const child = spawn3(process.execPath, [reconcileScript], {
@@ -28519,6 +28752,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
28519
28752
  stdio: "inherit"
28520
28753
  });
28521
28754
  child.unref();
28755
+ ctx.child(child.pid);
28522
28756
  child.on("error", (err) => console.error(`[calendar-reconcile] spawn-failed err="${err.message}"`));
28523
28757
  } catch (err) {
28524
28758
  console.error(`[calendar-reconcile] spawn-failed err="${err.message}"`);
@@ -28536,7 +28770,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
28536
28770
  const outlookPlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join42(__dirname, "..");
28537
28771
  const outlookScript = resolve36(outlookPlatformRoot, "plugins/outlook/mcp/dist/scripts/complete-registration.js");
28538
28772
  const OUTLOOK_COMPLETE_INTERVAL_MS = 3e4;
28539
- const runOutlookComplete = () => {
28773
+ const runOutlookComplete = (ctx) => {
28540
28774
  if (!existsSync38(outlookScript)) return;
28541
28775
  try {
28542
28776
  const child = spawn3(process.execPath, [outlookScript], {
@@ -28544,6 +28778,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
28544
28778
  stdio: "inherit"
28545
28779
  });
28546
28780
  child.unref();
28781
+ ctx.child(child.pid);
28547
28782
  child.on("error", (err) => console.error(`[outlook-complete] spawn-failed err="${err.message}"`));
28548
28783
  } catch (err) {
28549
28784
  console.error(`[outlook-complete] spawn-failed err="${err.message}"`);
@@ -28606,7 +28841,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
28606
28841
  });
28607
28842
  const googleAuditScript = resolve36(googleRoot, "plugins/google/mcp/dist/scripts/account-audit.js");
28608
28843
  const GOOGLE_ACCOUNT_AUDIT_INTERVAL_MS = 36e5;
28609
- const runGoogleAccountAudit = () => {
28844
+ const runGoogleAccountAudit = (ctx) => {
28610
28845
  if (!existsSync38(googleAuditScript)) return;
28611
28846
  try {
28612
28847
  const child = spawn3(process.execPath, [googleAuditScript], {
@@ -28614,6 +28849,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
28614
28849
  stdio: "inherit"
28615
28850
  });
28616
28851
  child.unref();
28852
+ ctx.child(child.pid);
28617
28853
  child.on(
28618
28854
  "error",
28619
28855
  (err) => console.error(`[google-account-audit] op=spawn-failed error="${err.message}"`)
@@ -28653,6 +28889,16 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
28653
28889
  firstRunDelayMs: 35e3,
28654
28890
  run: runPagesAuditSafe
28655
28891
  });
28892
+ const runDataPortalAuditSafe = () => runDataPortalAudit(auditPlatformRoot).catch((err) => {
28893
+ console.error(`[data-portal-audit] error="${err.message}"`);
28894
+ throw err;
28895
+ });
28896
+ registerLoop({
28897
+ name: "data-portal-audit",
28898
+ intervalMs: STORAGE_AUDIT_INTERVAL_MS,
28899
+ firstRunDelayMs: 5e4,
28900
+ run: runDataPortalAuditSafe
28901
+ });
28656
28902
  }
28657
28903
  {
28658
28904
  const publishPlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join42(__dirname, "..");
@@ -28666,7 +28912,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
28666
28912
  return null;
28667
28913
  }
28668
28914
  };
28669
- const spawnPublish = (account) => {
28915
+ const spawnPublish = (account, ctx) => {
28670
28916
  if (!existsSync38(publishScript)) return;
28671
28917
  try {
28672
28918
  const child = spawn3(process.execPath, [publishScript], {
@@ -28679,6 +28925,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
28679
28925
  stdio: "inherit"
28680
28926
  });
28681
28927
  child.unref();
28928
+ ctx.child(child.pid);
28682
28929
  child.on("error", (err) => console.error(`[calendar-availability] op=publish result=error reason=spawn-failed err="${err.message}"`));
28683
28930
  } catch (err) {
28684
28931
  console.error(`[calendar-availability] op=publish result=error reason=spawn-failed err="${err.message}"`);
@@ -28710,10 +28957,10 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
28710
28957
  const ageMs = Number.isNaN(parsed) ? null : Date.now() - parsed;
28711
28958
  console.error(`[calendar-availability] op=audit accountId=${account.accountId} snapshotAgeMs=${ageMs ?? "na"}`);
28712
28959
  };
28713
- const publishTick = () => {
28960
+ const publishTick = (ctx) => {
28714
28961
  const account = currentAccount();
28715
28962
  if (!account) return;
28716
- spawnPublish(account);
28963
+ spawnPublish(account, ctx);
28717
28964
  auditSnapshotAge(account);
28718
28965
  };
28719
28966
  registerLoop({
@@ -28731,7 +28978,7 @@ startLedgerCensus(() => getSession());
28731
28978
  const auditScript = resolve36(auditPlatformRoot, "plugins/connector/mcp/dist/scripts/audit-connectors.js");
28732
28979
  const auditLogDir = process.env.LOG_DIR ?? LOG_DIR;
28733
28980
  const CONNECTOR_AUDIT_INTERVAL_MS = 3e5;
28734
- const runConnectorAudit = () => {
28981
+ const runConnectorAudit = (ctx) => {
28735
28982
  if (!existsSync38(auditScript)) return;
28736
28983
  try {
28737
28984
  const child = spawn3(process.execPath, [auditScript], {
@@ -28739,6 +28986,7 @@ startLedgerCensus(() => getSession());
28739
28986
  stdio: "inherit"
28740
28987
  });
28741
28988
  child.unref();
28989
+ ctx.child(child.pid);
28742
28990
  child.on("error", (err) => console.error(`[connector-audit] spawn-failed err="${err.message}"`));
28743
28991
  } catch (err) {
28744
28992
  console.error(`[connector-audit] spawn-failed err="${err.message}"`);
@@ -14,6 +14,7 @@ import {
14
14
  makeHouseCfExec,
15
15
  makeHousePagesExec,
16
16
  readHouseCredential,
17
+ reconcileDataPortal,
17
18
  reconcilePages,
18
19
  reconcileStorage,
19
20
  registerResource,
@@ -22,7 +23,7 @@ import {
22
23
  stripAccountWideTokens,
23
24
  tooLargeMessage,
24
25
  validateMapping
25
- } from "./chunk-56WJMBQQ.js";
26
+ } from "./chunk-3XLLTG6R.js";
26
27
  import "./chunk-PFF6I7KP.js";
27
28
  export {
28
29
  D1_MAX_SQL_STATEMENT_BYTES,
@@ -39,6 +40,7 @@ export {
39
40
  makeHouseCfExec,
40
41
  makeHousePagesExec,
41
42
  readHouseCredential,
43
+ reconcileDataPortal,
42
44
  reconcilePages,
43
45
  reconcileStorage,
44
46
  registerResource,