@rubytech/create-maxy-code 0.1.275 → 0.1.277

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 (49) hide show
  1. package/package.json +1 -1
  2. package/payload/platform/plugins/admin/.claude-plugin/plugin.json +1 -1
  3. package/payload/platform/plugins/admin/PLUGIN.md +1 -4
  4. package/payload/platform/plugins/admin/mcp/dist/index.js +0 -8
  5. package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -1
  6. package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +3 -3
  7. package/payload/platform/plugins/docs/references/admin-identity-gate.md +77 -81
  8. package/payload/platform/plugins/docs/references/admin-ui.md +2 -2
  9. package/payload/platform/scripts/check-no-esm-require.mjs +3 -0
  10. package/payload/platform/scripts/setup-account.sh +0 -5
  11. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.d.ts.map +1 -1
  12. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js +0 -1
  13. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js.map +1 -1
  14. package/payload/platform/services/claude-session-manager/dist/http-server.d.ts +5 -0
  15. package/payload/platform/services/claude-session-manager/dist/http-server.d.ts.map +1 -1
  16. package/payload/platform/services/claude-session-manager/dist/http-server.js +52 -10
  17. package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -1
  18. package/payload/platform/services/claude-session-manager/dist/index.js +8 -17
  19. package/payload/platform/services/claude-session-manager/dist/index.js.map +1 -1
  20. package/payload/platform/services/claude-session-manager/dist/rc-daemon.d.ts.map +1 -1
  21. package/payload/platform/services/claude-session-manager/dist/rc-daemon.js +4 -5
  22. package/payload/platform/services/claude-session-manager/dist/rc-daemon.js.map +1 -1
  23. package/payload/platform/services/claude-session-manager/dist/wa-channel-mcp.d.ts +8 -0
  24. package/payload/platform/services/claude-session-manager/dist/wa-channel-mcp.d.ts.map +1 -1
  25. package/payload/platform/services/claude-session-manager/dist/wa-channel-mcp.js +13 -0
  26. package/payload/platform/services/claude-session-manager/dist/wa-channel-mcp.js.map +1 -1
  27. package/payload/platform/services/claude-session-manager/dist/wa-channel-store.d.ts +29 -0
  28. package/payload/platform/services/claude-session-manager/dist/wa-channel-store.d.ts.map +1 -0
  29. package/payload/platform/services/claude-session-manager/dist/wa-channel-store.js +124 -0
  30. package/payload/platform/services/claude-session-manager/dist/wa-channel-store.js.map +1 -0
  31. package/payload/platform/services/whatsapp-channel/dist/notification.d.ts +23 -0
  32. package/payload/platform/services/whatsapp-channel/dist/notification.d.ts.map +1 -1
  33. package/payload/platform/services/whatsapp-channel/dist/notification.js +26 -0
  34. package/payload/platform/services/whatsapp-channel/dist/notification.js.map +1 -1
  35. package/payload/platform/services/whatsapp-channel/dist/server.js +41 -14
  36. package/payload/platform/services/whatsapp-channel/dist/server.js.map +1 -1
  37. package/payload/premium-plugins/writer-craft/mcp/dist/index.js +1 -1
  38. package/payload/premium-plugins/writer-craft/mcp/dist/index.js.map +1 -1
  39. package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-distil-profile.d.ts +2 -0
  40. package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-distil-profile.d.ts.map +1 -1
  41. package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-distil-profile.js +1 -1
  42. package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-distil-profile.js.map +1 -1
  43. package/payload/premium-plugins/writer-craft/mcp/src/index.ts +1 -1
  44. package/payload/premium-plugins/writer-craft/mcp/src/tools/voice-distil-profile.ts +3 -1
  45. package/payload/server/{chunk-W4EM7RK4.js → chunk-QHD5TKLQ.js} +2 -0
  46. package/payload/server/maxy-edge.js +1 -1
  47. package/payload/server/server.js +551 -405
  48. package/payload/platform/plugins/admin/hooks/__tests__/pin-identity-gate.test.sh +0 -96
  49. package/payload/platform/plugins/admin/hooks/pin-identity-gate.sh +0 -146
@@ -11,6 +11,7 @@ import {
11
11
  Hono,
12
12
  HtmlEscapedCallbackPhase,
13
13
  INSTALL_OWNER_FILE,
14
+ LID_MAP_FILE,
14
15
  LOG_DIR,
15
16
  MAXY_DIR,
16
17
  PLATFORM_ROOT,
@@ -95,7 +96,7 @@ import {
95
96
  vncLog,
96
97
  walkPremiumBundles,
97
98
  writeAdminUserAndPerson
98
- } from "./chunk-W4EM7RK4.js";
99
+ } from "./chunk-QHD5TKLQ.js";
99
100
  import {
100
101
  __commonJS,
101
102
  __toESM
@@ -604,46 +605,6 @@ var require_dist2 = __commonJS({
604
605
  }
605
606
  });
606
607
 
607
- // ../lib/require-port-env/dist/index.js
608
- var require_dist3 = __commonJS({
609
- "../lib/require-port-env/dist/index.js"(exports) {
610
- "use strict";
611
- Object.defineProperty(exports, "__esModule", { value: true });
612
- exports.MissingPortEnvError = void 0;
613
- exports.requirePortEnv = requirePortEnv3;
614
- var MissingPortEnvError = class extends Error {
615
- envName;
616
- tag;
617
- failLine;
618
- constructor(envName, tag, failLine, message) {
619
- super(message);
620
- this.name = "MissingPortEnvError";
621
- this.envName = envName;
622
- this.tag = tag;
623
- this.failLine = failLine;
624
- }
625
- };
626
- exports.MissingPortEnvError = MissingPortEnvError;
627
- var VALID_PORT_MIN = 1;
628
- var VALID_PORT_MAX = 65535;
629
- function requirePortEnv3(envName, options = {}) {
630
- const tag = options.tag ?? "port-env";
631
- const failLine = options.failLine ?? "missing-port-env";
632
- const raw = process.env[envName];
633
- if (raw === void 0 || raw === "") {
634
- const msg = `[${tag}] ${failLine} reason=missing-env env=${envName}`;
635
- throw new MissingPortEnvError(envName, tag, failLine, msg);
636
- }
637
- const parsed = Number.parseInt(raw, 10);
638
- if (!Number.isInteger(parsed) || parsed < VALID_PORT_MIN || parsed > VALID_PORT_MAX || String(parsed) !== raw.trim()) {
639
- const msg = `[${tag}] ${failLine} reason=invalid-port-env env=${envName} value=${JSON.stringify(raw)}`;
640
- throw new MissingPortEnvError(envName, tag, failLine, msg);
641
- }
642
- return parsed;
643
- }
644
- }
645
- });
646
-
647
608
  // ../lib/graph-write/dist/audit.js
648
609
  var require_audit = __commonJS({
649
610
  "../lib/graph-write/dist/audit.js"(exports) {
@@ -725,7 +686,7 @@ var require_conversation_provenance = __commonJS({
725
686
  "use strict";
726
687
  Object.defineProperty(exports, "__esModule", { value: true });
727
688
  exports.injectConversationProvenance = injectConversationProvenance;
728
- var index_js_1 = require_dist4();
689
+ var index_js_1 = require_dist3();
729
690
  async function injectConversationProvenance(params) {
730
691
  const { session, relationships, accountId, writeLabels, conversationNodeId, logNamespace, tool } = params;
731
692
  const original = [...relationships];
@@ -775,7 +736,7 @@ var require_conversation_provenance = __commonJS({
775
736
  });
776
737
 
777
738
  // ../lib/graph-write/dist/index.js
778
- var require_dist4 = __commonJS({
739
+ var require_dist3 = __commonJS({
779
740
  "../lib/graph-write/dist/index.js"(exports) {
780
741
  "use strict";
781
742
  var __createBinding = exports && exports.__createBinding || (Object.create ? (function(o, m, k, k2) {
@@ -1240,7 +1201,7 @@ var serveStatic = (options = { root: "" }) => {
1240
1201
  };
1241
1202
 
1242
1203
  // server/index.ts
1243
- import { readFileSync as readFileSync22, existsSync as existsSync23, watchFile } from "fs";
1204
+ import { readFileSync as readFileSync23, existsSync as existsSync24, watchFile } from "fs";
1244
1205
  import { resolve as resolve26, join as join18, basename as basename6 } from "path";
1245
1206
  import { homedir as homedir2 } from "os";
1246
1207
  import { monitorEventLoopDelay } from "perf_hooks";
@@ -1528,7 +1489,7 @@ async function ensureAuth() {
1528
1489
  }
1529
1490
 
1530
1491
  // server/routes/health.ts
1531
- import { existsSync as existsSync2, readFileSync as readFileSync4 } from "fs";
1492
+ import { existsSync as existsSync3, readFileSync as readFileSync5 } from "fs";
1532
1493
  import { createConnection as createConnection2 } from "net";
1533
1494
 
1534
1495
  // app/lib/network.ts
@@ -2229,6 +2190,49 @@ async function resolveJidToE164(jid, lidMapping) {
2229
2190
  return null;
2230
2191
  }
2231
2192
 
2193
+ // app/lib/whatsapp/inbound/lid-map.ts
2194
+ import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
2195
+ function resolveViaLidMap(map, senderPhone) {
2196
+ const key = normalizeE164(senderPhone);
2197
+ if (!key) return null;
2198
+ const mapped = map[key];
2199
+ return mapped && mapped.length > 0 ? mapped : null;
2200
+ }
2201
+ function lidCaptureFor(senderJid, senderPhone) {
2202
+ if (!isLidJid(senderJid)) return null;
2203
+ const numericLid = jidToE164(senderJid);
2204
+ if (!numericLid) return null;
2205
+ const e164 = normalizeE164(senderPhone);
2206
+ if (!e164 || e164 === numericLid) return null;
2207
+ return { numericLid, e164 };
2208
+ }
2209
+ function readLidMap() {
2210
+ try {
2211
+ if (!existsSync2(LID_MAP_FILE)) return null;
2212
+ const raw = readFileSync2(LID_MAP_FILE, "utf-8").trim();
2213
+ if (!raw) return null;
2214
+ const parsed = JSON.parse(raw);
2215
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return null;
2216
+ return parsed;
2217
+ } catch {
2218
+ return null;
2219
+ }
2220
+ }
2221
+ function recordLidMapping(numericLid, e164) {
2222
+ try {
2223
+ const key = normalizeE164(numericLid);
2224
+ const val = normalizeE164(e164);
2225
+ if (!key || !val) return;
2226
+ const map = readLidMap() ?? {};
2227
+ if (map[key] === val) return;
2228
+ map[key] = val;
2229
+ writeFileSync2(LID_MAP_FILE, JSON.stringify(map, null, 2));
2230
+ console.error(`[lid-map] recorded lid=${key} userId-phone=present`);
2231
+ } catch (err) {
2232
+ console.error(`[lid-map] record-failed lid=${numericLid}: ${err instanceof Error ? err.message : String(err)}`);
2233
+ }
2234
+ }
2235
+
2232
2236
  // app/lib/whatsapp/inbound/extract.ts
2233
2237
  import {
2234
2238
  extractMessageContent,
@@ -2783,7 +2787,7 @@ async function ensureWhatsAppConversation(input) {
2783
2787
  }
2784
2788
 
2785
2789
  // ../lib/account-enumeration/src/index.ts
2786
- import { readdirSync, readFileSync as readFileSync2 } from "fs";
2790
+ import { readdirSync, readFileSync as readFileSync3 } from "fs";
2787
2791
  import { resolve } from "path";
2788
2792
  var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
2789
2793
  var cache = /* @__PURE__ */ new Map();
@@ -2805,7 +2809,7 @@ function enumerateValidAccountIds(accountsDir) {
2805
2809
  if (!UUID_RE.test(name)) continue;
2806
2810
  const configPath2 = resolve(accountsDir, name, "account.json");
2807
2811
  try {
2808
- JSON.parse(readFileSync2(configPath2, "utf-8"));
2812
+ JSON.parse(readFileSync3(configPath2, "utf-8"));
2809
2813
  valid.push(name);
2810
2814
  } catch (err) {
2811
2815
  const code = err.code;
@@ -3917,6 +3921,8 @@ async function handleInboundMessage(conn, msg) {
3917
3921
  const isGroup = isGroupJid(remoteJid);
3918
3922
  const senderJid = isGroup ? msg.key.participant ?? remoteJid : remoteJid;
3919
3923
  const senderPhone = await resolveJidToE164(senderJid, conn.lidMapping) ?? senderJid;
3924
+ const lidCapture = lidCaptureFor(senderJid, senderPhone);
3925
+ if (lidCapture) recordLidMapping(lidCapture.numericLid, lidCapture.e164);
3920
3926
  const selfPhone = conn.selfPhone ?? "";
3921
3927
  if (extracted.mediaType === "audio" && mediaResult?.path) {
3922
3928
  const conversationKey = isGroup ? remoteJid : senderPhone;
@@ -4037,7 +4043,7 @@ async function handleInboundMessage(conn, msg) {
4037
4043
  // app/lib/vnc.ts
4038
4044
  import { spawnSync, execFileSync } from "child_process";
4039
4045
  import { createConnection } from "net";
4040
- import { mkdirSync, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
4046
+ import { mkdirSync, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
4041
4047
  import { resolve as resolve2 } from "path";
4042
4048
  var PLATFORM_ROOT2 = process.env.MAXY_PLATFORM_ROOT ?? resolve2(process.cwd(), "..");
4043
4049
  var VNC_SCRIPT = resolve2(PLATFORM_ROOT2, "scripts/vnc.sh");
@@ -4095,7 +4101,7 @@ function discoverNativeDisplay() {
4095
4101
  const leaderPid = leaderResult.stdout?.trim();
4096
4102
  if (leaderPid) {
4097
4103
  try {
4098
- const environ = readFileSync3(`/proc/${leaderPid}/environ`, "utf8");
4104
+ const environ = readFileSync4(`/proc/${leaderPid}/environ`, "utf8");
4099
4105
  const match = environ.split("\0").find((e) => e.startsWith("WAYLAND_DISPLAY="));
4100
4106
  if (match) waylandDisplay = match.split("=")[1];
4101
4107
  } catch {
@@ -4233,7 +4239,7 @@ function killChromium() {
4233
4239
  function writeChromiumWrapper() {
4234
4240
  mkdirSync(BIN_DIR, { recursive: true });
4235
4241
  const wrapperPath = resolve2(BIN_DIR, "chromium");
4236
- writeFileSync2(wrapperPath, `#!/bin/bash
4242
+ writeFileSync3(wrapperPath, `#!/bin/bash
4237
4243
  LOG="${LOG_DIR}/chromium.log"
4238
4244
  echo "==== [$(date)] chromium wrapper ====" >> "$LOG"
4239
4245
  echo " DISPLAY=$DISPLAY WAYLAND=$WAYLAND_DISPLAY XDG_SESSION_TYPE=$XDG_SESSION_TYPE" >> "$LOG"
@@ -4354,8 +4360,8 @@ app.get("/", async (c) => {
4354
4360
  const browserTransport = resolveBrowserTransport(c.req.raw, c.env?.incoming?.socket?.remoteAddress);
4355
4361
  let pinConfigured = false;
4356
4362
  try {
4357
- if (existsSync2(USERS_FILE)) {
4358
- const raw = readFileSync4(USERS_FILE, "utf-8").trim();
4363
+ if (existsSync3(USERS_FILE)) {
4364
+ const raw = readFileSync5(USERS_FILE, "utf-8").trim();
4359
4365
  if (raw) {
4360
4366
  const users = JSON.parse(raw);
4361
4367
  pinConfigured = Array.isArray(users) && users.length > 0;
@@ -4674,6 +4680,7 @@ async function managerRcSpawn(opts) {
4674
4680
  closeAfterTurn: opts.closeAfterTurn,
4675
4681
  sliceToken: opts.sliceToken,
4676
4682
  personId: opts.personId,
4683
+ userId: opts.userId,
4677
4684
  waChannel: opts.waChannel
4678
4685
  })
4679
4686
  }).catch((err) => ({ __throw: err instanceof Error ? err.message : String(err) }));
@@ -5793,13 +5800,13 @@ var chat_default = app2;
5793
5800
 
5794
5801
  // server/routes/whatsapp.ts
5795
5802
  import { join as join6, resolve as resolve7 } from "path";
5796
- import { readdirSync as readdirSync3, readFileSync as readFileSync6, existsSync as existsSync4 } from "fs";
5803
+ import { readdirSync as readdirSync3, readFileSync as readFileSync7, existsSync as existsSync5 } from "fs";
5797
5804
 
5798
5805
  // app/lib/whatsapp/login.ts
5799
5806
  import { randomUUID as randomUUID6 } from "crypto";
5800
5807
 
5801
5808
  // app/lib/whatsapp/config-persist.ts
5802
- import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, existsSync as existsSync3 } from "fs";
5809
+ import { readFileSync as readFileSync6, writeFileSync as writeFileSync4, existsSync as existsSync4 } from "fs";
5803
5810
  import { resolve as resolve6, join as join4 } from "path";
5804
5811
  var TAG17 = "[whatsapp:config]";
5805
5812
  function configPath(accountDir) {
@@ -5807,12 +5814,12 @@ function configPath(accountDir) {
5807
5814
  }
5808
5815
  function readConfig(accountDir) {
5809
5816
  const path2 = configPath(accountDir);
5810
- if (!existsSync3(path2)) throw new Error(`account.json not found at ${path2}`);
5811
- return JSON.parse(readFileSync5(path2, "utf-8"));
5817
+ if (!existsSync4(path2)) throw new Error(`account.json not found at ${path2}`);
5818
+ return JSON.parse(readFileSync6(path2, "utf-8"));
5812
5819
  }
5813
5820
  function writeConfig(accountDir, config) {
5814
5821
  const path2 = configPath(accountDir);
5815
- writeFileSync3(path2, JSON.stringify(config, null, 2) + "\n", "utf-8");
5822
+ writeFileSync4(path2, JSON.stringify(config, null, 2) + "\n", "utf-8");
5816
5823
  }
5817
5824
  function reloadManagerConfig(accountDir) {
5818
5825
  try {
@@ -5953,7 +5960,7 @@ function setPublicAgent(accountDir, slug) {
5953
5960
  return { ok: false, error: "Agent slug cannot be empty." };
5954
5961
  }
5955
5962
  const agentConfigPath = join4(accountDir, "agents", trimmed, "config.json");
5956
- if (!existsSync3(agentConfigPath)) {
5963
+ if (!existsSync4(agentConfigPath)) {
5957
5964
  return { ok: false, error: `Agent "${trimmed}" not found \u2014 no config.json at ${agentConfigPath}. Check the agent slug and try again.` };
5958
5965
  }
5959
5966
  try {
@@ -6017,7 +6024,7 @@ function setGroupPublicAgent(accountDir, accountId, groupJid, slug) {
6017
6024
  if (!trimmedGroup) return { ok: false, error: "Group JID cannot be empty." };
6018
6025
  if (!trimmedAccount) return { ok: false, error: "Account ID cannot be empty." };
6019
6026
  const agentConfigPath = join4(accountDir, "agents", trimmedSlug, "config.json");
6020
- if (!existsSync3(agentConfigPath)) {
6027
+ if (!existsSync4(agentConfigPath)) {
6021
6028
  return { ok: false, error: `Agent "${trimmedSlug}" not found \u2014 no config.json at ${agentConfigPath}. Check the agent slug and try again.` };
6022
6029
  }
6023
6030
  try {
@@ -6703,15 +6710,15 @@ app3.post("/config", async (c) => {
6703
6710
  case "list-public-agents": {
6704
6711
  const agentsDir = resolve7(account.accountDir, "agents");
6705
6712
  const agents = [];
6706
- if (existsSync4(agentsDir)) {
6713
+ if (existsSync5(agentsDir)) {
6707
6714
  try {
6708
6715
  const entries = readdirSync3(agentsDir, { withFileTypes: true });
6709
6716
  for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
6710
6717
  if (!entry.isDirectory() || entry.name === "admin") continue;
6711
6718
  const configPath2 = resolve7(agentsDir, entry.name, "config.json");
6712
- if (!existsSync4(configPath2)) continue;
6719
+ if (!existsSync5(configPath2)) continue;
6713
6720
  try {
6714
- const config = JSON.parse(readFileSync6(configPath2, "utf-8"));
6721
+ const config = JSON.parse(readFileSync7(configPath2, "utf-8"));
6715
6722
  agents.push({ slug: entry.name, displayName: config.displayName ?? entry.name });
6716
6723
  } catch {
6717
6724
  console.error(`${TAG20} config action=list-public-agents error="failed to parse config.json for agent ${entry.name}" \u2014 skipping`);
@@ -6981,11 +6988,11 @@ var whatsapp_default = app3;
6981
6988
 
6982
6989
  // server/routes/onboarding.ts
6983
6990
  import { spawn, spawnSync as spawnSync2, execFileSync as execFileSync2 } from "child_process";
6984
- import { openSync, closeSync, writeFileSync as writeFileSync5, writeSync, existsSync as existsSync6, readFileSync as readFileSync8, unlinkSync } from "fs";
6991
+ import { openSync, closeSync, writeFileSync as writeFileSync6, writeSync, existsSync as existsSync7, readFileSync as readFileSync9, unlinkSync } from "fs";
6985
6992
  import { createHash as createHash2, randomUUID as randomUUID7 } from "crypto";
6986
6993
 
6987
6994
  // ../lib/admins-write/src/index.ts
6988
- import { existsSync as existsSync5, readFileSync as readFileSync7, writeFileSync as writeFileSync4, renameSync, mkdirSync as mkdirSync2, readdirSync as readdirSync4, statSync as statSync2 } from "fs";
6995
+ import { existsSync as existsSync6, readFileSync as readFileSync8, writeFileSync as writeFileSync5, renameSync, mkdirSync as mkdirSync2, readdirSync as readdirSync4, statSync as statSync2 } from "fs";
6989
6996
  import { dirname, join as join7 } from "path";
6990
6997
  function logLine(input, result) {
6991
6998
  const userIdShort = input.userId.slice(0, 8);
@@ -6996,15 +7003,15 @@ function logLine(input, result) {
6996
7003
  function writeFileAtomic(filePath, contents, mode) {
6997
7004
  mkdirSync2(dirname(filePath), { recursive: true });
6998
7005
  const tempPath = `${filePath}.tmp-${process.pid}-${Date.now()}`;
6999
- writeFileSync4(tempPath, contents, mode !== void 0 ? { mode } : void 0);
7006
+ writeFileSync5(tempPath, contents, mode !== void 0 ? { mode } : void 0);
7000
7007
  renameSync(tempPath, filePath);
7001
7008
  }
7002
7009
  function writeAdminEntry(input) {
7003
7010
  const result = { usersJsonResult: "noop", accountJsonResult: "noop" };
7004
7011
  try {
7005
7012
  let users = [];
7006
- if (existsSync5(input.usersFile)) {
7007
- const raw = readFileSync7(input.usersFile, "utf-8").trim();
7013
+ if (existsSync6(input.usersFile)) {
7014
+ const raw = readFileSync8(input.usersFile, "utf-8").trim();
7008
7015
  if (raw) {
7009
7016
  const parsed = JSON.parse(raw);
7010
7017
  if (!Array.isArray(parsed)) {
@@ -7029,10 +7036,10 @@ function writeAdminEntry(input) {
7029
7036
  }
7030
7037
  try {
7031
7038
  const accountJsonPath = join7(input.accountDir, "account.json");
7032
- if (!existsSync5(accountJsonPath)) {
7039
+ if (!existsSync6(accountJsonPath)) {
7033
7040
  throw new Error(`account.json not found at ${accountJsonPath}`);
7034
7041
  }
7035
- const config = JSON.parse(readFileSync7(accountJsonPath, "utf-8"));
7042
+ const config = JSON.parse(readFileSync8(accountJsonPath, "utf-8"));
7036
7043
  const admins = config.admins ?? [];
7037
7044
  if (admins.some((a) => a.userId === input.userId)) {
7038
7045
  result.accountJsonResult = "noop";
@@ -7056,9 +7063,9 @@ function checkAdminAuthInvariant(input) {
7056
7063
  usersWithoutAccount: []
7057
7064
  };
7058
7065
  const usersUserIds = /* @__PURE__ */ new Set();
7059
- if (existsSync5(input.usersFile)) {
7066
+ if (existsSync6(input.usersFile)) {
7060
7067
  try {
7061
- const raw = readFileSync7(input.usersFile, "utf-8").trim();
7068
+ const raw = readFileSync8(input.usersFile, "utf-8").trim();
7062
7069
  if (raw) {
7063
7070
  const users = JSON.parse(raw);
7064
7071
  for (const u of users) {
@@ -7071,7 +7078,7 @@ function checkAdminAuthInvariant(input) {
7071
7078
  }
7072
7079
  }
7073
7080
  const accountUserIds = /* @__PURE__ */ new Set();
7074
- if (existsSync5(input.accountsDir)) {
7081
+ if (existsSync6(input.accountsDir)) {
7075
7082
  let entries;
7076
7083
  try {
7077
7084
  entries = readdirSync4(input.accountsDir);
@@ -7090,10 +7097,10 @@ function checkAdminAuthInvariant(input) {
7090
7097
  continue;
7091
7098
  }
7092
7099
  const accountJsonPath = join7(accountDir, "account.json");
7093
- if (!existsSync5(accountJsonPath)) continue;
7100
+ if (!existsSync6(accountJsonPath)) continue;
7094
7101
  let admins = [];
7095
7102
  try {
7096
- const config = JSON.parse(readFileSync7(accountJsonPath, "utf-8"));
7103
+ const config = JSON.parse(readFileSync8(accountJsonPath, "utf-8"));
7097
7104
  admins = config.admins ?? [];
7098
7105
  } catch (err) {
7099
7106
  const msg = err instanceof Error ? err.message : String(err);
@@ -7133,8 +7140,8 @@ function hashPin(pin) {
7133
7140
  return createHash2("sha256").update(pin).digest("hex");
7134
7141
  }
7135
7142
  function readUsersFile() {
7136
- if (!existsSync6(USERS_FILE)) return null;
7137
- const raw = readFileSync8(USERS_FILE, "utf-8").trim();
7143
+ if (!existsSync7(USERS_FILE)) return null;
7144
+ const raw = readFileSync9(USERS_FILE, "utf-8").trim();
7138
7145
  if (!raw) return [];
7139
7146
  return JSON.parse(raw);
7140
7147
  }
@@ -7199,7 +7206,7 @@ app4.post("/claude-auth", async (c) => {
7199
7206
  return c.json({ launched: cdpReady });
7200
7207
  }
7201
7208
  ensureLogDir();
7202
- writeFileSync5(logPath("claude-auth"), "");
7209
+ writeFileSync6(logPath("claude-auth"), "");
7203
7210
  const claudeAuthLogFd = openSync(logPath("claude-auth"), "a");
7204
7211
  const onClaudeOutput = (chunk) => writeSync(claudeAuthLogFd, chunk);
7205
7212
  if (process.platform === "darwin") {
@@ -7288,9 +7295,9 @@ app4.post("/set-pin", async (c) => {
7288
7295
  console.log(`[set-pin] wrote users.json + account.json admins: userId=${userId.slice(0, 8)}\u2026 role=owner`);
7289
7296
  if (process.platform === "linux") {
7290
7297
  let installOwner = null;
7291
- if (existsSync6(INSTALL_OWNER_FILE)) {
7298
+ if (existsSync7(INSTALL_OWNER_FILE)) {
7292
7299
  try {
7293
- const raw = readFileSync8(INSTALL_OWNER_FILE, "utf-8").trim();
7300
+ const raw = readFileSync9(INSTALL_OWNER_FILE, "utf-8").trim();
7294
7301
  if (raw.length > 0) installOwner = raw;
7295
7302
  } catch (err) {
7296
7303
  console.error(`[set-pin] install-owner-read-failed path=${INSTALL_OWNER_FILE} error=${err instanceof Error ? err.message : String(err)}`);
@@ -7363,7 +7370,7 @@ app4.delete("/set-pin", async (c) => {
7363
7370
  unlinkSync(USERS_FILE);
7364
7371
  console.log(`[set-pin] cleared users.json (last entry removed): userId=${matchedUser.userId.slice(0, 8)}\u2026`);
7365
7372
  } else {
7366
- writeFileSync5(USERS_FILE, JSON.stringify(remaining), { mode: 384 });
7373
+ writeFileSync6(USERS_FILE, JSON.stringify(remaining), { mode: 384 });
7367
7374
  console.log(`[set-pin] removed entry from users.json: userId=${matchedUser.userId.slice(0, 8)}\u2026 remaining=${remaining.length}`);
7368
7375
  }
7369
7376
  return c.json({ ok: true });
@@ -7371,7 +7378,7 @@ app4.delete("/set-pin", async (c) => {
7371
7378
  var onboarding_default = app4;
7372
7379
 
7373
7380
  // server/routes/client-error.ts
7374
- import { appendFileSync, existsSync as existsSync7, renameSync as renameSync2, statSync as statSync3 } from "fs";
7381
+ import { appendFileSync, existsSync as existsSync8, renameSync as renameSync2, statSync as statSync3 } from "fs";
7375
7382
  import { join as join8 } from "path";
7376
7383
  var CLIENT_ERRORS_LOG = join8(LOG_DIR, "client-errors.log");
7377
7384
  var MAX_LOG_SIZE = 10 * 1024 * 1024;
@@ -7416,7 +7423,7 @@ function stackHead(stack) {
7416
7423
  }
7417
7424
  function rotateIfNeeded() {
7418
7425
  try {
7419
- if (!existsSync7(CLIENT_ERRORS_LOG)) return;
7426
+ if (!existsSync8(CLIENT_ERRORS_LOG)) return;
7420
7427
  const stats = statSync3(CLIENT_ERRORS_LOG);
7421
7428
  if (stats.size < MAX_LOG_SIZE) return;
7422
7429
  renameSync2(CLIENT_ERRORS_LOG, CLIENT_ERRORS_LOG + ".1");
@@ -7534,14 +7541,14 @@ import { randomUUID as randomUUID8 } from "crypto";
7534
7541
 
7535
7542
  // app/lib/admin-identity/pin-validator.ts
7536
7543
  import { createHash as createHash3 } from "crypto";
7537
- import { existsSync as existsSync8, readFileSync as readFileSync9, readdirSync as readdirSync5, statSync as statSync4 } from "fs";
7544
+ import { existsSync as existsSync9, readFileSync as readFileSync10, readdirSync as readdirSync5, statSync as statSync4 } from "fs";
7538
7545
  import { join as join9 } from "path";
7539
7546
  function hashPin2(pin) {
7540
7547
  return createHash3("sha256").update(pin).digest("hex");
7541
7548
  }
7542
7549
  function readUsersFile2(usersFilePath) {
7543
- if (!existsSync8(usersFilePath)) return null;
7544
- const raw = readFileSync9(usersFilePath, "utf-8").trim();
7550
+ if (!existsSync9(usersFilePath)) return null;
7551
+ const raw = readFileSync10(usersFilePath, "utf-8").trim();
7545
7552
  if (!raw) return [];
7546
7553
  return JSON.parse(raw);
7547
7554
  }
@@ -7560,7 +7567,7 @@ function validatePin(pin, usersFilePath) {
7560
7567
  function scanForOrphanedAccountAdmins(users, accountsDir) {
7561
7568
  const known = new Set(users.map((u) => u.userId));
7562
7569
  const orphans = [];
7563
- if (!existsSync8(accountsDir)) return orphans;
7570
+ if (!existsSync9(accountsDir)) return orphans;
7564
7571
  let entries;
7565
7572
  try {
7566
7573
  entries = readdirSync5(accountsDir);
@@ -7576,9 +7583,9 @@ function scanForOrphanedAccountAdmins(users, accountsDir) {
7576
7583
  continue;
7577
7584
  }
7578
7585
  const accountJsonPath = join9(accountDir, "account.json");
7579
- if (!existsSync8(accountJsonPath)) continue;
7586
+ if (!existsSync9(accountJsonPath)) continue;
7580
7587
  try {
7581
- const config = JSON.parse(readFileSync9(accountJsonPath, "utf-8"));
7588
+ const config = JSON.parse(readFileSync10(accountJsonPath, "utf-8"));
7582
7589
  const admins = config.admins ?? [];
7583
7590
  for (const a of admins) {
7584
7591
  if (typeof a.userId === "string" && !known.has(a.userId)) orphans.push(a.userId);
@@ -7758,18 +7765,18 @@ app6.post("/", async (c) => {
7758
7765
  var session_default = app6;
7759
7766
 
7760
7767
  // server/routes/admin/logs.ts
7761
- import { existsSync as existsSync10, readdirSync as readdirSync6, readFileSync as readFileSync10, statSync as statSync5 } from "fs";
7768
+ import { existsSync as existsSync11, readdirSync as readdirSync6, readFileSync as readFileSync11, statSync as statSync5 } from "fs";
7762
7769
  import { resolve as resolve8, basename as basename2 } from "path";
7763
7770
 
7764
7771
  // app/lib/logs-read-resolve.ts
7765
- import { existsSync as existsSync9 } from "fs";
7772
+ import { existsSync as existsSync10 } from "fs";
7766
7773
  import { join as join10 } from "path";
7767
7774
  function resolveSessionLogPaths(filename, logDirs) {
7768
7775
  const tried = [filename];
7769
7776
  const hits = [];
7770
7777
  for (const dir of logDirs) {
7771
7778
  const fullPath = join10(dir, filename);
7772
- if (existsSync9(fullPath)) {
7779
+ if (existsSync10(fullPath)) {
7773
7780
  hits.push({ path: fullPath, dir });
7774
7781
  }
7775
7782
  }
@@ -7797,7 +7804,7 @@ app7.get("/", async (c) => {
7797
7804
  const filePath = resolve8(dir, safe);
7798
7805
  searched.push(filePath);
7799
7806
  try {
7800
- const buffer = readFileSync10(filePath);
7807
+ const buffer = readFileSync11(filePath);
7801
7808
  const onDiskBytes = statSync5(filePath).size;
7802
7809
  const headers = {
7803
7810
  "Content-Type": "text/plain; charset=utf-8",
@@ -7862,7 +7869,7 @@ app7.get("/", async (c) => {
7862
7869
  console.info(`[admin/logs] resolved cacheKey=${cacheKeySlice} sessionId=${sessionIdSlice} via=${primaryId === cacheKey ? "cacheKey" : primaryId === sessionKeyFromConv ? "reverse-lookup" : "sessionId-fallback"}`);
7863
7870
  try {
7864
7871
  const filename = basename2(hit.path);
7865
- const buffer = readFileSync10(hit.path);
7872
+ const buffer = readFileSync11(hit.path);
7866
7873
  const onDiskBytes = statSync5(hit.path).size;
7867
7874
  const headers = {
7868
7875
  "Content-Type": "text/plain; charset=utf-8",
@@ -7897,7 +7904,7 @@ app7.get("/", async (c) => {
7897
7904
  const seen = /* @__PURE__ */ new Set();
7898
7905
  const logs = {};
7899
7906
  for (const dir of logDirs) {
7900
- if (!existsSync10(dir)) continue;
7907
+ if (!existsSync11(dir)) continue;
7901
7908
  let files;
7902
7909
  try {
7903
7910
  files = readdirSync6(dir).filter((f) => f.endsWith(".log"));
@@ -7909,7 +7916,7 @@ app7.get("/", async (c) => {
7909
7916
  files.filter((f) => !seen.has(f)).map((f) => ({ name: f, mtime: statSync5(resolve8(dir, f)).mtimeMs })).sort((a, b) => b.mtime - a.mtime).forEach(({ name }) => {
7910
7917
  seen.add(name);
7911
7918
  try {
7912
- const content = readFileSync10(resolve8(dir, name));
7919
+ const content = readFileSync11(resolve8(dir, name));
7913
7920
  const tail = content.length > TAIL_BYTES ? content.subarray(content.length - TAIL_BYTES).toString("utf-8") : content.toString("utf-8");
7914
7921
  logs[name] = tail.trim() || "(empty)";
7915
7922
  } catch (err) {
@@ -7948,7 +7955,7 @@ var claude_info_default = app8;
7948
7955
 
7949
7956
  // server/routes/admin/attachment.ts
7950
7957
  import { readFile as readFile2, readdir } from "fs/promises";
7951
- import { existsSync as existsSync11 } from "fs";
7958
+ import { existsSync as existsSync12 } from "fs";
7952
7959
  import { resolve as resolve9 } from "path";
7953
7960
  var app9 = new Hono();
7954
7961
  app9.get("/:attachmentId", requireAdminSession, async (c) => {
@@ -7962,11 +7969,11 @@ app9.get("/:attachmentId", requireAdminSession, async (c) => {
7962
7969
  return new Response("Not found", { status: 404 });
7963
7970
  }
7964
7971
  const dir = resolve9(ATTACHMENTS_ROOT, accountId, attachmentId);
7965
- if (!existsSync11(dir)) {
7972
+ if (!existsSync12(dir)) {
7966
7973
  return new Response("Not found", { status: 404 });
7967
7974
  }
7968
7975
  const metaPath = resolve9(dir, `${attachmentId}.meta.json`);
7969
- if (!existsSync11(metaPath)) {
7976
+ if (!existsSync12(metaPath)) {
7970
7977
  return new Response("Not found", { status: 404 });
7971
7978
  }
7972
7979
  let meta;
@@ -7994,13 +8001,13 @@ var attachment_default = app9;
7994
8001
 
7995
8002
  // server/routes/admin/agents.ts
7996
8003
  import { resolve as resolve10 } from "path";
7997
- import { readdirSync as readdirSync7, readFileSync as readFileSync11, existsSync as existsSync12, rmSync } from "fs";
8004
+ import { readdirSync as readdirSync7, readFileSync as readFileSync12, existsSync as existsSync13, rmSync } from "fs";
7998
8005
  var app10 = new Hono();
7999
8006
  app10.get("/", (c) => {
8000
8007
  const account = resolveAccount();
8001
8008
  if (!account) return c.json({ agents: [] });
8002
8009
  const agentsDir = resolve10(account.accountDir, "agents");
8003
- if (!existsSync12(agentsDir)) return c.json({ agents: [] });
8010
+ if (!existsSync13(agentsDir)) return c.json({ agents: [] });
8004
8011
  const agents = [];
8005
8012
  try {
8006
8013
  const entries = readdirSync7(agentsDir, { withFileTypes: true });
@@ -8008,9 +8015,9 @@ app10.get("/", (c) => {
8008
8015
  if (!entry.isDirectory()) continue;
8009
8016
  if (entry.name === "admin") continue;
8010
8017
  const configPath2 = resolve10(agentsDir, entry.name, "config.json");
8011
- if (!existsSync12(configPath2)) continue;
8018
+ if (!existsSync13(configPath2)) continue;
8012
8019
  try {
8013
- const config = JSON.parse(readFileSync11(configPath2, "utf-8"));
8020
+ const config = JSON.parse(readFileSync12(configPath2, "utf-8"));
8014
8021
  agents.push({
8015
8022
  slug: entry.name,
8016
8023
  displayName: config.displayName ?? entry.name,
@@ -8037,7 +8044,7 @@ app10.delete("/:slug", async (c) => {
8037
8044
  return c.json({ error: "Invalid agent slug" }, 400);
8038
8045
  }
8039
8046
  const agentDir = resolve10(account.accountDir, "agents", slug);
8040
- if (!existsSync12(agentDir)) {
8047
+ if (!existsSync13(agentDir)) {
8041
8048
  return c.json({ error: "Agent not found" }, 404);
8042
8049
  }
8043
8050
  try {
@@ -8067,7 +8074,7 @@ app10.post("/:slug/project", async (c) => {
8067
8074
  return c.json({ error: "Invalid agent slug" }, 400);
8068
8075
  }
8069
8076
  const agentDir = resolve10(account.accountDir, "agents", slug);
8070
- if (!existsSync12(agentDir)) {
8077
+ if (!existsSync13(agentDir)) {
8071
8078
  return c.json({ error: "Agent not found on disk" }, 404);
8072
8079
  }
8073
8080
  try {
@@ -8083,7 +8090,7 @@ var agents_default = app10;
8083
8090
  // server/routes/admin/sessions.ts
8084
8091
  import crypto2 from "crypto";
8085
8092
  import { resolve as resolvePath } from "path";
8086
- import { existsSync as existsSync13, mkdirSync as mkdirSync3, createWriteStream } from "fs";
8093
+ import { existsSync as existsSync14, mkdirSync as mkdirSync3, createWriteStream } from "fs";
8087
8094
 
8088
8095
  // ../lib/admin-conversation-purge/src/index.ts
8089
8096
  async function purgeAdminConversationJsonls(args) {
@@ -8243,7 +8250,7 @@ function validateAndShapeAttachments(raws, conversationAccountId, sessionId, mes
8243
8250
  let reason = null;
8244
8251
  if (!a.attachmentId || !a.filename || !a.mimeType || !a.storagePath) reason = "schema-fail";
8245
8252
  else if (a.accountId !== conversationAccountId) reason = "account-mismatch";
8246
- else if (!existsSync13(a.storagePath)) reason = "missing-file";
8253
+ else if (!existsSync14(a.storagePath)) reason = "missing-file";
8247
8254
  if (reason) {
8248
8255
  invalid++;
8249
8256
  try {
@@ -8434,11 +8441,11 @@ app11.delete("/:id", requireAdminSession, async (c) => {
8434
8441
  const owned = await verifyConversationOwnership(sessionId, accountId);
8435
8442
  if (!owned) return c.json({ error: "Conversation not found" }, 404);
8436
8443
  const managerPort = requirePortEnv("CLAUDE_SESSION_MANAGER_PORT", { tag: "admin-conversation-delete" });
8437
- const managerBase6 = `http://127.0.0.1:${managerPort}`;
8444
+ const managerBase5 = `http://127.0.0.1:${managerPort}`;
8438
8445
  const outcome = await cascadeAdminConversationDelete({
8439
8446
  sessionId,
8440
8447
  accountId,
8441
- managerBase: managerBase6
8448
+ managerBase: managerBase5
8442
8449
  });
8443
8450
  if (outcome.ok) return c.json({ ok: true, claudeSessionIds: outcome.claudeSessionIds });
8444
8451
  if (outcome.reason === "pty-still-alive") {
@@ -8770,7 +8777,7 @@ app11.put("/:id/label", requireAdminSession, async (c) => {
8770
8777
  var sessions_default = app11;
8771
8778
 
8772
8779
  // app/lib/claude-agent/spawn-context.ts
8773
- import { existsSync as existsSync14, readFileSync as readFileSync12, readdirSync as readdirSync8, statSync as statSync6 } from "fs";
8780
+ import { existsSync as existsSync15, readFileSync as readFileSync13, readdirSync as readdirSync8, statSync as statSync6 } from "fs";
8774
8781
  import { dirname as dirname2, resolve as resolve11, join as join11 } from "path";
8775
8782
  async function resolveOwnerProfileBlock(accountId, userId) {
8776
8783
  if (!userId) return { ok: false, reason: "missing-user-id" };
@@ -8805,9 +8812,9 @@ function extractPluginToolDescriptions(pluginDir) {
8805
8812
  ];
8806
8813
  let raw = null;
8807
8814
  for (const path2 of candidates) {
8808
- if (!existsSync14(path2)) continue;
8815
+ if (!existsSync15(path2)) continue;
8809
8816
  try {
8810
- raw = readFileSync12(path2, "utf-8");
8817
+ raw = readFileSync13(path2, "utf-8");
8811
8818
  break;
8812
8819
  } catch {
8813
8820
  }
@@ -8857,7 +8864,7 @@ function parseSkillPathsFromFrontmatter(fm) {
8857
8864
  function listPluginDirs() {
8858
8865
  const out = /* @__PURE__ */ new Map();
8859
8866
  const platformPlugins = resolve11(PLATFORM_ROOT, "plugins");
8860
- if (existsSync14(platformPlugins)) {
8867
+ if (existsSync15(platformPlugins)) {
8861
8868
  for (const name of readdirSync8(platformPlugins)) {
8862
8869
  const dir = join11(platformPlugins, name);
8863
8870
  try {
@@ -8867,10 +8874,10 @@ function listPluginDirs() {
8867
8874
  }
8868
8875
  }
8869
8876
  const premiumRoot = resolve11(dirname2(PLATFORM_ROOT), "premium-plugins");
8870
- if (existsSync14(premiumRoot)) {
8877
+ if (existsSync15(premiumRoot)) {
8871
8878
  for (const bundle of readdirSync8(premiumRoot)) {
8872
8879
  const bundlePlugins = join11(premiumRoot, bundle, "plugins");
8873
- if (!existsSync14(bundlePlugins)) continue;
8880
+ if (!existsSync15(bundlePlugins)) continue;
8874
8881
  try {
8875
8882
  for (const name of readdirSync8(bundlePlugins)) {
8876
8883
  const dir = join11(bundlePlugins, name);
@@ -8887,9 +8894,9 @@ function listPluginDirs() {
8887
8894
  }
8888
8895
  function readEnabledPlugins(accountId) {
8889
8896
  const accountFile = resolve11(PLATFORM_ROOT, "..", "data/accounts", accountId, "account.json");
8890
- if (!existsSync14(accountFile)) return /* @__PURE__ */ new Set();
8897
+ if (!existsSync15(accountFile)) return /* @__PURE__ */ new Set();
8891
8898
  try {
8892
- const parsed = JSON.parse(readFileSync12(accountFile, "utf-8"));
8899
+ const parsed = JSON.parse(readFileSync13(accountFile, "utf-8"));
8893
8900
  return new Set(
8894
8901
  Array.isArray(parsed.enabledPlugins) ? parsed.enabledPlugins.filter((p) => typeof p === "string") : []
8895
8902
  );
@@ -8898,10 +8905,10 @@ function readEnabledPlugins(accountId) {
8898
8905
  }
8899
8906
  }
8900
8907
  function readFrontmatter(path2) {
8901
- if (!existsSync14(path2)) return null;
8908
+ if (!existsSync15(path2)) return null;
8902
8909
  let raw;
8903
8910
  try {
8904
- raw = readFileSync12(path2, "utf-8");
8911
+ raw = readFileSync13(path2, "utf-8");
8905
8912
  } catch {
8906
8913
  return null;
8907
8914
  }
@@ -8933,7 +8940,7 @@ ${fm}
8933
8940
  `plugin-manifest-tool-coverage plugin=${name} tools=${tools.length} with-desc=${withDesc}`
8934
8941
  );
8935
8942
  if (toolNames.length > 0 && descMap.size === 0) {
8936
- const hasSource = existsSync14(join11(dir, "mcp/dist/index.js")) || existsSync14(join11(dir, "mcp/src/index.ts"));
8943
+ const hasSource = existsSync15(join11(dir, "mcp/dist/index.js")) || existsSync15(join11(dir, "mcp/src/index.ts"));
8937
8944
  if (hasSource) {
8938
8945
  console.warn(
8939
8946
  `[spawn-context] WARN plugin=${name} mcp source present but tool-description regex matched zero entries \u2014 SDK upgrade may have changed the registration shape`
@@ -8960,7 +8967,7 @@ ${skillFm}
8960
8967
  }
8961
8968
  function computeSpecialistDomains(accountId) {
8962
8969
  const specialistsDir = resolve11(PLATFORM_ROOT, "..", "data/accounts", accountId, "specialists", "agents");
8963
- if (!existsSync14(specialistsDir)) return [];
8970
+ if (!existsSync15(specialistsDir)) return [];
8964
8971
  let entries;
8965
8972
  try {
8966
8973
  entries = readdirSync8(specialistsDir);
@@ -9006,10 +9013,10 @@ ${fm}
9006
9013
  }
9007
9014
  function computeDormantPlugins(accountId) {
9008
9015
  const accountFile = resolve11(PLATFORM_ROOT, "..", "data/accounts", accountId, "account.json");
9009
- if (!existsSync14(accountFile)) return [];
9016
+ if (!existsSync15(accountFile)) return [];
9010
9017
  let enabled;
9011
9018
  try {
9012
- const raw = readFileSync12(accountFile, "utf-8");
9019
+ const raw = readFileSync13(accountFile, "utf-8");
9013
9020
  const parsed = JSON.parse(raw);
9014
9021
  enabled = new Set(
9015
9022
  Array.isArray(parsed.enabledPlugins) ? parsed.enabledPlugins.filter((p) => typeof p === "string") : []
@@ -9021,7 +9028,7 @@ function computeDormantPlugins(accountId) {
9021
9028
  return [];
9022
9029
  }
9023
9030
  const pluginsDir = resolve11(PLATFORM_ROOT, "plugins");
9024
- if (!existsSync14(pluginsDir)) return [];
9031
+ if (!existsSync15(pluginsDir)) return [];
9025
9032
  let entries;
9026
9033
  try {
9027
9034
  entries = readdirSync8(pluginsDir);
@@ -9032,10 +9039,10 @@ function computeDormantPlugins(accountId) {
9032
9039
  for (const name of entries) {
9033
9040
  if (enabled.has(name)) continue;
9034
9041
  const manifestPath = resolve11(pluginsDir, name, "PLUGIN.md");
9035
- if (!existsSync14(manifestPath)) continue;
9042
+ if (!existsSync15(manifestPath)) continue;
9036
9043
  let manifest;
9037
9044
  try {
9038
- manifest = readFileSync12(manifestPath, "utf-8");
9045
+ manifest = readFileSync13(manifestPath, "utf-8");
9039
9046
  } catch {
9040
9047
  continue;
9041
9048
  }
@@ -9049,13 +9056,13 @@ function computeDormantPlugins(accountId) {
9049
9056
  }
9050
9057
 
9051
9058
  // server/routes/admin/claude-sessions.ts
9052
- import { existsSync as existsSync15, readFileSync as readFileSync13 } from "fs";
9059
+ import { existsSync as existsSync16, readFileSync as readFileSync14 } from "fs";
9053
9060
  import { resolve as resolve12 } from "path";
9054
9061
  function readTunnelState(brandConfigDir) {
9055
9062
  const statePath = `${process.env.HOME ?? ""}/${brandConfigDir}/cloudflared/tunnel.state`;
9056
- if (!existsSync15(statePath)) return null;
9063
+ if (!existsSync16(statePath)) return null;
9057
9064
  try {
9058
- const parsed = JSON.parse(readFileSync13(statePath, "utf-8"));
9065
+ const parsed = JSON.parse(readFileSync14(statePath, "utf-8"));
9059
9066
  const tunnelId = typeof parsed.tunnelId === "string" ? parsed.tunnelId : null;
9060
9067
  const tunnelName = typeof parsed.tunnelName === "string" ? parsed.tunnelName : null;
9061
9068
  const domain = typeof parsed.domain === "string" ? parsed.domain : null;
@@ -9069,7 +9076,7 @@ function resolveTunnelUrl() {
9069
9076
  const platformRoot2 = process.env.MAXY_PLATFORM_ROOT ?? process.env.PLATFORM_ROOT ?? resolve12(process.cwd(), "..");
9070
9077
  let brand;
9071
9078
  try {
9072
- brand = JSON.parse(readFileSync13(resolve12(platformRoot2, "config", "brand.json"), "utf-8"));
9079
+ brand = JSON.parse(readFileSync14(resolve12(platformRoot2, "config", "brand.json"), "utf-8"));
9073
9080
  } catch {
9074
9081
  return null;
9075
9082
  }
@@ -9282,18 +9289,18 @@ var ALLOWED_EVENTS = /* @__PURE__ */ new Set([
9282
9289
  ]);
9283
9290
  var app14 = new Hono();
9284
9291
  app14.post("/", async (c) => {
9285
- const TAG31 = "[admin:events]";
9292
+ const TAG30 = "[admin:events]";
9286
9293
  let body;
9287
9294
  try {
9288
9295
  body = await c.req.json();
9289
9296
  } catch (err) {
9290
9297
  const detail = err instanceof Error ? err.message : String(err);
9291
- console.error(`${TAG31} reject reason=body-not-json detail=${detail}`);
9298
+ console.error(`${TAG30} reject reason=body-not-json detail=${detail}`);
9292
9299
  return c.json({ ok: false, detail: "Request body was not valid JSON" }, 400);
9293
9300
  }
9294
9301
  const event = typeof body.event === "string" ? body.event : "";
9295
9302
  if (!ALLOWED_EVENTS.has(event)) {
9296
- console.error(`${TAG31} reject reason=event-not-allowed event=${JSON.stringify(event)}`);
9303
+ console.error(`${TAG30} reject reason=event-not-allowed event=${JSON.stringify(event)}`);
9297
9304
  return c.json({ ok: false, detail: `Event "${event}" is not allowed` }, 400);
9298
9305
  }
9299
9306
  const rawFields = body.fields && typeof body.fields === "object" ? body.fields : {};
@@ -12106,7 +12113,7 @@ app18.post("/", requireAdminSession, async (c) => {
12106
12113
  return c.json({ error: "AdminConversation node is missing sessionId" }, 500);
12107
12114
  }
12108
12115
  const managerPort = requirePortEnv("CLAUDE_SESSION_MANAGER_PORT", { tag: "graph-page-cascade" });
12109
- const managerBase6 = `http://127.0.0.1:${managerPort}`;
12116
+ const managerBase5 = `http://127.0.0.1:${managerPort}`;
12110
12117
  try {
12111
12118
  await session.close();
12112
12119
  } catch {
@@ -12114,7 +12121,7 @@ app18.post("/", requireAdminSession, async (c) => {
12114
12121
  const outcome = await cascadeAdminConversationDelete({
12115
12122
  sessionId,
12116
12123
  accountId,
12117
- managerBase: managerBase6
12124
+ managerBase: managerBase5
12118
12125
  });
12119
12126
  const elapsed2 = Date.now() - started;
12120
12127
  const labelSummary2 = preflightLabels.join(",");
@@ -12494,7 +12501,7 @@ var graph_default_view_default = app21;
12494
12501
  // server/routes/admin/sidebar-artefacts.ts
12495
12502
  import { readdir as readdir4, stat as stat5 } from "fs/promises";
12496
12503
  import { resolve as resolve16, relative as relative3, isAbsolute, sep as sep5, basename as basename5 } from "path";
12497
- import { existsSync as existsSync16 } from "fs";
12504
+ import { existsSync as existsSync17 } from "fs";
12498
12505
  var LIMIT = 50;
12499
12506
  var ADMIN_AGENT_FILES = ["IDENTITY.md", "SOUL.md", "KNOWLEDGE.md"];
12500
12507
  function toDataRootRelPath(absPath) {
@@ -12509,27 +12516,27 @@ app22.get("/", requireAdminSession, async (c) => {
12509
12516
  return c.json({ error: "Account not found for session" }, 401);
12510
12517
  }
12511
12518
  const start = Date.now();
12512
- const outputs = await fetchOutputArtefacts(accountId);
12513
- if (outputs === null) {
12519
+ const accountFiles = await fetchAccountFileArtefacts(accountId);
12520
+ if (accountFiles === null) {
12514
12521
  return c.json({ error: "Failed to load artefacts" }, 500);
12515
12522
  }
12516
12523
  const accountDir = resolve16(ACCOUNTS_DIR, accountId);
12517
12524
  const agents = await fetchAgentTemplateRows(accountDir);
12518
- const artefacts = [...outputs, ...agents].sort(
12525
+ const artefacts = [...accountFiles, ...agents].sort(
12519
12526
  (a, b) => (b.updatedAt ?? "").localeCompare(a.updatedAt ?? "")
12520
12527
  ).slice(0, LIMIT);
12521
12528
  const ms = Date.now() - start;
12522
12529
  console.log(
12523
- `[admin/sidebar-artefacts] account=${accountId} count=${artefacts.length} outputs=${outputs.length} agents=${agents.length} ms=${ms}`
12530
+ `[admin/sidebar-artefacts] account=${accountId} count=${artefacts.length} accountFiles=${accountFiles.length} agents=${agents.length} ms=${ms}`
12524
12531
  );
12525
12532
  return c.json({ artefacts });
12526
12533
  });
12527
- async function fetchOutputArtefacts(accountId) {
12534
+ async function fetchAccountFileArtefacts(accountId) {
12528
12535
  const session = getSession();
12529
12536
  try {
12530
12537
  const result = await session.run(
12531
12538
  `MATCH (f:FileArtifact { accountId: $accountId })
12532
- WHERE f.relativePath CONTAINS '/output/'
12539
+ WHERE f.relativePath STARTS WITH 'accounts/'
12533
12540
  RETURN f.relativePath AS relativePath,
12534
12541
  f.displayName AS displayName,
12535
12542
  f.mimeType AS mimeType,
@@ -12544,9 +12551,9 @@ async function fetchOutputArtefacts(accountId) {
12544
12551
  const mimeType = r.get("mimeType") ?? "";
12545
12552
  const modifiedAt = r.get("modifiedAt") ?? "";
12546
12553
  return {
12547
- id: `output-file:${relativePath}`,
12554
+ id: `account-file:${relativePath}`,
12548
12555
  name: displayName ?? basename5(relativePath),
12549
- kind: "output-file",
12556
+ kind: "account-file",
12550
12557
  updatedAt: modifiedAt,
12551
12558
  mimeType,
12552
12559
  downloadPath: relativePath,
@@ -12600,7 +12607,7 @@ async function fetchAgentTemplateRows(accountDir) {
12600
12607
  async function unionSpecialistFilenames(overrideDir, bundledDir) {
12601
12608
  const names = /* @__PURE__ */ new Set();
12602
12609
  for (const dir of [overrideDir, bundledDir]) {
12603
- if (!existsSync16(dir)) continue;
12610
+ if (!existsSync17(dir)) continue;
12604
12611
  try {
12605
12612
  const entries = await readdir4(dir);
12606
12613
  for (const entry of entries) {
@@ -12615,7 +12622,7 @@ async function unionSpecialistFilenames(overrideDir, bundledDir) {
12615
12622
  }
12616
12623
  async function readAgentTemplateRow(inp) {
12617
12624
  let chosenPath = null;
12618
- if (existsSync16(inp.overridePath)) {
12625
+ if (existsSync17(inp.overridePath)) {
12619
12626
  try {
12620
12627
  validateFilePathInAccount(inp.overridePath, inp.overrideRoot);
12621
12628
  chosenPath = inp.overridePath;
@@ -12626,7 +12633,7 @@ async function readAgentTemplateRow(inp) {
12626
12633
  );
12627
12634
  return null;
12628
12635
  }
12629
- } else if (existsSync16(inp.bundledPath)) {
12636
+ } else if (existsSync17(inp.bundledPath)) {
12630
12637
  if (!isWithin(inp.bundledPath, inp.bundledRoot)) {
12631
12638
  console.error(
12632
12639
  `[admin/sidebar-artefacts] agent-template-read-failed agent=${inp.displayName} kind=${inp.logName} error="bundled path outside PLATFORM_ROOT"`
@@ -12668,7 +12675,7 @@ function isWithin(target, root) {
12668
12675
  var sidebar_artefacts_default = app22;
12669
12676
 
12670
12677
  // server/routes/admin/sidebar-sessions.ts
12671
- import { readdirSync as readdirSync9, readFileSync as readFileSync14, statSync as statSync7 } from "fs";
12678
+ import { readdirSync as readdirSync9, readFileSync as readFileSync15, statSync as statSync7 } from "fs";
12672
12679
  import { join as join14, resolve as resolve17 } from "path";
12673
12680
  var SESSION_ID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\.jsonl$/i;
12674
12681
  var PID_FILE_RE = /^([0-9]+)\.json$/;
@@ -12738,7 +12745,7 @@ function sessionIdToPidMap(configDir2) {
12738
12745
  if (!Number.isFinite(pid) || pid <= 0) continue;
12739
12746
  let body;
12740
12747
  try {
12741
- body = readFileSync14(join14(sessionsDir, entry.name), "utf8");
12748
+ body = readFileSync15(join14(sessionsDir, entry.name), "utf8");
12742
12749
  } catch {
12743
12750
  continue;
12744
12751
  }
@@ -12760,7 +12767,7 @@ function loadUserTitles(accountDir) {
12760
12767
  const path2 = join14(accountDir, "session-titles.json");
12761
12768
  let raw;
12762
12769
  try {
12763
- raw = readFileSync14(path2, "utf8");
12770
+ raw = readFileSync15(path2, "utf8");
12764
12771
  } catch {
12765
12772
  return out;
12766
12773
  }
@@ -12785,7 +12792,7 @@ function loadUserTitles(accountDir) {
12785
12792
  function readSidecarMeta(metaPath) {
12786
12793
  let raw;
12787
12794
  try {
12788
- raw = readFileSync14(metaPath, "utf8");
12795
+ raw = readFileSync15(metaPath, "utf8");
12789
12796
  } catch {
12790
12797
  return { bridgeIds: [], role: null, channel: null };
12791
12798
  }
@@ -12875,7 +12882,7 @@ app23.get("/", requireAdminSession, async (c) => {
12875
12882
  let body;
12876
12883
  let mtimeMs;
12877
12884
  try {
12878
- body = readFileSync14(path2, "utf8");
12885
+ body = readFileSync15(path2, "utf8");
12879
12886
  mtimeMs = statSync7(path2).mtimeMs;
12880
12887
  } catch {
12881
12888
  continue;
@@ -12912,7 +12919,7 @@ app23.get("/", requireAdminSession, async (c) => {
12912
12919
  var sidebar_sessions_default = app23;
12913
12920
 
12914
12921
  // server/routes/admin/session-delete.ts
12915
- import { existsSync as existsSync17, readdirSync as readdirSync10, readFileSync as readFileSync15, unlinkSync as unlinkSync2 } from "fs";
12922
+ import { existsSync as existsSync18, readdirSync as readdirSync10, readFileSync as readFileSync16, unlinkSync as unlinkSync2 } from "fs";
12916
12923
  import { join as join15, resolve as resolve18 } from "path";
12917
12924
  var app24 = new Hono();
12918
12925
  var SESSION_ID_RE2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
@@ -12936,7 +12943,7 @@ function findPidForSession(configDir2, sessionId) {
12936
12943
  if (!Number.isFinite(pid) || pid <= 0) continue;
12937
12944
  let body;
12938
12945
  try {
12939
- body = readFileSync15(join15(sessionsDir, entry.name), "utf8");
12946
+ body = readFileSync16(join15(sessionsDir, entry.name), "utf8");
12940
12947
  } catch {
12941
12948
  continue;
12942
12949
  }
@@ -13059,7 +13066,7 @@ app24.post("/", requireAdminSession, async (c) => {
13059
13066
  }
13060
13067
  let deleted = false;
13061
13068
  try {
13062
- if (existsSync17(jsonlPath)) {
13069
+ if (existsSync18(jsonlPath)) {
13063
13070
  unlinkSync2(jsonlPath);
13064
13071
  deleted = true;
13065
13072
  }
@@ -13072,17 +13079,17 @@ app24.post("/", requireAdminSession, async (c) => {
13072
13079
  return c.json({ error: `unlink failed: ${msg}` }, 500);
13073
13080
  }
13074
13081
  try {
13075
- if (existsSync17(sidecarPath)) unlinkSync2(sidecarPath);
13082
+ if (existsSync18(sidecarPath)) unlinkSync2(sidecarPath);
13076
13083
  } catch {
13077
13084
  }
13078
13085
  if (pid !== null) {
13079
13086
  try {
13080
13087
  const pidFile = join15(configDir2, "sessions", `${pid}.json`);
13081
- if (existsSync17(pidFile)) unlinkSync2(pidFile);
13088
+ if (existsSync18(pidFile)) unlinkSync2(pidFile);
13082
13089
  } catch {
13083
13090
  }
13084
13091
  }
13085
- if (existsSync17(jsonlPath)) {
13092
+ if (existsSync18(jsonlPath)) {
13086
13093
  console.error(`[sessions-delete] resurrection jsonlPath=${jsonlPath} sessionId=${sessionId}`);
13087
13094
  return c.json(
13088
13095
  { error: "jsonl-resurrected", jsonlPath, pidKilled, waitForExitMs, escalatedToSIGKILL },
@@ -13261,14 +13268,14 @@ app26.get("/", async (c) => {
13261
13268
  var system_stats_default = app26;
13262
13269
 
13263
13270
  // server/routes/admin/health.ts
13264
- import { existsSync as existsSync18, readFileSync as readFileSync16 } from "fs";
13271
+ import { existsSync as existsSync19, readFileSync as readFileSync17 } from "fs";
13265
13272
  import { resolve as resolve19, join as join16 } from "path";
13266
13273
  var PLATFORM_ROOT6 = process.env.MAXY_PLATFORM_ROOT ?? resolve19(process.cwd(), "..");
13267
13274
  var brandHostname = "maxy";
13268
13275
  var brandJsonPath = join16(PLATFORM_ROOT6, "config", "brand.json");
13269
- if (existsSync18(brandJsonPath)) {
13276
+ if (existsSync19(brandJsonPath)) {
13270
13277
  try {
13271
- const brand = JSON.parse(readFileSync16(brandJsonPath, "utf-8"));
13278
+ const brand = JSON.parse(readFileSync17(brandJsonPath, "utf-8"));
13272
13279
  if (brand.hostname) brandHostname = brand.hostname;
13273
13280
  } catch {
13274
13281
  }
@@ -13277,8 +13284,8 @@ var VERSION_FILE = resolve19(PLATFORM_ROOT6, `config/.${brandHostname}-version`)
13277
13284
  var PROCESS_STARTED_AT = (/* @__PURE__ */ new Date()).toISOString();
13278
13285
  var PROBE_TIMEOUT_MS = 1e3;
13279
13286
  function readVersion() {
13280
- if (!existsSync18(VERSION_FILE)) return "unknown";
13281
- return readFileSync16(VERSION_FILE, "utf-8").trim() || "unknown";
13287
+ if (!existsSync19(VERSION_FILE)) return "unknown";
13288
+ return readFileSync17(VERSION_FILE, "utf-8").trim() || "unknown";
13282
13289
  }
13283
13290
  async function probeConversationDb() {
13284
13291
  let session;
@@ -13752,61 +13759,9 @@ app32.post("/", async (c) => {
13752
13759
  });
13753
13760
  var access_session_evict_default = app32;
13754
13761
 
13755
- // server/routes/admin/identity.ts
13756
- var import_dist4 = __toESM(require_dist3(), 1);
13757
- var app33 = new Hono();
13758
- var TAG28 = "[admin-identity]";
13759
- function managerBase5() {
13760
- const port2 = (0, import_dist4.requirePortEnv)("CLAUDE_SESSION_MANAGER_PORT", { tag: "admin-identity" });
13761
- return `http://127.0.0.1:${port2}`;
13762
- }
13763
- app33.post("/validate", async (c) => {
13764
- const remoteAddr = c.env?.incoming?.socket?.remoteAddress ?? "";
13765
- const isLoopback = remoteAddr === "127.0.0.1" || remoteAddr === "::1" || remoteAddr === "::ffff:127.0.0.1";
13766
- if (!isLoopback || c.req.header("x-forwarded-for")) {
13767
- return c.json({ error: "forbidden" }, 403);
13768
- }
13769
- let body;
13770
- try {
13771
- body = await c.req.json();
13772
- } catch {
13773
- return c.json({ error: "bad-request" }, 400);
13774
- }
13775
- const pin = typeof body.pin === "string" ? body.pin : "";
13776
- const sessionId = typeof body.sessionId === "string" ? body.sessionId : void 0;
13777
- if (!pin) return c.json({ error: "bad-request" }, 400);
13778
- const fireStop = () => {
13779
- if (sessionId) {
13780
- void fetch(`${managerBase5()}/${encodeURIComponent(sessionId)}/stop`, { method: "POST" }).catch(
13781
- () => {
13782
- }
13783
- );
13784
- }
13785
- };
13786
- const verdict = validatePin(pin, USERS_FILE);
13787
- if (verdict.kind !== "match") {
13788
- if (verdict.kind === "miss") fireStop();
13789
- console.log(`${TAG28} op=endpoint-validate result=${verdict.kind}${sessionId ? ` sessionId=${sessionId.slice(0, 8)}` : ""}`);
13790
- return c.json({ kind: verdict.kind });
13791
- }
13792
- const { userId } = verdict;
13793
- const accounts = resolveUserAccounts(userId);
13794
- if (accounts.length === 0) {
13795
- fireStop();
13796
- console.log(`${TAG28} op=endpoint-validate result=no-account userId=${userId.slice(0, 8)}`);
13797
- return c.json({ kind: "miss" });
13798
- }
13799
- const accountId = accounts[0].accountId;
13800
- const { userName } = await resolveUserIdentity(accountId, userId);
13801
- const aboutOwner = await resolveOwnerProfileBlock(accountId, userId);
13802
- console.log(`${TAG28} op=endpoint-validate result=match userId=${userId.slice(0, 8)} aboutOwner=${aboutOwner.ok ? "ok" : `unresolved:${aboutOwner.reason}`}`);
13803
- return c.json({ kind: "match", userId, userName, aboutOwner });
13804
- });
13805
- var identity_default = app33;
13806
-
13807
13762
  // server/routes/admin/browser.ts
13808
- var app34 = new Hono();
13809
- app34.post("/launch", requireAdminSession, async (c) => {
13763
+ var app33 = new Hono();
13764
+ app33.post("/launch", requireAdminSession, async (c) => {
13810
13765
  try {
13811
13766
  const transport = resolveBrowserTransport(c.req.raw, c.env?.incoming?.socket?.remoteAddress);
13812
13767
  console.error(`[admin/browser/launch] op=request transport=${transport}`);
@@ -13834,44 +13789,43 @@ app34.post("/launch", requireAdminSession, async (c) => {
13834
13789
  );
13835
13790
  }
13836
13791
  });
13837
- var browser_default = app34;
13792
+ var browser_default = app33;
13838
13793
 
13839
13794
  // server/routes/admin/index.ts
13840
- var app35 = new Hono();
13841
- app35.route("/session", session_default);
13842
- app35.route("/identity", identity_default);
13843
- app35.route("/logs", logs_default);
13844
- app35.route("/claude-info", claude_info_default);
13845
- app35.route("/attachment", attachment_default);
13846
- app35.route("/agents", agents_default);
13847
- app35.route("/sessions", sessions_default);
13848
- app35.route("/claude-sessions", claude_sessions_default);
13849
- app35.route("/log-ingest", log_ingest_default);
13850
- app35.route("/events", events_default);
13851
- app35.route("/files", files_default);
13852
- app35.route("/graph-search", graph_search_default);
13853
- app35.route("/graph-subgraph", graph_subgraph_default);
13854
- app35.route("/graph-delete", graph_delete_default);
13855
- app35.route("/graph-restore", graph_restore_default);
13856
- app35.route("/graph-labels-in-graph", graph_labels_in_graph_default);
13857
- app35.route("/graph-default-view", graph_default_view_default);
13858
- app35.route("/sidebar-artefacts", sidebar_artefacts_default);
13859
- app35.route("/sidebar-sessions", sidebar_sessions_default);
13860
- app35.route("/session-delete", session_delete_default);
13861
- app35.route("/browser", browser_default);
13862
- app35.route("/session-rc-spawn", session_rc_spawn_default);
13863
- app35.route("/system-stats", system_stats_default);
13864
- app35.route("/health-brand", health_default2);
13865
- app35.route("/linkedin-ingest", linkedin_ingest_default);
13866
- app35.route("/post-turn-context", post_turn_context_default);
13867
- app35.route("/public-session-context", public_session_context_default);
13868
- app35.route("/public-session-exit", public_session_exit_default);
13869
- app35.route("/access-session-evict", access_session_evict_default);
13870
- var admin_default = app35;
13795
+ var app34 = new Hono();
13796
+ app34.route("/session", session_default);
13797
+ app34.route("/logs", logs_default);
13798
+ app34.route("/claude-info", claude_info_default);
13799
+ app34.route("/attachment", attachment_default);
13800
+ app34.route("/agents", agents_default);
13801
+ app34.route("/sessions", sessions_default);
13802
+ app34.route("/claude-sessions", claude_sessions_default);
13803
+ app34.route("/log-ingest", log_ingest_default);
13804
+ app34.route("/events", events_default);
13805
+ app34.route("/files", files_default);
13806
+ app34.route("/graph-search", graph_search_default);
13807
+ app34.route("/graph-subgraph", graph_subgraph_default);
13808
+ app34.route("/graph-delete", graph_delete_default);
13809
+ app34.route("/graph-restore", graph_restore_default);
13810
+ app34.route("/graph-labels-in-graph", graph_labels_in_graph_default);
13811
+ app34.route("/graph-default-view", graph_default_view_default);
13812
+ app34.route("/sidebar-artefacts", sidebar_artefacts_default);
13813
+ app34.route("/sidebar-sessions", sidebar_sessions_default);
13814
+ app34.route("/session-delete", session_delete_default);
13815
+ app34.route("/browser", browser_default);
13816
+ app34.route("/session-rc-spawn", session_rc_spawn_default);
13817
+ app34.route("/system-stats", system_stats_default);
13818
+ app34.route("/health-brand", health_default2);
13819
+ app34.route("/linkedin-ingest", linkedin_ingest_default);
13820
+ app34.route("/post-turn-context", post_turn_context_default);
13821
+ app34.route("/public-session-context", public_session_context_default);
13822
+ app34.route("/public-session-exit", public_session_exit_default);
13823
+ app34.route("/access-session-evict", access_session_evict_default);
13824
+ var admin_default = app34;
13871
13825
 
13872
13826
  // app/lib/access-gate.ts
13873
13827
  import neo4j4 from "neo4j-driver";
13874
- import { readFileSync as readFileSync17 } from "fs";
13828
+ import { readFileSync as readFileSync18 } from "fs";
13875
13829
  import { resolve as resolve20 } from "path";
13876
13830
  import { randomUUID as randomUUID10 } from "crypto";
13877
13831
  var PLATFORM_ROOT7 = process.env.MAXY_PLATFORM_ROOT ?? resolve20(process.cwd(), "..");
@@ -13880,7 +13834,7 @@ function readPassword() {
13880
13834
  if (process.env.NEO4J_PASSWORD) return process.env.NEO4J_PASSWORD;
13881
13835
  const passwordFile = resolve20(PLATFORM_ROOT7, "config/.neo4j-password");
13882
13836
  try {
13883
- return readFileSync17(passwordFile, "utf-8").trim();
13837
+ return readFileSync18(passwordFile, "utf-8").trim();
13884
13838
  } catch {
13885
13839
  throw new Error(
13886
13840
  `Neo4j password not found. Expected at ${passwordFile} or in NEO4J_PASSWORD env var.`
@@ -14078,11 +14032,11 @@ async function generateNewMagicToken(grantId) {
14078
14032
  }
14079
14033
 
14080
14034
  // server/routes/access/verify-token.ts
14081
- var TAG29 = "[access-verify]";
14035
+ var TAG28 = "[access-verify]";
14082
14036
  var MINT_TAG = "[access-session-mint]";
14083
14037
  var COOKIE_NAME = "__access_session";
14084
- var app36 = new Hono();
14085
- app36.post("/", async (c) => {
14038
+ var app35 = new Hono();
14039
+ app35.post("/", async (c) => {
14086
14040
  const ip = c.var.clientIp || "unknown";
14087
14041
  let body;
14088
14042
  try {
@@ -14097,39 +14051,39 @@ app36.post("/", async (c) => {
14097
14051
  }
14098
14052
  const rateMsg = checkAccessRateLimit(ip, agentSlug);
14099
14053
  if (rateMsg) {
14100
- console.error(`${TAG29} grantId=- agentSlug=${agentSlug} result=rate-limited ip=${ip}`);
14054
+ console.error(`${TAG28} grantId=- agentSlug=${agentSlug} result=rate-limited ip=${ip}`);
14101
14055
  return c.json({ error: rateMsg }, 429);
14102
14056
  }
14103
14057
  const grant = await findGrantByMagicToken(token);
14104
14058
  if (!grant) {
14105
14059
  recordAccessFailedAttempt(ip, agentSlug);
14106
- console.error(`${TAG29} grantId=- agentSlug=${agentSlug} result=notfound ip=${ip}`);
14060
+ console.error(`${TAG28} grantId=- agentSlug=${agentSlug} result=notfound ip=${ip}`);
14107
14061
  return c.json({ error: "invalid-or-expired-link" }, 401);
14108
14062
  }
14109
14063
  if (grant.agentSlug !== agentSlug) {
14110
14064
  recordAccessFailedAttempt(ip, agentSlug);
14111
14065
  console.error(
14112
- `${TAG29} grantId=${grant.grantId} agentSlug=${agentSlug} result=agent-mismatch grantAgent=${grant.agentSlug} ip=${ip}`
14066
+ `${TAG28} grantId=${grant.grantId} agentSlug=${agentSlug} result=agent-mismatch grantAgent=${grant.agentSlug} ip=${ip}`
14113
14067
  );
14114
14068
  return c.json({ error: "invalid-or-expired-link" }, 401);
14115
14069
  }
14116
14070
  if (grant.status === "expired" || grant.status === "revoked") {
14117
14071
  recordAccessFailedAttempt(ip, agentSlug);
14118
14072
  console.error(
14119
- `${TAG29} grantId=${grant.grantId} agentSlug=${agentSlug} result=expired status=${grant.status} ip=${ip}`
14073
+ `${TAG28} grantId=${grant.grantId} agentSlug=${agentSlug} result=expired status=${grant.status} ip=${ip}`
14120
14074
  );
14121
14075
  return c.json({ error: "access-no-longer-valid" }, 401);
14122
14076
  }
14123
14077
  if (grant.expiresAt !== null && grant.expiresAt < Date.now()) {
14124
14078
  recordAccessFailedAttempt(ip, agentSlug);
14125
14079
  console.error(
14126
- `${TAG29} grantId=${grant.grantId} agentSlug=${agentSlug} result=expired reason=expiresAt-past ip=${ip}`
14080
+ `${TAG28} grantId=${grant.grantId} agentSlug=${agentSlug} result=expired reason=expiresAt-past ip=${ip}`
14127
14081
  );
14128
14082
  return c.json({ error: "access-no-longer-valid" }, 401);
14129
14083
  }
14130
14084
  if (!grant.sliceToken) {
14131
14085
  console.error(
14132
- `${TAG29} grantId=${grant.grantId} agentSlug=${agentSlug} result=no-slice-token reason=schema-violation`
14086
+ `${TAG28} grantId=${grant.grantId} agentSlug=${agentSlug} result=no-slice-token reason=schema-violation`
14133
14087
  );
14134
14088
  return c.json({ error: "grant-misconfigured" }, 500);
14135
14089
  }
@@ -14145,12 +14099,12 @@ app36.post("/", async (c) => {
14145
14099
  await consumeMagicTokenAndActivate(grant.grantId);
14146
14100
  } catch (err) {
14147
14101
  console.error(
14148
- `${TAG29} grantId=${grant.grantId} agentSlug=${agentSlug} result=consume-failed err="${err instanceof Error ? err.message : String(err)}"`
14102
+ `${TAG28} grantId=${grant.grantId} agentSlug=${agentSlug} result=consume-failed err="${err instanceof Error ? err.message : String(err)}"`
14149
14103
  );
14150
14104
  return c.json({ error: "verification-failed" }, 500);
14151
14105
  }
14152
14106
  clearAccessRateLimit(ip, agentSlug);
14153
- console.log(`${TAG29} grantId=${grant.grantId} agentSlug=${agentSlug} result=ok ip=${ip}`);
14107
+ console.log(`${TAG28} grantId=${grant.grantId} agentSlug=${agentSlug} result=ok ip=${ip}`);
14154
14108
  console.log(
14155
14109
  `${MINT_TAG} grantId=${grant.grantId} sliceToken=${grant.sliceToken.slice(0, 8)} agentSlug=${agentSlug} personId=${grant.personId ?? "none"}`
14156
14110
  );
@@ -14164,7 +14118,7 @@ app36.post("/", async (c) => {
14164
14118
  displayName: grant.displayName
14165
14119
  });
14166
14120
  });
14167
- var verify_token_default = app36;
14121
+ var verify_token_default = app35;
14168
14122
 
14169
14123
  // app/lib/access-email.ts
14170
14124
  import { spawn as spawn2 } from "child_process";
@@ -14226,10 +14180,10 @@ async function sendMagicLinkEmail(payload) {
14226
14180
  }
14227
14181
 
14228
14182
  // server/routes/access/request-magic-link.ts
14229
- var TAG30 = "[access-request-link]";
14230
- var app37 = new Hono();
14183
+ var TAG29 = "[access-request-link]";
14184
+ var app36 = new Hono();
14231
14185
  var VISITOR_MESSAGE = "If that email is on the invite list, a fresh link is on the way.";
14232
- app37.post("/", async (c) => {
14186
+ app36.post("/", async (c) => {
14233
14187
  let body;
14234
14188
  try {
14235
14189
  body = await c.req.json();
@@ -14243,18 +14197,18 @@ app37.post("/", async (c) => {
14243
14197
  }
14244
14198
  const rateMsg = checkRequestLinkRateLimit(contactValue);
14245
14199
  if (rateMsg) {
14246
- console.error(`${TAG30} contactValue=${maskContact(contactValue)} result=rate-limited`);
14200
+ console.error(`${TAG29} contactValue=${maskContact(contactValue)} result=rate-limited`);
14247
14201
  return c.json({ error: rateMsg }, 429);
14248
14202
  }
14249
14203
  recordRequestLinkAttempt(contactValue);
14250
14204
  const accountId = process.env.ACCOUNT_ID ?? "";
14251
14205
  if (!accountId) {
14252
- console.error(`${TAG30} contactValue=${maskContact(contactValue)} result=no-account-id`);
14206
+ console.error(`${TAG29} contactValue=${maskContact(contactValue)} result=no-account-id`);
14253
14207
  return c.json({ message: VISITOR_MESSAGE }, 200);
14254
14208
  }
14255
14209
  const grant = await findActiveGrantByContact(contactValue, agentSlug, accountId);
14256
14210
  if (!grant) {
14257
- console.log(`${TAG30} contactValue=${maskContact(contactValue)} result=notfound`);
14211
+ console.log(`${TAG29} contactValue=${maskContact(contactValue)} result=notfound`);
14258
14212
  return c.json({ message: VISITOR_MESSAGE }, 200);
14259
14213
  }
14260
14214
  let token;
@@ -14262,7 +14216,7 @@ app37.post("/", async (c) => {
14262
14216
  token = await generateNewMagicToken(grant.grantId);
14263
14217
  } catch (err) {
14264
14218
  console.error(
14265
- `${TAG30} contactValue=${maskContact(contactValue)} result=mint-failed err="${err instanceof Error ? err.message : String(err)}"`
14219
+ `${TAG29} contactValue=${maskContact(contactValue)} result=mint-failed err="${err instanceof Error ? err.message : String(err)}"`
14266
14220
  );
14267
14221
  return c.json({ message: VISITOR_MESSAGE }, 200);
14268
14222
  }
@@ -14294,25 +14248,25 @@ app37.post("/", async (c) => {
14294
14248
  });
14295
14249
  if (!sendResult.ok) {
14296
14250
  console.error(
14297
- `${TAG30} contactValue=${maskContact(contactValue)} result=send-failed err="${sendResult.error}"`
14251
+ `${TAG29} contactValue=${maskContact(contactValue)} result=send-failed err="${sendResult.error}"`
14298
14252
  );
14299
14253
  return c.json({ message: VISITOR_MESSAGE }, 200);
14300
14254
  }
14301
14255
  console.log(
14302
- `${TAG30} contactValue=${maskContact(contactValue)} result=ok messageId=${sendResult.messageId}`
14256
+ `${TAG29} contactValue=${maskContact(contactValue)} result=ok messageId=${sendResult.messageId}`
14303
14257
  );
14304
14258
  return c.json({ message: VISITOR_MESSAGE }, 200);
14305
14259
  });
14306
- var request_magic_link_default = app37;
14260
+ var request_magic_link_default = app36;
14307
14261
 
14308
14262
  // server/routes/access/index.ts
14309
- var app38 = new Hono();
14310
- app38.route("/verify-token", verify_token_default);
14311
- app38.route("/request-magic-link", request_magic_link_default);
14312
- var access_default = app38;
14263
+ var app37 = new Hono();
14264
+ app37.route("/verify-token", verify_token_default);
14265
+ app37.route("/request-magic-link", request_magic_link_default);
14266
+ var access_default = app37;
14313
14267
 
14314
14268
  // server/routes/sites.ts
14315
- import { existsSync as existsSync19, readFileSync as readFileSync18, realpathSync as realpathSync5, statSync as statSync8 } from "fs";
14269
+ import { existsSync as existsSync20, readFileSync as readFileSync19, realpathSync as realpathSync5, statSync as statSync8 } from "fs";
14316
14270
  import { resolve as resolve22 } from "path";
14317
14271
  var SAFE_SEG_RE = /^[a-z0-9_][a-z0-9_.-]{0,99}$/i;
14318
14272
  var MIME = {
@@ -14344,8 +14298,8 @@ function getExt(p) {
14344
14298
  if (idx < p.lastIndexOf("/")) return "";
14345
14299
  return p.slice(idx).toLowerCase();
14346
14300
  }
14347
- var app39 = new Hono();
14348
- app39.get("/:rel{.*}", (c) => {
14301
+ var app38 = new Hono();
14302
+ app38.get("/:rel{.*}", (c) => {
14349
14303
  const reqPath = c.req.path;
14350
14304
  const rawRel = c.req.param("rel") ?? "";
14351
14305
  const trimmed = rawRel.replace(/^\/+/, "").replace(/\/+$/, "");
@@ -14378,7 +14332,7 @@ app39.get("/:rel{.*}", (c) => {
14378
14332
  }
14379
14333
  let stat7;
14380
14334
  try {
14381
- stat7 = existsSync19(filePath) ? statSync8(filePath) : null;
14335
+ stat7 = existsSync20(filePath) ? statSync8(filePath) : null;
14382
14336
  } catch {
14383
14337
  stat7 = null;
14384
14338
  }
@@ -14397,7 +14351,7 @@ app39.get("/:rel{.*}", (c) => {
14397
14351
  console.error(`[sites] path-traversal-rejected path=${reqPath} reason=escape status=403`);
14398
14352
  return c.text("Forbidden", 403);
14399
14353
  }
14400
- if (!existsSync19(filePath)) {
14354
+ if (!existsSync20(filePath)) {
14401
14355
  console.error(`[sites] not-found path=${reqPath} status=404`);
14402
14356
  return c.text("Not found", 404);
14403
14357
  }
@@ -14416,7 +14370,7 @@ app39.get("/:rel{.*}", (c) => {
14416
14370
  }
14417
14371
  let body;
14418
14372
  try {
14419
- body = readFileSync18(realPath);
14373
+ body = readFileSync19(realPath);
14420
14374
  } catch (err) {
14421
14375
  const code = err?.code;
14422
14376
  if (code === "EISDIR") {
@@ -14448,11 +14402,11 @@ app39.get("/:rel{.*}", (c) => {
14448
14402
  "X-Content-Type-Options": "nosniff"
14449
14403
  });
14450
14404
  });
14451
- var sites_default = app39;
14405
+ var sites_default = app38;
14452
14406
 
14453
14407
  // app/lib/visitor-token.ts
14454
14408
  import { createHmac, randomBytes, timingSafeEqual } from "crypto";
14455
- import { mkdirSync as mkdirSync4, readFileSync as readFileSync19, writeFileSync as writeFileSync6 } from "fs";
14409
+ import { mkdirSync as mkdirSync4, readFileSync as readFileSync20, writeFileSync as writeFileSync7 } from "fs";
14456
14410
  import { dirname as dirname4 } from "path";
14457
14411
  var TOKEN_PREFIX = "v1.";
14458
14412
  var SECRET_BYTES = 32;
@@ -14461,7 +14415,7 @@ var cachedSecret = null;
14461
14415
  function getSecret() {
14462
14416
  if (cachedSecret) return cachedSecret;
14463
14417
  try {
14464
- const hex2 = readFileSync19(VISITOR_TOKEN_SECRET_FILE, "utf-8").trim();
14418
+ const hex2 = readFileSync20(VISITOR_TOKEN_SECRET_FILE, "utf-8").trim();
14465
14419
  if (hex2.length === SECRET_BYTES * 2) {
14466
14420
  cachedSecret = Buffer.from(hex2, "hex");
14467
14421
  return cachedSecret;
@@ -14471,11 +14425,11 @@ function getSecret() {
14471
14425
  const fresh = randomBytes(SECRET_BYTES).toString("hex");
14472
14426
  try {
14473
14427
  mkdirSync4(dirname4(VISITOR_TOKEN_SECRET_FILE), { recursive: true, mode: 448 });
14474
- writeFileSync6(VISITOR_TOKEN_SECRET_FILE, fresh, { mode: 384, flag: "wx" });
14428
+ writeFileSync7(VISITOR_TOKEN_SECRET_FILE, fresh, { mode: 384, flag: "wx" });
14475
14429
  console.log(`[visitor-token] secret minted path=${VISITOR_TOKEN_SECRET_FILE}`);
14476
14430
  } catch {
14477
14431
  }
14478
- const hex = readFileSync19(VISITOR_TOKEN_SECRET_FILE, "utf-8").trim();
14432
+ const hex = readFileSync20(VISITOR_TOKEN_SECRET_FILE, "utf-8").trim();
14479
14433
  cachedSecret = Buffer.from(hex, "hex");
14480
14434
  return cachedSecret;
14481
14435
  }
@@ -14528,7 +14482,7 @@ var VISITOR_COOKIE_NAME = "mxy_v";
14528
14482
  var VISITOR_COOKIE_MAX_AGE_SECONDS = Math.floor(DEFAULT_TTL_MS / 1e3);
14529
14483
 
14530
14484
  // app/lib/brand-config.ts
14531
- import { existsSync as existsSync20, readFileSync as readFileSync20 } from "fs";
14485
+ import { existsSync as existsSync21, readFileSync as readFileSync21 } from "fs";
14532
14486
  import { join as join17 } from "path";
14533
14487
  var cached2 = null;
14534
14488
  var cachedAttempted = false;
@@ -14538,9 +14492,9 @@ function readBrandConfig() {
14538
14492
  const platformRoot2 = process.env.MAXY_PLATFORM_ROOT;
14539
14493
  if (!platformRoot2) return null;
14540
14494
  const brandPath = join17(platformRoot2, "config", "brand.json");
14541
- if (!existsSync20(brandPath)) return null;
14495
+ if (!existsSync21(brandPath)) return null;
14542
14496
  try {
14543
- cached2 = JSON.parse(readFileSync20(brandPath, "utf-8"));
14497
+ cached2 = JSON.parse(readFileSync21(brandPath, "utf-8"));
14544
14498
  return cached2;
14545
14499
  } catch {
14546
14500
  return null;
@@ -14548,7 +14502,7 @@ function readBrandConfig() {
14548
14502
  }
14549
14503
 
14550
14504
  // server/routes/visitor-consent.ts
14551
- var app40 = new Hono();
14505
+ var app39 = new Hono();
14552
14506
  var CONSENT_COOKIE_NAME = "mxy_consent";
14553
14507
  var CONSENT_COOKIE_MAX_AGE_SECONDS = 60 * 60 * 24 * 365;
14554
14508
  var DEFAULT_CONSENT_COPY = {
@@ -14593,17 +14547,17 @@ function siteSlugFromReferer(referer) {
14593
14547
  return "";
14594
14548
  }
14595
14549
  }
14596
- app40.options("/consent", (c) => {
14550
+ app39.options("/consent", (c) => {
14597
14551
  const origin = getOrigin(c);
14598
14552
  setCorsHeaders(c, origin);
14599
14553
  return c.body(null, 204);
14600
14554
  });
14601
- app40.options("/brand-config", (c) => {
14555
+ app39.options("/brand-config", (c) => {
14602
14556
  const origin = getOrigin(c);
14603
14557
  setCorsHeaders(c, origin);
14604
14558
  return c.body(null, 204);
14605
14559
  });
14606
- app40.post("/consent", async (c) => {
14560
+ app39.post("/consent", async (c) => {
14607
14561
  const origin = getOrigin(c);
14608
14562
  setCorsHeaders(c, origin);
14609
14563
  let raw;
@@ -14643,7 +14597,7 @@ app40.post("/consent", async (c) => {
14643
14597
  console.log(`[consent] ${parsed.decision} site=${site} brand=${brandName} tokenBound=${tokenBound}`);
14644
14598
  return c.body(null, 204);
14645
14599
  });
14646
- app40.get("/brand-config", (c) => {
14600
+ app39.get("/brand-config", (c) => {
14647
14601
  const origin = getOrigin(c);
14648
14602
  setCorsHeaders(c, origin);
14649
14603
  const brand = readBrandConfig();
@@ -14653,7 +14607,7 @@ app40.get("/brand-config", (c) => {
14653
14607
  c.header("Cache-Control", "public, max-age=300");
14654
14608
  return c.json({ consent: { copy, palette } });
14655
14609
  });
14656
- var visitor_consent_default = app40;
14610
+ var visitor_consent_default = app39;
14657
14611
 
14658
14612
  // server/routes/listings.ts
14659
14613
  function getCookie(headerValue, name) {
@@ -14680,8 +14634,8 @@ function appendConsentParams(pageUrl, token) {
14680
14634
  }
14681
14635
  var SAFE_SLUG_RE = /^[a-z0-9](?:[a-z0-9-]{0,118}[a-z0-9])?$/;
14682
14636
  var CHAT_SESSION_KEY_RE = /^[A-Za-z0-9][A-Za-z0-9_-]{7,127}$/;
14683
- var app41 = new Hono();
14684
- app41.get("/:slug/click", async (c) => {
14637
+ var app40 = new Hono();
14638
+ app40.get("/:slug/click", async (c) => {
14685
14639
  const slug = c.req.param("slug") ?? "";
14686
14640
  const rawSession = c.req.query("session") ?? "";
14687
14641
  const sessionKey = CHAT_SESSION_KEY_RE.test(rawSession) ? rawSession : "invalid";
@@ -14745,10 +14699,10 @@ app41.get("/:slug/click", async (c) => {
14745
14699
  console.log(`[property-card-click] sessionKey=${sessionKey} listingSlug=${slug} consent=${consentCookie ?? "absent"} ts=${(/* @__PURE__ */ new Date()).toISOString()}`);
14746
14700
  return c.redirect(redirectUrl, 302);
14747
14701
  });
14748
- var listings_default = app41;
14702
+ var listings_default = app40;
14749
14703
 
14750
14704
  // server/routes/visitor-event.ts
14751
- var app42 = new Hono();
14705
+ var app41 = new Hono();
14752
14706
  var BOT_UA_RE = /\b(bot|crawl|spider|slurp|headlesschrome|phantomjs|googlebot|bingbot|yandex|baiduspider|ahrefsbot|semrushbot|mj12bot|dotbot|petalbot)\b/i;
14753
14707
  var buckets = /* @__PURE__ */ new Map();
14754
14708
  var RATE_LIMIT = 60;
@@ -14815,12 +14769,12 @@ function originAllowed(origin, allowlist) {
14815
14769
  return false;
14816
14770
  }
14817
14771
  }
14818
- app42.options("/event", (c) => {
14772
+ app41.options("/event", (c) => {
14819
14773
  const origin = getOrigin2(c);
14820
14774
  setCorsHeaders2(c, origin);
14821
14775
  return c.body(null, 204);
14822
14776
  });
14823
- app42.post("/event", async (c) => {
14777
+ app41.post("/event", async (c) => {
14824
14778
  const origin = getOrigin2(c);
14825
14779
  setCorsHeaders2(c, origin);
14826
14780
  const ua = c.req.header("user-agent") ?? "";
@@ -15025,17 +14979,17 @@ async function writeEvent(opts) {
15025
14979
  );
15026
14980
  }
15027
14981
  }
15028
- var visitor_event_default = app42;
14982
+ var visitor_event_default = app41;
15029
14983
 
15030
14984
  // server/routes/session.ts
15031
14985
  import { resolve as resolve23 } from "path";
15032
- import { existsSync as existsSync21, writeFileSync as writeFileSync7, mkdirSync as mkdirSync5 } from "fs";
14986
+ import { existsSync as existsSync22, writeFileSync as writeFileSync8, mkdirSync as mkdirSync5 } from "fs";
15033
14987
  var UUID_RE4 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
15034
14988
  function writeBrandingCache(accountId, agentSlug, branding) {
15035
14989
  try {
15036
14990
  const cacheDir = resolve23(MAXY_DIR, "branding-cache", accountId);
15037
14991
  mkdirSync5(cacheDir, { recursive: true });
15038
- writeFileSync7(resolve23(cacheDir, `${agentSlug}.json`), JSON.stringify(branding), "utf-8");
14992
+ writeFileSync8(resolve23(cacheDir, `${agentSlug}.json`), JSON.stringify(branding), "utf-8");
15039
14993
  } catch (err) {
15040
14994
  console.error(`[branding] cache write failed: ${err instanceof Error ? err.message : String(err)}`);
15041
14995
  }
@@ -15071,8 +15025,8 @@ function withVisitorCookie(response, visitorId) {
15071
15025
  headers
15072
15026
  });
15073
15027
  }
15074
- var app43 = new Hono();
15075
- app43.post("/", async (c) => {
15028
+ var app42 = new Hono();
15029
+ app42.post("/", async (c) => {
15076
15030
  let body;
15077
15031
  try {
15078
15032
  body = await c.req.json();
@@ -15124,7 +15078,7 @@ app43.post("/", async (c) => {
15124
15078
  let agentConfig = null;
15125
15079
  if (account) {
15126
15080
  const agentDir = resolve23(account.accountDir, "agents", agentSlug);
15127
- if (!existsSync21(agentDir) || !existsSync21(resolve23(agentDir, "config.json"))) {
15081
+ if (!existsSync22(agentDir) || !existsSync22(resolve23(agentDir, "config.json"))) {
15128
15082
  return c.json({ error: "Agent not found" }, 404);
15129
15083
  }
15130
15084
  agentConfig = resolveAgentConfig(account.accountDir, agentSlug);
@@ -15283,10 +15237,10 @@ app43.post("/", async (c) => {
15283
15237
  newVisitorId
15284
15238
  );
15285
15239
  });
15286
- var session_default2 = app43;
15240
+ var session_default2 = app42;
15287
15241
 
15288
15242
  // app/lib/graph-health.ts
15289
- var import_dist5 = __toESM(require_dist4(), 1);
15243
+ var import_dist4 = __toESM(require_dist3(), 1);
15290
15244
  var HOUR_MS = 60 * 60 * 1e3;
15291
15245
  function renderLabelTop(rows) {
15292
15246
  return rows.map((b) => {
@@ -15331,7 +15285,7 @@ async function runGraphHealthTick() {
15331
15285
  console.error(
15332
15286
  `[graph-health] userprofile-multi accounts=${upAccounts} top=${upTop.length > 0 ? upTop.join(",") : "none"}`
15333
15287
  );
15334
- const indexed = [...import_dist5.VECTOR_INDEXED_LABELS];
15288
+ const indexed = [...import_dist4.VECTOR_INDEXED_LABELS];
15335
15289
  const embCount = await session.run(
15336
15290
  `MATCH (n) WHERE n.embedding IS NULL AND any(l IN labels(n) WHERE l IN $indexed)
15337
15291
  RETURN count(n) AS total`,
@@ -15516,44 +15470,115 @@ var InboundHub = class {
15516
15470
  state(senderId) {
15517
15471
  let s = this.senders.get(senderId);
15518
15472
  if (!s) {
15519
- s = { subscriber: null, queue: [] };
15473
+ s = { subscriber: null, ready: false, inFlight: [] };
15520
15474
  this.senders.set(senderId, s);
15521
15475
  }
15522
15476
  return s;
15523
15477
  }
15524
- /** Route an inbound message to its sender. Delivers immediately if that
15525
- * sender's channel server is attached; otherwise queues it (cold start). */
15526
- deliver(payload) {
15478
+ /** Route an inbound message. Delivers immediately only if the sender's channel
15479
+ * server is attached AND has signalled readiness; otherwise queues it (cold
15480
+ * start, or the attach->ready window) so it is never written before the
15481
+ * channel server can consume it. Every accepted message is tracked in-flight
15482
+ * until it reaches a terminal state. */
15483
+ deliver(payload, now) {
15527
15484
  const s = this.state(payload.senderId);
15528
- if (s.subscriber) {
15485
+ if (s.subscriber && s.ready) {
15529
15486
  s.subscriber(payload);
15487
+ s.inFlight.push({ payload, state: "drained", enqueuedAt: now, drainedAt: now });
15530
15488
  } else {
15531
- s.queue.push(payload);
15489
+ s.inFlight.push({ payload, state: "queued", enqueuedAt: now, drainedAt: null });
15532
15490
  }
15533
15491
  }
15534
- /** A sender's channel server connected. Drain any queued messages to it in
15535
- * arrival order, then route live. Replaces any prior subscriber. */
15492
+ /** A sender's channel server connected. Register the subscriber but do NOT
15493
+ * drain yet the drain waits for markReady so nothing is written before the
15494
+ * channel server is consuming. Replaces any prior subscriber. */
15536
15495
  attach(senderId, subscriber) {
15537
15496
  const s = this.state(senderId);
15538
15497
  s.subscriber = subscriber;
15539
- if (s.queue.length > 0) {
15540
- const pending = s.queue;
15541
- s.queue = [];
15542
- for (const payload of pending) subscriber(payload);
15498
+ s.ready = false;
15499
+ }
15500
+ /** The sender's channel server reported it is connected and consuming. Drain
15501
+ * every queued message to it in arrival order, marking each drained-awaiting-
15502
+ * ack, then route live. Returns the number drained. */
15503
+ markReady(senderId, now) {
15504
+ const s = this.state(senderId);
15505
+ s.ready = true;
15506
+ if (!s.subscriber) return 0;
15507
+ let count = 0;
15508
+ for (const e of s.inFlight) {
15509
+ if (e.state === "queued") {
15510
+ s.subscriber(e.payload);
15511
+ e.state = "drained";
15512
+ e.drainedAt = now;
15513
+ count++;
15514
+ }
15543
15515
  }
15516
+ return count;
15517
+ }
15518
+ /** The channel server confirmed it received (emitted) a drained message. The
15519
+ * message has reached the agent; drop it from in-flight. Returns whether a
15520
+ * matching in-flight message was found. */
15521
+ ack(senderId, waMessageId) {
15522
+ const s = this.senders.get(senderId);
15523
+ if (!s) return false;
15524
+ const i = s.inFlight.findIndex((e) => e.payload.waMessageId === waMessageId);
15525
+ if (i < 0) return false;
15526
+ s.inFlight.splice(i, 1);
15527
+ return true;
15544
15528
  }
15545
15529
  /** A sender's channel server disconnected (session ended/restarting).
15546
- * Subsequent messages queue again until re-attach, so nothing is lost
15547
- * across a restart. */
15530
+ * Subsequent messages queue again until re-attach + ready, so nothing is lost
15531
+ * across a restart. In-flight drained messages stay tracked for ack/sweep. */
15548
15532
  detach(senderId) {
15549
15533
  const s = this.senders.get(senderId);
15550
- if (s) s.subscriber = null;
15534
+ if (s) {
15535
+ s.subscriber = null;
15536
+ s.ready = false;
15537
+ }
15538
+ }
15539
+ /** Reconciliation from queue state alone — does not wait for a future inbound
15540
+ * to expose a loss. Returns (and removes) a terminal event for every message
15541
+ * that never reached the agent:
15542
+ * - a still-queued message older than queuedThresholdMs WHILE a subscriber
15543
+ * is attached -> inbound-undelivered (the readiness handoff broke);
15544
+ * - a still-queued message older than queuedThresholdMs with NO subscriber
15545
+ * attached -> spawn-undelivered (the channel server never attached: spawn
15546
+ * failed, or the session ended and never resumed);
15547
+ * - a drained message unacked past ackThresholdMs -> drain-undelivered (the
15548
+ * channel server never confirmed receipt — the silent cold-start drop).
15549
+ * Each message is emitted at most once (removed when emitted). */
15550
+ sweep(now, t) {
15551
+ const events = [];
15552
+ for (const [senderId, s] of this.senders) {
15553
+ s.inFlight = s.inFlight.filter((e) => {
15554
+ if (e.state === "queued" && s.subscriber && now - e.enqueuedAt > t.queuedThresholdMs) {
15555
+ events.push({ kind: "inbound-undelivered", senderId, waMessageId: e.payload.waMessageId, ageMs: now - e.enqueuedAt });
15556
+ return false;
15557
+ }
15558
+ if (e.state === "queued" && !s.subscriber && now - e.enqueuedAt > t.queuedThresholdMs) {
15559
+ events.push({ kind: "spawn-undelivered", senderId, waMessageId: e.payload.waMessageId, ageMs: now - e.enqueuedAt });
15560
+ return false;
15561
+ }
15562
+ if (e.state === "drained" && e.drainedAt != null && now - e.drainedAt > t.ackThresholdMs) {
15563
+ events.push({ kind: "drain-undelivered", senderId, waMessageId: e.payload.waMessageId, ageMs: now - e.drainedAt });
15564
+ return false;
15565
+ }
15566
+ return true;
15567
+ });
15568
+ }
15569
+ return events;
15551
15570
  }
15552
- /** Whether a sender currently has a live channel server. The gateway uses
15553
- * this to decide whether an inbound needs a cold-start spawn/resume. */
15571
+ /** Whether a sender currently has a live channel server attached. The gateway
15572
+ * uses this to decide whether an inbound needs a cold-start spawn/resume. */
15554
15573
  hasSubscriber(senderId) {
15555
15574
  return this.senders.get(senderId)?.subscriber != null;
15556
15575
  }
15576
+ /** Whether a sender's channel server is attached AND has signalled readiness.
15577
+ * Used only to log delivery=immediate vs queued accurately. */
15578
+ isReady(senderId) {
15579
+ const s = this.senders.get(senderId);
15580
+ return s?.subscriber != null && s.ready;
15581
+ }
15557
15582
  };
15558
15583
 
15559
15584
  // node_modules/hono/dist/utils/stream.js
@@ -15707,8 +15732,8 @@ var streamSSE = (c, cb, onError) => {
15707
15732
 
15708
15733
  // app/lib/whatsapp/gateway/routes.ts
15709
15734
  function createWaChannelRoutes(deps) {
15710
- const app45 = new Hono();
15711
- app45.get("/wa-channel/inbound", (c) => {
15735
+ const app44 = new Hono();
15736
+ app44.get("/wa-channel/inbound", (c) => {
15712
15737
  const senderId = c.req.query("senderId");
15713
15738
  if (!senderId) return c.json({ error: "senderId required" }, 400);
15714
15739
  return streamSSE(c, async (stream2) => {
@@ -15726,7 +15751,7 @@ function createWaChannelRoutes(deps) {
15726
15751
  }
15727
15752
  });
15728
15753
  });
15729
- app45.post("/wa-channel/reply", async (c) => {
15754
+ app44.post("/wa-channel/reply", async (c) => {
15730
15755
  const body = await c.req.json().catch(() => null);
15731
15756
  const senderId = body?.senderId;
15732
15757
  const text = body?.text;
@@ -15745,7 +15770,7 @@ function createWaChannelRoutes(deps) {
15745
15770
  console.error(`[whatsapp-native] op=reply-dispatch senderId=${senderId} bytes=${bytes}`);
15746
15771
  return c.json({ ok: true });
15747
15772
  });
15748
- app45.post("/wa-channel/ready", async (c) => {
15773
+ app44.post("/wa-channel/ready", async (c) => {
15749
15774
  const body = await c.req.json().catch(() => null);
15750
15775
  const senderId = body?.senderId;
15751
15776
  if (typeof senderId !== "string") {
@@ -15754,10 +15779,23 @@ function createWaChannelRoutes(deps) {
15754
15779
  deps.onReady?.(senderId);
15755
15780
  return c.json({ ok: true });
15756
15781
  });
15757
- return app45;
15782
+ app44.post("/wa-channel/received", async (c) => {
15783
+ const body = await c.req.json().catch(() => null);
15784
+ const senderId = body?.senderId;
15785
+ const waMessageId = body?.waMessageId;
15786
+ if (typeof senderId !== "string" || typeof waMessageId !== "string") {
15787
+ return c.json({ error: "senderId and waMessageId required" }, 400);
15788
+ }
15789
+ deps.onReceived?.(senderId, waMessageId);
15790
+ return c.json({ ok: true });
15791
+ });
15792
+ return app44;
15758
15793
  }
15759
15794
 
15760
15795
  // app/lib/whatsapp/gateway/wa-gateway.ts
15796
+ var QUEUED_THRESHOLD_MS = 3e4;
15797
+ var ACK_THRESHOLD_MS = 3e4;
15798
+ var SWEEP_INTERVAL_MS = 15e3;
15761
15799
  var WaGateway = class {
15762
15800
  constructor(deps) {
15763
15801
  this.deps = deps;
@@ -15770,17 +15808,55 @@ var WaGateway = class {
15770
15808
  routes() {
15771
15809
  return createWaChannelRoutes({
15772
15810
  hub: this.hub,
15773
- sendOutbound: (senderId, text) => this.sendOutbound(senderId, text)
15811
+ sendOutbound: (senderId, text) => this.sendOutbound(senderId, text),
15812
+ onReady: (senderId) => {
15813
+ const count = this.hub.markReady(senderId, Date.now());
15814
+ if (count > 0) console.error(`[whatsapp-native] op=drained senderId=${senderId} count=${count}`);
15815
+ },
15816
+ onReceived: (senderId, waMessageId) => {
15817
+ const found = this.hub.ack(senderId, waMessageId);
15818
+ console.error(
15819
+ `[whatsapp-native] op=channel-received senderId=${senderId} waMessageId=${waMessageId}${found ? "" : " note=unknown"}`
15820
+ );
15821
+ }
15774
15822
  });
15775
15823
  }
15824
+ /** Run one reconciliation pass: log every terminal delivery event the sweep
15825
+ * surfaces — a queued message that never drained while a subscriber was
15826
+ * attached (inbound-undelivered), a queued message whose channel server never
15827
+ * attached (spawn-undelivered), or a drained message the channel server never
15828
+ * confirmed (drain-undelivered). Returns the events (for tests). Driven by
15829
+ * startSweeper() in production; called directly with an injected clock in
15830
+ * tests. */
15831
+ reconcile(now) {
15832
+ const events = this.hub.sweep(now, {
15833
+ queuedThresholdMs: QUEUED_THRESHOLD_MS,
15834
+ ackThresholdMs: ACK_THRESHOLD_MS
15835
+ });
15836
+ for (const e of events) {
15837
+ console.error(
15838
+ `[whatsapp-native] op=${e.kind} senderId=${e.senderId} waMessageId=${e.waMessageId} ageMs=${e.ageMs}`
15839
+ );
15840
+ }
15841
+ return events;
15842
+ }
15843
+ /** Start the periodic reconciliation sweep in the long-lived UI process.
15844
+ * Returns the interval handle. */
15845
+ startSweeper(intervalMs = SWEEP_INTERVAL_MS) {
15846
+ return setInterval(() => this.reconcile(Date.now()), intervalMs);
15847
+ }
15776
15848
  /** Handle one inbound admin WhatsApp message. */
15777
15849
  async handleInbound(input) {
15778
15850
  const bytes = Buffer.byteLength(input.text, "utf8");
15779
15851
  const hadSubscriber = this.hub.hasSubscriber(input.senderId);
15852
+ const willDeliverNow = this.hub.isReady(input.senderId);
15780
15853
  this.replies.set(input.senderId, input.reply);
15781
- this.hub.deliver({ senderId: input.senderId, text: input.text, waMessageId: `wa-${++this.seq}` });
15854
+ this.hub.deliver(
15855
+ { senderId: input.senderId, text: input.text, waMessageId: `wa-${++this.seq}` },
15856
+ Date.now()
15857
+ );
15782
15858
  console.error(
15783
- `[whatsapp-native] op=inbound senderId=${input.senderId} accountId=${input.accountId} bytes=${bytes} delivery=${hadSubscriber ? "immediate" : "queued"}`
15859
+ `[whatsapp-native] op=inbound senderId=${input.senderId} accountId=${input.accountId} bytes=${bytes} delivery=${willDeliverNow ? "immediate" : "queued"}`
15784
15860
  );
15785
15861
  if (!hadSubscriber && !this.spawning.has(input.senderId)) {
15786
15862
  this.spawning.add(input.senderId);
@@ -15804,6 +15880,71 @@ var WaGateway = class {
15804
15880
  }
15805
15881
  };
15806
15882
 
15883
+ // app/lib/whatsapp/inbound/resolve-admin-userid.ts
15884
+ function classifyAdminUserId(users, admins, senderPhone) {
15885
+ const matched = users.find((u) => typeof u.phone === "string" && phonesMatch(u.phone, senderPhone));
15886
+ if (!matched) return { kind: "no-users-binding" };
15887
+ return admins.some((a) => a.userId === matched.userId) ? { kind: "resolved", userId: matched.userId } : { kind: "userid-not-admin", userId: matched.userId };
15888
+ }
15889
+ function pickAdminUserId(users, admins, senderPhone) {
15890
+ const r = classifyAdminUserId(users, admins, senderPhone);
15891
+ return r.kind === "resolved" ? r.userId : null;
15892
+ }
15893
+ function resolveAdminUserId(params) {
15894
+ try {
15895
+ const users = readUsersFile2(USERS_FILE);
15896
+ if (!users) return null;
15897
+ const account = listValidAccounts().find((a) => a.accountId === params.accountId);
15898
+ const admins = account?.config.admins ?? [];
15899
+ const direct = pickAdminUserId(users, admins, params.senderPhone);
15900
+ if (direct) return direct;
15901
+ const mapped = resolveViaLidMap(readLidMap() ?? {}, params.senderPhone);
15902
+ if (!mapped) return null;
15903
+ const viaLid = pickAdminUserId(users, admins, mapped);
15904
+ if (viaLid) {
15905
+ console.error(`[lid-map] resolved-via-cache lid=${params.senderPhone} userId=${viaLid.slice(0, 8)}`);
15906
+ }
15907
+ return viaLid;
15908
+ } catch {
15909
+ return null;
15910
+ }
15911
+ }
15912
+
15913
+ // app/lib/whatsapp/inbound/channel-admin-binding-drift.ts
15914
+ function findChannelAdminBindingDrift(adminPhones, admins, users) {
15915
+ const drift = [];
15916
+ for (const phone of adminPhones) {
15917
+ const r = classifyAdminUserId(users, admins, phone);
15918
+ if (r.kind === "no-users-binding") drift.push({ phone, reason: "no-users-binding" });
15919
+ else if (r.kind === "userid-not-admin") drift.push({ phone, reason: "userid-not-admin", userId: r.userId });
15920
+ }
15921
+ return drift;
15922
+ }
15923
+ function warnOnChannelAdminBindingDrift() {
15924
+ let users;
15925
+ try {
15926
+ users = readUsersFile2(USERS_FILE) ?? [];
15927
+ } catch {
15928
+ return;
15929
+ }
15930
+ let accounts;
15931
+ try {
15932
+ accounts = listValidAccounts();
15933
+ } catch {
15934
+ return;
15935
+ }
15936
+ for (const acct of accounts) {
15937
+ const adminPhones = readAdminPhones(acct.accountDir);
15938
+ const drift = findChannelAdminBindingDrift(adminPhones, acct.config.admins ?? [], users);
15939
+ for (const d of drift) {
15940
+ const tail = d.reason === "userid-not-admin" ? ` userId=${d.userId}` : "";
15941
+ console.error(
15942
+ `[admin-identity] binding-drift accountId=${acct.accountId} phone=${d.phone} reason=${d.reason}${tail}`
15943
+ );
15944
+ }
15945
+ }
15946
+ }
15947
+
15807
15948
  // app/lib/admin-sse-registry.ts
15808
15949
  var activeAdminSSEControllers = /* @__PURE__ */ new Set();
15809
15950
  function broadcastAdminShutdown(reason) {
@@ -15834,7 +15975,7 @@ function broadcastAdminShutdown(reason) {
15834
15975
 
15835
15976
  // ../lib/entitlement/src/index.ts
15836
15977
  import { createPublicKey, createHash as createHash5, verify as cryptoVerify } from "crypto";
15837
- import { existsSync as existsSync22, readFileSync as readFileSync21, statSync as statSync9 } from "fs";
15978
+ import { existsSync as existsSync23, readFileSync as readFileSync22, statSync as statSync9 } from "fs";
15838
15979
  import { resolve as resolve25 } from "path";
15839
15980
 
15840
15981
  // ../lib/entitlement/src/canonicalize.ts
@@ -15883,7 +16024,7 @@ function resolveEntitlement(brand, account) {
15883
16024
  return logResolved(implicitTrust(account), null);
15884
16025
  }
15885
16026
  const entitlementPath = resolve25(brand.configDir, "entitlement.json");
15886
- if (!existsSync22(entitlementPath)) {
16027
+ if (!existsSync23(entitlementPath)) {
15887
16028
  return logResolved(anonymousFallback("missing"), { reason: "missing" });
15888
16029
  }
15889
16030
  const stat7 = statSync9(entitlementPath);
@@ -15898,7 +16039,7 @@ function resolveEntitlement(brand, account) {
15898
16039
  function verifyAndResolve(brand, entitlementPath, account) {
15899
16040
  let pubkeyPem;
15900
16041
  try {
15901
- pubkeyPem = readFileSync21(pubkeyPath(brand), "utf-8");
16042
+ pubkeyPem = readFileSync22(pubkeyPath(brand), "utf-8");
15902
16043
  } catch (err) {
15903
16044
  return logResolved(anonymousFallback("pubkey-missing"), {
15904
16045
  reason: "pubkey-missing"
@@ -15912,7 +16053,7 @@ function verifyAndResolve(brand, entitlementPath, account) {
15912
16053
  }
15913
16054
  let envelope;
15914
16055
  try {
15915
- envelope = JSON.parse(readFileSync21(entitlementPath, "utf-8"));
16056
+ envelope = JSON.parse(readFileSync22(entitlementPath, "utf-8"));
15916
16057
  } catch {
15917
16058
  return logResolved(anonymousFallback("malformed"), { reason: "malformed" });
15918
16059
  }
@@ -16075,12 +16216,12 @@ function clientFrom(c) {
16075
16216
  var PLATFORM_ROOT9 = process.env.MAXY_PLATFORM_ROOT || "";
16076
16217
  var BRAND_JSON_PATH = PLATFORM_ROOT9 ? join18(PLATFORM_ROOT9, "config", "brand.json") : "";
16077
16218
  var BRAND = { productName: "Maxy", hostname: "maxy", configDir: ".maxy", marketingUrl: "https://getmaxy.com" };
16078
- if (BRAND_JSON_PATH && !existsSync23(BRAND_JSON_PATH)) {
16219
+ if (BRAND_JSON_PATH && !existsSync24(BRAND_JSON_PATH)) {
16079
16220
  console.error(`[brand] WARNING: brand.json not found at ${BRAND_JSON_PATH} \u2014 using Maxy defaults`);
16080
16221
  }
16081
- if (BRAND_JSON_PATH && existsSync23(BRAND_JSON_PATH)) {
16222
+ if (BRAND_JSON_PATH && existsSync24(BRAND_JSON_PATH)) {
16082
16223
  try {
16083
- const parsed = JSON.parse(readFileSync22(BRAND_JSON_PATH, "utf-8"));
16224
+ const parsed = JSON.parse(readFileSync23(BRAND_JSON_PATH, "utf-8"));
16084
16225
  BRAND = { ...BRAND, ...parsed };
16085
16226
  } catch (err) {
16086
16227
  console.error(`[brand] Failed to parse brand.json: ${err.message}`);
@@ -16102,8 +16243,8 @@ var brandLoginOpts = {
16102
16243
  var ALIAS_DOMAINS_PATH = join18(homedir2(), BRAND.configDir, "alias-domains.json");
16103
16244
  function loadAliasDomains() {
16104
16245
  try {
16105
- if (!existsSync23(ALIAS_DOMAINS_PATH)) return null;
16106
- const parsed = JSON.parse(readFileSync22(ALIAS_DOMAINS_PATH, "utf-8"));
16246
+ if (!existsSync24(ALIAS_DOMAINS_PATH)) return null;
16247
+ const parsed = JSON.parse(readFileSync23(ALIAS_DOMAINS_PATH, "utf-8"));
16107
16248
  if (!Array.isArray(parsed)) {
16108
16249
  console.error("[alias-domains] malformed alias-domains.json \u2014 expected array");
16109
16250
  return null;
@@ -16127,13 +16268,16 @@ watchFile(ALIAS_DOMAINS_PATH, { interval: 2e3 }, () => {
16127
16268
  function isPublicHost(host) {
16128
16269
  return host.startsWith("public.") || aliasDomains.has(host);
16129
16270
  }
16130
- var app44 = new Hono();
16271
+ var app43 = new Hono();
16131
16272
  var waGateway = new WaGateway({
16132
16273
  gatewayUrl: `http://127.0.0.1:${process.env.MAXY_UI_INTERNAL_PORT ?? ""}`,
16133
16274
  serverPath: process.env.MAXY_WA_CHANNEL_SERVER_PATH ?? resolve26(process.env.MAXY_PLATFORM_ROOT ?? join18(__dirname, ".."), "services/whatsapp-channel/dist/server.js"),
16134
16275
  ensureChannelSession: async ({ accountId, senderId, gatewayUrl, serverPath }) => {
16276
+ const userId = resolveAdminUserId({ accountId, senderPhone: senderId }) ?? void 0;
16277
+ console.error(`[whatsapp-native] op=admin-identity senderId=${senderId} userId=${userId ?? "owner-fallback"}`);
16135
16278
  const result = await managerRcSpawn({
16136
16279
  sessionId: adminSessionIdFor(accountId, senderId),
16280
+ userId,
16137
16281
  waChannel: { senderId, gatewayUrl, serverPath },
16138
16282
  logContext: `channel=whatsapp-native senderId=${senderId}`
16139
16283
  });
@@ -16142,9 +16286,10 @@ var waGateway = new WaGateway({
16142
16286
  }
16143
16287
  }
16144
16288
  });
16145
- app44.route("/", waGateway.routes());
16146
- app44.use("*", clientIpMiddleware);
16147
- app44.use("*", async (c, next) => {
16289
+ app43.route("/", waGateway.routes());
16290
+ waGateway.startSweeper();
16291
+ app43.use("*", clientIpMiddleware);
16292
+ app43.use("*", async (c, next) => {
16148
16293
  await next();
16149
16294
  c.header("X-Content-Type-Options", "nosniff");
16150
16295
  c.header("Referrer-Policy", "strict-origin-when-cross-origin");
@@ -16154,7 +16299,7 @@ app44.use("*", async (c, next) => {
16154
16299
  );
16155
16300
  });
16156
16301
  var HTTP_LOG_PATHS = /* @__PURE__ */ new Set(["/vnc-viewer.html", "/vnc-popout.html"]);
16157
- app44.use("*", async (c, next) => {
16302
+ app43.use("*", async (c, next) => {
16158
16303
  if (!HTTP_LOG_PATHS.has(c.req.path)) {
16159
16304
  await next();
16160
16305
  return;
@@ -16172,7 +16317,7 @@ app44.use("*", async (c, next) => {
16172
16317
  });
16173
16318
  }
16174
16319
  });
16175
- app44.use("*", async (c, next) => {
16320
+ app43.use("*", async (c, next) => {
16176
16321
  const host = (c.req.header("host") ?? "").split(":")[0];
16177
16322
  if (!isPublicHost(host)) {
16178
16323
  await next();
@@ -16203,7 +16348,7 @@ function resolveRemoteAuthOpts() {
16203
16348
  return brandLoginOpts;
16204
16349
  }
16205
16350
  var MAX_LOGIN_BODY = 8 * 1024;
16206
- app44.post("/__remote-auth/login", async (c) => {
16351
+ app43.post("/__remote-auth/login", async (c) => {
16207
16352
  const client = clientFrom(c);
16208
16353
  const clientIp = client.ip || "unknown";
16209
16354
  if (!requestIsTlsTerminated(c)) {
@@ -16248,7 +16393,7 @@ app44.post("/__remote-auth/login", async (c) => {
16248
16393
  }
16249
16394
  });
16250
16395
  });
16251
- app44.get("/__remote-auth/logout", (c) => {
16396
+ app43.get("/__remote-auth/logout", (c) => {
16252
16397
  const client = clientFrom(c);
16253
16398
  const clientIp = client.ip || "unknown";
16254
16399
  console.error(`[remote-auth] logout ip=${clientIp}`);
@@ -16261,7 +16406,7 @@ app44.get("/__remote-auth/logout", (c) => {
16261
16406
  }
16262
16407
  });
16263
16408
  });
16264
- app44.post("/__remote-auth/change-password", async (c) => {
16409
+ app43.post("/__remote-auth/change-password", async (c) => {
16265
16410
  const client = clientFrom(c);
16266
16411
  const clientIp = client.ip || "unknown";
16267
16412
  const rateLimited = checkRateLimit(client);
@@ -16312,13 +16457,13 @@ app44.post("/__remote-auth/change-password", async (c) => {
16312
16457
  return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(), mode: "change", changeError: "Failed to save password", redirect }), 200);
16313
16458
  }
16314
16459
  });
16315
- app44.get("/__remote-auth/setup", (c) => {
16460
+ app43.get("/__remote-auth/setup", (c) => {
16316
16461
  if (isRemoteAuthConfigured()) {
16317
16462
  return c.redirect("/");
16318
16463
  }
16319
16464
  return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(), mode: "setup" }), 200);
16320
16465
  });
16321
- app44.post("/__remote-auth/set-initial-password", async (c) => {
16466
+ app43.post("/__remote-auth/set-initial-password", async (c) => {
16322
16467
  if (isRemoteAuthConfigured()) {
16323
16468
  return c.redirect("/");
16324
16469
  }
@@ -16356,10 +16501,10 @@ app44.post("/__remote-auth/set-initial-password", async (c) => {
16356
16501
  return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(), mode: "setup", setupError: "Failed to save password. Please try again." }), 200);
16357
16502
  }
16358
16503
  });
16359
- app44.get("/api/remote-auth/status", (c) => {
16504
+ app43.get("/api/remote-auth/status", (c) => {
16360
16505
  return c.json({ configured: isRemoteAuthConfigured() });
16361
16506
  });
16362
- app44.post("/api/remote-auth/set-password", async (c) => {
16507
+ app43.post("/api/remote-auth/set-password", async (c) => {
16363
16508
  let body;
16364
16509
  try {
16365
16510
  body = await c.req.json();
@@ -16390,9 +16535,9 @@ app44.post("/api/remote-auth/set-password", async (c) => {
16390
16535
  return c.json({ error: "Failed to save password" }, 500);
16391
16536
  }
16392
16537
  });
16393
- app44.route("/api/_client-error", client_error_default);
16538
+ app43.route("/api/_client-error", client_error_default);
16394
16539
  console.log("[client-error-route] mounted");
16395
- app44.use("*", async (c, next) => {
16540
+ app43.use("*", async (c, next) => {
16396
16541
  const host = (c.req.header("host") ?? "").split(":")[0];
16397
16542
  const path2 = c.req.path;
16398
16543
  if (path2 === "/favicon.ico" || path2.startsWith("/assets/") || path2.startsWith("/brand/")) {
@@ -16425,13 +16570,13 @@ app44.use("*", async (c, next) => {
16425
16570
  console.error(`[remote-auth] login required ip=${clientIp} path=${path2} ${disambig}`);
16426
16571
  return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(), redirect: path2 }), 200);
16427
16572
  });
16428
- app44.route("/api/health", health_default);
16429
- app44.route("/api/chat", chat_default);
16430
- app44.route("/api/whatsapp", whatsapp_default);
16431
- app44.route("/api/onboarding", onboarding_default);
16432
- app44.route("/api/admin", admin_default);
16433
- app44.route("/api/access", access_default);
16434
- app44.route("/api/session", session_default2);
16573
+ app43.route("/api/health", health_default);
16574
+ app43.route("/api/chat", chat_default);
16575
+ app43.route("/api/whatsapp", whatsapp_default);
16576
+ app43.route("/api/onboarding", onboarding_default);
16577
+ app43.route("/api/admin", admin_default);
16578
+ app43.route("/api/access", access_default);
16579
+ app43.route("/api/session", session_default2);
16435
16580
  var SAFE_SLUG_RE2 = /^[a-z][a-z0-9-]{2,49}$/;
16436
16581
  var SAFE_FILENAME_RE = /^[a-z0-9_][a-z0-9_.-]{0,99}$/i;
16437
16582
  var IMAGE_MIME = {
@@ -16443,7 +16588,7 @@ var IMAGE_MIME = {
16443
16588
  ".svg": "image/svg+xml",
16444
16589
  ".ico": "image/x-icon"
16445
16590
  };
16446
- app44.get("/agent-assets/:slug/:filename", (c) => {
16591
+ app43.get("/agent-assets/:slug/:filename", (c) => {
16447
16592
  const slug = c.req.param("slug");
16448
16593
  const filename = c.req.param("filename");
16449
16594
  if (!SAFE_SLUG_RE2.test(slug)) {
@@ -16465,20 +16610,20 @@ app44.get("/agent-assets/:slug/:filename", (c) => {
16465
16610
  console.error(`[agent-assets] path-traversal-rejected slug=${slug} file=${filename}`);
16466
16611
  return c.text("Forbidden", 403);
16467
16612
  }
16468
- if (!existsSync23(filePath)) {
16613
+ if (!existsSync24(filePath)) {
16469
16614
  console.error(`[agent-assets] serve slug=${slug} file=${filename} status=404`);
16470
16615
  return c.text("Not found", 404);
16471
16616
  }
16472
16617
  const ext = "." + filename.split(".").pop()?.toLowerCase();
16473
16618
  const contentType = IMAGE_MIME[ext] || "application/octet-stream";
16474
16619
  console.log(`[agent-assets] serve slug=${slug} file=${filename} status=200`);
16475
- const body = readFileSync22(filePath);
16620
+ const body = readFileSync23(filePath);
16476
16621
  return c.body(body, 200, {
16477
16622
  "Content-Type": contentType,
16478
16623
  "Cache-Control": "public, max-age=3600"
16479
16624
  });
16480
16625
  });
16481
- app44.get("/generated/:filename", (c) => {
16626
+ app43.get("/generated/:filename", (c) => {
16482
16627
  const filename = c.req.param("filename");
16483
16628
  if (!SAFE_FILENAME_RE.test(filename) || filename.includes("..")) {
16484
16629
  console.error(`[generated] serve file=${filename} status=403`);
@@ -16495,29 +16640,29 @@ app44.get("/generated/:filename", (c) => {
16495
16640
  console.error(`[generated] serve file=${filename} status=403`);
16496
16641
  return c.text("Forbidden", 403);
16497
16642
  }
16498
- if (!existsSync23(filePath)) {
16643
+ if (!existsSync24(filePath)) {
16499
16644
  console.error(`[generated] serve file=${filename} status=404`);
16500
16645
  return c.text("Not found", 404);
16501
16646
  }
16502
16647
  const ext = "." + filename.split(".").pop()?.toLowerCase();
16503
16648
  const contentType = IMAGE_MIME[ext] || "application/octet-stream";
16504
16649
  console.log(`[generated] serve file=${filename} status=200`);
16505
- const body = readFileSync22(filePath);
16650
+ const body = readFileSync23(filePath);
16506
16651
  return c.body(body, 200, {
16507
16652
  "Content-Type": contentType,
16508
16653
  "Cache-Control": "public, max-age=86400"
16509
16654
  });
16510
16655
  });
16511
- app44.route("/sites", sites_default);
16512
- app44.route("/listings", listings_default);
16513
- app44.route("/v", visitor_event_default);
16514
- app44.route("/v", visitor_consent_default);
16656
+ app43.route("/sites", sites_default);
16657
+ app43.route("/listings", listings_default);
16658
+ app43.route("/v", visitor_event_default);
16659
+ app43.route("/v", visitor_consent_default);
16515
16660
  var htmlCache = /* @__PURE__ */ new Map();
16516
16661
  var brandLogoPath = "/brand/maxy-monochrome.png";
16517
16662
  var brandIconPath = "/brand/maxy-monochrome.png";
16518
- if (BRAND_JSON_PATH && existsSync23(BRAND_JSON_PATH)) {
16663
+ if (BRAND_JSON_PATH && existsSync24(BRAND_JSON_PATH)) {
16519
16664
  try {
16520
- const fullBrand = JSON.parse(readFileSync22(BRAND_JSON_PATH, "utf-8"));
16665
+ const fullBrand = JSON.parse(readFileSync23(BRAND_JSON_PATH, "utf-8"));
16521
16666
  if (fullBrand.assets?.logo) brandLogoPath = `/brand/${fullBrand.assets.logo}`;
16522
16667
  brandIconPath = fullBrand.assets?.icon ? `/brand/${fullBrand.assets.icon}` : brandLogoPath;
16523
16668
  } catch {
@@ -16535,8 +16680,8 @@ function readInstalledVersion() {
16535
16680
  try {
16536
16681
  if (!PLATFORM_ROOT9) return "unknown";
16537
16682
  const versionFile = join18(PLATFORM_ROOT9, "config", `.${BRAND.hostname}-version`);
16538
- if (!existsSync23(versionFile)) return "unknown";
16539
- const content = readFileSync22(versionFile, "utf-8").trim();
16683
+ if (!existsSync24(versionFile)) return "unknown";
16684
+ const content = readFileSync23(versionFile, "utf-8").trim();
16540
16685
  return content || "unknown";
16541
16686
  } catch {
16542
16687
  return "unknown";
@@ -16577,7 +16722,7 @@ var clientErrorReporterScript = `<script>
16577
16722
  function cachedHtml(file) {
16578
16723
  let html = htmlCache.get(file);
16579
16724
  if (!html) {
16580
- html = readFileSync22(resolve26(process.cwd(), "public", file), "utf-8");
16725
+ html = readFileSync23(resolve26(process.cwd(), "public", file), "utf-8");
16581
16726
  const productNameEsc = escapeHtml(BRAND.productName);
16582
16727
  html = html.replace(/<title>([^<]*)<\/title>/, (_match, inner) => `<title>${escapeHtml(inner).replace(/Maxy/g, productNameEsc)}</title>`);
16583
16728
  html = html.replace('href="/favicon.ico"', `href="${escapeHtml(brandFaviconPath)}"`);
@@ -16598,8 +16743,8 @@ function loadBrandingCache(agentSlug) {
16598
16743
  const accountId = getDefaultAccountId();
16599
16744
  if (!accountId) return null;
16600
16745
  const cachePath = join18(configDir2, "branding-cache", accountId, `${agentSlug}.json`);
16601
- if (!existsSync23(cachePath)) return null;
16602
- return JSON.parse(readFileSync22(cachePath, "utf-8"));
16746
+ if (!existsSync24(cachePath)) return null;
16747
+ return JSON.parse(readFileSync23(cachePath, "utf-8"));
16603
16748
  } catch {
16604
16749
  return null;
16605
16750
  }
@@ -16642,7 +16787,7 @@ function escapeHtml(s) {
16642
16787
  function agentUnavailableHtml() {
16643
16788
  return `<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>${escapeHtml(BRAND.productName)}</title></head><body style="font-family:system-ui,sans-serif;max-width:32rem;margin:4rem auto;padding:0 1.5rem;color:#222"><h1 style="font-size:1.25rem">Agent unavailable</h1><p>This agent isn't available right now. If you reached this page from a saved link, the agent may have been turned off.</p></body></html>`;
16644
16789
  }
16645
- app44.get("/", (c) => {
16790
+ app43.get("/", (c) => {
16646
16791
  const host = (c.req.header("host") ?? "").split(":")[0];
16647
16792
  if (isPublicHost(host)) {
16648
16793
  const defaultSlug = resolveDefaultSlug();
@@ -16658,12 +16803,12 @@ app44.get("/", (c) => {
16658
16803
  }
16659
16804
  return c.html(cachedHtml("index.html"));
16660
16805
  });
16661
- app44.get("/public", (c) => {
16806
+ app43.get("/public", (c) => {
16662
16807
  const host = (c.req.header("host") ?? "").split(":")[0];
16663
16808
  if (isPublicHost(host)) return c.text("Not found", 404);
16664
16809
  return c.html(cachedHtml("public.html"));
16665
16810
  });
16666
- app44.get("/chat", (c) => {
16811
+ app43.get("/chat", (c) => {
16667
16812
  const host = (c.req.header("host") ?? "").split(":")[0];
16668
16813
  if (isPublicHost(host)) return c.text("Not found", 404);
16669
16814
  return c.html(cachedHtml("public.html"));
@@ -16682,12 +16827,12 @@ async function logViewerFetch(c, next) {
16682
16827
  duration_ms: Date.now() - start
16683
16828
  });
16684
16829
  }
16685
- app44.use("/vnc-viewer.html", logViewerFetch);
16686
- app44.use("/vnc-popout.html", logViewerFetch);
16687
- app44.get("/vnc-popout.html", (c) => {
16830
+ app43.use("/vnc-viewer.html", logViewerFetch);
16831
+ app43.use("/vnc-popout.html", logViewerFetch);
16832
+ app43.get("/vnc-popout.html", (c) => {
16688
16833
  let html = htmlCache.get("vnc-popout.html");
16689
16834
  if (!html) {
16690
- html = readFileSync22(resolve26(process.cwd(), "public", "vnc-popout.html"), "utf-8");
16835
+ html = readFileSync23(resolve26(process.cwd(), "public", "vnc-popout.html"), "utf-8");
16691
16836
  const name = escapeHtml(BRAND.productName);
16692
16837
  html = html.replace("<title>Browser \u2014 Maxy</title>", `<title>${name}</title>`);
16693
16838
  html = html.replace("</head>", ` ${brandScript}
@@ -16697,7 +16842,7 @@ app44.get("/vnc-popout.html", (c) => {
16697
16842
  }
16698
16843
  return c.html(html);
16699
16844
  });
16700
- app44.post("/api/vnc/client-event", async (c) => {
16845
+ app43.post("/api/vnc/client-event", async (c) => {
16701
16846
  let body;
16702
16847
  try {
16703
16848
  body = await c.req.json();
@@ -16718,30 +16863,30 @@ app44.post("/api/vnc/client-event", async (c) => {
16718
16863
  });
16719
16864
  return c.json({ ok: true });
16720
16865
  });
16721
- app44.get("/g/:slug", (c) => {
16866
+ app43.get("/g/:slug", (c) => {
16722
16867
  return c.html(brandedPublicHtml());
16723
16868
  });
16724
- app44.get("/graph", (c) => {
16869
+ app43.get("/graph", (c) => {
16725
16870
  const host = (c.req.header("host") ?? "").split(":")[0];
16726
16871
  if (isPublicHost(host)) return c.text("Not found", 404);
16727
16872
  return c.html(cachedHtml("graph.html"));
16728
16873
  });
16729
- app44.get("/sessions", (c) => {
16874
+ app43.get("/sessions", (c) => {
16730
16875
  const host = (c.req.header("host") ?? "").split(":")[0];
16731
16876
  if (isPublicHost(host)) return c.text("Not found", 404);
16732
16877
  return c.html(cachedHtml("sessions.html"));
16733
16878
  });
16734
- app44.get("/data", (c) => {
16879
+ app43.get("/data", (c) => {
16735
16880
  const host = (c.req.header("host") ?? "").split(":")[0];
16736
16881
  if (isPublicHost(host)) return c.text("Not found", 404);
16737
16882
  return c.html(cachedHtml("data.html"));
16738
16883
  });
16739
- app44.get("/browser", (c) => {
16884
+ app43.get("/browser", (c) => {
16740
16885
  const host = (c.req.header("host") ?? "").split(":")[0];
16741
16886
  if (isPublicHost(host)) return c.text("Not found", 404);
16742
16887
  return c.html(cachedHtml("browser.html"));
16743
16888
  });
16744
- app44.get("/:slug", async (c, next) => {
16889
+ app43.get("/:slug", async (c, next) => {
16745
16890
  const slug = c.req.param("slug");
16746
16891
  if (AGENT_SLUG_PATTERN.test(`/${slug}`)) {
16747
16892
  const account = resolveAccount();
@@ -16756,13 +16901,13 @@ app44.get("/:slug", async (c, next) => {
16756
16901
  await next();
16757
16902
  });
16758
16903
  if (brandFaviconPath !== "/favicon.ico") {
16759
- app44.get("/favicon.ico", (c) => {
16904
+ app43.get("/favicon.ico", (c) => {
16760
16905
  c.header("Cache-Control", "public, max-age=300");
16761
16906
  return c.redirect(brandFaviconPath, 302);
16762
16907
  });
16763
16908
  }
16764
- app44.use("/*", serveStatic({ root: "./public" }));
16765
- app44.all("*", (c) => {
16909
+ app43.use("/*", serveStatic({ root: "./public" }));
16910
+ app43.all("*", (c) => {
16766
16911
  const host = (c.req.header("host") ?? "").split(":")[0];
16767
16912
  const path2 = c.req.path;
16768
16913
  if (isPublicHost(host)) {
@@ -16776,7 +16921,7 @@ app44.all("*", (c) => {
16776
16921
  });
16777
16922
  var port = requirePortEnv("MAXY_UI_INTERNAL_PORT", { tag: "ui-server" });
16778
16923
  var hostname = process.env.HOSTNAME ?? "127.0.0.1";
16779
- var httpServer = serve({ fetch: app44.fetch, port, hostname });
16924
+ var httpServer = serve({ fetch: app43.fetch, port, hostname });
16780
16925
  console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
16781
16926
  startAuthHealthHeartbeat();
16782
16927
  {
@@ -16813,7 +16958,7 @@ for (const m of SUBAPP_MANIFEST) {
16813
16958
  }
16814
16959
  try {
16815
16960
  const registered = [];
16816
- for (const r of app44.routes ?? []) {
16961
+ for (const r of app43.routes ?? []) {
16817
16962
  if (typeof r.path !== "string" || r.path.includes(":") || r.path.includes("*")) continue;
16818
16963
  if (AGENT_SLUG_PATTERN.test(r.path)) {
16819
16964
  registered.push({ method: (r.method ?? "ALL").toUpperCase(), path: r.path });
@@ -16841,11 +16986,11 @@ try {
16841
16986
  var ADMINUSER_RECONCILE_INTERVAL_MS = 60 * 60 * 1e3;
16842
16987
  async function runAdminUserReconcileTick() {
16843
16988
  try {
16844
- if (!existsSync23(USERS_FILE)) {
16989
+ if (!existsSync24(USERS_FILE)) {
16845
16990
  console.error("[adminuser-self-heal] skip reason=no-users-file");
16846
16991
  return;
16847
16992
  }
16848
- const usersRaw = readFileSync22(USERS_FILE, "utf-8").trim();
16993
+ const usersRaw = readFileSync23(USERS_FILE, "utf-8").trim();
16849
16994
  if (!usersRaw) {
16850
16995
  console.error("[adminuser-self-heal] skip reason=empty-users-file");
16851
16996
  return;
@@ -16910,6 +17055,7 @@ reconcileEnabledPlugins(bootAccount?.accountDir, bootAccount?.config, bootAccoun
16910
17055
  for (const acct of listValidAccounts()) {
16911
17056
  cleanupLeakedPremiumSubs(acct.accountDir, acct.config, acct.accountId);
16912
17057
  }
17058
+ warnOnChannelAdminBindingDrift();
16913
17059
  var bootEnabled = Array.isArray(bootAccountConfig?.enabledPlugins) ? bootAccountConfig.enabledPlugins : [];
16914
17060
  var bootDelivered = [];
16915
17061
  var bootDistMissing = [];