@rubytech/create-maxy-code 0.1.485 → 0.1.487

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 (105) hide show
  1. package/package.json +1 -1
  2. package/payload/platform/docs/superpowers/plans/2026-07-21-graph-caption-resolver.md +124 -0
  3. package/payload/platform/docs/superpowers/plans/2026-07-21-task-1877-bash-schema-enforcement.md +107 -0
  4. package/payload/platform/docs/superpowers/plans/2026-07-21-task-1887-reconcile-allowed-top-level.md +205 -0
  5. package/payload/platform/lib/graph-style/dist/index.d.ts +4 -1
  6. package/payload/platform/lib/graph-style/dist/index.d.ts.map +1 -1
  7. package/payload/platform/lib/graph-style/dist/index.js +109 -1
  8. package/payload/platform/lib/graph-style/dist/index.js.map +1 -1
  9. package/payload/platform/lib/graph-style/src/__tests__/caption-coverage.test.ts +117 -0
  10. package/payload/platform/lib/graph-style/src/index.ts +81 -1
  11. package/payload/platform/plugins/admin/hooks/__tests__/fs-schema-guard-bash.test.sh +159 -0
  12. package/payload/platform/plugins/admin/hooks/__tests__/fs-schema-guard.test.sh +18 -6
  13. package/payload/platform/plugins/admin/hooks/fs-schema-guard-bash-post.sh +141 -0
  14. package/payload/platform/plugins/admin/hooks/fs-schema-guard-bash-pre.sh +66 -0
  15. package/payload/platform/plugins/admin/hooks/fs-schema-guard.sh +33 -1
  16. package/payload/platform/plugins/admin/skills/professional-document/SKILL.md +3 -3
  17. package/payload/platform/plugins/cloudflare/PLUGIN.md +5 -3
  18. package/payload/platform/plugins/cloudflare/bin/portal-enrol.mjs +23 -2
  19. package/payload/platform/plugins/cloudflare/bin/portal-index-push.mjs +30 -8
  20. package/payload/platform/plugins/cloudflare/bin/schema-exposed-dirs.mjs +22 -2
  21. package/payload/platform/plugins/cloudflare/mcp/__tests__/authorize.test.ts +2 -2
  22. package/payload/platform/plugins/cloudflare/mcp/__tests__/portal-directory-listing.test.ts +49 -2
  23. package/payload/platform/plugins/cloudflare/mcp/__tests__/portal-enrol.test.ts +57 -3
  24. package/payload/platform/plugins/cloudflare/mcp/__tests__/portal-index-push.test.ts +23 -0
  25. package/payload/platform/plugins/cloudflare/mcp/__tests__/portal-indexed-download.test.ts +44 -2
  26. package/payload/platform/plugins/cloudflare/mcp/__tests__/portal-session-grant.test.ts +67 -0
  27. package/payload/platform/plugins/cloudflare/mcp/__tests__/schema-exposed-dirs.test.ts +43 -0
  28. package/payload/platform/plugins/cloudflare/skills/data-portal/SKILL.md +54 -5
  29. package/payload/platform/plugins/cloudflare/skills/data-portal/schema.sql +10 -1
  30. package/payload/platform/plugins/cloudflare/skills/data-portal/template/functions/api/_lib/session.ts +19 -5
  31. package/payload/platform/plugins/cloudflare/skills/data-portal/template/functions/api/download.ts +19 -2
  32. package/payload/platform/plugins/cloudflare/skills/data-portal/template/functions/api/files.ts +8 -2
  33. package/payload/platform/plugins/cloudflare/skills/data-portal/template/index.html +28 -5
  34. package/payload/platform/plugins/cloudflare/skills/data-portal/template/portal.css +195 -6
  35. package/payload/platform/plugins/cloudflare/skills/data-portal/template/portal.js +193 -10
  36. package/payload/platform/plugins/cloudflare/skills/data-portal/template/wrangler.toml +4 -3
  37. package/payload/platform/plugins/email/.claude-plugin/plugin.json +1 -1
  38. package/payload/platform/plugins/email/PLUGIN.md +5 -1
  39. package/payload/platform/plugins/email/mcp/dist/__tests__/attachment-select.test.d.ts +2 -0
  40. package/payload/platform/plugins/email/mcp/dist/__tests__/attachment-select.test.d.ts.map +1 -0
  41. package/payload/platform/plugins/email/mcp/dist/__tests__/attachment-select.test.js +50 -0
  42. package/payload/platform/plugins/email/mcp/dist/__tests__/attachment-select.test.js.map +1 -0
  43. package/payload/platform/plugins/email/mcp/dist/__tests__/attachment-text.test.d.ts +2 -0
  44. package/payload/platform/plugins/email/mcp/dist/__tests__/attachment-text.test.d.ts.map +1 -0
  45. package/payload/platform/plugins/email/mcp/dist/__tests__/attachment-text.test.js +51 -0
  46. package/payload/platform/plugins/email/mcp/dist/__tests__/attachment-text.test.js.map +1 -0
  47. package/payload/platform/plugins/email/mcp/dist/__tests__/email-fetch-attachment.test.d.ts +2 -0
  48. package/payload/platform/plugins/email/mcp/dist/__tests__/email-fetch-attachment.test.d.ts.map +1 -0
  49. package/payload/platform/plugins/email/mcp/dist/__tests__/email-fetch-attachment.test.js +89 -0
  50. package/payload/platform/plugins/email/mcp/dist/__tests__/email-fetch-attachment.test.js.map +1 -0
  51. package/payload/platform/plugins/email/mcp/dist/index.js +28 -0
  52. package/payload/platform/plugins/email/mcp/dist/index.js.map +1 -1
  53. package/payload/platform/plugins/email/mcp/dist/lib/attachment-text.d.ts +22 -0
  54. package/payload/platform/plugins/email/mcp/dist/lib/attachment-text.d.ts.map +1 -0
  55. package/payload/platform/plugins/email/mcp/dist/lib/attachment-text.js +55 -0
  56. package/payload/platform/plugins/email/mcp/dist/lib/attachment-text.js.map +1 -0
  57. package/payload/platform/plugins/email/mcp/dist/lib/imap.d.ts +32 -0
  58. package/payload/platform/plugins/email/mcp/dist/lib/imap.d.ts.map +1 -1
  59. package/payload/platform/plugins/email/mcp/dist/lib/imap.js +75 -1
  60. package/payload/platform/plugins/email/mcp/dist/lib/imap.js.map +1 -1
  61. package/payload/platform/plugins/email/mcp/dist/tools/email-fetch-attachment.d.ts +19 -0
  62. package/payload/platform/plugins/email/mcp/dist/tools/email-fetch-attachment.d.ts.map +1 -0
  63. package/payload/platform/plugins/email/mcp/dist/tools/email-fetch-attachment.js +49 -0
  64. package/payload/platform/plugins/email/mcp/dist/tools/email-fetch-attachment.js.map +1 -0
  65. package/payload/platform/plugins/email/references/email-reference.md +7 -1
  66. package/payload/platform/plugins/memory/references/schema-construction.md +18 -12
  67. package/payload/platform/plugins/memory/references/schema-estate-agent.md +11 -6
  68. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/free-busy-url.test.d.ts +2 -0
  69. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/free-busy-url.test.d.ts.map +1 -0
  70. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/free-busy-url.test.js +12 -0
  71. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/free-busy-url.test.js.map +1 -0
  72. package/payload/platform/plugins/scheduling/mcp/dist/lib/free-busy-url.d.ts +8 -0
  73. package/payload/platform/plugins/scheduling/mcp/dist/lib/free-busy-url.d.ts.map +1 -0
  74. package/payload/platform/plugins/scheduling/mcp/dist/lib/free-busy-url.js +12 -0
  75. package/payload/platform/plugins/scheduling/mcp/dist/lib/free-busy-url.js.map +1 -0
  76. package/payload/platform/plugins/scheduling/mcp/dist/scripts/publish-availability.js +12 -2
  77. package/payload/platform/plugins/scheduling/mcp/dist/scripts/publish-availability.js.map +1 -1
  78. package/payload/platform/scripts/__tests__/account-schema-owned-dirs.test.sh +75 -6
  79. package/payload/platform/scripts/lib/account-schema-owned-dirs.py +111 -22
  80. package/payload/platform/scripts/lib/provision-account-dir.sh +8 -1
  81. package/payload/platform/services/claude-session-manager/dist/account-dir-schema-reconcile.d.ts +39 -0
  82. package/payload/platform/services/claude-session-manager/dist/account-dir-schema-reconcile.d.ts.map +1 -0
  83. package/payload/platform/services/claude-session-manager/dist/account-dir-schema-reconcile.js +315 -0
  84. package/payload/platform/services/claude-session-manager/dist/account-dir-schema-reconcile.js.map +1 -0
  85. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.d.ts.map +1 -1
  86. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js +1 -0
  87. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js.map +1 -1
  88. package/payload/platform/services/claude-session-manager/dist/index.js +15 -0
  89. package/payload/platform/services/claude-session-manager/dist/index.js.map +1 -1
  90. package/payload/platform/services/claude-session-manager/dist/system-prompt.d.ts.map +1 -1
  91. package/payload/platform/services/claude-session-manager/dist/system-prompt.js +4 -0
  92. package/payload/platform/services/claude-session-manager/dist/system-prompt.js.map +1 -1
  93. package/payload/platform/templates/account-schema/SCHEMA.md +17 -0
  94. package/payload/platform/templates/specialists/agents/personal-assistant.md +1 -1
  95. package/payload/server/{chunk-XGNRSM57.js → chunk-HN4HFTXE.js} +6 -0
  96. package/payload/server/maxy-edge.js +1 -1
  97. package/payload/server/public/assets/{admin-rScqRq-R.js → admin-BoVCP4Mw.js} +1 -1
  98. package/payload/server/public/assets/{data-TOWuhMFb.js → data-CtOxa94P.js} +1 -1
  99. package/payload/server/public/assets/{graph-labels-Axa04w8A.js → graph-labels-C8-sIKuv.js} +1 -1
  100. package/payload/server/public/assets/{graph-BEGs_P2m.js → graph-mJ9ky5dc.js} +1 -1
  101. package/payload/server/public/assets/{page-6CZ8R57f.js → page-Cv0qUwMG.js} +1 -1
  102. package/payload/server/public/data.html +3 -3
  103. package/payload/server/public/graph.html +2 -2
  104. package/payload/server/public/index.html +3 -3
  105. package/payload/server/server.js +231 -103
@@ -117,7 +117,7 @@ import {
117
117
  vncLog,
118
118
  walkPremiumBundles,
119
119
  writeAdminUserAndPerson
120
- } from "./chunk-XGNRSM57.js";
120
+ } from "./chunk-HN4HFTXE.js";
121
121
  import {
122
122
  D1_MAX_SQL_STATEMENT_BYTES,
123
123
  D1_QUERY_MAX_BODY_BYTES,
@@ -798,6 +798,21 @@ var require_dist3 = __commonJS({
798
798
  return null;
799
799
  return m[1];
800
800
  }
801
+ function estateAddress(props) {
802
+ const address = typeof props.address === "string" ? props.address : "";
803
+ const slug = typeof props.slug === "string" ? props.slug : "";
804
+ return address.length > 0 ? address : slug;
805
+ }
806
+ function estateListingName(props) {
807
+ const displayName = typeof props.displayName === "string" ? props.displayName : "";
808
+ const slug = typeof props.slug === "string" ? props.slug : "";
809
+ return displayName.length > 0 ? displayName : slug;
810
+ }
811
+ function offerCaption(props) {
812
+ const status = typeof props.status === "string" ? props.status : "";
813
+ const price = typeof props.price === "number" ? `\xA3${props.price}` : typeof props.price === "string" && props.price.length > 0 ? `\xA3${props.price}` : "";
814
+ return [status, price].filter((s) => s.length > 0).join(" ");
815
+ }
801
816
  function pickShortLabel(node) {
802
817
  const props = node.properties;
803
818
  const primaryLabel2 = node.labels[0];
@@ -870,6 +885,40 @@ var require_dist3 = __commonJS({
870
885
  if (ts)
871
886
  return `${roleShort} ${ts}`;
872
887
  }
888
+ } else if (primaryLabel2 === "Property") {
889
+ const v = estateAddress(props);
890
+ if (v.length > 0)
891
+ return v.length > 24 ? v.slice(0, 24) + "\u2026" : v;
892
+ } else if (primaryLabel2 === "Listing") {
893
+ const v = estateListingName(props);
894
+ if (v.length > 0)
895
+ return v.length > 24 ? v.slice(0, 24) + "\u2026" : v;
896
+ } else if (primaryLabel2 === "Viewing") {
897
+ const v = typeof props.date === "string" ? props.date : "";
898
+ if (v.length > 0)
899
+ return v.length > 24 ? v.slice(0, 24) + "\u2026" : v;
900
+ } else if (primaryLabel2 === "Offer") {
901
+ const v = offerCaption(props);
902
+ if (v.length > 0)
903
+ return v.length > 24 ? v.slice(0, 24) + "\u2026" : v;
904
+ } else if (primaryLabel2 === "Visit") {
905
+ const purpose = typeof props.purpose === "string" ? props.purpose : "";
906
+ const status = typeof props.status === "string" ? props.status : "";
907
+ const v = purpose.length > 0 ? purpose : status;
908
+ if (v.length > 0)
909
+ return v.length > 24 ? v.slice(0, 24) + "\u2026" : v;
910
+ } else if (primaryLabel2 === "PurchaseOrder") {
911
+ const v = typeof props.poNumber === "string" ? props.poNumber : "";
912
+ if (v.length > 0)
913
+ return v.length > 24 ? v.slice(0, 24) + "\u2026" : v;
914
+ } else if (primaryLabel2 === "Invoice") {
915
+ const v = typeof props.confirmationNumber === "string" ? props.confirmationNumber : "";
916
+ if (v.length > 0)
917
+ return v.length > 24 ? v.slice(0, 24) + "\u2026" : v;
918
+ } else if (primaryLabel2 === "Risk" || primaryLabel2 === "Finding" || primaryLabel2 === "Hypothesis") {
919
+ const v = typeof props.statement === "string" ? props.statement : "";
920
+ if (v.length > 0)
921
+ return v.length > 24 ? v.slice(0, 24) + "\u2026" : v;
873
922
  }
874
923
  for (const k of ["name", "title", "summary", "givenName", "subject", "text"]) {
875
924
  const v = props[k];
@@ -938,6 +987,40 @@ var require_dist3 = __commonJS({
938
987
  const desc = typeof props.description === "string" ? props.description : "";
939
988
  if (desc.length > 0)
940
989
  return desc;
990
+ } else if (primaryLabel2 === "Property") {
991
+ const v = estateAddress(props);
992
+ if (v.length > 0)
993
+ return v;
994
+ } else if (primaryLabel2 === "Listing") {
995
+ const v = estateListingName(props);
996
+ if (v.length > 0)
997
+ return v;
998
+ } else if (primaryLabel2 === "Viewing") {
999
+ const v = typeof props.date === "string" ? props.date : "";
1000
+ if (v.length > 0)
1001
+ return v;
1002
+ } else if (primaryLabel2 === "Offer") {
1003
+ const v = offerCaption(props);
1004
+ if (v.length > 0)
1005
+ return v;
1006
+ } else if (primaryLabel2 === "Visit") {
1007
+ const purpose = typeof props.purpose === "string" ? props.purpose : "";
1008
+ const status = typeof props.status === "string" ? props.status : "";
1009
+ const v = purpose.length > 0 ? purpose : status;
1010
+ if (v.length > 0)
1011
+ return v;
1012
+ } else if (primaryLabel2 === "PurchaseOrder") {
1013
+ const v = typeof props.poNumber === "string" ? props.poNumber : "";
1014
+ if (v.length > 0)
1015
+ return v;
1016
+ } else if (primaryLabel2 === "Invoice") {
1017
+ const v = typeof props.confirmationNumber === "string" ? props.confirmationNumber : "";
1018
+ if (v.length > 0)
1019
+ return v;
1020
+ } else if (primaryLabel2 === "Risk" || primaryLabel2 === "Finding" || primaryLabel2 === "Hypothesis") {
1021
+ const v = typeof props.statement === "string" ? props.statement : "";
1022
+ if (v.length > 0)
1023
+ return v;
941
1024
  }
942
1025
  for (const k of ["name", "title", "summary", "givenName", "subject", "text"]) {
943
1026
  const v = props[k];
@@ -1560,10 +1643,10 @@ var serveStatic = (options = { root: "" }) => {
1560
1643
  };
1561
1644
 
1562
1645
  // server/index.ts
1563
- import { readFileSync as readFileSync42, existsSync as existsSync38, watchFile, readdirSync as readdirSync26, statSync as statSync17 } from "fs";
1646
+ import { readFileSync as readFileSync43, existsSync as existsSync39, watchFile, readdirSync as readdirSync26, statSync as statSync17 } from "fs";
1564
1647
  import { spawn as spawn3 } from "child_process";
1565
1648
  import { createHash as createHash7 } from "crypto";
1566
- import { resolve as resolve38, join as join45, basename as basename12 } from "path";
1649
+ import { resolve as resolve39, join as join45, basename as basename12 } from "path";
1567
1650
  import { homedir as homedir4 } from "os";
1568
1651
  import { monitorEventLoopDelay } from "perf_hooks";
1569
1652
 
@@ -2837,7 +2920,7 @@ var credsSaveQueue = Promise.resolve();
2837
2920
  async function drainCredsSaveQueue(timeoutMs = 5e3) {
2838
2921
  console.error(`${TAG6} draining credential save queue\u2026`);
2839
2922
  const timer = new Promise(
2840
- (resolve39) => setTimeout(() => resolve39("timeout"), timeoutMs)
2923
+ (resolve40) => setTimeout(() => resolve40("timeout"), timeoutMs)
2841
2924
  );
2842
2925
  const result = await Promise.race([
2843
2926
  credsSaveQueue.then(() => "drained"),
@@ -3014,11 +3097,11 @@ async function createWaSocket(opts) {
3014
3097
  return sock;
3015
3098
  }
3016
3099
  async function waitForConnection(sock) {
3017
- return new Promise((resolve39, reject) => {
3100
+ return new Promise((resolve40, reject) => {
3018
3101
  const handler = (update) => {
3019
3102
  if (update.connection === "open") {
3020
3103
  sock.ev.off("connection.update", handler);
3021
- resolve39();
3104
+ resolve40();
3022
3105
  }
3023
3106
  if (update.connection === "close") {
3024
3107
  sock.ev.off("connection.update", handler);
@@ -3126,7 +3209,7 @@ function isTimeoutError(err) {
3126
3209
  return Boolean(err?.__maxyTimeout);
3127
3210
  }
3128
3211
  function withTimeout(label, promise, timeoutMs) {
3129
- return new Promise((resolve39, reject) => {
3212
+ return new Promise((resolve40, reject) => {
3130
3213
  const timer = setTimeout(() => {
3131
3214
  const err = new Error(`${label} timed out after ${timeoutMs}ms`);
3132
3215
  err.__maxyTimeout = true;
@@ -3136,7 +3219,7 @@ function withTimeout(label, promise, timeoutMs) {
3136
3219
  promise.then(
3137
3220
  (value) => {
3138
3221
  clearTimeout(timer);
3139
- resolve39(value);
3222
+ resolve40(value);
3140
3223
  },
3141
3224
  (err) => {
3142
3225
  clearTimeout(timer);
@@ -4195,8 +4278,8 @@ async function persistWhatsAppMessage(input) {
4195
4278
  const { givenName, familyName } = splitName(input.pushName);
4196
4279
  const prev = sessionWriteLocks.get(input.cacheKey);
4197
4280
  let release;
4198
- const mine = new Promise((resolve39) => {
4199
- release = resolve39;
4281
+ const mine = new Promise((resolve40) => {
4282
+ release = resolve40;
4200
4283
  });
4201
4284
  const chained = (prev ?? Promise.resolve()).then(() => mine);
4202
4285
  sessionWriteLocks.set(input.cacheKey, chained);
@@ -5933,11 +6016,11 @@ async function connectWithReconnect(conn) {
5933
6016
  console.error(
5934
6017
  `${TAG17} reconnecting account=${conn.accountId} in ${delay}ms (attempt ${decision.nextAttempts}/${maxAttempts})`
5935
6018
  );
5936
- await new Promise((resolve39) => {
5937
- const timer = setTimeout(resolve39, delay);
6019
+ await new Promise((resolve40) => {
6020
+ const timer = setTimeout(resolve40, delay);
5938
6021
  conn.abortController.signal.addEventListener("abort", () => {
5939
6022
  clearTimeout(timer);
5940
- resolve39();
6023
+ resolve40();
5941
6024
  }, { once: true });
5942
6025
  });
5943
6026
  }
@@ -5945,16 +6028,16 @@ async function connectWithReconnect(conn) {
5945
6028
  }
5946
6029
  }
5947
6030
  function waitForDisconnectEvent(conn) {
5948
- return new Promise((resolve39) => {
6031
+ return new Promise((resolve40) => {
5949
6032
  if (!conn.sock) {
5950
- resolve39();
6033
+ resolve40();
5951
6034
  return;
5952
6035
  }
5953
6036
  const sock = conn.sock;
5954
6037
  const handler = (update) => {
5955
6038
  if (update.connection === "close") {
5956
6039
  sock.ev.off("connection.update", handler);
5957
- resolve39();
6040
+ resolve40();
5958
6041
  }
5959
6042
  };
5960
6043
  sock.ev.on("connection.update", handler);
@@ -6428,8 +6511,8 @@ async function handleInboundMessage(conn, msg) {
6428
6511
  const conversationKey = isGroup ? remoteJid : senderPhone;
6429
6512
  const debounceKey = `${conn.accountId}:${conversationKey}:${senderPhone}`;
6430
6513
  let resolvePending;
6431
- const sttPending = new Promise((resolve39) => {
6432
- resolvePending = resolve39;
6514
+ const sttPending = new Promise((resolve40) => {
6515
+ resolvePending = resolve40;
6433
6516
  });
6434
6517
  if (conn.debouncer) conn.debouncer.registerPending(debounceKey, sttPending);
6435
6518
  try {
@@ -6818,20 +6901,20 @@ function buildX11Env(chromiumWrapperPath, transport = "vnc") {
6818
6901
 
6819
6902
  // server/routes/health.ts
6820
6903
  function checkPort(port2, timeoutMs = 500) {
6821
- return new Promise((resolve39) => {
6904
+ return new Promise((resolve40) => {
6822
6905
  const socket = createConnection2(port2, "127.0.0.1");
6823
6906
  socket.setTimeout(timeoutMs);
6824
6907
  socket.once("connect", () => {
6825
6908
  socket.destroy();
6826
- resolve39(true);
6909
+ resolve40(true);
6827
6910
  });
6828
6911
  socket.once("error", () => {
6829
6912
  socket.destroy();
6830
- resolve39(false);
6913
+ resolve40(false);
6831
6914
  });
6832
6915
  socket.once("timeout", () => {
6833
6916
  socket.destroy();
6834
- resolve39(false);
6917
+ resolve40(false);
6835
6918
  });
6836
6919
  });
6837
6920
  }
@@ -8162,8 +8245,8 @@ async function startLogin(opts) {
8162
8245
  await clearAuth(authDir);
8163
8246
  let resolveCode = null;
8164
8247
  let rejectCode = null;
8165
- const codePromise = new Promise((resolve39, reject) => {
8166
- resolveCode = resolve39;
8248
+ const codePromise = new Promise((resolve40, reject) => {
8249
+ resolveCode = resolve40;
8167
8250
  rejectCode = reject;
8168
8251
  });
8169
8252
  const codeTimer = setTimeout(
@@ -14598,15 +14681,15 @@ function operatorRoleFor(config, userId) {
14598
14681
  async function buildAccountOptionList(accounts, userId) {
14599
14682
  const houseAccount = accounts.find((a) => a.config.role === "house");
14600
14683
  console.log(`[admin-accounts] op=list count=${accounts.length} house=${houseAccount?.accountId ?? "none"}`);
14601
- const resolve39 = async (a) => {
14684
+ const resolve40 = async (a) => {
14602
14685
  const businessName = await fetchAccountName(a.accountId) || void 0;
14603
14686
  return { accountId: a.accountId, businessName, role: operatorRoleFor(a.config, userId), isHouse: a.config.role === "house" };
14604
14687
  };
14605
14688
  const houseOptions = await Promise.all(
14606
- accounts.filter((a) => a.config.role === "house").map(resolve39)
14689
+ accounts.filter((a) => a.config.role === "house").map(resolve40)
14607
14690
  );
14608
14691
  const nonHouseOptions = await Promise.all(
14609
- accounts.filter((a) => a.config.role !== "house").map(resolve39)
14692
+ accounts.filter((a) => a.config.role !== "house").map(resolve40)
14610
14693
  );
14611
14694
  const sortKey = (o) => (o.businessName || o.accountId).toLowerCase();
14612
14695
  nonHouseOptions.sort((x, y) => {
@@ -24411,7 +24494,17 @@ app62.get("/free-busy", async (c) => {
24411
24494
  if (Number.isNaN(Date.parse(from)) || Number.isNaN(Date.parse(to))) {
24412
24495
  return c.json({ error: "from and to must be parseable ISO timestamps" }, 400);
24413
24496
  }
24414
- const account = resolveAccount();
24497
+ const accountParam = c.req.query("account");
24498
+ let account;
24499
+ if (accountParam) {
24500
+ account = listValidAccounts().find((a) => a.accountId === accountParam) ?? null;
24501
+ if (!account) {
24502
+ console.error(`[calendar] op=free-busy-unknown-account account="${accountParam}"`);
24503
+ return c.json({ error: "Availability not configured" }, 404);
24504
+ }
24505
+ } else {
24506
+ account = resolveAccount();
24507
+ }
24415
24508
  if (!account) return c.json({ error: "No account configured" }, 500);
24416
24509
  let config;
24417
24510
  try {
@@ -24463,6 +24556,8 @@ var ONT_END = "<!-- ontology-buckets:end -->";
24463
24556
  var OWNED_START = "<!-- plugin-owned-dirs:start -->";
24464
24557
  var OWNED_END = "<!-- plugin-owned-dirs:end -->";
24465
24558
  var ALWAYS_EXPOSED = "output";
24559
+ var SYSTEM_DIRS = /* @__PURE__ */ new Set(["agents", "secrets", "specialists", "state", "memory", "cache", "logs", "tmp"]);
24560
+ var SAFE_FOLDER = /^[A-Za-z0-9_-]+$/;
24466
24561
  var DOMAIN_LINE = /^- `([^`/]+)\/` - one folder per /;
24467
24562
  var OWNED_LINE = /^- `([^`/]+)\/` — /;
24468
24563
  function region(text, start, end) {
@@ -24482,7 +24577,7 @@ function allowedBlockOrNull(text) {
24482
24577
  }
24483
24578
  return null;
24484
24579
  }
24485
- function resolveExposedDirs(schemaText) {
24580
+ function resolveExposedDirs(schemaText, exposeFolders = []) {
24486
24581
  const empty = {
24487
24582
  schemaPresent: false,
24488
24583
  allowed: (
@@ -24524,7 +24619,11 @@ function resolveExposedDirs(schemaText) {
24524
24619
  const pluginOwned = dirsIn(owned, OWNED_LINE);
24525
24620
  const collisions = domainBuckets.filter((d) => pluginOwned.includes(d));
24526
24621
  const candidates = [ALWAYS_EXPOSED, ...domainBuckets.filter((d) => !collisions.includes(d))];
24527
- const exposed = [...new Set(candidates)].filter((d) => allowed.includes(d)).sort();
24622
+ const ontologyExposed = [...new Set(candidates)].filter((d) => allowed.includes(d));
24623
+ const extras = (Array.isArray(exposeFolders) ? exposeFolders : []).filter(
24624
+ (f) => typeof f === "string" && SAFE_FOLDER.test(f) && !SYSTEM_DIRS.has(f)
24625
+ );
24626
+ const exposed = [.../* @__PURE__ */ new Set([...ontologyExposed, ...extras])].sort();
24528
24627
  return { schemaPresent: true, allowed, domainBuckets, pluginOwned, collisions, exposed };
24529
24628
  }
24530
24629
 
@@ -24573,6 +24672,14 @@ async function readAccountSecret(accountId) {
24573
24672
  function isValidAccountId2(value) {
24574
24673
  return /^[A-Za-z0-9-]+$/.test(value);
24575
24674
  }
24675
+ async function readExposeFolders(accountDir) {
24676
+ try {
24677
+ const cfg = JSON.parse(await readFile6(join42(accountDir, "data-portal.json"), "utf8"));
24678
+ return Array.isArray(cfg?.exposeFolders) ? cfg.exposeFolders.filter((f) => typeof f === "string") : [];
24679
+ } catch {
24680
+ return [];
24681
+ }
24682
+ }
24576
24683
  async function resolveFetchTarget(accountDir, relPath) {
24577
24684
  let schemaText = null;
24578
24685
  try {
@@ -24580,7 +24687,8 @@ async function resolveFetchTarget(accountDir, relPath) {
24580
24687
  } catch {
24581
24688
  schemaText = null;
24582
24689
  }
24583
- const { exposed } = resolveExposedDirs(schemaText);
24690
+ const exposeFolders = await readExposeFolders(accountDir);
24691
+ const { exposed } = resolveExposedDirs(schemaText, exposeFolders);
24584
24692
  if (exposed.length === 0) return { ok: false, reason: "not-exposed" };
24585
24693
  const lexical = resolve32(accountDir, relPath);
24586
24694
  let root;
@@ -26401,16 +26509,16 @@ var WebchatGateway = class _WebchatGateway {
26401
26509
  * The public /api/chat route awaits this to return the reply on the same SSE
26402
26510
  * response, preserving the pre-756 POST→single-blob browser contract. */
26403
26511
  awaitReply(key, timeoutMs) {
26404
- return new Promise((resolve39) => {
26512
+ return new Promise((resolve40) => {
26405
26513
  const timer = setTimeout(() => {
26406
26514
  this.replyAwaiters.delete(key);
26407
- resolve39({ timeout: true });
26515
+ resolve40({ timeout: true });
26408
26516
  }, timeoutMs);
26409
26517
  if (timer.unref) timer.unref();
26410
26518
  this.replyAwaiters.set(key, (r) => {
26411
26519
  clearTimeout(timer);
26412
26520
  this.replyAwaiters.delete(key);
26413
- resolve39(r);
26521
+ resolve40(r);
26414
26522
  });
26415
26523
  });
26416
26524
  }
@@ -26421,11 +26529,11 @@ var WebchatGateway = class _WebchatGateway {
26421
26529
  * terminal dialog stays answerable). */
26422
26530
  awaitPermissionVerdict(p, timeoutMs) {
26423
26531
  const k = _WebchatGateway.promptKey(p.key, p.requestId);
26424
- return new Promise((resolve39) => {
26532
+ return new Promise((resolve40) => {
26425
26533
  this.pendingPrompts.get(k)?.resolve({ timeout: true });
26426
26534
  const timer = setTimeout(() => {
26427
26535
  this.pendingPrompts.delete(k);
26428
- resolve39({ timeout: true });
26536
+ resolve40({ timeout: true });
26429
26537
  }, timeoutMs);
26430
26538
  if (timer.unref) timer.unref();
26431
26539
  this.pendingPrompts.set(k, {
@@ -26434,7 +26542,7 @@ var WebchatGateway = class _WebchatGateway {
26434
26542
  resolve: (r) => {
26435
26543
  clearTimeout(timer);
26436
26544
  this.pendingPrompts.delete(k);
26437
- resolve39(r);
26545
+ resolve40(r);
26438
26546
  }
26439
26547
  });
26440
26548
  console.error(`[webchat:perm] op=open key=${keyDisplay(p.key)} id=${p.requestId} tool=${p.toolName}`);
@@ -28058,6 +28166,22 @@ function warnOnChannelAdminBindingDrift() {
28058
28166
  }
28059
28167
  }
28060
28168
 
28169
+ // server/lib/booking-site-accounts.ts
28170
+ import { existsSync as existsSync37, readFileSync as readFileSync41 } from "fs";
28171
+ import { resolve as resolve37 } from "path";
28172
+ function listBookingSiteAccounts(accounts) {
28173
+ return accounts.filter((a) => {
28174
+ const availPath = resolve37(a.accountDir, "calendar-availability.json");
28175
+ if (!existsSync37(availPath)) return false;
28176
+ try {
28177
+ const cfg = JSON.parse(readFileSync41(availPath, "utf-8"));
28178
+ return typeof cfg.bookingDbName === "string" && cfg.bookingDbName.length > 0;
28179
+ } catch {
28180
+ return false;
28181
+ }
28182
+ });
28183
+ }
28184
+
28061
28185
  // app/lib/admin-sse-registry.ts
28062
28186
  var activeAdminSSEControllers = /* @__PURE__ */ new Set();
28063
28187
  function broadcastAdminShutdown(reason) {
@@ -28088,8 +28212,8 @@ function broadcastAdminShutdown(reason) {
28088
28212
 
28089
28213
  // ../lib/entitlement/src/index.ts
28090
28214
  import { createPublicKey, createHash as createHash6, verify as cryptoVerify } from "crypto";
28091
- import { existsSync as existsSync37, readFileSync as readFileSync41, statSync as statSync16 } from "fs";
28092
- import { resolve as resolve37 } from "path";
28215
+ import { existsSync as existsSync38, readFileSync as readFileSync42, statSync as statSync16 } from "fs";
28216
+ import { resolve as resolve38 } from "path";
28093
28217
 
28094
28218
  // ../lib/entitlement/src/canonicalize.ts
28095
28219
  function canonicalize(value) {
@@ -28124,7 +28248,7 @@ var PUBKEY_SHA256 = "8eee6bcb33545fd13b16d3199a5735ca5db5062834c7b49dfe4f23801d9
28124
28248
  var GRACE_DAYS = 7;
28125
28249
  var GRACE_MS = GRACE_DAYS * 24 * 60 * 60 * 1e3;
28126
28250
  function pubkeyPath(brand) {
28127
- return resolve37(brand.platformRoot, "lib", "entitlement", "rubytech-pubkey.pem");
28251
+ return resolve38(brand.platformRoot, "lib", "entitlement", "rubytech-pubkey.pem");
28128
28252
  }
28129
28253
  var memo = null;
28130
28254
  function memoKey(mtimeMs, account) {
@@ -28136,8 +28260,8 @@ function resolveEntitlement(brand, account) {
28136
28260
  if (brand.commercialMode !== true) {
28137
28261
  return logResolved(implicitTrust(account), null);
28138
28262
  }
28139
- const entitlementPath = resolve37(brand.configDir, "entitlement.json");
28140
- if (!existsSync37(entitlementPath)) {
28263
+ const entitlementPath = resolve38(brand.configDir, "entitlement.json");
28264
+ if (!existsSync38(entitlementPath)) {
28141
28265
  return logResolved(anonymousFallback("missing"), { reason: "missing" });
28142
28266
  }
28143
28267
  const stat9 = statSync16(entitlementPath);
@@ -28152,7 +28276,7 @@ function resolveEntitlement(brand, account) {
28152
28276
  function verifyAndResolve(brand, entitlementPath, account) {
28153
28277
  let pubkeyPem;
28154
28278
  try {
28155
- pubkeyPem = readFileSync41(pubkeyPath(brand), "utf-8");
28279
+ pubkeyPem = readFileSync42(pubkeyPath(brand), "utf-8");
28156
28280
  } catch (err) {
28157
28281
  return logResolved(anonymousFallback("pubkey-missing"), {
28158
28282
  reason: "pubkey-missing"
@@ -28166,7 +28290,7 @@ function verifyAndResolve(brand, entitlementPath, account) {
28166
28290
  }
28167
28291
  let envelope;
28168
28292
  try {
28169
- envelope = JSON.parse(readFileSync41(entitlementPath, "utf-8"));
28293
+ envelope = JSON.parse(readFileSync42(entitlementPath, "utf-8"));
28170
28294
  } catch {
28171
28295
  return logResolved(anonymousFallback("malformed"), { reason: "malformed" });
28172
28296
  }
@@ -28330,12 +28454,12 @@ installMediaDownloadGuard();
28330
28454
  var PLATFORM_ROOT8 = process.env.MAXY_PLATFORM_ROOT || "";
28331
28455
  var BRAND_JSON_PATH = PLATFORM_ROOT8 ? join45(PLATFORM_ROOT8, "config", "brand.json") : "";
28332
28456
  var BRAND = { productName: "Maxy", hostname: "maxy", configDir: ".maxy", marketingUrl: "https://getmaxy.com" };
28333
- if (BRAND_JSON_PATH && !existsSync38(BRAND_JSON_PATH)) {
28457
+ if (BRAND_JSON_PATH && !existsSync39(BRAND_JSON_PATH)) {
28334
28458
  console.error(`[brand] WARNING: brand.json not found at ${BRAND_JSON_PATH} \u2014 using Maxy defaults`);
28335
28459
  }
28336
- if (BRAND_JSON_PATH && existsSync38(BRAND_JSON_PATH)) {
28460
+ if (BRAND_JSON_PATH && existsSync39(BRAND_JSON_PATH)) {
28337
28461
  try {
28338
- const parsed = JSON.parse(readFileSync42(BRAND_JSON_PATH, "utf-8"));
28462
+ const parsed = JSON.parse(readFileSync43(BRAND_JSON_PATH, "utf-8"));
28339
28463
  BRAND = { ...BRAND, ...parsed };
28340
28464
  } catch (err) {
28341
28465
  console.error(`[brand] Failed to parse brand.json: ${err.message}`);
@@ -28370,8 +28494,8 @@ var brandLoginOpts = {
28370
28494
  var ALIAS_DOMAINS_PATH = join45(homedir4(), BRAND.configDir, "alias-domains.json");
28371
28495
  function loadAliasDomains() {
28372
28496
  try {
28373
- if (!existsSync38(ALIAS_DOMAINS_PATH)) return null;
28374
- const parsed = JSON.parse(readFileSync42(ALIAS_DOMAINS_PATH, "utf-8"));
28497
+ if (!existsSync39(ALIAS_DOMAINS_PATH)) return null;
28498
+ const parsed = JSON.parse(readFileSync43(ALIAS_DOMAINS_PATH, "utf-8"));
28375
28499
  if (!Array.isArray(parsed)) {
28376
28500
  console.error("[alias-domains] malformed alias-domains.json \u2014 expected array");
28377
28501
  return null;
@@ -28444,7 +28568,7 @@ var waGateway = new WaGateway({
28444
28568
  fetchStandingRules: fetchAccountStandingRules,
28445
28569
  resolveSessionOverride: (accountId, senderId) => resolveChannelOverride(accountId, "whatsapp", senderId),
28446
28570
  gatewayUrl: `http://127.0.0.1:${process.env.MAXY_UI_INTERNAL_PORT ?? ""}`,
28447
- serverPath: process.env.MAXY_WA_CHANNEL_SERVER_PATH ?? resolve38(process.env.MAXY_PLATFORM_ROOT ?? join45(__dirname, ".."), "services/whatsapp-channel/dist/server.js"),
28571
+ serverPath: process.env.MAXY_WA_CHANNEL_SERVER_PATH ?? resolve39(process.env.MAXY_PLATFORM_ROOT ?? join45(__dirname, ".."), "services/whatsapp-channel/dist/server.js"),
28448
28572
  // Task 751 / 1390 — file delivery on the native channel. `maxyAccountId` (the
28449
28573
  // path-validation scope) is the sender's effective SESSION account, resolved
28450
28574
  // once at the inbound gate and threaded here via the gateway's per-sender doc
@@ -28459,7 +28583,7 @@ var waGateway = new WaGateway({
28459
28583
  caption,
28460
28584
  accountId,
28461
28585
  maxyAccountId,
28462
- platformRoot: resolve38(process.env.MAXY_PLATFORM_ROOT ?? join45(__dirname, ".."))
28586
+ platformRoot: resolve39(process.env.MAXY_PLATFORM_ROOT ?? join45(__dirname, ".."))
28463
28587
  });
28464
28588
  return result.ok ? { ok: true, messageId: result.messageId } : { ok: false, error: result.error };
28465
28589
  },
@@ -29135,20 +29259,20 @@ app64.get("/agent-assets/:slug/:filename", (c) => {
29135
29259
  console.error(`[agent-assets] no-account slug=${slug} file=${filename}`);
29136
29260
  return c.text("Not found", 404);
29137
29261
  }
29138
- const filePath = resolve38(account.accountDir, "agents", slug, "assets", filename);
29139
- const expectedDir = resolve38(account.accountDir, "agents", slug, "assets");
29262
+ const filePath = resolve39(account.accountDir, "agents", slug, "assets", filename);
29263
+ const expectedDir = resolve39(account.accountDir, "agents", slug, "assets");
29140
29264
  if (!filePath.startsWith(expectedDir + "/")) {
29141
29265
  console.error(`[agent-assets] path-traversal-rejected slug=${slug} file=${filename}`);
29142
29266
  return c.text("Forbidden", 403);
29143
29267
  }
29144
- if (!existsSync38(filePath)) {
29268
+ if (!existsSync39(filePath)) {
29145
29269
  console.error(`[agent-assets] serve slug=${slug} file=${filename} status=404`);
29146
29270
  return c.text("Not found", 404);
29147
29271
  }
29148
29272
  const ext = "." + filename.split(".").pop()?.toLowerCase();
29149
29273
  const contentType = IMAGE_MIME[ext] || "application/octet-stream";
29150
29274
  console.log(`[agent-assets] serve slug=${slug} file=${filename} status=200`);
29151
- const body = readFileSync42(filePath);
29275
+ const body = readFileSync43(filePath);
29152
29276
  return c.body(body, 200, {
29153
29277
  "Content-Type": contentType,
29154
29278
  "Cache-Control": "public, max-age=3600"
@@ -29165,20 +29289,20 @@ app64.get("/generated/:filename", (c) => {
29165
29289
  console.error(`[generated] serve file=${filename} status=404`);
29166
29290
  return c.text("Not found", 404);
29167
29291
  }
29168
- const filePath = resolve38(account.accountDir, "generated", filename);
29169
- const expectedDir = resolve38(account.accountDir, "generated");
29292
+ const filePath = resolve39(account.accountDir, "generated", filename);
29293
+ const expectedDir = resolve39(account.accountDir, "generated");
29170
29294
  if (!filePath.startsWith(expectedDir + "/")) {
29171
29295
  console.error(`[generated] serve file=${filename} status=403`);
29172
29296
  return c.text("Forbidden", 403);
29173
29297
  }
29174
- if (!existsSync38(filePath)) {
29298
+ if (!existsSync39(filePath)) {
29175
29299
  console.error(`[generated] serve file=${filename} status=404`);
29176
29300
  return c.text("Not found", 404);
29177
29301
  }
29178
29302
  const ext = "." + filename.split(".").pop()?.toLowerCase();
29179
29303
  const contentType = IMAGE_MIME[ext] || "application/octet-stream";
29180
29304
  console.log(`[generated] serve file=${filename} status=200`);
29181
- const body = readFileSync42(filePath);
29305
+ const body = readFileSync43(filePath);
29182
29306
  return c.body(body, 200, {
29183
29307
  "Content-Type": contentType,
29184
29308
  "Cache-Control": "public, max-age=86400"
@@ -29191,9 +29315,9 @@ app64.route("/v", visitor_consent_default);
29191
29315
  var htmlCache = /* @__PURE__ */ new Map();
29192
29316
  var brandLogoPath = "/brand/maxy-monochrome.png";
29193
29317
  var brandIconPath = "/brand/maxy-monochrome.png";
29194
- if (BRAND_JSON_PATH && existsSync38(BRAND_JSON_PATH)) {
29318
+ if (BRAND_JSON_PATH && existsSync39(BRAND_JSON_PATH)) {
29195
29319
  try {
29196
- const fullBrand = JSON.parse(readFileSync42(BRAND_JSON_PATH, "utf-8"));
29320
+ const fullBrand = JSON.parse(readFileSync43(BRAND_JSON_PATH, "utf-8"));
29197
29321
  if (fullBrand.assets?.logo) brandLogoPath = `/brand/${fullBrand.assets.logo}`;
29198
29322
  brandIconPath = fullBrand.assets?.icon ? `/brand/${fullBrand.assets.icon}` : brandLogoPath;
29199
29323
  } catch {
@@ -29220,11 +29344,11 @@ var brandScript = `<script>window.__BRAND__=${JSON.stringify({
29220
29344
  var brandThemeColor = BRAND.defaultColors?.primary ?? "#000000";
29221
29345
  var brandBackgroundColor = BRAND.defaultColors?.background ?? "#ffffff";
29222
29346
  var brandAppIconsExist = [brandAppIcon192Path, brandAppIcon512Path, brandMaskableIconPath].every(
29223
- (p) => existsSync38(resolve38(process.cwd(), "public", p.replace(/^\//, "")))
29347
+ (p) => existsSync39(resolve39(process.cwd(), "public", p.replace(/^\//, "")))
29224
29348
  );
29225
29349
  var SW_SOURCE = (() => {
29226
29350
  try {
29227
- return readFileSync42(resolve38(process.cwd(), "public", "sw.js"), "utf-8");
29351
+ return readFileSync43(resolve39(process.cwd(), "public", "sw.js"), "utf-8");
29228
29352
  } catch {
29229
29353
  return null;
29230
29354
  }
@@ -29233,8 +29357,8 @@ function readInstalledVersion() {
29233
29357
  try {
29234
29358
  if (!PLATFORM_ROOT8) return "unknown";
29235
29359
  const versionFile = join45(PLATFORM_ROOT8, "config", `.${BRAND.hostname}-version`);
29236
- if (!existsSync38(versionFile)) return "unknown";
29237
- const content = readFileSync42(versionFile, "utf-8").trim();
29360
+ if (!existsSync39(versionFile)) return "unknown";
29361
+ const content = readFileSync43(versionFile, "utf-8").trim();
29238
29362
  return content || "unknown";
29239
29363
  } catch {
29240
29364
  return "unknown";
@@ -29275,7 +29399,7 @@ var clientErrorReporterScript = `<script>
29275
29399
  function cachedHtml(file) {
29276
29400
  let html = htmlCache.get(file);
29277
29401
  if (!html) {
29278
- html = readFileSync42(resolve38(process.cwd(), "public", file), "utf-8");
29402
+ html = readFileSync43(resolve39(process.cwd(), "public", file), "utf-8");
29279
29403
  const productNameEsc = escapeHtml(BRAND.productName);
29280
29404
  html = html.replace(/<title>([^<]*)<\/title>/, (_match, inner) => `<title>${escapeHtml(inner).replace(/Maxy/g, productNameEsc)}</title>`);
29281
29405
  html = html.replace('href="/favicon.ico"', `href="${escapeHtml(brandFaviconPath)}"`);
@@ -29297,8 +29421,8 @@ function loadBrandingCache(agentSlug) {
29297
29421
  const accountId = getDefaultAccountId();
29298
29422
  if (!accountId) return null;
29299
29423
  const cachePath = join45(configDir2, "branding-cache", accountId, `${agentSlug}.json`);
29300
- if (!existsSync38(cachePath)) return null;
29301
- return JSON.parse(readFileSync42(cachePath, "utf-8"));
29424
+ if (!existsSync39(cachePath)) return null;
29425
+ return JSON.parse(readFileSync43(cachePath, "utf-8"));
29302
29426
  } catch {
29303
29427
  return null;
29304
29428
  }
@@ -29393,7 +29517,7 @@ app64.use("/vnc-popout.html", logViewerFetch);
29393
29517
  app64.get("/vnc-popout.html", (c) => {
29394
29518
  let html = htmlCache.get("vnc-popout.html");
29395
29519
  if (!html) {
29396
- html = readFileSync42(resolve38(process.cwd(), "public", "vnc-popout.html"), "utf-8");
29520
+ html = readFileSync43(resolve39(process.cwd(), "public", "vnc-popout.html"), "utf-8");
29397
29521
  const name = escapeHtml(BRAND.productName);
29398
29522
  html = html.replace("<title>Browser \u2014 Maxy</title>", `<title>${name}</title>`);
29399
29523
  html = html.replace("</head>", ` ${brandScript}
@@ -29531,10 +29655,10 @@ var httpServer = serve({ fetch: app64.fetch, port, hostname });
29531
29655
  console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
29532
29656
  {
29533
29657
  const reconcilePlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join45(__dirname, "..");
29534
- const reconcileScript = resolve38(reconcilePlatformRoot, "plugins/scheduling/mcp/dist/scripts/reconcile-bookings.js");
29658
+ const reconcileScript = resolve39(reconcilePlatformRoot, "plugins/scheduling/mcp/dist/scripts/reconcile-bookings.js");
29535
29659
  const RECONCILE_INTERVAL_MS = 12e4;
29536
29660
  const runReconcile = (ctx) => {
29537
- if (!existsSync38(reconcileScript)) return;
29661
+ if (!existsSync39(reconcileScript)) return;
29538
29662
  try {
29539
29663
  const child = spawn3(process.execPath, [reconcileScript], {
29540
29664
  env: { ...process.env, PLATFORM_ROOT: reconcilePlatformRoot },
@@ -29557,10 +29681,10 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
29557
29681
  }
29558
29682
  {
29559
29683
  const outlookPlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join45(__dirname, "..");
29560
- const outlookScript = resolve38(outlookPlatformRoot, "plugins/outlook/mcp/dist/scripts/complete-registration.js");
29684
+ const outlookScript = resolve39(outlookPlatformRoot, "plugins/outlook/mcp/dist/scripts/complete-registration.js");
29561
29685
  const OUTLOOK_COMPLETE_INTERVAL_MS = 3e4;
29562
29686
  const runOutlookComplete = (ctx) => {
29563
- if (!existsSync38(outlookScript)) return;
29687
+ if (!existsSync39(outlookScript)) return;
29564
29688
  try {
29565
29689
  const child = spawn3(process.execPath, [outlookScript], {
29566
29690
  env: { ...process.env, PLATFORM_ROOT: outlookPlatformRoot },
@@ -29583,18 +29707,18 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
29583
29707
  }
29584
29708
  {
29585
29709
  const auditRoot = process.env.MAXY_PLATFORM_ROOT ?? join45(__dirname, "..");
29586
- const strandedAccountsDir = resolve38(auditRoot, "..", "data/accounts");
29710
+ const strandedAccountsDir = resolve39(auditRoot, "..", "data/accounts");
29587
29711
  const STRANDED_AUDIT_INTERVAL_MS = 3e5;
29588
29712
  const STRANDED_AGE_MS = 16 * 6e4;
29589
29713
  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;
29590
29714
  const runStrandedAudit = () => {
29591
29715
  try {
29592
- if (!existsSync38(strandedAccountsDir)) return;
29716
+ if (!existsSync39(strandedAccountsDir)) return;
29593
29717
  const now = Date.now();
29594
29718
  for (const name of readdirSync26(strandedAccountsDir)) {
29595
29719
  if (!STRANDED_UUID_RE.test(name)) continue;
29596
- const pendingPath2 = resolve38(strandedAccountsDir, name, "secrets/outlook/pending-devicecode.enc");
29597
- if (!existsSync38(pendingPath2)) continue;
29720
+ const pendingPath2 = resolve39(strandedAccountsDir, name, "secrets/outlook/pending-devicecode.enc");
29721
+ if (!existsSync39(pendingPath2)) continue;
29598
29722
  const ageMs = now - statSync17(pendingPath2).mtimeMs;
29599
29723
  if (ageMs > STRANDED_AGE_MS) {
29600
29724
  console.error(`[outlook-mcp] devicecode-stranded account=${name} ageSec=${Math.floor(ageMs / 1e3)}`);
@@ -29613,7 +29737,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
29613
29737
  }
29614
29738
  {
29615
29739
  const googleRoot = process.env.MAXY_PLATFORM_ROOT ?? join45(__dirname, "..");
29616
- const googleAccountsDir = resolve38(googleRoot, "..", "data/accounts");
29740
+ const googleAccountsDir = resolve39(googleRoot, "..", "data/accounts");
29617
29741
  const GOOGLE_PENDING_AUDIT_INTERVAL_MS = 3e5;
29618
29742
  const runGooglePendingAuditSafe = () => {
29619
29743
  try {
@@ -29628,10 +29752,10 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
29628
29752
  firstRunDelayMs: 27e3,
29629
29753
  run: runGooglePendingAuditSafe
29630
29754
  });
29631
- const googleAuditScript = resolve38(googleRoot, "plugins/google/mcp/dist/scripts/account-audit.js");
29755
+ const googleAuditScript = resolve39(googleRoot, "plugins/google/mcp/dist/scripts/account-audit.js");
29632
29756
  const GOOGLE_ACCOUNT_AUDIT_INTERVAL_MS = 36e5;
29633
29757
  const runGoogleAccountAudit = (ctx) => {
29634
- if (!existsSync38(googleAuditScript)) return;
29758
+ if (!existsSync39(googleAuditScript)) return;
29635
29759
  try {
29636
29760
  const child = spawn3(process.execPath, [googleAuditScript], {
29637
29761
  env: { ...process.env, PLATFORM_ROOT: googleRoot },
@@ -29691,18 +29815,18 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
29691
29815
  }
29692
29816
  {
29693
29817
  const publishPlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join45(__dirname, "..");
29694
- const publishScript = resolve38(publishPlatformRoot, "plugins/scheduling/mcp/dist/scripts/publish-availability.js");
29818
+ const publishScript = resolve39(publishPlatformRoot, "plugins/scheduling/mcp/dist/scripts/publish-availability.js");
29695
29819
  const PUBLISH_INTERVAL_MS = 3e5;
29696
- const currentAccount = () => {
29820
+ const bookingAccounts = () => {
29697
29821
  try {
29698
- return resolveAccount();
29822
+ return listBookingSiteAccounts(listValidAccounts());
29699
29823
  } catch (err) {
29700
- console.error(`[calendar-availability] op=publish result=error reason=account-resolve-failed err="${err.message}"`);
29701
- return null;
29824
+ console.error(`[calendar-availability] op=publish result=error reason=enumerate-failed err="${err.message}"`);
29825
+ return [];
29702
29826
  }
29703
29827
  };
29704
29828
  const spawnPublish = (account, ctx) => {
29705
- if (!existsSync38(publishScript)) return;
29829
+ if (!existsSync39(publishScript)) return;
29706
29830
  try {
29707
29831
  const child = spawn3(process.execPath, [publishScript], {
29708
29832
  env: {
@@ -29721,21 +29845,21 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
29721
29845
  }
29722
29846
  };
29723
29847
  const auditSnapshotAge = (account) => {
29724
- const availPath = resolve38(account.accountDir, "calendar-availability.json");
29848
+ const availPath = resolve39(account.accountDir, "calendar-availability.json");
29725
29849
  let hasBookingSite = false;
29726
29850
  try {
29727
- if (existsSync38(availPath)) {
29728
- const cfg = JSON.parse(readFileSync42(availPath, "utf-8"));
29851
+ if (existsSync39(availPath)) {
29852
+ const cfg = JSON.parse(readFileSync43(availPath, "utf-8"));
29729
29853
  hasBookingSite = typeof cfg.bookingDbName === "string" && cfg.bookingDbName.length > 0;
29730
29854
  }
29731
29855
  } catch {
29732
29856
  }
29733
29857
  if (!hasBookingSite) return;
29734
- const statePath = resolve38(account.accountDir, "state", "booking-availability", "last-publish.json");
29858
+ const statePath = resolve39(account.accountDir, "state", "booking-availability", "last-publish.json");
29735
29859
  let lastSuccessAt = null;
29736
- if (existsSync38(statePath)) {
29860
+ if (existsSync39(statePath)) {
29737
29861
  try {
29738
- const rec = JSON.parse(readFileSync42(statePath, "utf-8"));
29862
+ const rec = JSON.parse(readFileSync43(statePath, "utf-8"));
29739
29863
  lastSuccessAt = rec.lastSuccessAt ?? null;
29740
29864
  } catch {
29741
29865
  console.error(`[calendar-availability] op=audit accountId=${account.accountId} snapshotAgeMs=na reason=bad-state-file`);
@@ -29747,10 +29871,14 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
29747
29871
  console.error(`[calendar-availability] op=audit accountId=${account.accountId} snapshotAgeMs=${ageMs ?? "na"}`);
29748
29872
  };
29749
29873
  const publishTick = (ctx) => {
29750
- const account = currentAccount();
29751
- if (!account) return;
29752
- spawnPublish(account, ctx);
29753
- auditSnapshotAge(account);
29874
+ for (const account of bookingAccounts()) {
29875
+ try {
29876
+ spawnPublish(account, ctx);
29877
+ auditSnapshotAge(account);
29878
+ } catch (err) {
29879
+ console.error(`[calendar-availability] op=publish accountId=${account.accountId} result=error reason=tick-failed err="${err.message}"`);
29880
+ }
29881
+ }
29754
29882
  };
29755
29883
  registerLoop({
29756
29884
  name: "calendar-availability-publish",
@@ -29764,11 +29892,11 @@ startTimeEntryCensus(() => getSession());
29764
29892
  startLedgerCensus(() => getSession());
29765
29893
  {
29766
29894
  const auditPlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join45(__dirname, "..");
29767
- const auditScript = resolve38(auditPlatformRoot, "plugins/connector/mcp/dist/scripts/audit-connectors.js");
29895
+ const auditScript = resolve39(auditPlatformRoot, "plugins/connector/mcp/dist/scripts/audit-connectors.js");
29768
29896
  const auditLogDir = process.env.LOG_DIR ?? LOG_DIR;
29769
29897
  const CONNECTOR_AUDIT_INTERVAL_MS = 3e5;
29770
29898
  const runConnectorAudit = (ctx) => {
29771
- if (!existsSync38(auditScript)) return;
29899
+ if (!existsSync39(auditScript)) return;
29772
29900
  try {
29773
29901
  const child = spawn3(process.execPath, [auditScript], {
29774
29902
  env: { ...process.env, PLATFORM_ROOT: auditPlatformRoot, LOG_DIR: auditLogDir },
@@ -29888,11 +30016,11 @@ try {
29888
30016
  var ADMINUSER_RECONCILE_INTERVAL_MS = 60 * 60 * 1e3;
29889
30017
  async function runAdminUserReconcileTick() {
29890
30018
  try {
29891
- if (!existsSync38(USERS_FILE)) {
30019
+ if (!existsSync39(USERS_FILE)) {
29892
30020
  console.error("[adminuser-self-heal] skip reason=no-users-file");
29893
30021
  return;
29894
30022
  }
29895
- const usersRaw = readFileSync42(USERS_FILE, "utf-8").trim();
30023
+ const usersRaw = readFileSync43(USERS_FILE, "utf-8").trim();
29896
30024
  if (!usersRaw) {
29897
30025
  console.error("[adminuser-self-heal] skip reason=empty-users-file");
29898
30026
  return;
@@ -29939,8 +30067,8 @@ registerLoop({
29939
30067
  });
29940
30068
  var USERS_RECONCILE_INTERVAL_MS = 60 * 60 * 1e3;
29941
30069
  function countUsersRows() {
29942
- if (!existsSync38(USERS_FILE)) return 0;
29943
- const raw = readFileSync42(USERS_FILE, "utf-8").trim();
30070
+ if (!existsSync39(USERS_FILE)) return 0;
30071
+ const raw = readFileSync43(USERS_FILE, "utf-8").trim();
29944
30072
  if (!raw) return 0;
29945
30073
  const users = JSON.parse(raw);
29946
30074
  return users.filter((u) => typeof u.userId === "string").length;
@@ -30073,7 +30201,7 @@ if (bootAccountConfig?.whatsapp) {
30073
30201
  }
30074
30202
  init({
30075
30203
  configDir: configDirForWhatsApp,
30076
- platformRoot: resolve38(process.env.MAXY_PLATFORM_ROOT ?? join45(__dirname, "..")),
30204
+ platformRoot: resolve39(process.env.MAXY_PLATFORM_ROOT ?? join45(__dirname, "..")),
30077
30205
  accountConfig: bootAccountConfig,
30078
30206
  onMessage: async (msg) => {
30079
30207
  if (isObserveAccount(msg.accountId)) {