@rubytech/create-maxy-code 0.1.476 → 0.1.477

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 (59) hide show
  1. package/package.json +1 -1
  2. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/booking-block-sqlite.test.d.ts +2 -0
  3. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/booking-block-sqlite.test.d.ts.map +1 -0
  4. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/booking-block-sqlite.test.js +150 -0
  5. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/booking-block-sqlite.test.js.map +1 -0
  6. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/booking-block.test.d.ts +2 -0
  7. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/booking-block.test.d.ts.map +1 -0
  8. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/booking-block.test.js +248 -0
  9. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/booking-block.test.js.map +1 -0
  10. package/payload/platform/plugins/scheduling/mcp/dist/lib/booking-reconcile.d.ts +90 -0
  11. package/payload/platform/plugins/scheduling/mcp/dist/lib/booking-reconcile.d.ts.map +1 -1
  12. package/payload/platform/plugins/scheduling/mcp/dist/lib/booking-reconcile.js +105 -0
  13. package/payload/platform/plugins/scheduling/mcp/dist/lib/booking-reconcile.js.map +1 -1
  14. package/payload/platform/plugins/scheduling/mcp/dist/scripts/reconcile-bookings.js +88 -10
  15. package/payload/platform/plugins/scheduling/mcp/dist/scripts/reconcile-bookings.js.map +1 -1
  16. package/payload/platform/scripts/cpu-triage.sh +313 -0
  17. package/payload/server/{chunk-LBCMFD4O.js → chunk-JECAP3Z2.js} +126 -37
  18. package/payload/server/maxy-edge.js +1 -1
  19. package/payload/server/public/activity.html +6 -6
  20. package/payload/server/public/assets/{AdminLoginScreens-DximwPlS.js → AdminLoginScreens-mEKP4pDi.js} +1 -1
  21. package/payload/server/public/assets/{AdminShell-BehbH-Oa.js → AdminShell-BwSaZ88d.js} +1 -1
  22. package/payload/server/public/assets/{Checkbox-DM-eHqOS.js → Checkbox-CN4uQ80w.js} +1 -1
  23. package/payload/server/public/assets/activity-CIQ8ozNZ.js +1 -0
  24. package/payload/server/public/assets/{admin-CAkDnGdk.js → admin-BVhgfgs1.js} +1 -1
  25. package/payload/server/public/assets/{browser-DXl8hOoi.js → browser-D-HMmHcM.js} +1 -1
  26. package/payload/server/public/assets/{calendar-DP6hn4-6.js → calendar-tGWvFyFn.js} +1 -1
  27. package/payload/server/public/assets/chat-DkttVxAz.js +1 -0
  28. package/payload/server/public/assets/chevron-left-CZ4ez9G5.js +1 -0
  29. package/payload/server/public/assets/data-P-mcmnNi.js +1 -0
  30. package/payload/server/public/assets/{graph-labels-CZykslZM.js → graph-labels-qnBleOE6.js} +1 -1
  31. package/payload/server/public/assets/{graph-CHcYoEJ5.js → graph-zEw610xK.js} +1 -1
  32. package/payload/server/public/assets/{maximize-2-31AZEcMS.js → maximize-2-BlTjXT_Y.js} +1 -1
  33. package/payload/server/public/assets/{operator-BGGpdsO_.js → operator-C7oIw2PG.js} +1 -1
  34. package/payload/server/public/assets/{page-DOeiiqbR.js → page-CTP7OFZa.js} +1 -1
  35. package/payload/server/public/assets/{page-BloC6ygA.js → page-DL6Zsdvk.js} +1 -1
  36. package/payload/server/public/assets/{public-CGgO6IZv.js → public-r1A9dqR_.js} +1 -1
  37. package/payload/server/public/assets/{rotate-ccw-BlAi6iFg.js → rotate-ccw-CzkkKx4-.js} +1 -1
  38. package/payload/server/public/assets/{tasks-C1giFRRL.js → tasks-fIJwYFWG.js} +1 -1
  39. package/payload/server/public/assets/{time-entry-format-CiUUQdq7.js → time-entry-format-j669DgXj.js} +1 -1
  40. package/payload/server/public/assets/{triangle-alert-CVqKuLoa.js → triangle-alert-BPgCgIef.js} +1 -1
  41. package/payload/server/public/assets/{useCopyFeedback-ksyUe_g1.js → useCopyFeedback-BmLa2aes.js} +1 -1
  42. package/payload/server/public/assets/{useSelectionMode-BPrBkdTd.js → useSelectionMode-DnnM7A8-.js} +1 -1
  43. package/payload/server/public/assets/{useSubAccountSwitcher-K4kYeN6i.css → useSubAccountSwitcher-C_E8h07P.css} +1 -1
  44. package/payload/server/public/assets/{useVoiceRecorder-CTXP8oTW.js → useVoiceRecorder-u2dwZfen.js} +1 -1
  45. package/payload/server/public/browser.html +5 -5
  46. package/payload/server/public/calendar.html +6 -6
  47. package/payload/server/public/chat.html +12 -12
  48. package/payload/server/public/data.html +11 -11
  49. package/payload/server/public/graph.html +11 -11
  50. package/payload/server/public/index.html +14 -14
  51. package/payload/server/public/operator.html +14 -14
  52. package/payload/server/public/public.html +12 -12
  53. package/payload/server/public/tasks.html +5 -5
  54. package/payload/server/server.js +173 -60
  55. package/payload/server/public/assets/activity-hDwUeQd6.js +0 -1
  56. package/payload/server/public/assets/chat-DhOfVGYM.js +0 -1
  57. package/payload/server/public/assets/chevron-left-DYNBoLii.js +0 -1
  58. package/payload/server/public/assets/data-bq8fTo_s.js +0 -1
  59. /package/payload/server/public/assets/{useSubAccountSwitcher-DzgzTRRn.js → useSubAccountSwitcher-CFa6ZAT1.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-JECAP3Z2.js";
119
121
  import {
120
122
  D1_MAX_SQL_STATEMENT_BYTES,
121
123
  D1_QUERY_MAX_BODY_BYTES,
@@ -9636,36 +9638,43 @@ app3.post("/r2/object/delete", envelopeBodyLimit("write"), async (c) => {
9636
9638
  var storage_broker_default = app3;
9637
9639
  async function runStorageAudit(root) {
9638
9640
  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 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
+ // Its own session per invocation, never one captured from the enclosing
9647
+ // scope. reconcileStorage issues the "d1" and "r2" reads through the same
9648
+ // Promise.all, and a Neo4j session admits one query at a time — sharing one
9649
+ // made the second reject, which left this audit reporting nothing for 802
9650
+ // consecutive ticks on maxy-code and 295 on sitedesk-code. Sessions come
9651
+ // from the driver pool, so one per read is cheap; the alternative,
9652
+ // serialising the library's fan-out, would bend it around this caller.
9653
+ registeredNames: async (kind) => {
9654
+ const session = getSession();
9655
+ try {
9647
9656
  const res = await session.run(
9648
9657
  "MATCH (r:StorageResource {kind: $kind}) RETURN r.name AS name",
9649
9658
  { kind }
9650
9659
  );
9651
9660
  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;
9661
+ } finally {
9662
+ await session.close();
9661
9663
  }
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
- }
9664
+ },
9665
+ accountSecretsFiles: async () => {
9666
+ if (!existsSync9(accountsDir)) return [];
9667
+ const files = [];
9668
+ for (const accountId of readdirSync8(accountsDir)) {
9669
+ const p = join13(accountsDir, accountId, "secrets", "cloudflare.env");
9670
+ if (existsSync9(p)) files.push({ accountId, path: p, contents: readFileSync15(p, "utf8") });
9671
+ }
9672
+ return files;
9673
+ }
9674
+ })).strays;
9675
+ console.error(
9676
+ `[storage-audit] strays=${strays.length}${strays.map((s) => ` ${s.kind}:${s.detail}`).join("")}`
9677
+ );
9669
9678
  }
9670
9679
  async function runPagesAudit(root) {
9671
9680
  const { reconcilePages } = await import("./src-JYPKMWJR.js");
@@ -13905,12 +13914,21 @@ function checkRateLimit2(ip, cls) {
13905
13914
  }
13906
13915
  return { allow: true, blocked: 0, shouldLog: false };
13907
13916
  }
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);
13917
+ registerLoop({
13918
+ name: "client-error-rate-cleanup",
13919
+ intervalMs: WINDOW_MS,
13920
+ run: () => {
13921
+ const now = Date.now();
13922
+ let purged = 0;
13923
+ for (const [key, rec] of rateHits) {
13924
+ if (now - rec.windowStart >= WINDOW_MS * 2) {
13925
+ rateHits.delete(key);
13926
+ purged += 1;
13927
+ }
13928
+ }
13929
+ return `purged ${purged}, ${rateHits.size} live`;
13912
13930
  }
13913
- }, WINDOW_MS).unref();
13931
+ });
13914
13932
  function truncate(value, max) {
13915
13933
  if (typeof value !== "string") return "";
13916
13934
  return value.length > max ? value.slice(0, max) + "\u2026" : value;
@@ -22386,6 +22404,9 @@ app52.get("/", requireAdminSession, async (c) => {
22386
22404
  if (name) names[a.accountId] = name;
22387
22405
  })
22388
22406
  );
22407
+ const sampledAt = Date.now();
22408
+ sampleCpu(sampledAt);
22409
+ const cpu = sampleProcessCpu(sampledAt);
22389
22410
  const loopRows = listLoops();
22390
22411
  const armed2 = armedCount();
22391
22412
  const byState = loopRows.reduce((acc, r) => {
@@ -22393,13 +22414,22 @@ app52.get("/", requireAdminSession, async (c) => {
22393
22414
  return acc;
22394
22415
  }, {});
22395
22416
  console.log(
22396
- `${TAG42} op=loops req=${req} armed=${armed2} rendered=${loopRows.length} ` + Object.entries(byState).map(([s, n]) => `${s}=${n}`).join(" ")
22417
+ `${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
22418
  );
22398
22419
  return c.json({
22399
22420
  rows,
22400
22421
  census,
22401
22422
  censusAgeMs,
22402
- loops: { armed: armed2, rows: loopRows },
22423
+ loops: {
22424
+ armed: armed2,
22425
+ rows: loopRows,
22426
+ // The server's own CPU beside what the registry can explain. The GAP is
22427
+ // the point: loops summing to 2% against a process at 60% says the cost
22428
+ // is somewhere else, which small numbers alone would never tell you.
22429
+ processPct: cpu.processPct,
22430
+ attributedPct: cpu.attributedPct,
22431
+ unattributedPct: cpu.unattributedPct
22432
+ },
22403
22433
  accounts: {
22404
22434
  onDisk,
22405
22435
  valid: valid.length,
@@ -23143,12 +23173,21 @@ function rateLimit(ip, now = Date.now()) {
23143
23173
  if (b.count > RATE_LIMIT) return { ok: false, resetIn: b.resetAt - now };
23144
23174
  return { ok: true, resetIn: b.resetAt - now };
23145
23175
  }
23146
- setInterval(() => {
23147
- const now = Date.now();
23148
- for (const [ip, b] of buckets) {
23149
- if (now >= b.resetAt) buckets.delete(ip);
23176
+ registerLoop({
23177
+ name: "visitor-event-rate-cleanup",
23178
+ intervalMs: 5 * RATE_WINDOW_MS,
23179
+ run: () => {
23180
+ const now = Date.now();
23181
+ let purged = 0;
23182
+ for (const [ip, b] of buckets) {
23183
+ if (now >= b.resetAt) {
23184
+ buckets.delete(ip);
23185
+ purged += 1;
23186
+ }
23187
+ }
23188
+ return `purged ${purged}, ${buckets.size} live`;
23150
23189
  }
23151
- }, 5 * RATE_WINDOW_MS).unref?.();
23190
+ });
23152
23191
  var VALID_TYPES = /* @__PURE__ */ new Set(["pageview", "click", "scroll", "dwell"]);
23153
23192
  var ID_RE = /^[A-Za-z0-9_-]{6,128}$/;
23154
23193
  function parseEvent(body) {
@@ -24976,10 +25015,28 @@ var WaGateway = class {
24976
25015
  }
24977
25016
  return events;
24978
25017
  }
24979
- /** Start the periodic reconciliation sweep in the long-lived UI process.
24980
- * Returns the interval handle. */
25018
+ /** Arm the periodic reconciliation sweep in the long-lived UI process.
25019
+ *
25020
+ * Task 1814 — armed through the registry, so it appears on /activity. This
25021
+ * gateway is constructed once at module scope in server/index.ts and reached
25022
+ * everywhere else through the singleton accessor in ./instance, so the sweep
25023
+ * is per-install, not per-instance. Returns the registry's disarm function
25024
+ * rather than the interval handle; no caller reads it.
25025
+ *
25026
+ * Two semantics move with it. The timer is now unref'd, where the bare
25027
+ * setInterval was not: a loop is a passenger of the process, never what
25028
+ * keeps it alive. And a throwing sweep is recorded as lastError instead of
25029
+ * surfacing as an uncaught timer exception, so a sweep that fails every
25030
+ * tick now reads as a failing row on /activity rather than a crash. */
24981
25031
  startSweeper(intervalMs = SWEEP_INTERVAL_MS) {
24982
- return setInterval(() => this.reconcile(Date.now()), intervalMs);
25032
+ return registerLoop({
25033
+ name: "whatsapp-native-sweep",
25034
+ intervalMs,
25035
+ run: () => {
25036
+ const events = this.reconcile(Date.now());
25037
+ return events.length > 0 ? `${events.length} terminal events` : void 0;
25038
+ }
25039
+ });
24983
25040
  }
24984
25041
  /** Handle one inbound admin WhatsApp message. */
24985
25042
  async handleInbound(input) {
@@ -25552,11 +25609,22 @@ var WebchatGateway = class _WebchatGateway {
25552
25609
  void this.deps.deleteSession?.(rec.sessionId);
25553
25610
  }
25554
25611
  }
25555
- /** Start the periodic public idle reaper in the long-lived UI process. */
25612
+ /** Arm the periodic public idle reaper in the long-lived UI process.
25613
+ *
25614
+ * Task 1814 — armed through the registry, so it appears on /activity. Like
25615
+ * the sweeper below, this is per-install: one gateway per server, reached
25616
+ * through the singleton accessor in ./instance. Returns the registry's
25617
+ * disarm function rather than the interval handle; no caller reads it.
25618
+ *
25619
+ * This one already unref'd its handle, so only the error semantics move: a
25620
+ * throwing reap is recorded as lastError rather than surfacing as an
25621
+ * uncaught timer exception. */
25556
25622
  startPublicReaper(intervalMs = 6e4) {
25557
- const handle = setInterval(() => this.reapPublic(Date.now()), intervalMs);
25558
- if (handle.unref) handle.unref();
25559
- return handle;
25623
+ return registerLoop({
25624
+ name: "webchat-public-reaper",
25625
+ intervalMs,
25626
+ run: () => this.reapPublic(Date.now())
25627
+ });
25560
25628
  }
25561
25629
  /** A gated public PTY exited explicitly on the manager side (operator stop,
25562
25630
  * claude crash, manager DELETE), not via the idle reaper. The manager POSTs
@@ -25638,15 +25706,36 @@ var WebchatGateway = class _WebchatGateway {
25638
25706
  }
25639
25707
  return events;
25640
25708
  }
25641
- /** Start the periodic reconciliation sweep in the long-lived UI process. Each
25709
+ /** Arm the periodic reconciliation sweep in the long-lived UI process. Each
25642
25710
  * tick runs the sync sweep (drain-/inbound-undelivered) AND the async
25643
- * recovery pass for queued-no-subscriber messages (Task-962). */
25711
+ * recovery pass for queued-no-subscriber messages (Task-962).
25712
+ *
25713
+ * Task 1814 — armed through the registry, so it appears on /activity. This
25714
+ * gateway is constructed once at module scope in server/index.ts and reached
25715
+ * everywhere else through the singleton accessor in ./instance, so the sweep
25716
+ * is per-install, not per-instance. Returns the registry's disarm function
25717
+ * rather than the interval handle; no caller reads it.
25718
+ *
25719
+ * Two semantics move with it. The timer is now unref'd, where the bare
25720
+ * setInterval was not: a loop is a passenger of the process, never what
25721
+ * keeps it alive. And a throwing sweep is recorded as lastError instead of
25722
+ * surfacing as an uncaught timer exception, so a sweep that fails every
25723
+ * tick now reads as a failing row on /activity rather than a crash.
25724
+ *
25725
+ * recoverStalled stays fire-and-forget, exactly as it was: run() returns the
25726
+ * sync note, so the registry's duration covers the sweep and not the
25727
+ * recovery pass. */
25644
25728
  startSweeper(intervalMs = SWEEP_INTERVAL_MS2) {
25645
- return setInterval(() => {
25646
- const now = Date.now();
25647
- this.reconcile(now);
25648
- void this.recoverStalled(now);
25649
- }, intervalMs);
25729
+ return registerLoop({
25730
+ name: "webchat-inbound-sweep",
25731
+ intervalMs,
25732
+ run: () => {
25733
+ const now = Date.now();
25734
+ const events = this.reconcile(now);
25735
+ void this.recoverStalled(now);
25736
+ return events.length > 0 ? `${events.length} terminal events` : void 0;
25737
+ }
25738
+ });
25650
25739
  }
25651
25740
  /** Task-962 — the per-session delivery-failure marker for the /session pointer.
25652
25741
  * Non-null only when redelivery was exhausted for `key`; cleared on the next
@@ -26584,9 +26673,28 @@ var TelegramGateway = class {
26584
26673
  }
26585
26674
  return events;
26586
26675
  }
26587
- /** Start the periodic reconciliation sweep in the long-lived UI process. */
26676
+ /** Arm the periodic reconciliation sweep in the long-lived UI process.
26677
+ *
26678
+ * Task 1814 — armed through the registry, so it appears on /activity. This
26679
+ * gateway is constructed once at module scope in server/index.ts and reached
26680
+ * everywhere else through the singleton accessor in ./instance, so the sweep
26681
+ * is per-install, not per-instance. Returns the registry's disarm function
26682
+ * rather than the interval handle; no caller reads it.
26683
+ *
26684
+ * Two semantics move with it. The timer is now unref'd, where the bare
26685
+ * setInterval was not: a loop is a passenger of the process, never what
26686
+ * keeps it alive. And a throwing sweep is recorded as lastError instead of
26687
+ * surfacing as an uncaught timer exception, so a sweep that fails every
26688
+ * tick now reads as a failing row on /activity rather than a crash. */
26588
26689
  startSweeper(intervalMs = SWEEP_INTERVAL_MS3) {
26589
- return setInterval(() => this.reconcile(Date.now()), intervalMs);
26690
+ return registerLoop({
26691
+ name: "telegram-native-sweep",
26692
+ intervalMs,
26693
+ run: () => {
26694
+ const events = this.reconcile(Date.now());
26695
+ return events.length > 0 ? `${events.length} terminal events` : void 0;
26696
+ }
26697
+ });
26590
26698
  }
26591
26699
  /** Handle one inbound telegram message. */
26592
26700
  async handleInbound(input) {
@@ -28511,7 +28619,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
28511
28619
  const reconcilePlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join42(__dirname, "..");
28512
28620
  const reconcileScript = resolve36(reconcilePlatformRoot, "plugins/scheduling/mcp/dist/scripts/reconcile-bookings.js");
28513
28621
  const RECONCILE_INTERVAL_MS = 12e4;
28514
- const runReconcile = () => {
28622
+ const runReconcile = (ctx) => {
28515
28623
  if (!existsSync38(reconcileScript)) return;
28516
28624
  try {
28517
28625
  const child = spawn3(process.execPath, [reconcileScript], {
@@ -28519,6 +28627,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
28519
28627
  stdio: "inherit"
28520
28628
  });
28521
28629
  child.unref();
28630
+ ctx.child(child.pid);
28522
28631
  child.on("error", (err) => console.error(`[calendar-reconcile] spawn-failed err="${err.message}"`));
28523
28632
  } catch (err) {
28524
28633
  console.error(`[calendar-reconcile] spawn-failed err="${err.message}"`);
@@ -28536,7 +28645,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
28536
28645
  const outlookPlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join42(__dirname, "..");
28537
28646
  const outlookScript = resolve36(outlookPlatformRoot, "plugins/outlook/mcp/dist/scripts/complete-registration.js");
28538
28647
  const OUTLOOK_COMPLETE_INTERVAL_MS = 3e4;
28539
- const runOutlookComplete = () => {
28648
+ const runOutlookComplete = (ctx) => {
28540
28649
  if (!existsSync38(outlookScript)) return;
28541
28650
  try {
28542
28651
  const child = spawn3(process.execPath, [outlookScript], {
@@ -28544,6 +28653,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
28544
28653
  stdio: "inherit"
28545
28654
  });
28546
28655
  child.unref();
28656
+ ctx.child(child.pid);
28547
28657
  child.on("error", (err) => console.error(`[outlook-complete] spawn-failed err="${err.message}"`));
28548
28658
  } catch (err) {
28549
28659
  console.error(`[outlook-complete] spawn-failed err="${err.message}"`);
@@ -28606,7 +28716,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
28606
28716
  });
28607
28717
  const googleAuditScript = resolve36(googleRoot, "plugins/google/mcp/dist/scripts/account-audit.js");
28608
28718
  const GOOGLE_ACCOUNT_AUDIT_INTERVAL_MS = 36e5;
28609
- const runGoogleAccountAudit = () => {
28719
+ const runGoogleAccountAudit = (ctx) => {
28610
28720
  if (!existsSync38(googleAuditScript)) return;
28611
28721
  try {
28612
28722
  const child = spawn3(process.execPath, [googleAuditScript], {
@@ -28614,6 +28724,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
28614
28724
  stdio: "inherit"
28615
28725
  });
28616
28726
  child.unref();
28727
+ ctx.child(child.pid);
28617
28728
  child.on(
28618
28729
  "error",
28619
28730
  (err) => console.error(`[google-account-audit] op=spawn-failed error="${err.message}"`)
@@ -28666,7 +28777,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
28666
28777
  return null;
28667
28778
  }
28668
28779
  };
28669
- const spawnPublish = (account) => {
28780
+ const spawnPublish = (account, ctx) => {
28670
28781
  if (!existsSync38(publishScript)) return;
28671
28782
  try {
28672
28783
  const child = spawn3(process.execPath, [publishScript], {
@@ -28679,6 +28790,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
28679
28790
  stdio: "inherit"
28680
28791
  });
28681
28792
  child.unref();
28793
+ ctx.child(child.pid);
28682
28794
  child.on("error", (err) => console.error(`[calendar-availability] op=publish result=error reason=spawn-failed err="${err.message}"`));
28683
28795
  } catch (err) {
28684
28796
  console.error(`[calendar-availability] op=publish result=error reason=spawn-failed err="${err.message}"`);
@@ -28710,10 +28822,10 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
28710
28822
  const ageMs = Number.isNaN(parsed) ? null : Date.now() - parsed;
28711
28823
  console.error(`[calendar-availability] op=audit accountId=${account.accountId} snapshotAgeMs=${ageMs ?? "na"}`);
28712
28824
  };
28713
- const publishTick = () => {
28825
+ const publishTick = (ctx) => {
28714
28826
  const account = currentAccount();
28715
28827
  if (!account) return;
28716
- spawnPublish(account);
28828
+ spawnPublish(account, ctx);
28717
28829
  auditSnapshotAge(account);
28718
28830
  };
28719
28831
  registerLoop({
@@ -28731,7 +28843,7 @@ startLedgerCensus(() => getSession());
28731
28843
  const auditScript = resolve36(auditPlatformRoot, "plugins/connector/mcp/dist/scripts/audit-connectors.js");
28732
28844
  const auditLogDir = process.env.LOG_DIR ?? LOG_DIR;
28733
28845
  const CONNECTOR_AUDIT_INTERVAL_MS = 3e5;
28734
- const runConnectorAudit = () => {
28846
+ const runConnectorAudit = (ctx) => {
28735
28847
  if (!existsSync38(auditScript)) return;
28736
28848
  try {
28737
28849
  const child = spawn3(process.execPath, [auditScript], {
@@ -28739,6 +28851,7 @@ startLedgerCensus(() => getSession());
28739
28851
  stdio: "inherit"
28740
28852
  });
28741
28853
  child.unref();
28854
+ ctx.child(child.pid);
28742
28855
  child.on("error", (err) => console.error(`[connector-audit] spawn-failed err="${err.message}"`));
28743
28856
  } catch (err) {
28744
28857
  console.error(`[connector-audit] spawn-failed err="${err.message}"`);
@@ -1 +0,0 @@
1
- import{o as e}from"./chunk-TPjIAO9U.js";import{D as t,S as n,T as r,_ as i,d as a,g as o,r as s,w as c}from"./useSubAccountSwitcher-DzgzTRRn.js";import{t as l,y as u}from"./AdminShell-BehbH-Oa.js";import"./useSelectionMode-BPrBkdTd.js";import{t as d}from"./triangle-alert-CVqKuLoa.js";var f=i(`arrow-down`,[[`path`,{d:`M12 5v14`,key:`s699le`}],[`path`,{d:`m19 12-7 7-7-7`,key:`1idqje`}]]),p=i(`arrow-up`,[[`path`,{d:`m5 12 7-7 7 7`,key:`hav0vg`}],[`path`,{d:`M12 19V5`,key:`x0mq9r`}]]),m=r(),h=e(t(),1),g=n(),_=`—`;function v(e){switch(e){case`never-run`:return`Never run`;case`never-succeeded`:return`Never succeeded`;case`failing`:return`Failing`;case`stalled`:return`Stalled`;case`running`:return`Running`;case`ok`:return`Healthy`}}var y={"never-run":`Armed when the server started and has not fired once since. This is not the same as idle.`,"never-succeeded":`It has fired, and not one run has finished. The work this loop exists to do has never happened.`,failing:`It worked before and is failing now.`,stalled:`It has not started within three of its own intervals. Something is holding it up.`,running:`A run is in progress right now.`,ok:`The last run finished and the next is due within the interval.`},b=new Set([`never-run`,`never-succeeded`,`failing`,`stalled`]);function x(e,t){return t?`detached`:e===null||!Number.isFinite(e)?_:`${Math.round(e)} ms`}function S(e){return e<6e4?`${Math.round(e/1e3)}s`:e<36e5?`${Math.round(e/6e4)}m`:`${Math.round(e/36e5)}h`}function C(e,t){if(e===null)return _;let n=t-e;return n<0?`in ${S(-n)}`:n<6e4?`${Math.round(n/1e3)}s ago`:n<36e5?`${Math.round(n/6e4)}m ago`:`${Math.round(n/36e5)}h ago`}function w(e,t){if(e===null)return _;let n=e-t;return n>=0?`in ${S(n)}`:`overdue by ${S(-n)}`}function T({loops:e,nowMs:t=Date.now()}){if(!e)return(0,g.jsxs)(`section`,{className:`activity-loops`,children:[(0,g.jsx)(`h2`,{className:`activity-loops-heading`,children:`Scheduled work`}),(0,g.jsx)(`p`,{className:`activity-loops-empty`,children:`Scheduled work could not be read from the server.`})]});let n=e.armed-e.rows.length;return(0,g.jsxs)(`section`,{className:`activity-loops`,children:[(0,g.jsx)(`h2`,{className:`activity-loops-heading`,children:`Scheduled work`}),(0,g.jsxs)(`p`,{className:`activity-loops-sub`,children:[e.armed,` recurring `,e.armed===1?`loop is`:`loops are`,` armed on this install.`]}),n>0&&(0,g.jsxs)(`p`,{className:`activity-loops-omission`,role:`status`,children:[n,` armed `,n===1?`loop is`:`loops are`,` missing from this list. The server armed `,e.armed,` but sent `,e.rows.length,`.`]}),(0,g.jsxs)(`table`,{className:`activity-table`,children:[(0,g.jsx)(`thead`,{children:(0,g.jsxs)(`tr`,{children:[(0,g.jsx)(`th`,{scope:`col`,className:`activity-th`,children:`Loop`}),(0,g.jsx)(`th`,{scope:`col`,className:`activity-th`,children:`State`}),(0,g.jsx)(`th`,{scope:`col`,className:`activity-th`,children:`Every`}),(0,g.jsx)(`th`,{scope:`col`,className:`activity-th`,children:`Last run`}),(0,g.jsx)(`th`,{scope:`col`,className:`activity-th`,children:`Took`}),(0,g.jsx)(`th`,{scope:`col`,className:`activity-th`,children:`CPU`}),(0,g.jsx)(`th`,{scope:`col`,className:`activity-th`,children:`Runs`}),(0,g.jsx)(`th`,{scope:`col`,className:`activity-th`,children:`Produced`}),(0,g.jsx)(`th`,{scope:`col`,className:`activity-th`,children:`Next due`})]})}),(0,g.jsx)(`tbody`,{children:e.rows.map(e=>(0,g.jsxs)(`tr`,{className:b.has(e.state)?`activity-loop-row activity-loop-unhealthy`:`activity-loop-row`,children:[(0,g.jsx)(`td`,{className:`activity-td`,children:e.name}),(0,g.jsx)(`td`,{className:`activity-td`,title:y[e.state],children:v(e.state)}),(0,g.jsx)(`td`,{className:`activity-td activity-num`,children:S(e.intervalMs)}),(0,g.jsx)(`td`,{className:`activity-td activity-num`,children:C(e.lastStartMs,t)}),(0,g.jsx)(`td`,{className:`activity-td activity-num`,children:e.lastDurationMs===null?_:`${e.lastDurationMs} ms`}),(0,g.jsx)(`td`,{className:`activity-td activity-num`,title:e.detached?`This loop hands its work to a separate process, so its cost is real but cannot be measured here.`:void 0,children:x(e.lastCpuMs,e.detached)}),(0,g.jsxs)(`td`,{className:`activity-td activity-num`,children:[e.successCount,`/`,e.runCount]}),(0,g.jsx)(`td`,{className:`activity-td activity-loop-note`,children:e.lastError??e.lastNote??_}),(0,g.jsx)(`td`,{className:`activity-td activity-num`,children:w(e.nextDueMs,t)})]},e.name))})]}),(0,g.jsxs)(`dl`,{className:`activity-legend`,children:[(0,g.jsxs)(`div`,{className:`activity-legend-item`,children:[(0,g.jsx)(`dt`,{className:`activity-legend-term`,children:`Runs`}),(0,g.jsx)(`dd`,{className:`activity-legend-def`,children:`Finished runs out of attempted runs since the server started. 0 out of a large number means it has tried many times and never once finished.`})]}),(0,g.jsxs)(`div`,{className:`activity-legend-item`,children:[(0,g.jsx)(`dt`,{className:`activity-legend-term`,children:`CPU`}),(0,g.jsx)(`dd`,{className:`activity-legend-def`,children:`Processor time the last run used. “detached” means the loop hands its work to a separate process, so the cost is real but cannot be measured from here.`})]}),(0,g.jsxs)(`div`,{className:`activity-legend-item`,children:[(0,g.jsx)(`dt`,{className:`activity-legend-term`,children:_}),(0,g.jsx)(`dd`,{className:`activity-legend-def`,children:`Not measured. It does not mean zero.`})]})]})]})}var E=`maxy-admin-session-key`;function D(e){switch(e.costUnavailable){case`metering-failed`:return`Cost could not be calculated: ${e.meteringError??`the transcript could not be read`}`;case`model-not-priced`:return`No published price for this model, so cost cannot be estimated.`;case`no-transcript`:return`This session has no transcript yet, so there is nothing to cost.`;default:return}}var O=[{key:`accountId`,label:`Account`,numeric:!1,help:`The business this session belongs to. "untagged" means the session records no account.`},{key:`title`,label:`Session`,numeric:!1,help:`The conversation name, or its short id when it has none yet.`},{key:`model`,label:`Model`,numeric:!1,help:`The model that answered the most recent turn.`},{key:`status`,label:`Status`,numeric:!1,help:`What the process is doing right now.`},{key:`ageMs`,label:`Age`,numeric:!0,help:`How long ago this conversation started.`},{key:`lastMessageAt`,label:`Last activity`,numeric:!0,help:`The clock time of the most recent message, in your timezone. A date appears once it is not today.`},{key:`turnCount`,label:`Turns`,numeric:!0,help:`Messages exchanged. A trailing + means the count is a minimum — the conversation is too long to count exactly.`},{key:`rssKb`,label:`RAM`,numeric:!0,help:`Memory held by this session and all its helper processes together.`},{key:`cpuPct`,label:`CPU (avg)`,numeric:!0,help:`AVERAGE share of one processor core over the last census interval, up to 60 seconds. It is not a reading of this instant, so a session that answered a turn a minute ago still shows a high figure while sitting quiet now. Can exceed 100% because a session runs many processes at once.`},{key:`usedTokens`,label:`Tokens`,numeric:!0,help:`Size of the conversation the model currently reads back on every turn.`},{key:`costGbp`,label:`Cost`,numeric:!0,help:`Estimated spend on this conversation so far, at published prices for its model.`}],k=[`lastMessageAt`,`turnCount`,`rssKb`,`cpuPct`];function A(e,t){let n=t?.[e];return n&&n.trim()!==``?n:e.slice(0,8)}var j=`—`;function M(e){if(e===null||!Number.isFinite(e)||e<0)return j;let t=Math.floor(e/1e3);if(t<60)return`${t}s`;let n=Math.floor(t/60);if(n<60)return`${n}m`;let r=Math.floor(n/60);return r<24?`${r}h ${n%60}m`:`${Math.floor(r/24)}d ${r%24}h`}function N(e){return e===null||!Number.isFinite(e)?j:`${(e/1024/1024).toFixed(1)} GB`}function P(e,t){if(!e)return j;let n=Date.parse(e);if(!Number.isFinite(n))return j;let r=new Date(n),i=new Date(t),a=`${String(r.getHours()).padStart(2,`0`)}:${String(r.getMinutes()).padStart(2,`0`)}`;return r.getFullYear()===i.getFullYear()&&r.getMonth()===i.getMonth()&&r.getDate()===i.getDate()?a:`${r.getDate()} ${r.toLocaleString(`en-GB`,{month:`short`})} ${a}`}function F(e){return e===null||!Number.isFinite(e)?j:`${e.toFixed(1)}%`}function I(e){return e===null||!Number.isFinite(e)?j:e<1e3?String(e):e<1e6?`${(e/1e3).toFixed(1)}k`:`${(e/1e6).toFixed(2)}M`}function L(e){return e===null||!Number.isFinite(e)?j:`£${e.toFixed(2)}`}function R(e,t,n,r){let i=e[n],a=t[n];if(i===null&&a===null)return 0;if(i===null)return 1;if(a===null)return-1;if(n===`lastMessageAt`){let e=Date.parse(String(i)),t=Date.parse(String(a));return!Number.isFinite(e)&&!Number.isFinite(t)?0:Number.isFinite(e)?Number.isFinite(t)?(e-t)*r:-1:1}return typeof i==`number`&&typeof a==`number`?(i-a)*r:String(i).localeCompare(String(a))*r}function z({adminFetch:e}){let[t,n]=(0,h.useState)(null),[r,i]=(0,h.useState)(null),[s,c]=(0,h.useState)(!1),[l,u]=(0,h.useState)(`cpuPct`),[m,_]=(0,h.useState)(-1),v=(0,h.useRef)(!1),y=(0,h.useCallback)(async()=>{if(!v.current){v.current=!0;try{let t=await e(`/api/admin/activity`);if(t.status===403){i(`This view is available to the house account only.`),c(!0);return}if(!t.ok){i(`The session manager did not answer (status ${t.status}).`),c(!0);return}n(await t.json()),i(null)}catch(e){i(e instanceof Error?e.message:String(e))}finally{c(!0),v.current=!1}}},[e]);(0,h.useEffect)(()=>{y();let e=setInterval(()=>void y(),15e3);return()=>clearInterval(e)},[y]);let b=(0,h.useMemo)(()=>t?[...t.rows].sort((e,t)=>R(e,t,l,m)):[],[t,l,m]),x=e=>{e===l?_(e=>e===1?-1:1):(u(e),_(-1))};if(!s)return(0,g.jsxs)(`div`,{className:`activity-loading`,children:[(0,g.jsx)(a,{size:18,className:`spin`}),` Loading…`]});if(r)return(0,g.jsx)(`div`,{className:`activity-empty`,children:r});if(!t)return(0,g.jsx)(`div`,{className:`activity-empty`,children:`No response.`});let{accounts:S,census:C,censusAgeMs:w}=t,E=Date.now(),z=S.onDisk!==null&&S.onDisk>S.valid;return(0,g.jsxs)(`div`,{className:`activity-body`,children:[(0,g.jsxs)(`div`,{className:`activity-head`,children:[(0,g.jsxs)(`span`,{className:`activity-title`,children:[(0,g.jsx)(o,{size:16}),` Live sessions`]}),(0,g.jsxs)(`span`,{className:`activity-meta`,children:[b.length,` live · `,S.valid,` accounts`,S.untaggedCount>0?` · ${S.untaggedCount} untagged`:``]})]}),(0,g.jsx)(`div`,{className:`activity-census`,children:C===null?`No census collected yet, so memory and CPU are unavailable.`:`Memory and CPU measured ${M(w)} ago, averaged over the minute before that · ${C.cpuNull} of ${C.live} without a CPU reading`}),z&&(0,g.jsxs)(`div`,{className:`activity-warning`,children:[(0,g.jsx)(d,{size:14}),` `,S.onDisk,` account folders on disk but only`,` `,S.valid,` are readable. Sessions belonging to the rest cannot be attributed.`]}),b.length===0?(0,g.jsx)(`div`,{className:`activity-empty`,children:`No sessions are running.`}):(0,g.jsx)(`div`,{className:`activity-scroll`,children:(0,g.jsxs)(`table`,{className:`activity-table`,children:[(0,g.jsx)(`thead`,{children:(0,g.jsx)(`tr`,{children:O.map(e=>(0,g.jsx)(`th`,{className:e.numeric?`activity-th activity-num`:`activity-th`,title:e.help,children:(0,g.jsxs)(`button`,{type:`button`,className:`activity-sort`,onClick:()=>x(e.key),children:[e.label,l===e.key&&(m===1?(0,g.jsx)(p,{size:11}):(0,g.jsx)(f,{size:11}))]})},e.key))})}),(0,g.jsx)(`tbody`,{children:b.map(e=>(0,g.jsxs)(`tr`,{className:`activity-row`,children:[(0,g.jsx)(`td`,{className:`activity-td`,title:e.accountId??void 0,children:e.accountId===null?(0,g.jsx)(`span`,{className:`activity-untagged`,title:`This session records no account, so it cannot be attributed to a business.`,children:`untagged`}):A(e.accountId,S.names)}),(0,g.jsx)(`td`,{className:`activity-td`,children:e.title??e.sessionId.slice(0,8)}),(0,g.jsx)(`td`,{className:`activity-td`,children:e.model??j}),(0,g.jsx)(`td`,{className:`activity-td`,children:e.status??j}),(0,g.jsx)(`td`,{className:`activity-td activity-num`,children:M(e.ageMs)}),(0,g.jsx)(`td`,{className:`activity-td activity-num`,children:P(e.lastMessageAt,E)}),(0,g.jsx)(`td`,{className:`activity-td activity-num`,children:e.turnCount===null?j:`${e.turnCount}${e.turnCountCapped?`+`:``}`}),(0,g.jsx)(`td`,{className:`activity-td activity-num`,children:N(e.rssKb)}),(0,g.jsx)(`td`,{className:`activity-td activity-num`,children:F(e.cpuPct)}),(0,g.jsx)(`td`,{className:`activity-td activity-num`,children:I(e.usedTokens)}),(0,g.jsx)(`td`,{className:`activity-td activity-num`,title:D(e),children:L(e.costGbp)})]},e.sessionId))})]})}),b.length>0&&(0,g.jsxs)(`dl`,{className:`activity-legend`,children:[k.map(e=>{let t=O.find(t=>t.key===e);return(0,g.jsxs)(`div`,{className:`activity-legend-item`,children:[(0,g.jsx)(`dt`,{className:`activity-legend-term`,children:t.label}),(0,g.jsx)(`dd`,{className:`activity-legend-def`,children:t.help})]},e)}),(0,g.jsxs)(`div`,{className:`activity-legend-item`,children:[(0,g.jsx)(`dt`,{className:`activity-legend-term`,children:j}),(0,g.jsx)(`dd`,{className:`activity-legend-def`,children:`Not measured. It does not mean zero — hover the cell for the reason where one exists.`})]})]}),(0,g.jsx)(T,{loops:t?.loops})]})}function B({cacheKey:e}){let{adminFetch:t}=c({initialCacheKey:e,surface:`activity`});return(0,g.jsx)(z,{adminFetch:t})}function V(){let[e,t]=(0,h.useState)(null),[n,r]=(0,h.useState)(!1),[i,o]=(0,h.useState)(void 0),[c,d]=(0,h.useState)(null),[f,p]=(0,h.useState)(null),[m,_]=(0,h.useState)(null),[v,y]=(0,h.useState)(null);(0,h.useEffect)(()=>{let e=!1,n=null;try{n=sessionStorage.getItem(E)}catch{}if(!n){t(null),r(!0);return}return fetch(`/api/admin/session?session_key=${encodeURIComponent(n)}`).then(async i=>{if(!e){if(i.status===401){try{sessionStorage.removeItem(E)}catch{}window.location.href=`/`;return}if(i.ok)try{let e=await i.json();typeof e.businessName==`string`&&o(e.businessName),e.sessionId!==void 0&&d(e.sessionId??null),p(e.role??null),_(e.userName===void 0?null:e.userName),y(e.avatar??null)}catch{}t(n),r(!0)}}).catch(()=>{e||(t(n),r(!0))}),()=>{e=!0}},[]);let b=(0,h.useCallback)(()=>{try{sessionStorage.removeItem(E)}catch{}window.location.href=`/`},[]),[x,S]=(0,h.useState)(!1),C=(0,h.useCallback)(async()=>{S(!0);try{let e=await u();if(e){try{sessionStorage.removeItem(E)}catch{}window.location.href=`/`}return e}finally{S(!1)}},[]);return n?e?(0,g.jsx)(s,{cacheKey:e,surface:`activity`,onSessionExpired:({code:e,path:t})=>{console.warn(`[admin-auth] outcome=session-expired-redirect code=${e} path=${t} surface=activity`);try{sessionStorage.removeItem(E)}catch{}window.location.href=`/`},children:(0,g.jsx)(l,{cacheKey:e,businessName:i,sessionId:c,onLogout:b,onDisconnect:C,disconnecting:x,userName:m,userAvatar:v,role:f,children:(0,g.jsx)(B,{cacheKey:e})})}):(0,g.jsx)(`div`,{className:`activity-page`,children:(0,g.jsxs)(`div`,{className:`activity-empty`,children:[(0,g.jsx)(`p`,{children:`You are not signed in.`}),(0,g.jsxs)(`p`,{children:[`Open the `,(0,g.jsx)(`a`,{href:`/`,className:`activity-link`,children:`main admin page`}),` and log in, then return here.`]})]})}):(0,g.jsx)(`div`,{className:`activity-page`,children:(0,g.jsxs)(`div`,{className:`activity-loading`,children:[(0,g.jsx)(a,{size:18,className:`spin`}),` Loading…`]})})}(0,m.createRoot)(document.getElementById(`root`)).render((0,g.jsx)(V,{}));
@@ -1 +0,0 @@
1
- import{S as e,T as t}from"./useSubAccountSwitcher-DzgzTRRn.js";import"./useVoiceRecorder-CTXP8oTW.js";import"./AdminShell-BehbH-Oa.js";import{n}from"./page-DOeiiqbR.js";import"./useCopyFeedback-ksyUe_g1.js";import"./useSelectionMode-BPrBkdTd.js";var r=t(),i=e();(0,r.createRoot)(document.getElementById(`root`)).render((0,i.jsx)(n,{}));
@@ -1 +0,0 @@
1
- import{_ as e}from"./useSubAccountSwitcher-DzgzTRRn.js";var t=e(`chevron-left`,[[`path`,{d:`m15 18-6-6 6-6`,key:`1wnfg3`}]]);export{t};
@@ -1 +0,0 @@
1
- import{S as e,T as t}from"./useSubAccountSwitcher-DzgzTRRn.js";import"./useVoiceRecorder-CTXP8oTW.js";import{t as n}from"./page-BloC6ygA.js";import"./useSelectionMode-BPrBkdTd.js";import"./graph-labels-CZykslZM.js";var r=t(),i=e();(0,r.createRoot)(document.getElementById(`root`)).render((0,i.jsx)(n,{}));