@rubytech/create-realagent 1.0.678 → 1.0.681

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 (60) hide show
  1. package/dist/index.js +232 -39
  2. package/package.json +1 -1
  3. package/payload/platform/lib/graph-mcp/dist/__tests__/cypher-validate.test.d.ts +2 -0
  4. package/payload/platform/lib/graph-mcp/dist/__tests__/cypher-validate.test.d.ts.map +1 -0
  5. package/payload/platform/lib/graph-mcp/dist/__tests__/cypher-validate.test.js +112 -0
  6. package/payload/platform/lib/graph-mcp/dist/__tests__/cypher-validate.test.js.map +1 -0
  7. package/payload/platform/lib/graph-mcp/dist/__tests__/schema-cache.test.d.ts +2 -0
  8. package/payload/platform/lib/graph-mcp/dist/__tests__/schema-cache.test.d.ts.map +1 -0
  9. package/payload/platform/lib/graph-mcp/dist/__tests__/schema-cache.test.js +163 -0
  10. package/payload/platform/lib/graph-mcp/dist/__tests__/schema-cache.test.js.map +1 -0
  11. package/payload/platform/lib/graph-mcp/dist/cypher-validate.d.ts +38 -0
  12. package/payload/platform/lib/graph-mcp/dist/cypher-validate.d.ts.map +1 -0
  13. package/payload/platform/lib/graph-mcp/dist/cypher-validate.js +130 -0
  14. package/payload/platform/lib/graph-mcp/dist/cypher-validate.js.map +1 -0
  15. package/payload/platform/lib/graph-mcp/dist/index.js +201 -45
  16. package/payload/platform/lib/graph-mcp/dist/index.js.map +1 -1
  17. package/payload/platform/lib/graph-mcp/dist/schema-cache.d.ts +78 -0
  18. package/payload/platform/lib/graph-mcp/dist/schema-cache.d.ts.map +1 -0
  19. package/payload/platform/lib/graph-mcp/dist/schema-cache.js +194 -0
  20. package/payload/platform/lib/graph-mcp/dist/schema-cache.js.map +1 -0
  21. package/payload/platform/lib/graph-mcp/src/__tests__/cypher-validate.test.ts +141 -0
  22. package/payload/platform/lib/graph-mcp/src/__tests__/schema-cache.test.ts +169 -0
  23. package/payload/platform/lib/graph-mcp/src/cypher-validate.ts +157 -0
  24. package/payload/platform/lib/graph-mcp/src/index.ts +247 -47
  25. package/payload/platform/lib/graph-mcp/src/schema-cache.ts +212 -0
  26. package/payload/platform/lib/graph-trash/dist/index.d.ts +8 -0
  27. package/payload/platform/lib/graph-trash/dist/index.d.ts.map +1 -1
  28. package/payload/platform/lib/graph-trash/dist/index.js +109 -14
  29. package/payload/platform/lib/graph-trash/dist/index.js.map +1 -1
  30. package/payload/platform/lib/graph-trash/src/index.ts +136 -21
  31. package/payload/platform/plugins/docs/references/deployment.md +4 -2
  32. package/payload/platform/plugins/docs/references/memory-guide.md +5 -1
  33. package/payload/platform/plugins/docs/references/platform.md +1 -1
  34. package/payload/platform/plugins/docs/references/troubleshooting.md +20 -0
  35. package/payload/platform/plugins/memory/PLUGIN.md +1 -0
  36. package/payload/platform/plugins/memory/mcp/dist/index.js +54 -6
  37. package/payload/platform/plugins/memory/mcp/dist/index.js.map +1 -1
  38. package/payload/platform/plugins/memory/mcp/dist/lib/filter-token.d.ts +36 -0
  39. package/payload/platform/plugins/memory/mcp/dist/lib/filter-token.d.ts.map +1 -0
  40. package/payload/platform/plugins/memory/mcp/dist/lib/filter-token.js +86 -0
  41. package/payload/platform/plugins/memory/mcp/dist/lib/filter-token.js.map +1 -0
  42. package/payload/platform/plugins/memory/mcp/dist/tools/memory-delete.d.ts +23 -0
  43. package/payload/platform/plugins/memory/mcp/dist/tools/memory-delete.d.ts.map +1 -1
  44. package/payload/platform/plugins/memory/mcp/dist/tools/memory-delete.js +47 -1
  45. package/payload/platform/plugins/memory/mcp/dist/tools/memory-delete.js.map +1 -1
  46. package/payload/platform/plugins/memory/mcp/dist/tools/memory-find-candidates.d.ts +58 -0
  47. package/payload/platform/plugins/memory/mcp/dist/tools/memory-find-candidates.d.ts.map +1 -0
  48. package/payload/platform/plugins/memory/mcp/dist/tools/memory-find-candidates.js +125 -0
  49. package/payload/platform/plugins/memory/mcp/dist/tools/memory-find-candidates.js.map +1 -0
  50. package/payload/platform/scripts/vnc.sh +12 -409
  51. package/payload/platform/templates/agents/admin/IDENTITY.md +16 -0
  52. package/payload/platform/templates/dotfiles/.tmux.conf +1 -0
  53. package/payload/platform/templates/systemd/maxy-ttyd.service +25 -0
  54. package/payload/server/chunk-3RBKKDHC.js +783 -0
  55. package/payload/server/maxy-edge.js +377 -8
  56. package/payload/server/public/assets/admin-CIkyOur7.js +362 -0
  57. package/payload/server/public/assets/admin-kHJ-D0s7.css +1 -0
  58. package/payload/server/public/index.html +2 -1
  59. package/payload/server/server.js +391 -412
  60. package/payload/server/public/assets/admin-BBL1no_g.js +0 -352
@@ -13,7 +13,6 @@ import {
13
13
  clearRateLimit,
14
14
  createRemoteSession,
15
15
  hashPassword,
16
- invalidateRemoteSession,
17
16
  isPasswordValid,
18
17
  isRemoteAuthConfigured,
19
18
  recordFailedAttempt,
@@ -25,7 +24,7 @@ import {
25
24
  verifyPassword,
26
25
  verifyRemotePassword,
27
26
  vncLog
28
- } from "./chunk-5YIXIF6C.js";
27
+ } from "./chunk-3RBKKDHC.js";
29
28
 
30
29
  // ../lib/models/dist/index.js
31
30
  var require_dist = __commonJS({
@@ -1204,14 +1203,14 @@ var Hono = class _Hono {
1204
1203
  * app.route("/api", app2) // GET /api/user
1205
1204
  * ```
1206
1205
  */
1207
- route(path2, app36) {
1206
+ route(path2, app35) {
1208
1207
  const subApp = this.basePath(path2);
1209
- app36.routes.map((r) => {
1208
+ app35.routes.map((r) => {
1210
1209
  let handler;
1211
- if (app36.errorHandler === errorHandler) {
1210
+ if (app35.errorHandler === errorHandler) {
1212
1211
  handler = r.handler;
1213
1212
  } else {
1214
- handler = async (c, next) => (await compose([], app36.errorHandler)(c, () => r.handler(c, next))).res;
1213
+ handler = async (c, next) => (await compose([], app35.errorHandler)(c, () => r.handler(c, next))).res;
1215
1214
  handler[COMPOSED_HANDLER] = r.handler;
1216
1215
  }
1217
1216
  subApp.#addRoute(r.method, r.path, handler);
@@ -2895,7 +2894,7 @@ var serveStatic = (options = { root: "" }) => {
2895
2894
  };
2896
2895
 
2897
2896
  // server/index.ts
2898
- import { readFileSync as readFileSync24, existsSync as existsSync23, watchFile } from "fs";
2897
+ import { readFileSync as readFileSync25, existsSync as existsSync23, watchFile } from "fs";
2899
2898
  import { resolve as resolve27, join as join12, basename as basename7 } from "path";
2900
2899
  import { homedir as homedir4 } from "os";
2901
2900
 
@@ -2908,7 +2907,7 @@ import { spawn as spawn2, spawnSync as spawnSync2 } from "child_process";
2908
2907
  import { randomUUID as randomUUID2 } from "crypto";
2909
2908
  import { resolve as resolve5, join as join3 } from "path";
2910
2909
  import { platform as osPlatform } from "os";
2911
- import { readFileSync as readFileSync6, writeFileSync as writeFileSync5, readdirSync as readdirSync2, existsSync as existsSync5, mkdirSync as mkdirSync4, createWriteStream, statSync as statSync3, unlinkSync as unlinkSync3, cpSync, rmSync as rmSync2, appendFileSync, openSync as openSync2, readSync as readSync2, closeSync as closeSync2 } from "fs";
2910
+ import { readFileSync as readFileSync7, writeFileSync as writeFileSync5, readdirSync as readdirSync2, existsSync as existsSync5, mkdirSync as mkdirSync4, createWriteStream, statSync as statSync3, unlinkSync as unlinkSync3, cpSync, rmSync as rmSync2, appendFileSync, openSync as openSync2, readSync as readSync2, closeSync as closeSync2 } from "fs";
2912
2911
  import { lookup as dnsLookup } from "dns/promises";
2913
2912
  import { createConnection as netConnect } from "net";
2914
2913
  import { StringDecoder } from "string_decoder";
@@ -3265,7 +3264,6 @@ function resolveBrowserTransport(req, remoteAddress) {
3265
3264
  return transport;
3266
3265
  }
3267
3266
  var currentCdpDisplay = null;
3268
- var currentTerminalDisplay = null;
3269
3267
  function discoverNativeDisplay() {
3270
3268
  const fallback = { sessionType: "x11", display: ":0", waylandDisplay: "" };
3271
3269
  try {
@@ -3436,137 +3434,6 @@ async function ensureCdp(transport = "vnc") {
3436
3434
  function killChromium() {
3437
3435
  spawnSync("pkill", ["-f", "chromium"], { stdio: "pipe" });
3438
3436
  }
3439
- function terminalAlive() {
3440
- const result = spawnSync("bash", [VNC_SCRIPT, "status-terminal"], {
3441
- stdio: "pipe",
3442
- timeout: 2e3
3443
- });
3444
- return result.status === 0;
3445
- }
3446
- function extractFailureLine(stderr, signal) {
3447
- if (signal === "SIGTERM") {
3448
- return '[terminal-launch] failed err="vnc.sh timed out after 10s \u2014 X server on :99 may be wedged (check Xtigervnc + websockify in vnc-boot.log)"';
3449
- }
3450
- const raw2 = (stderr ?? "").trim();
3451
- if (!raw2) return "Terminal failed to start (no diagnostic captured)";
3452
- const lines = raw2.split("\n").filter((l) => l.trim().length > 0);
3453
- const lastFailed = [...lines].reverse().find((l) => l.includes("[terminal-launch] failed"));
3454
- return (lastFailed ?? lines[lines.length - 1]).trim();
3455
- }
3456
- async function ensureTerminal(transport = "vnc") {
3457
- const targetSentinel = transport === "native" ? "native" : ":99";
3458
- if (terminalAlive()) {
3459
- if (currentTerminalDisplay !== null && currentTerminalDisplay !== targetSentinel) {
3460
- console.error(`[ensureTerminal] Display switch: ${currentTerminalDisplay} \u2192 ${targetSentinel}`);
3461
- vncLog("ensure-terminal", {
3462
- action: "display-switch",
3463
- transport,
3464
- from_display: currentTerminalDisplay,
3465
- to_display: targetSentinel
3466
- });
3467
- killTerminal();
3468
- await sleep(500);
3469
- } else {
3470
- if (currentTerminalDisplay === null) {
3471
- currentTerminalDisplay = targetSentinel;
3472
- }
3473
- const check = spawnSync("bash", [VNC_SCRIPT, transport === "native" ? "start-terminal-native" : "start-terminal"], {
3474
- stdio: ["ignore", "pipe", "pipe"],
3475
- timeout: 1e4
3476
- });
3477
- if (check.status !== 0) {
3478
- const errorLine = extractFailureLine(check.stderr?.toString(), check.signal);
3479
- vncLog("ensure-terminal", { action: "already-running-self-heal-failed", transport, windowPresent: false, err: errorLine });
3480
- return { ok: false, error: errorLine };
3481
- }
3482
- vncLog("ensure-terminal", { action: "already-running", transport, windowPresent: true });
3483
- return { ok: true };
3484
- }
3485
- }
3486
- const vncCommand = transport === "native" ? "start-terminal-native" : "start-terminal";
3487
- if (transport === "vnc") {
3488
- const xAlive = await waitForPort(5900, 1e3);
3489
- if (!xAlive) {
3490
- console.error("[ensureTerminal] X server down on :5900 \u2014 escalating to full VNC restart");
3491
- vncLog("ensure-terminal", { action: "escalate-vnc-restart", reason: "x-down", transport });
3492
- const vncOk = await ensureVnc();
3493
- if (!vncOk) {
3494
- console.error("[ensureTerminal] Full VNC restart failed \u2014 terminal degraded");
3495
- vncLog("ensure-terminal", { action: "degraded", reason: "vnc-restart-failed", transport });
3496
- return { ok: false, error: "VNC failed to start after recovery attempt" };
3497
- }
3498
- }
3499
- }
3500
- if (transport === "native") {
3501
- const nativeInfo = discoverNativeDisplay();
3502
- vncLog("ensure-terminal", {
3503
- action: "native-display-resolved",
3504
- transport,
3505
- session_type: nativeInfo.sessionType,
3506
- display: nativeInfo.display
3507
- });
3508
- }
3509
- console.error(`[ensureTerminal] Launching terminal (${vncCommand}) for transport=${transport}`);
3510
- vncLog("ensure-terminal", { action: `launch-${vncCommand}`, transport });
3511
- const result = spawnSync("bash", [VNC_SCRIPT, vncCommand], {
3512
- stdio: ["ignore", "pipe", "pipe"],
3513
- timeout: 1e4
3514
- });
3515
- if (result.status !== 0) {
3516
- const errorLine = extractFailureLine(result.stderr?.toString(), result.signal);
3517
- vncLog("ensure-terminal", { action: "launch-failed", transport, windowPresent: false, err: errorLine });
3518
- return { ok: false, error: errorLine };
3519
- }
3520
- currentTerminalDisplay = targetSentinel;
3521
- vncLog("ensure-terminal", { action: "launch-complete", transport, sentinel: targetSentinel, windowPresent: true });
3522
- return { ok: true };
3523
- }
3524
- function killTerminal() {
3525
- spawnSync("bash", [VNC_SCRIPT, "kill-terminal"], { stdio: "pipe", timeout: 5e3 });
3526
- currentTerminalDisplay = null;
3527
- }
3528
- async function ensureTerminalUpgrade(transport = "vnc") {
3529
- const targetSentinel = ":99";
3530
- vncLog("ensure-terminal", {
3531
- action: "launch-upgrade",
3532
- transport,
3533
- cmd: "npx -y @rubytech/create-maxy@latest"
3534
- });
3535
- killTerminal();
3536
- await sleep(500);
3537
- if (transport === "vnc") {
3538
- const xAlive = await waitForPort(5900, 1e3);
3539
- if (!xAlive) {
3540
- console.error("[ensureTerminalUpgrade] X server down on :5900 \u2014 escalating to full VNC restart");
3541
- vncLog("ensure-terminal", { action: "escalate-vnc-restart", reason: "x-down", transport, upgrade: true });
3542
- const vncOk = await ensureVnc();
3543
- if (!vncOk) {
3544
- console.error("[ensureTerminalUpgrade] Full VNC restart failed \u2014 upgrade degraded");
3545
- vncLog("ensure-terminal", { action: "launch-upgrade-failed", transport, windowPresent: false, err: "VNC failed to start after recovery attempt" });
3546
- return { ok: false, error: "VNC failed to start after recovery attempt" };
3547
- }
3548
- }
3549
- }
3550
- console.error(`[ensureTerminalUpgrade] Launching upgrade terminal transport=${transport}`);
3551
- const result = spawnSync("bash", [VNC_SCRIPT, "start-terminal-upgrade"], {
3552
- stdio: ["ignore", "pipe", "pipe"],
3553
- timeout: 1e4
3554
- });
3555
- if (result.status !== 0) {
3556
- const errorLine = extractFailureLine(result.stderr?.toString(), result.signal);
3557
- vncLog("ensure-terminal", { action: "launch-upgrade-failed", transport, windowPresent: false, err: errorLine });
3558
- return { ok: false, error: errorLine };
3559
- }
3560
- currentTerminalDisplay = targetSentinel;
3561
- vncLog("ensure-terminal", {
3562
- action: "launch-upgrade-complete",
3563
- transport,
3564
- sentinel: targetSentinel,
3565
- windowPresent: true,
3566
- cmd: "npx -y @rubytech/create-maxy@latest"
3567
- });
3568
- return { ok: true };
3569
- }
3570
3437
  function writeChromiumWrapper() {
3571
3438
  mkdirSync2(BIN_DIR, { recursive: true });
3572
3439
  const wrapperPath = resolve2(BIN_DIR, "chromium");
@@ -5803,6 +5670,101 @@ async function criticAndRecord(opts) {
5803
5670
  }
5804
5671
  }
5805
5672
 
5673
+ // app/lib/admin-schema-block.ts
5674
+ import { readFileSync as readFileSync6 } from "fs";
5675
+ var EDGE_TAXONOMY = [
5676
+ { type: "PART_OF", direction: "(Message)-[:PART_OF]->(Conversation)", note: "Messages belong to exactly one Conversation. Do NOT use :HAS_MESSAGE \u2014 that edge does not exist." },
5677
+ { type: "NEXT", direction: "(Message)-[:NEXT]->(Message)", note: "Chain order within a Conversation. Each Message has at most one NEXT successor." },
5678
+ { type: "BELONGS_TO", direction: "(Conversation|Task|...)-[:BELONGS_TO]->(LocalBusiness)", note: "Account scope. Universal \u2014 almost every tenant-owned node has one." },
5679
+ { type: "ADMIN_OF", direction: "(AdminUser)-[:ADMIN_OF]->(LocalBusiness)", note: "Device-level admin membership (see admin-add / admin-remove)." },
5680
+ { type: "AUTHORED_BY", direction: "(Message)-[:AUTHORED_BY]->(Person)", note: "Message authorship (when the author is an identified Person)." },
5681
+ { type: "HAS_TOOL_CALL", direction: "(Message)-[:HAS_TOOL_CALL]->(ToolCall)", note: "Tool calls attached to an assistant message." },
5682
+ { type: "HAS_RESULT", direction: "(WorkflowStep|ToolCall)-[:HAS_RESULT]->(StepResult)", note: "Workflow step / tool-call outputs." },
5683
+ { type: "HAS_STEP", direction: "(Workflow)-[:HAS_STEP]->(WorkflowStep)", note: "Workflow composition." },
5684
+ { type: "RUN_OF", direction: "(WorkflowRun)-[:RUN_OF]->(Workflow)", note: "Workflow execution instance." },
5685
+ { type: "HAS_SECTION", direction: "(KnowledgeDocument)-[:HAS_SECTION]->(Section)", note: "Document hierarchy." },
5686
+ { type: "HAS_CHUNK", direction: "(Section)-[:HAS_CHUNK]->(Chunk)", note: "Section-to-embeddable-chunk." },
5687
+ { type: "HAS_PREFERENCE", direction: "(Person)-[:HAS_PREFERENCE]->(Preference)", note: "Person-scoped preferences (profile memory)." },
5688
+ { type: "HAS_ACCESS", direction: "(Person)-[:HAS_ACCESS]->(AccessGrant)", note: "Public agent access grants." },
5689
+ { type: "HAS_TASK", direction: "(Person|LocalBusiness)-[:HAS_TASK]->(Task)", note: "Task ownership / assignment." },
5690
+ { type: "HAS_PRICING", direction: "(Service)-[:HAS_PRICING]->(PriceSpecification)", note: "Service pricing." },
5691
+ { type: "HAS_HOURS", direction: "(LocalBusiness)-[:HAS_HOURS]->(OpeningHoursSpecification)", note: "Opening hours." },
5692
+ { type: "HAS_FAQ", direction: "(LocalBusiness)-[:HAS_FAQ]->(Question)", note: "FAQ attached to business." },
5693
+ { type: "HAS_BRAND_ASSET", direction: "(LocalBusiness)-[:HAS_BRAND_ASSET]->(DigitalDocument|ImageObject)", note: "Branding assets." },
5694
+ { type: "OFFERS", direction: "(LocalBusiness)-[:OFFERS]->(Service)", note: "Service catalogue." },
5695
+ { type: "CONTAINS", direction: "(KnowledgeDocument)-[:CONTAINS]->(Chunk)", note: "Flat document-to-chunk (alternative to HAS_SECTION then HAS_CHUNK)." },
5696
+ { type: "REFERENCES", direction: "(Message|KnowledgeDocument)-[:REFERENCES]->(*)", note: "Soft reference link." },
5697
+ { type: "ABOUT", direction: "(Review|Message)-[:ABOUT]->(*)", note: "Subject pointer." },
5698
+ { type: "REPLY_TO", direction: "(Email)-[:REPLY_TO]->(Email)", note: "Email threading." },
5699
+ { type: "RECEIVED_BY", direction: "(Email)-[:RECEIVED_BY]->(Person)", note: "Email recipient." },
5700
+ { type: "RAISED_BY", direction: "(ReviewAlert)-[:RAISED_BY]->(*)", note: "Alert provenance." },
5701
+ { type: "AFFECTS", direction: "(ReviewAlert)-[:AFFECTS]->(*)", note: "Alert impact surface." },
5702
+ { type: "BLOCKS", direction: "(Task)-[:BLOCKS]->(Task)", note: "Task dependency." },
5703
+ { type: "OBSERVED_IN", direction: "(*)-[:OBSERVED_IN]->(Conversation)", note: "Observation provenance." }
5704
+ ];
5705
+ var SUBLABELS = [
5706
+ { base: "Conversation", sub: "AdminConversation", note: "Admin-only conversation (admin chat, not public)." },
5707
+ { base: "Conversation", sub: "PublicConversation", note: "Public agent conversation (visitor-facing)." },
5708
+ { base: "Message", sub: "UserMessage", note: "Message authored by the human user." },
5709
+ { base: "Message", sub: "AssistantMessage", note: "Message authored by the assistant." }
5710
+ ];
5711
+ var LABEL_PATTERN = /FOR\s*\(\s*\w+\s*:\s*(\w+)\s*\)/g;
5712
+ function parseLabelsFromSchemaCypher(schemaCypher) {
5713
+ const found = /* @__PURE__ */ new Set();
5714
+ for (const match2 of schemaCypher.matchAll(LABEL_PATTERN)) {
5715
+ found.add(match2[1]);
5716
+ }
5717
+ return [...found].sort();
5718
+ }
5719
+ function buildSchemaBlock(schemaCypherText) {
5720
+ const baseLabels = parseLabelsFromSchemaCypher(schemaCypherText);
5721
+ const sublabels = SUBLABELS.map((s) => s.sub);
5722
+ const lines = [];
5723
+ lines.push("# SCHEMA (Neo4j graph, canonical reference)");
5724
+ lines.push("");
5725
+ lines.push("The Neo4j instance backing this admin session contains exactly the labels and relationship types below. Never invent names outside this list. If you need cypher against a token not listed here, first invoke `maxy-graph-get_neo4j_schema` to confirm it exists \u2014 do not guess.");
5726
+ lines.push("");
5727
+ lines.push("## Labels (from platform/neo4j/schema.cypher)");
5728
+ lines.push("");
5729
+ for (const label of baseLabels) {
5730
+ lines.push(`- :${label}`);
5731
+ }
5732
+ lines.push("");
5733
+ lines.push("### Sublabels (Task 633 role-cue variants)");
5734
+ lines.push("");
5735
+ for (const s of SUBLABELS) {
5736
+ lines.push(`- :${s.sub} (on :${s.base}) \u2014 ${s.note}`);
5737
+ }
5738
+ lines.push("");
5739
+ lines.push("## Relationship types (from .docs/neo4j.md)");
5740
+ lines.push("");
5741
+ for (const e of EDGE_TAXONOMY) {
5742
+ lines.push(`- :${e.type} \u2014 ${e.direction} \u2014 ${e.note}`);
5743
+ }
5744
+ lines.push("");
5745
+ lines.push("The cypher MCP validator (graph-mcp proxy, Task 654) rejects write cypher that references unknown labels or edges and warns on read cypher. Stick to the taxonomy above; if you believe a token is missing, invoke `maxy-graph-get_neo4j_schema` for the live snapshot.");
5746
+ const text = lines.join("\n");
5747
+ return {
5748
+ text,
5749
+ labelCount: baseLabels.length + sublabels.length,
5750
+ relationshipTypeCount: EDGE_TAXONOMY.length,
5751
+ bytes: Buffer.byteLength(text, "utf-8")
5752
+ };
5753
+ }
5754
+ function loadAdminSchemaBlock(schemaCypherPath) {
5755
+ let text;
5756
+ try {
5757
+ text = readFileSync6(schemaCypherPath, "utf-8");
5758
+ } catch (err) {
5759
+ const msg = err instanceof Error ? err.message : String(err);
5760
+ console.error(
5761
+ `[admin-identity] schema.cypher unreadable path=${schemaCypherPath} error="${msg.replace(/"/g, "'")}" block=omitted`
5762
+ );
5763
+ return null;
5764
+ }
5765
+ return buildSchemaBlock(text);
5766
+ }
5767
+
5806
5768
  // app/lib/claude-agent.ts
5807
5769
  var LOG_RETENTION_DAYS = 7;
5808
5770
  var BROWSER_TOOL_PREFIXES = [
@@ -6063,7 +6025,7 @@ function sampleProcState(pid) {
6063
6025
  let sockets2 = 0;
6064
6026
  for (const tcpFile of ["/proc/" + pid + "/net/tcp", "/proc/" + pid + "/net/tcp6"]) {
6065
6027
  try {
6066
- const raw2 = readFileSync6(tcpFile, "utf-8");
6028
+ const raw2 = readFileSync7(tcpFile, "utf-8");
6067
6029
  const lines2 = raw2.split("\n");
6068
6030
  for (let i = 1; i < lines2.length; i++) {
6069
6031
  const line = lines2[i].trim();
@@ -6079,7 +6041,7 @@ function sampleProcState(pid) {
6079
6041
  }
6080
6042
  let rssMb = 0;
6081
6043
  try {
6082
- const statm = readFileSync6(`/proc/${pid}/statm`, "utf-8").trim().split(/\s+/);
6044
+ const statm = readFileSync7(`/proc/${pid}/statm`, "utf-8").trim().split(/\s+/);
6083
6045
  const rssPages = parseInt(statm[1] ?? "0", 10);
6084
6046
  if (Number.isFinite(rssPages)) rssMb = Math.round(rssPages * 4096 / (1024 * 1024));
6085
6047
  } catch {
@@ -6126,7 +6088,7 @@ function resolveAccount() {
6126
6088
  let usersJsonUserId = null;
6127
6089
  if (existsSync5(usersFilePath)) {
6128
6090
  try {
6129
- const raw2 = readFileSync6(usersFilePath, "utf-8").trim();
6091
+ const raw2 = readFileSync7(usersFilePath, "utf-8").trim();
6130
6092
  if (raw2) {
6131
6093
  const users = JSON.parse(raw2);
6132
6094
  if (users.length > 0) {
@@ -6142,7 +6104,7 @@ function resolveAccount() {
6142
6104
  if (!entry.isDirectory()) continue;
6143
6105
  const configPath2 = resolve5(ACCOUNTS_DIR, entry.name, "account.json");
6144
6106
  if (!existsSync5(configPath2)) continue;
6145
- const raw2 = readFileSync6(configPath2, "utf-8");
6107
+ const raw2 = readFileSync7(configPath2, "utf-8");
6146
6108
  let config;
6147
6109
  try {
6148
6110
  config = JSON.parse(raw2);
@@ -6177,7 +6139,7 @@ function resolveAccount() {
6177
6139
  function readAgentFile(accountDir, agentName, filename) {
6178
6140
  const filePath = resolve5(accountDir, "agents", agentName, filename);
6179
6141
  if (!existsSync5(filePath)) return null;
6180
- return readFileSync6(filePath, "utf-8");
6142
+ return readFileSync7(filePath, "utf-8");
6181
6143
  }
6182
6144
  function readIdentity(accountDir, agentName) {
6183
6145
  return readAgentFile(accountDir, agentName, "IDENTITY.md");
@@ -6219,7 +6181,7 @@ function resolveDefaultAgentSlug(accountDir) {
6219
6181
  }
6220
6182
  let config;
6221
6183
  try {
6222
- config = JSON.parse(readFileSync6(configPath2, "utf-8"));
6184
+ config = JSON.parse(readFileSync7(configPath2, "utf-8"));
6223
6185
  } catch (err) {
6224
6186
  console.error("[agent-resolve] failed to read account.json:", err);
6225
6187
  return null;
@@ -6310,14 +6272,14 @@ function resolveAgentConfig(accountDir, agentName) {
6310
6272
  const knowledgeMtime = statSync3(knowledgePath).mtimeMs;
6311
6273
  const summaryMtime = statSync3(summaryPath).mtimeMs;
6312
6274
  if (summaryMtime >= knowledgeMtime) {
6313
- knowledge = readFileSync6(summaryPath, "utf-8");
6275
+ knowledge = readFileSync7(summaryPath, "utf-8");
6314
6276
  } else {
6315
6277
  console.warn(`[agent-config] ${agentName}: KNOWLEDGE-SUMMARY.md is stale (KNOWLEDGE.md is newer) \u2014 using full knowledge`);
6316
- knowledge = readFileSync6(knowledgePath, "utf-8");
6278
+ knowledge = readFileSync7(knowledgePath, "utf-8");
6317
6279
  }
6318
6280
  knowledgeBaked = true;
6319
6281
  } else if (hasKnowledge) {
6320
- knowledge = readFileSync6(knowledgePath, "utf-8");
6282
+ knowledge = readFileSync7(knowledgePath, "utf-8");
6321
6283
  knowledgeBaked = true;
6322
6284
  }
6323
6285
  let budget = null;
@@ -6343,7 +6305,7 @@ function parsePluginFrontmatter(pluginDir) {
6343
6305
  if (!existsSync5(pluginPath)) return null;
6344
6306
  let raw2;
6345
6307
  try {
6346
- raw2 = readFileSync6(pluginPath, "utf-8");
6308
+ raw2 = readFileSync7(pluginPath, "utf-8");
6347
6309
  } catch {
6348
6310
  console.warn(`[plugins] cannot read ${pluginPath}`);
6349
6311
  return null;
@@ -6419,7 +6381,7 @@ function autoDeliverPremiumPlugins(purchasedPlugins) {
6419
6381
  if (isBundle) {
6420
6382
  let bundleRaw;
6421
6383
  try {
6422
- bundleRaw = readFileSync6(bundlePath, "utf-8");
6384
+ bundleRaw = readFileSync7(bundlePath, "utf-8");
6423
6385
  } catch (err) {
6424
6386
  console.log(`${TAG19} ${pluginName}: cannot read BUNDLE.md \u2014 ${err instanceof Error ? err.message : String(err)}`);
6425
6387
  continue;
@@ -6512,7 +6474,7 @@ function migratePluginRenames(accountDir, config) {
6512
6474
  if (!changed) return;
6513
6475
  const configPath2 = resolve5(accountDir, "account.json");
6514
6476
  try {
6515
- const raw2 = readFileSync6(configPath2, "utf-8");
6477
+ const raw2 = readFileSync7(configPath2, "utf-8");
6516
6478
  const parsed = JSON.parse(raw2);
6517
6479
  parsed.enabledPlugins = migrated;
6518
6480
  writeFileSync5(configPath2, JSON.stringify(parsed, null, 2) + "\n");
@@ -6546,7 +6508,7 @@ function autoDeliverBundleAgents(accountDir, purchasedPlugins) {
6546
6508
  const agentsmdPath = resolve5(accountDir, "agents", "admin", "AGENTS.md");
6547
6509
  let agentsmd = "";
6548
6510
  try {
6549
- agentsmd = existsSync5(agentsmdPath) ? readFileSync6(agentsmdPath, "utf-8") : "";
6511
+ agentsmd = existsSync5(agentsmdPath) ? readFileSync7(agentsmdPath, "utf-8") : "";
6550
6512
  } catch {
6551
6513
  }
6552
6514
  let delivered = 0;
@@ -6570,7 +6532,7 @@ function autoDeliverBundleAgents(accountDir, purchasedPlugins) {
6570
6532
  continue;
6571
6533
  }
6572
6534
  try {
6573
- const content = readFileSync6(target, "utf-8");
6535
+ const content = readFileSync7(target, "utf-8");
6574
6536
  const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
6575
6537
  if (fmMatch) {
6576
6538
  const nameMatch = fmMatch[1].match(/^name:\s*(.+)/m);
@@ -6606,7 +6568,7 @@ function assemblePublicPluginContent(pluginDir) {
6606
6568
  const pluginPath = resolve5(pluginRoot, "PLUGIN.md");
6607
6569
  let raw2;
6608
6570
  try {
6609
- raw2 = readFileSync6(pluginPath, "utf-8");
6571
+ raw2 = readFileSync7(pluginPath, "utf-8");
6610
6572
  } catch {
6611
6573
  return null;
6612
6574
  }
@@ -6627,7 +6589,7 @@ function assemblePublicPluginContent(pluginDir) {
6627
6589
  const skillMdPath = resolve5(skillDir, "SKILL.md");
6628
6590
  let skillRaw;
6629
6591
  try {
6630
- skillRaw = readFileSync6(skillMdPath, "utf-8");
6592
+ skillRaw = readFileSync7(skillMdPath, "utf-8");
6631
6593
  } catch {
6632
6594
  continue;
6633
6595
  }
@@ -6678,7 +6640,7 @@ function assemblePublicPluginContent(pluginDir) {
6678
6640
  }
6679
6641
  for (const refFile of refFiles) {
6680
6642
  try {
6681
- const refContent = readFileSync6(resolve5(refsDir, refFile), "utf-8").trim();
6643
+ const refContent = readFileSync7(resolve5(refsDir, refFile), "utf-8").trim();
6682
6644
  if (refContent) {
6683
6645
  parts.push(`
6684
6646
  <!-- reference: ${refFile} -->`);
@@ -6752,7 +6714,7 @@ function loadEmbeddedPlugins(agentType, selectedPlugins, enabledPlugins) {
6752
6714
  const pluginPath = resolve5(pluginsDir, dir, "PLUGIN.md");
6753
6715
  let raw2;
6754
6716
  try {
6755
- raw2 = readFileSync6(pluginPath, "utf-8");
6717
+ raw2 = readFileSync7(pluginPath, "utf-8");
6756
6718
  } catch (err) {
6757
6719
  console.warn(`[plugins] ${dir}: failed to read PLUGIN.md for ${agentType} embed: ${String(err)}`);
6758
6720
  continue;
@@ -7098,7 +7060,7 @@ function resolveUserAccounts(userId) {
7098
7060
  if (!existsSync5(configPath2)) continue;
7099
7061
  let config;
7100
7062
  try {
7101
- config = JSON.parse(readFileSync6(configPath2, "utf-8"));
7063
+ config = JSON.parse(readFileSync7(configPath2, "utf-8"));
7102
7064
  } catch {
7103
7065
  console.error(`[session] account.json corrupt at ${configPath2} \u2014 skipping`);
7104
7066
  continue;
@@ -7414,7 +7376,7 @@ function readBrandHostname() {
7414
7376
  if (cachedBrandHostname !== null) return cachedBrandHostname;
7415
7377
  try {
7416
7378
  const brandPath = resolve5(PLATFORM_ROOT3, "config", "brand.json");
7417
- const parsed = JSON.parse(readFileSync6(brandPath, "utf-8"));
7379
+ const parsed = JSON.parse(readFileSync7(brandPath, "utf-8"));
7418
7380
  cachedBrandHostname = typeof parsed.hostname === "string" && parsed.hostname.length > 0 ? parsed.hostname : "maxy";
7419
7381
  } catch {
7420
7382
  cachedBrandHostname = "maxy";
@@ -10219,7 +10181,7 @@ ${sessionContext}`;
10219
10181
  const skillPath = resolve5(PLATFORM_ROOT3, "plugins/admin/skills/onboarding/SKILL.md");
10220
10182
  let skillContent = "";
10221
10183
  try {
10222
- skillContent = readFileSync6(skillPath, "utf-8");
10184
+ skillContent = readFileSync7(skillPath, "utf-8");
10223
10185
  } catch (err) {
10224
10186
  console.error(`[onboarding-inject] accountId=${accountId.slice(0, 8)}\u2026 error=skill-read-failed path=${skillPath} reason=${err instanceof Error ? err.message : String(err)}`);
10225
10187
  }
@@ -10271,13 +10233,23 @@ ${manifest}`;
10271
10233
  }
10272
10234
  const graphRefPath = resolve5(PLATFORM_ROOT3, "plugins/memory/references/graph-primitives.md");
10273
10235
  try {
10274
- const graphRef = readFileSync6(graphRefPath, "utf-8");
10236
+ const graphRef = readFileSync7(graphRefPath, "utf-8");
10275
10237
  baseSystemPrompt += `
10276
10238
 
10277
10239
  ${graphRef}`;
10278
10240
  } catch (err) {
10279
10241
  console.error(`[graph-primitives] reference missing at ${graphRefPath} \u2014 admin session will have no Cypher cookbook: ${err instanceof Error ? err.message : String(err)}`);
10280
10242
  }
10243
+ const schemaCypherPath = resolve5(PLATFORM_ROOT3, "neo4j/schema.cypher");
10244
+ const schemaBlock = loadAdminSchemaBlock(schemaCypherPath);
10245
+ if (schemaBlock) {
10246
+ baseSystemPrompt += `
10247
+
10248
+ ${schemaBlock.text}`;
10249
+ console.log(
10250
+ `[admin-identity] schemaBlockBytes=${schemaBlock.bytes} labels=${schemaBlock.labelCount} relationshipTypes=${schemaBlock.relationshipTypeCount}`
10251
+ );
10252
+ }
10281
10253
  }
10282
10254
  if (agentConfig?.budget) {
10283
10255
  const pluginTokens = embeddedPlugins.reduce((sum, p) => sum + estimateTokens(p.body), 0);
@@ -10486,7 +10458,7 @@ var clientIpMiddleware = async (c, next) => {
10486
10458
  };
10487
10459
 
10488
10460
  // server/routes/health.ts
10489
- import { existsSync as existsSync10, readFileSync as readFileSync11 } from "fs";
10461
+ import { existsSync as existsSync10, readFileSync as readFileSync12 } from "fs";
10490
10462
  import { createConnection as createConnection2 } from "net";
10491
10463
 
10492
10464
  // app/lib/network.ts
@@ -10511,7 +10483,7 @@ function getLanIp() {
10511
10483
  import { basename as basename2 } from "path";
10512
10484
 
10513
10485
  // app/lib/review-detector/rules.ts
10514
- import { readFileSync as readFileSync7, writeFileSync as writeFileSync6, existsSync as existsSync6, statSync as statSync4, mkdirSync as mkdirSync5, renameSync as renameSync2 } from "fs";
10486
+ import { readFileSync as readFileSync8, writeFileSync as writeFileSync6, existsSync as existsSync6, statSync as statSync4, mkdirSync as mkdirSync5, renameSync as renameSync2 } from "fs";
10515
10487
  import { resolve as resolve6, dirname as dirname3 } from "path";
10516
10488
  var DEFAULT_SCAN_INTERVAL_MS = 5e3;
10517
10489
  var RATE_LIMIT_PATTERN = "rate[- ]?limit(?:ed| reached| hit)|(?:HTTP|status)[^a-z]{0,3}429|too many requests";
@@ -10959,7 +10931,7 @@ function loadRules(configDir2) {
10959
10931
  if (!existsSync6(path2)) {
10960
10932
  throw new Error(`rules file missing at ${path2}`);
10961
10933
  }
10962
- const raw2 = readFileSync7(path2, "utf-8");
10934
+ const raw2 = readFileSync8(path2, "utf-8");
10963
10935
  let parsed;
10964
10936
  try {
10965
10937
  parsed = JSON.parse(raw2);
@@ -11124,7 +11096,7 @@ function validateRule(input, label, seenIds) {
11124
11096
  }
11125
11097
 
11126
11098
  // app/lib/review-detector/sources.ts
11127
- import { existsSync as existsSync7, readdirSync as readdirSync3, statSync as statSync5, writeFileSync as writeFileSync7, renameSync as renameSync3, mkdirSync as mkdirSync6, openSync as openSync3, readSync as readSync3, closeSync as closeSync3, readFileSync as readFileSync8 } from "fs";
11099
+ import { existsSync as existsSync7, readdirSync as readdirSync3, statSync as statSync5, writeFileSync as writeFileSync7, renameSync as renameSync3, mkdirSync as mkdirSync6, openSync as openSync3, readSync as readSync3, closeSync as closeSync3, readFileSync as readFileSync9 } from "fs";
11128
11100
  import { resolve as resolve7, join as join4, basename, dirname as dirname4 } from "path";
11129
11101
  function tailStatePath(configDir2) {
11130
11102
  return resolve7(configDir2, "review-state.json");
@@ -11133,7 +11105,7 @@ function loadTailState(configDir2) {
11133
11105
  const path2 = tailStatePath(configDir2);
11134
11106
  if (!existsSync7(path2)) return {};
11135
11107
  try {
11136
- const raw2 = readFileSync8(path2, "utf-8");
11108
+ const raw2 = readFileSync9(path2, "utf-8");
11137
11109
  const parsed = JSON.parse(raw2);
11138
11110
  if (!parsed || typeof parsed !== "object") return {};
11139
11111
  const clean = {};
@@ -11303,7 +11275,7 @@ function sourceKey(file) {
11303
11275
  }
11304
11276
 
11305
11277
  // app/lib/review-detector/writer.ts
11306
- import { appendFileSync as appendFileSync2, existsSync as existsSync8, mkdirSync as mkdirSync7, readFileSync as readFileSync9, writeFileSync as writeFileSync8, renameSync as renameSync4, statSync as statSync6 } from "fs";
11278
+ import { appendFileSync as appendFileSync2, existsSync as existsSync8, mkdirSync as mkdirSync7, readFileSync as readFileSync10, writeFileSync as writeFileSync8, renameSync as renameSync4, statSync as statSync6 } from "fs";
11307
11279
  import { resolve as resolve8, dirname as dirname5 } from "path";
11308
11280
  import { randomUUID as randomUUID3 } from "crypto";
11309
11281
  function reviewLogPath(configDir2) {
@@ -11442,7 +11414,7 @@ function queueAlert(configDir2, accountId, match2) {
11442
11414
  async function drainPendingAlerts(configDir2) {
11443
11415
  const path2 = pendingAlertsPath(configDir2);
11444
11416
  if (!existsSync8(path2)) return { drained: 0, remaining: 0 };
11445
- const raw2 = readFileSync9(path2, "utf-8");
11417
+ const raw2 = readFileSync10(path2, "utf-8");
11446
11418
  const lines = raw2.split("\n").filter((l) => l.trim().length > 0);
11447
11419
  if (lines.length === 0) return { drained: 0, remaining: 0 };
11448
11420
  const remaining = [];
@@ -12105,7 +12077,7 @@ var WhatsAppConfigSchema = z.object({
12105
12077
  });
12106
12078
 
12107
12079
  // app/lib/whatsapp/config-persist.ts
12108
- import { readFileSync as readFileSync10, writeFileSync as writeFileSync9, existsSync as existsSync9 } from "fs";
12080
+ import { readFileSync as readFileSync11, writeFileSync as writeFileSync9, existsSync as existsSync9 } from "fs";
12109
12081
  import { resolve as resolve10, join as join5 } from "path";
12110
12082
  var TAG3 = "[whatsapp:config]";
12111
12083
  function configPath(accountDir) {
@@ -12114,7 +12086,7 @@ function configPath(accountDir) {
12114
12086
  function readConfig(accountDir) {
12115
12087
  const path2 = configPath(accountDir);
12116
12088
  if (!existsSync9(path2)) throw new Error(`account.json not found at ${path2}`);
12117
- return JSON.parse(readFileSync10(path2, "utf-8"));
12089
+ return JSON.parse(readFileSync11(path2, "utf-8"));
12118
12090
  }
12119
12091
  function writeConfig(accountDir, config) {
12120
12092
  const path2 = configPath(accountDir);
@@ -14406,7 +14378,7 @@ app.get("/", async (c) => {
14406
14378
  let pinConfigured = false;
14407
14379
  try {
14408
14380
  if (existsSync10(USERS_FILE)) {
14409
- const raw2 = readFileSync11(USERS_FILE, "utf-8").trim();
14381
+ const raw2 = readFileSync12(USERS_FILE, "utf-8").trim();
14410
14382
  if (raw2) {
14411
14383
  const users = JSON.parse(raw2);
14412
14384
  pinConfigured = Array.isArray(users) && users.length > 0;
@@ -15523,7 +15495,7 @@ var group_default = app4;
15523
15495
 
15524
15496
  // app/lib/access-gate.ts
15525
15497
  import neo4j2 from "neo4j-driver";
15526
- import { readFileSync as readFileSync12 } from "fs";
15498
+ import { readFileSync as readFileSync13 } from "fs";
15527
15499
  import { resolve as resolve13 } from "path";
15528
15500
  import { randomUUID as randomUUID7, randomInt } from "crypto";
15529
15501
  var PLATFORM_ROOT5 = process.env.MAXY_PLATFORM_ROOT ?? resolve13(process.cwd(), "..");
@@ -15532,7 +15504,7 @@ function readPassword2() {
15532
15504
  if (process.env.NEO4J_PASSWORD) return process.env.NEO4J_PASSWORD;
15533
15505
  const passwordFile = resolve13(PLATFORM_ROOT5, "config/.neo4j-password");
15534
15506
  try {
15535
- return readFileSync12(passwordFile, "utf-8").trim();
15507
+ return readFileSync13(passwordFile, "utf-8").trim();
15536
15508
  } catch {
15537
15509
  throw new Error(
15538
15510
  `Neo4j password not found. Expected at ${passwordFile} or in NEO4J_PASSWORD env var.`
@@ -15843,7 +15815,7 @@ async function findActiveGrantByContact(contactValue, agentSlug, accountId) {
15843
15815
  }
15844
15816
 
15845
15817
  // app/lib/brevo-sms.ts
15846
- import { readFileSync as readFileSync13, writeFileSync as writeFileSync11, mkdirSync as mkdirSync9, existsSync as existsSync12, chmodSync } from "fs";
15818
+ import { readFileSync as readFileSync14, writeFileSync as writeFileSync11, mkdirSync as mkdirSync9, existsSync as existsSync12, chmodSync } from "fs";
15847
15819
  import { dirname as dirname6 } from "path";
15848
15820
  import { resolve as resolve14 } from "path";
15849
15821
  var BREVO_API_KEY_FILE = resolve14(MAXY_DIR, ".brevo-api-key");
@@ -15855,7 +15827,7 @@ if (platformRoot) {
15855
15827
  try {
15856
15828
  const brandPath = resolve14(platformRoot, "config", "brand.json");
15857
15829
  if (existsSync12(brandPath)) {
15858
- const brand = JSON.parse(readFileSync13(brandPath, "utf-8"));
15830
+ const brand = JSON.parse(readFileSync14(brandPath, "utf-8"));
15859
15831
  if (brand.productName) BREVO_SENDER = brand.productName;
15860
15832
  }
15861
15833
  } catch {
@@ -15863,7 +15835,7 @@ if (platformRoot) {
15863
15835
  }
15864
15836
  function readBrevoApiKey() {
15865
15837
  try {
15866
- const key = readFileSync13(BREVO_API_KEY_FILE, "utf-8").trim();
15838
+ const key = readFileSync14(BREVO_API_KEY_FILE, "utf-8").trim();
15867
15839
  if (!key) {
15868
15840
  throw new Error(`Brevo API key file is empty: ${BREVO_API_KEY_FILE}`);
15869
15841
  }
@@ -16294,7 +16266,7 @@ app5.post("/send-otp", async (c) => {
16294
16266
  var access_default = app5;
16295
16267
 
16296
16268
  // server/routes/telegram.ts
16297
- import { existsSync as existsSync13, readFileSync as readFileSync14 } from "fs";
16269
+ import { existsSync as existsSync13, readFileSync as readFileSync15 } from "fs";
16298
16270
  import { timingSafeEqual } from "crypto";
16299
16271
 
16300
16272
  // app/lib/telegram/access-control.ts
@@ -16332,7 +16304,7 @@ function getWebhookSecret(botType) {
16332
16304
  const filePath = botType === "admin" ? TELEGRAM_ADMIN_WEBHOOK_SECRET_FILE : TELEGRAM_WEBHOOK_SECRET_FILE;
16333
16305
  try {
16334
16306
  if (!existsSync13(filePath)) return null;
16335
- const secret = readFileSync14(filePath, "utf-8").trim();
16307
+ const secret = readFileSync15(filePath, "utf-8").trim();
16336
16308
  return secret || null;
16337
16309
  } catch {
16338
16310
  return null;
@@ -16492,7 +16464,7 @@ var telegram_default = app6;
16492
16464
  // server/routes/whatsapp.ts
16493
16465
  import { join as join8, resolve as resolve15, basename as basename4 } from "path";
16494
16466
  import { readFile as readFile2, stat as stat3 } from "fs/promises";
16495
- import { realpathSync as realpathSync2, readdirSync as readdirSync4, readFileSync as readFileSync15, existsSync as existsSync14 } from "fs";
16467
+ import { realpathSync as realpathSync2, readdirSync as readdirSync4, readFileSync as readFileSync16, existsSync as existsSync14 } from "fs";
16496
16468
 
16497
16469
  // app/lib/whatsapp/login.ts
16498
16470
  import { randomUUID as randomUUID8 } from "crypto";
@@ -16980,7 +16952,7 @@ app7.post("/config", async (c) => {
16980
16952
  const configPath2 = resolve15(agentsDir, entry.name, "config.json");
16981
16953
  if (!existsSync14(configPath2)) continue;
16982
16954
  try {
16983
- const config = JSON.parse(readFileSync15(configPath2, "utf-8"));
16955
+ const config = JSON.parse(readFileSync16(configPath2, "utf-8"));
16984
16956
  agents.push({ slug: entry.name, displayName: config.displayName ?? entry.name });
16985
16957
  } catch {
16986
16958
  console.error(`${TAG18} config action=list-public-agents error="failed to parse config.json for agent ${entry.name}" \u2014 skipping`);
@@ -17192,7 +17164,7 @@ var whatsapp_default = app7;
17192
17164
 
17193
17165
  // server/routes/onboarding.ts
17194
17166
  import { spawn as spawn3, execFileSync as execFileSync2 } from "child_process";
17195
- import { openSync as openSync4, closeSync as closeSync4, writeFileSync as writeFileSync12, writeSync, existsSync as existsSync15, mkdirSync as mkdirSync10, readFileSync as readFileSync16, unlinkSync as unlinkSync4 } from "fs";
17167
+ import { openSync as openSync4, closeSync as closeSync4, writeFileSync as writeFileSync12, writeSync, existsSync as existsSync15, mkdirSync as mkdirSync10, readFileSync as readFileSync17, unlinkSync as unlinkSync4 } from "fs";
17196
17168
  import { resolve as resolve16, dirname as dirname7 } from "path";
17197
17169
  import { createHash, randomUUID as randomUUID9 } from "crypto";
17198
17170
  var PLATFORM_ROOT7 = process.env.MAXY_PLATFORM_ROOT || "";
@@ -17201,7 +17173,7 @@ function hashPin(pin) {
17201
17173
  }
17202
17174
  function readUsersFile() {
17203
17175
  if (!existsSync15(USERS_FILE)) return null;
17204
- const raw2 = readFileSync16(USERS_FILE, "utf-8").trim();
17176
+ const raw2 = readFileSync17(USERS_FILE, "utf-8").trim();
17205
17177
  if (!raw2) return [];
17206
17178
  return JSON.parse(raw2);
17207
17179
  }
@@ -17312,7 +17284,7 @@ app8.post("/set-pin", async (c) => {
17312
17284
  const account = resolveAccount();
17313
17285
  if (account) {
17314
17286
  try {
17315
- const config = JSON.parse(readFileSync16(`${account.accountDir}/account.json`, "utf-8"));
17287
+ const config = JSON.parse(readFileSync17(`${account.accountDir}/account.json`, "utf-8"));
17316
17288
  if (!config.admins) config.admins = [];
17317
17289
  if (!config.admins.some((a) => a.userId === userId)) {
17318
17290
  config.admins.push({ userId, role: "owner" });
@@ -17370,7 +17342,7 @@ app8.post("/skip", async (c) => {
17370
17342
  const brandPath = PLATFORM_ROOT7 ? resolve16(PLATFORM_ROOT7, "config", "brand.json") : "";
17371
17343
  if (brandPath && existsSync15(brandPath)) {
17372
17344
  try {
17373
- const brand = JSON.parse(readFileSync16(brandPath, "utf-8"));
17345
+ const brand = JSON.parse(readFileSync17(brandPath, "utf-8"));
17374
17346
  if (brand.productName) agentName = brand.productName;
17375
17347
  } catch (err) {
17376
17348
  console.error(`[onboarding-skip] brand.json read failed: ${err instanceof Error ? err.message : String(err)}`);
@@ -17564,14 +17536,14 @@ app9.post("/", async (c) => {
17564
17536
  var client_error_default = app9;
17565
17537
 
17566
17538
  // server/routes/admin/session.ts
17567
- import { readFileSync as readFileSync17, existsSync as existsSync17 } from "fs";
17539
+ import { readFileSync as readFileSync18, existsSync as existsSync17 } from "fs";
17568
17540
  import { createHash as createHash2 } from "crypto";
17569
17541
  function hashPin2(pin) {
17570
17542
  return createHash2("sha256").update(pin).digest("hex");
17571
17543
  }
17572
17544
  function readUsersFile2() {
17573
17545
  if (!existsSync17(USERS_FILE)) return null;
17574
- const raw2 = readFileSync17(USERS_FILE, "utf-8").trim();
17546
+ const raw2 = readFileSync18(USERS_FILE, "utf-8").trim();
17575
17547
  if (!raw2) return [];
17576
17548
  return JSON.parse(raw2);
17577
17549
  }
@@ -18267,7 +18239,7 @@ app12.post("/", requireAdminSession, async (c) => {
18267
18239
  var compact_default = app12;
18268
18240
 
18269
18241
  // server/routes/admin/logs.ts
18270
- import { existsSync as existsSync18, readdirSync as readdirSync5, readFileSync as readFileSync18, statSync as statSync9 } from "fs";
18242
+ import { existsSync as existsSync18, readdirSync as readdirSync5, readFileSync as readFileSync19, statSync as statSync9 } from "fs";
18271
18243
  import { resolve as resolve18, basename as basename5 } from "path";
18272
18244
  var TAIL_BYTES = 8192;
18273
18245
  var app13 = new Hono2();
@@ -18286,7 +18258,7 @@ app13.get("/", async (c) => {
18286
18258
  const filePath = resolve18(dir, safe);
18287
18259
  searched.push(filePath);
18288
18260
  try {
18289
- const content = readFileSync18(filePath, "utf-8");
18261
+ const content = readFileSync19(filePath, "utf-8");
18290
18262
  const headers = { "Content-Type": "text/plain; charset=utf-8" };
18291
18263
  if (download) headers["Content-Disposition"] = `attachment; filename="${safe}"`;
18292
18264
  return new Response(content, { headers });
@@ -18328,7 +18300,7 @@ app13.get("/", async (c) => {
18328
18300
  const filePath = resolve18(dir, fileName);
18329
18301
  searched.push(filePath);
18330
18302
  try {
18331
- const content = readFileSync18(filePath, "utf-8");
18303
+ const content = readFileSync19(filePath, "utf-8");
18332
18304
  const headers = { "Content-Type": "text/plain; charset=utf-8" };
18333
18305
  if (download) headers["Content-Disposition"] = `attachment; filename="${fileName}"`;
18334
18306
  return new Response(content, { headers });
@@ -18355,7 +18327,7 @@ app13.get("/", async (c) => {
18355
18327
  files.filter((f) => !seen.has(f)).map((f) => ({ name: f, mtime: statSync9(resolve18(dir, f)).mtimeMs })).sort((a, b) => b.mtime - a.mtime).forEach(({ name }) => {
18356
18328
  seen.add(name);
18357
18329
  try {
18358
- const content = readFileSync18(resolve18(dir, name));
18330
+ const content = readFileSync19(resolve18(dir, name));
18359
18331
  const tail = content.length > TAIL_BYTES ? content.subarray(content.length - TAIL_BYTES).toString("utf-8") : content.toString("utf-8");
18360
18332
  logs[name] = tail.trim() || "(empty)";
18361
18333
  } catch (err) {
@@ -18440,7 +18412,7 @@ app15.get("/:attachmentId", requireAdminSession, async (c) => {
18440
18412
  var attachment_default = app15;
18441
18413
 
18442
18414
  // server/routes/admin/account.ts
18443
- import { readFileSync as readFileSync19, writeFileSync as writeFileSync13 } from "fs";
18415
+ import { readFileSync as readFileSync20, writeFileSync as writeFileSync13 } from "fs";
18444
18416
  import { resolve as resolve20 } from "path";
18445
18417
  var VALID_CONTEXT_MODES = ["managed", "claude-code"];
18446
18418
  var app16 = new Hono2();
@@ -18459,7 +18431,7 @@ app16.patch("/", requireAdminSession, async (c) => {
18459
18431
  if (!account) return c.json({ error: "No account configured" }, 500);
18460
18432
  const configPath2 = resolve20(account.accountDir, "account.json");
18461
18433
  try {
18462
- const raw2 = readFileSync19(configPath2, "utf-8");
18434
+ const raw2 = readFileSync20(configPath2, "utf-8");
18463
18435
  const config = JSON.parse(raw2);
18464
18436
  config.contextMode = contextMode;
18465
18437
  writeFileSync13(configPath2, JSON.stringify(config, null, 2) + "\n", "utf-8");
@@ -18474,7 +18446,7 @@ var account_default = app16;
18474
18446
 
18475
18447
  // server/routes/admin/agents.ts
18476
18448
  import { resolve as resolve21 } from "path";
18477
- import { readdirSync as readdirSync6, readFileSync as readFileSync20, existsSync as existsSync20, rmSync as rmSync3 } from "fs";
18449
+ import { readdirSync as readdirSync6, readFileSync as readFileSync21, existsSync as existsSync20, rmSync as rmSync3 } from "fs";
18478
18450
  var app17 = new Hono2();
18479
18451
  app17.get("/", (c) => {
18480
18452
  const account = resolveAccount();
@@ -18490,7 +18462,7 @@ app17.get("/", (c) => {
18490
18462
  const configPath2 = resolve21(agentsDir, entry.name, "config.json");
18491
18463
  if (!existsSync20(configPath2)) continue;
18492
18464
  try {
18493
- const config = JSON.parse(readFileSync20(configPath2, "utf-8"));
18465
+ const config = JSON.parse(readFileSync21(configPath2, "utf-8"));
18494
18466
  agents.push({
18495
18467
  slug: entry.name,
18496
18468
  displayName: config.displayName ?? entry.name,
@@ -18532,7 +18504,7 @@ app17.delete("/:slug", (c) => {
18532
18504
  var agents_default = app17;
18533
18505
 
18534
18506
  // server/routes/admin/version.ts
18535
- import { existsSync as existsSync21, readFileSync as readFileSync21 } from "fs";
18507
+ import { existsSync as existsSync21, readFileSync as readFileSync22 } from "fs";
18536
18508
  import { resolve as resolve22, join as join10 } from "path";
18537
18509
  var PLATFORM_ROOT8 = process.env.MAXY_PLATFORM_ROOT ?? resolve22(process.cwd(), "..");
18538
18510
  var brandHostname = "maxy";
@@ -18540,7 +18512,7 @@ var brandNpmPackage = "@rubytech/create-maxy";
18540
18512
  var brandJsonPath = join10(PLATFORM_ROOT8, "config", "brand.json");
18541
18513
  if (existsSync21(brandJsonPath)) {
18542
18514
  try {
18543
- const brand = JSON.parse(readFileSync21(brandJsonPath, "utf-8"));
18515
+ const brand = JSON.parse(readFileSync22(brandJsonPath, "utf-8"));
18544
18516
  if (brand.hostname) brandHostname = brand.hostname;
18545
18517
  if (brand.npm?.packageName) brandNpmPackage = brand.npm.packageName;
18546
18518
  } catch {
@@ -18552,7 +18524,7 @@ var REGISTRY_URL = `https://registry.npmjs.org/${NPM_PACKAGE}/latest`;
18552
18524
  var FETCH_TIMEOUT_MS = 5e3;
18553
18525
  function readInstalled() {
18554
18526
  if (!existsSync21(VERSION_FILE)) return "unknown";
18555
- const content = readFileSync21(VERSION_FILE, "utf-8").trim();
18527
+ const content = readFileSync22(VERSION_FILE, "utf-8").trim();
18556
18528
  return content || "unknown";
18557
18529
  }
18558
18530
  async function fetchLatest() {
@@ -18806,66 +18778,6 @@ app20.post("/launch", async (c) => {
18806
18778
  });
18807
18779
  var browser_default = app20;
18808
18780
 
18809
- // server/routes/admin/terminal.ts
18810
- var app21 = new Hono2();
18811
- app21.post("/launch", async (c) => {
18812
- try {
18813
- const transport = resolveBrowserTransport(c.req.raw, c.env?.incoming?.socket?.remoteAddress);
18814
- if (transport === "vnc") {
18815
- const vncOk = await ensureVnc();
18816
- if (!vncOk) {
18817
- return c.json({ ok: false, error: "VNC failed to start" }, 502);
18818
- }
18819
- }
18820
- const terminalResult = await ensureTerminal(transport);
18821
- if (!terminalResult.ok) {
18822
- return c.json({ ok: false, error: terminalResult.error ?? "Terminal failed to start" }, 502);
18823
- }
18824
- return c.json({ ok: true, transport });
18825
- } catch (err) {
18826
- console.error("[admin/terminal/launch] Failed to start terminal:", err);
18827
- return c.json(
18828
- { ok: false, error: err instanceof Error ? err.message : "Unknown error" },
18829
- 500
18830
- );
18831
- }
18832
- });
18833
- app21.post("/launch-upgrade", async (c) => {
18834
- try {
18835
- const transport = resolveBrowserTransport(c.req.raw, c.env?.incoming?.socket?.remoteAddress);
18836
- if (transport === "vnc") {
18837
- const vncOk = await ensureVnc();
18838
- if (!vncOk) {
18839
- return c.json({ ok: false, error: "VNC failed to start" }, 502);
18840
- }
18841
- }
18842
- const result = await ensureTerminalUpgrade(transport);
18843
- if (!result.ok) {
18844
- return c.json({ ok: false, error: result.error ?? "Upgrade terminal failed to start" }, 502);
18845
- }
18846
- return c.json({ ok: true, transport });
18847
- } catch (err) {
18848
- console.error("[admin/terminal/launch-upgrade] Failed to start upgrade terminal:", err);
18849
- return c.json(
18850
- { ok: false, error: err instanceof Error ? err.message : "Unknown error" },
18851
- 500
18852
- );
18853
- }
18854
- });
18855
- app21.post("/close", (c) => {
18856
- try {
18857
- killTerminal();
18858
- return c.json({ ok: true });
18859
- } catch (err) {
18860
- console.error("[admin/terminal/close] kill failed:", err);
18861
- return c.json(
18862
- { ok: false, error: err instanceof Error ? err.message : "Unknown error" },
18863
- 500
18864
- );
18865
- }
18866
- });
18867
- var terminal_default = app21;
18868
-
18869
18781
  // app/lib/cdp-client.ts
18870
18782
  var CDP_HOST = "127.0.0.1";
18871
18783
  var CDP_PORT = 9222;
@@ -18907,8 +18819,8 @@ async function cdpNavigateNewTab(url, opts = {}) {
18907
18819
  }
18908
18820
 
18909
18821
  // server/routes/admin/device-browser.ts
18910
- var app22 = new Hono2();
18911
- app22.post("/navigate", async (c) => {
18822
+ var app21 = new Hono2();
18823
+ app21.post("/navigate", async (c) => {
18912
18824
  const TAG19 = "[device-url:click]";
18913
18825
  let body;
18914
18826
  try {
@@ -18995,7 +18907,7 @@ app22.post("/navigate", async (c) => {
18995
18907
  targetId: outcome.targetId
18996
18908
  });
18997
18909
  });
18998
- var device_browser_default = app22;
18910
+ var device_browser_default = app21;
18999
18911
 
19000
18912
  // server/routes/admin/events.ts
19001
18913
  var ALLOWED_EVENTS = /* @__PURE__ */ new Set([
@@ -19004,8 +18916,8 @@ var ALLOWED_EVENTS = /* @__PURE__ */ new Set([
19004
18916
  "device-url:vnc-surface-shown",
19005
18917
  "device-url:malformed"
19006
18918
  ]);
19007
- var app23 = new Hono2();
19008
- app23.post("/", async (c) => {
18919
+ var app22 = new Hono2();
18920
+ app22.post("/", async (c) => {
19009
18921
  const TAG19 = "[admin:events]";
19010
18922
  let body;
19011
18923
  try {
@@ -19036,12 +18948,12 @@ app23.post("/", async (c) => {
19036
18948
  console.error(`[${event}] ${formatted}`);
19037
18949
  return c.json({ ok: true });
19038
18950
  });
19039
- var events_default = app23;
18951
+ var events_default = app22;
19040
18952
 
19041
18953
  // server/routes/admin/cloudflare.ts
19042
18954
  import { homedir as homedir3 } from "os";
19043
18955
  import { resolve as resolve24 } from "path";
19044
- import { readFileSync as readFileSync23 } from "fs";
18956
+ import { readFileSync as readFileSync24 } from "fs";
19045
18957
 
19046
18958
  // app/lib/dns-label.ts
19047
18959
  var VALID_LABEL = /^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$/;
@@ -19057,14 +18969,14 @@ function isValidDomain(value) {
19057
18969
  }
19058
18970
 
19059
18971
  // app/lib/alias-domains.ts
19060
- import { existsSync as existsSync22, mkdirSync as mkdirSync12, readFileSync as readFileSync22, writeFileSync as writeFileSync14 } from "fs";
18972
+ import { existsSync as existsSync22, mkdirSync as mkdirSync12, readFileSync as readFileSync23, writeFileSync as writeFileSync14 } from "fs";
19061
18973
  import { dirname as dirname9 } from "path";
19062
18974
  import { resolve as resolve23 } from "path";
19063
18975
  var ALIAS_DOMAINS_PATH = resolve23(MAXY_DIR, "alias-domains.json");
19064
18976
  function readExisting() {
19065
18977
  if (!existsSync22(ALIAS_DOMAINS_PATH)) return /* @__PURE__ */ new Set();
19066
18978
  try {
19067
- const parsed = JSON.parse(readFileSync22(ALIAS_DOMAINS_PATH, "utf-8"));
18979
+ const parsed = JSON.parse(readFileSync23(ALIAS_DOMAINS_PATH, "utf-8"));
19068
18980
  if (!Array.isArray(parsed)) return /* @__PURE__ */ new Set();
19069
18981
  return new Set(parsed.filter((h) => typeof h === "string"));
19070
18982
  } catch {
@@ -19086,7 +18998,7 @@ function loadBrandInfo() {
19086
18998
  const platformRoot2 = process.env.MAXY_PLATFORM_ROOT ?? resolve24(process.cwd(), "..");
19087
18999
  const brandPath = resolve24(platformRoot2, "config", "brand.json");
19088
19000
  try {
19089
- const parsed = JSON.parse(readFileSync23(brandPath, "utf-8"));
19001
+ const parsed = JSON.parse(readFileSync24(brandPath, "utf-8"));
19090
19002
  const hostname2 = typeof parsed.hostname === "string" && parsed.hostname ? parsed.hostname : "maxy";
19091
19003
  const configDir2 = typeof parsed.configDir === "string" && parsed.configDir ? parsed.configDir : ".maxy";
19092
19004
  return { hostname: hostname2, configDir: configDir2 };
@@ -19129,7 +19041,7 @@ function validateBody(body) {
19129
19041
  }
19130
19042
  return null;
19131
19043
  }
19132
- var app24 = new Hono2();
19044
+ var app23 = new Hono2();
19133
19045
  function fieldFromReason(reason) {
19134
19046
  switch (reason) {
19135
19047
  case "not-signed-in":
@@ -19146,7 +19058,7 @@ function fieldFromReason(reason) {
19146
19058
  return "script";
19147
19059
  }
19148
19060
  }
19149
- app24.get("/domains", requireAdminSession, async (c) => {
19061
+ app23.get("/domains", requireAdminSession, async (c) => {
19150
19062
  const started = Date.now();
19151
19063
  const sessionKey = c.var.sessionKey;
19152
19064
  let correlationId;
@@ -19239,7 +19151,7 @@ ${result.stderr}` : ""}`;
19239
19151
  );
19240
19152
  return c.json(success, 200);
19241
19153
  });
19242
- app24.post("/setup", requireAdminSession, async (c) => {
19154
+ app23.post("/setup", requireAdminSession, async (c) => {
19243
19155
  const started = Date.now();
19244
19156
  const sessionKey = c.var.sessionKey;
19245
19157
  let correlationId;
@@ -19377,7 +19289,7 @@ ${result.stderr}` : ""}`;
19377
19289
  log2(`phase=response-sent`);
19378
19290
  return resp;
19379
19291
  });
19380
- var cloudflare_default = app24;
19292
+ var cloudflare_default = app23;
19381
19293
 
19382
19294
  // server/routes/admin/files.ts
19383
19295
  import { createReadStream as createReadStream3 } from "fs";
@@ -19489,25 +19401,72 @@ async function trashNode(params) {
19489
19401
  const setNullClauses = Object.keys(originalKeys).map((k) => `n.\`${k}\` = null`).join(", ");
19490
19402
  const setNullSuffix = setNullClauses ? `, ${setNullClauses}` : "";
19491
19403
  const trashedAt = (/* @__PURE__ */ new Date()).toISOString();
19492
- await session.run(
19493
- `MATCH (n) WHERE elementId(n) = $eid
19494
- SET n:Trashed,
19495
- n.trashedAt = datetime($trashedAt),
19496
- n.trashedBy = $by,
19497
- n.trashReason = $reason,
19498
- n._trashedKeys = $trashedKeysJson${setNullSuffix}`,
19499
- {
19500
- eid: elementId,
19501
- trashedAt,
19502
- by,
19503
- reason: reason ?? null,
19504
- trashedKeysJson: JSON.stringify(originalKeys)
19404
+ const isConversation = baseLabels.includes("Conversation");
19405
+ const messageUniqueKeys = UNIQUE_KEYS_BY_LABEL["Message"] ?? [];
19406
+ let cascadedMessageCount = 0;
19407
+ await session.executeWrite(async (tx) => {
19408
+ await tx.run(
19409
+ `MATCH (n) WHERE elementId(n) = $eid
19410
+ SET n:Trashed,
19411
+ n.trashedAt = datetime($trashedAt),
19412
+ n.trashedBy = $by,
19413
+ n.trashReason = $reason,
19414
+ n._trashedKeys = $trashedKeysJson${setNullSuffix}`,
19415
+ {
19416
+ eid: elementId,
19417
+ trashedAt,
19418
+ by,
19419
+ reason: reason ?? null,
19420
+ trashedKeysJson: JSON.stringify(originalKeys)
19421
+ }
19422
+ );
19423
+ if (isConversation) {
19424
+ const collectKeys = messageUniqueKeys.length > 0 ? messageUniqueKeys.map((k) => `\`${k}\`: m.\`${k}\``).join(", ") : "";
19425
+ const collectMsgProps = collectKeys ? `, {${collectKeys}}` : ", {}";
19426
+ const collected = await tx.run(
19427
+ `MATCH (c) WHERE elementId(c) = $eid
19428
+ MATCH (m:Message)-[:PART_OF]->(c)
19429
+ WHERE NOT m:Trashed
19430
+ RETURN elementId(m) AS meid${collectMsgProps} AS keys`,
19431
+ { eid: elementId }
19432
+ );
19433
+ for (const rec of collected.records) {
19434
+ const meid = rec.get("meid");
19435
+ const keys = rec.get("keys");
19436
+ const liveKeys = {};
19437
+ for (const k of messageUniqueKeys) {
19438
+ if (keys[k] !== void 0 && keys[k] !== null) liveKeys[k] = keys[k];
19439
+ }
19440
+ const msgSetNulls = Object.keys(liveKeys).length > 0 ? ", " + Object.keys(liveKeys).map((k) => `m.\`${k}\` = null`).join(", ") : "";
19441
+ await tx.run(
19442
+ `MATCH (m) WHERE elementId(m) = $meid
19443
+ SET m:Trashed,
19444
+ m.trashedAt = datetime($trashedAt),
19445
+ m.trashedBy = $by,
19446
+ m.trashReason = $reason,
19447
+ m._trashedKeys = $trashedKeysJson${msgSetNulls}`,
19448
+ {
19449
+ meid,
19450
+ trashedAt,
19451
+ by: `${by}:cascade-from-conversation`,
19452
+ reason: reason ?? `cascade from Conversation ${elementId}`,
19453
+ trashedKeysJson: JSON.stringify(liveKeys)
19454
+ }
19455
+ );
19456
+ }
19457
+ cascadedMessageCount = collected.records.length;
19505
19458
  }
19506
- );
19459
+ });
19507
19460
  process.stderr.write(
19508
19461
  `[trash:marked] accountId=${accountId} elementId=${elementId} labels=${baseLabels.join(",")} by=${by} reason=${reason ?? "null"}
19509
19462
  `
19510
19463
  );
19464
+ if (isConversation) {
19465
+ process.stderr.write(
19466
+ `[trash:cascaded] accountId=${accountId} conversationElementId=${elementId} messageCount=${cascadedMessageCount} by=${by}
19467
+ `
19468
+ );
19469
+ }
19511
19470
  return {
19512
19471
  trashed: true,
19513
19472
  alreadyTrashed: false,
@@ -19558,16 +19517,50 @@ async function restoreNode(params) {
19558
19517
  const setSuffix = setClauses ? `, ${setClauses}` : "";
19559
19518
  const setParams = { eid: elementId };
19560
19519
  for (const [k, v] of Object.entries(originalKeys)) setParams[`val_${k}`] = v;
19561
- await session.run(
19562
- `MATCH (n:Trashed) WHERE elementId(n) = $eid
19563
- REMOVE n:Trashed, n.trashedAt, n.trashedBy, n.trashReason, n._trashedKeys
19564
- SET n.restoredAt = datetime()${setSuffix}`,
19565
- setParams
19566
- );
19520
+ const isConversation = baseLabels.includes("Conversation");
19521
+ let cascadedMessageCount = 0;
19522
+ await session.executeWrite(async (tx) => {
19523
+ await tx.run(
19524
+ `MATCH (n:Trashed) WHERE elementId(n) = $eid
19525
+ REMOVE n:Trashed, n.trashedAt, n.trashedBy, n.trashReason, n._trashedKeys
19526
+ SET n.restoredAt = datetime()${setSuffix}`,
19527
+ setParams
19528
+ );
19529
+ if (isConversation) {
19530
+ const collected = await tx.run(
19531
+ `MATCH (c) WHERE elementId(c) = $eid
19532
+ MATCH (m:Trashed:Message)-[:PART_OF]->(c)
19533
+ WHERE m.trashedBy ENDS WITH ':cascade-from-conversation'
19534
+ RETURN elementId(m) AS meid, m._trashedKeys AS keysJson`,
19535
+ { eid: elementId }
19536
+ );
19537
+ for (const rec of collected.records) {
19538
+ const meid = rec.get("meid");
19539
+ const keysJson2 = rec.get("keysJson");
19540
+ const keys = keysJson2 ? JSON.parse(keysJson2) : {};
19541
+ const setClause = Object.keys(keys).length > 0 ? ", " + Object.keys(keys).map((k) => `m.\`${k}\` = $val_${k}`).join(", ") : "";
19542
+ const msgParams = { meid };
19543
+ for (const [k, v] of Object.entries(keys)) msgParams[`val_${k}`] = v;
19544
+ await tx.run(
19545
+ `MATCH (m) WHERE elementId(m) = $meid
19546
+ REMOVE m:Trashed, m.trashedAt, m.trashedBy, m.trashReason, m._trashedKeys
19547
+ SET m.restoredAt = datetime()${setClause}`,
19548
+ msgParams
19549
+ );
19550
+ }
19551
+ cascadedMessageCount = collected.records.length;
19552
+ }
19553
+ });
19567
19554
  process.stderr.write(
19568
19555
  `[trash:restored] accountId=${accountId} elementId=${elementId} labels=${baseLabels.join(",")}
19569
19556
  `
19570
19557
  );
19558
+ if (isConversation) {
19559
+ process.stderr.write(
19560
+ `[trash:cascade-restored] accountId=${accountId} conversationElementId=${elementId} messageCount=${cascadedMessageCount}
19561
+ `
19562
+ );
19563
+ }
19571
19564
  return {
19572
19565
  restored: true,
19573
19566
  nodeId: elementId,
@@ -19729,8 +19722,8 @@ function buildDisplayPath(relPath, accountNames) {
19729
19722
  return dn ? { name: seg, displayName: dn } : { name: seg };
19730
19723
  });
19731
19724
  }
19732
- var app25 = new Hono2();
19733
- app25.get("/", requireAdminSession, async (c) => {
19725
+ var app24 = new Hono2();
19726
+ app24.get("/", requireAdminSession, async (c) => {
19734
19727
  const sessionKey = c.var.sessionKey;
19735
19728
  if (!getAccountIdForSession(sessionKey)) {
19736
19729
  console.error(`[data] auth-rejected endpoint="/api/admin/files" reason="no account for session"`);
@@ -19791,7 +19784,7 @@ app25.get("/", requireAdminSession, async (c) => {
19791
19784
  return c.json({ error: message }, 500);
19792
19785
  }
19793
19786
  });
19794
- app25.get("/download", requireAdminSession, async (c) => {
19787
+ app24.get("/download", requireAdminSession, async (c) => {
19795
19788
  const sessionKey = c.var.sessionKey;
19796
19789
  if (!getAccountIdForSession(sessionKey)) {
19797
19790
  console.error(`[data] auth-rejected endpoint="/api/admin/files/download" reason="no account for session"`);
@@ -19839,7 +19832,7 @@ app25.get("/download", requireAdminSession, async (c) => {
19839
19832
  return c.json({ error: message }, 500);
19840
19833
  }
19841
19834
  });
19842
- app25.post("/upload", requireAdminSession, async (c) => {
19835
+ app24.post("/upload", requireAdminSession, async (c) => {
19843
19836
  const sessionKey = c.var.sessionKey;
19844
19837
  const accountId = getAccountIdForSession(sessionKey);
19845
19838
  if (!accountId) {
@@ -19897,7 +19890,7 @@ app25.post("/upload", requireAdminSession, async (c) => {
19897
19890
  mimeType: file.type
19898
19891
  });
19899
19892
  });
19900
- app25.delete("/", requireAdminSession, async (c) => {
19893
+ app24.delete("/", requireAdminSession, async (c) => {
19901
19894
  const sessionKey = c.var.sessionKey;
19902
19895
  const accountId = getAccountIdForSession(sessionKey);
19903
19896
  if (!accountId) {
@@ -19964,13 +19957,13 @@ app25.delete("/", requireAdminSession, async (c) => {
19964
19957
  return c.json({ error: message }, 500);
19965
19958
  }
19966
19959
  });
19967
- var files_default = app25;
19960
+ var files_default = app24;
19968
19961
 
19969
19962
  // server/routes/admin/graph-search.ts
19970
19963
  var DEFAULT_LIMIT = 20;
19971
19964
  var MAX_LIMIT = 100;
19972
- var app26 = new Hono2();
19973
- app26.get("/", requireAdminSession, async (c) => {
19965
+ var app25 = new Hono2();
19966
+ app25.get("/", requireAdminSession, async (c) => {
19974
19967
  const sessionKey = c.var.sessionKey;
19975
19968
  const q = (c.req.query("q") ?? "").trim();
19976
19969
  const rawLimit = c.req.query("limit");
@@ -19995,7 +19988,7 @@ app26.get("/", requireAdminSession, async (c) => {
19995
19988
  return c.json({ error: `Graph search unavailable: ${message}` }, 503);
19996
19989
  }
19997
19990
  });
19998
- var graph_search_default = app26;
19991
+ var graph_search_default = app25;
19999
19992
 
20000
19993
  // server/routes/admin/graph-subgraph.ts
20001
19994
  import neo4j3 from "neo4j-driver";
@@ -20124,8 +20117,8 @@ var STRIPPED_PROPERTIES = /* @__PURE__ */ new Set([
20124
20117
  "otpCode",
20125
20118
  "sessionKey"
20126
20119
  ]);
20127
- var app27 = new Hono2();
20128
- app27.get("/", requireAdminSession, async (c) => {
20120
+ var app26 = new Hono2();
20121
+ app26.get("/", requireAdminSession, async (c) => {
20129
20122
  const sessionKey = c.var.sessionKey;
20130
20123
  const accountId = getAccountIdForSession(sessionKey);
20131
20124
  if (!accountId) {
@@ -20401,12 +20394,12 @@ function pruneNode(node, warnedClasses) {
20401
20394
  const labels = (node.labels ?? []).filter((l) => l !== "Trashed");
20402
20395
  return trashed ? { id: node.id, labels, properties, trashed: true } : { id: node.id, labels, properties };
20403
20396
  }
20404
- var graph_subgraph_default = app27;
20397
+ var graph_subgraph_default = app26;
20405
20398
 
20406
20399
  // server/routes/admin/graph-delete.ts
20407
20400
  var ALLOWED_BY = ["graph-page", "graph-drag-trash"];
20408
- var app28 = new Hono2();
20409
- app28.post("/", requireAdminSession, async (c) => {
20401
+ var app27 = new Hono2();
20402
+ app27.post("/", requireAdminSession, async (c) => {
20410
20403
  const sessionKey = c.var.sessionKey;
20411
20404
  const accountId = getAccountIdForSession(sessionKey);
20412
20405
  if (!accountId) {
@@ -20477,11 +20470,11 @@ app28.post("/", requireAdminSession, async (c) => {
20477
20470
  }
20478
20471
  }
20479
20472
  });
20480
- var graph_delete_default = app28;
20473
+ var graph_delete_default = app27;
20481
20474
 
20482
20475
  // server/routes/admin/graph-restore.ts
20483
- var app29 = new Hono2();
20484
- app29.post("/", requireAdminSession, async (c) => {
20476
+ var app28 = new Hono2();
20477
+ app28.post("/", requireAdminSession, async (c) => {
20485
20478
  const sessionKey = c.var.sessionKey;
20486
20479
  const accountId = getAccountIdForSession(sessionKey);
20487
20480
  if (!accountId) {
@@ -20545,11 +20538,11 @@ app29.post("/", requireAdminSession, async (c) => {
20545
20538
  }
20546
20539
  }
20547
20540
  });
20548
- var graph_restore_default = app29;
20541
+ var graph_restore_default = app28;
20549
20542
 
20550
20543
  // server/routes/admin/graph-labels-in-graph.ts
20551
- var app30 = new Hono2();
20552
- app30.get("/", requireAdminSession, async (c) => {
20544
+ var app29 = new Hono2();
20545
+ app29.get("/", requireAdminSession, async (c) => {
20553
20546
  const sessionKey = c.var.sessionKey;
20554
20547
  const accountId = getAccountIdForSession(sessionKey);
20555
20548
  if (!accountId) {
@@ -20610,11 +20603,11 @@ var LABELS_IN_GRAPH_CYPHER = `
20610
20603
  sum(halfEdges) AS relDegree
20611
20604
  RETURN label, nodeCount, relDegree
20612
20605
  `;
20613
- var graph_labels_in_graph_default = app30;
20606
+ var graph_labels_in_graph_default = app29;
20614
20607
 
20615
20608
  // server/routes/admin/graph-default-view.ts
20616
- var app31 = new Hono2();
20617
- app31.get("/", requireAdminSession, async (c) => {
20609
+ var app30 = new Hono2();
20610
+ app30.get("/", requireAdminSession, async (c) => {
20618
20611
  const sessionKey = c.var.sessionKey;
20619
20612
  const accountId = getAccountIdForSession(sessionKey);
20620
20613
  const userId = getUserIdForSession(sessionKey);
@@ -20652,7 +20645,7 @@ app31.get("/", requireAdminSession, async (c) => {
20652
20645
  }
20653
20646
  }
20654
20647
  });
20655
- app31.put("/", requireAdminSession, async (c) => {
20648
+ app30.put("/", requireAdminSession, async (c) => {
20656
20649
  const sessionKey = c.var.sessionKey;
20657
20650
  const accountId = getAccountIdForSession(sessionKey);
20658
20651
  const userId = getUserIdForSession(sessionKey);
@@ -20734,11 +20727,11 @@ var WRITE_CYPHER = `
20734
20727
  p.updatedAt = $updatedAt
20735
20728
  RETURN p.labels AS labels
20736
20729
  `;
20737
- var graph_default_view_default = app31;
20730
+ var graph_default_view_default = app30;
20738
20731
 
20739
20732
  // server/routes/admin/file-attach.ts
20740
- var app32 = new Hono2();
20741
- app32.post("/", async (c) => {
20733
+ var app31 = new Hono2();
20734
+ app31.post("/", async (c) => {
20742
20735
  try {
20743
20736
  const body = await c.req.json();
20744
20737
  const { filePath, accountId } = body;
@@ -20761,11 +20754,11 @@ app32.post("/", async (c) => {
20761
20754
  return c.json({ error: message }, 500);
20762
20755
  }
20763
20756
  });
20764
- var file_attach_default = app32;
20757
+ var file_attach_default = app31;
20765
20758
 
20766
20759
  // server/routes/admin/adherence.ts
20767
- var app33 = new Hono2();
20768
- app33.get("/", requireAdminSession, async (c) => {
20760
+ var app32 = new Hono2();
20761
+ app32.get("/", requireAdminSession, async (c) => {
20769
20762
  const agent = c.req.query("agent") ?? "admin";
20770
20763
  const includeBlock = c.req.query("block") === "1";
20771
20764
  const account = resolveAccount();
@@ -20786,35 +20779,34 @@ app33.get("/", requireAdminSession, async (c) => {
20786
20779
  return c.json({ error: "Failed to read adherence ledger", agent }, 500);
20787
20780
  }
20788
20781
  });
20789
- var adherence_default = app33;
20782
+ var adherence_default = app32;
20790
20783
 
20791
20784
  // server/routes/admin/index.ts
20792
- var app34 = new Hono2();
20793
- app34.route("/session", session_default2);
20794
- app34.route("/chat", chat_default2);
20795
- app34.route("/compact", compact_default);
20796
- app34.route("/logs", logs_default);
20797
- app34.route("/claude-info", claude_info_default);
20798
- app34.route("/attachment", attachment_default);
20799
- app34.route("/account", account_default);
20800
- app34.route("/agents", agents_default);
20801
- app34.route("/version", version_default);
20802
- app34.route("/sessions", sessions_default);
20803
- app34.route("/browser", browser_default);
20804
- app34.route("/terminal", terminal_default);
20805
- app34.route("/device-browser", device_browser_default);
20806
- app34.route("/events", events_default);
20807
- app34.route("/cloudflare", cloudflare_default);
20808
- app34.route("/files", files_default);
20809
- app34.route("/graph-search", graph_search_default);
20810
- app34.route("/graph-subgraph", graph_subgraph_default);
20811
- app34.route("/graph-delete", graph_delete_default);
20812
- app34.route("/graph-restore", graph_restore_default);
20813
- app34.route("/graph-labels-in-graph", graph_labels_in_graph_default);
20814
- app34.route("/graph-default-view", graph_default_view_default);
20815
- app34.route("/file-attach", file_attach_default);
20816
- app34.route("/adherence", adherence_default);
20817
- var admin_default = app34;
20785
+ var app33 = new Hono2();
20786
+ app33.route("/session", session_default2);
20787
+ app33.route("/chat", chat_default2);
20788
+ app33.route("/compact", compact_default);
20789
+ app33.route("/logs", logs_default);
20790
+ app33.route("/claude-info", claude_info_default);
20791
+ app33.route("/attachment", attachment_default);
20792
+ app33.route("/account", account_default);
20793
+ app33.route("/agents", agents_default);
20794
+ app33.route("/version", version_default);
20795
+ app33.route("/sessions", sessions_default);
20796
+ app33.route("/browser", browser_default);
20797
+ app33.route("/device-browser", device_browser_default);
20798
+ app33.route("/events", events_default);
20799
+ app33.route("/cloudflare", cloudflare_default);
20800
+ app33.route("/files", files_default);
20801
+ app33.route("/graph-search", graph_search_default);
20802
+ app33.route("/graph-subgraph", graph_subgraph_default);
20803
+ app33.route("/graph-delete", graph_delete_default);
20804
+ app33.route("/graph-restore", graph_restore_default);
20805
+ app33.route("/graph-labels-in-graph", graph_labels_in_graph_default);
20806
+ app33.route("/graph-default-view", graph_default_view_default);
20807
+ app33.route("/file-attach", file_attach_default);
20808
+ app33.route("/adherence", adherence_default);
20809
+ var admin_default = app33;
20818
20810
 
20819
20811
  // server/index.ts
20820
20812
  var PLATFORM_ROOT10 = process.env.MAXY_PLATFORM_ROOT || "";
@@ -20825,7 +20817,7 @@ if (BRAND_JSON_PATH && !existsSync23(BRAND_JSON_PATH)) {
20825
20817
  }
20826
20818
  if (BRAND_JSON_PATH && existsSync23(BRAND_JSON_PATH)) {
20827
20819
  try {
20828
- const parsed = JSON.parse(readFileSync24(BRAND_JSON_PATH, "utf-8"));
20820
+ const parsed = JSON.parse(readFileSync25(BRAND_JSON_PATH, "utf-8"));
20829
20821
  BRAND = { ...BRAND, ...parsed };
20830
20822
  } catch (err) {
20831
20823
  console.error(`[brand] Failed to parse brand.json: ${err.message}`);
@@ -20848,7 +20840,7 @@ var ALIAS_DOMAINS_PATH2 = join12(homedir4(), BRAND.configDir, "alias-domains.jso
20848
20840
  function loadAliasDomains() {
20849
20841
  try {
20850
20842
  if (!existsSync23(ALIAS_DOMAINS_PATH2)) return null;
20851
- const parsed = JSON.parse(readFileSync24(ALIAS_DOMAINS_PATH2, "utf-8"));
20843
+ const parsed = JSON.parse(readFileSync25(ALIAS_DOMAINS_PATH2, "utf-8"));
20852
20844
  if (!Array.isArray(parsed)) {
20853
20845
  console.error("[alias-domains] malformed alias-domains.json \u2014 expected array");
20854
20846
  return null;
@@ -20872,9 +20864,9 @@ watchFile(ALIAS_DOMAINS_PATH2, { interval: 2e3 }, () => {
20872
20864
  function isPublicHost(host) {
20873
20865
  return host.startsWith("public.") || aliasDomains.has(host);
20874
20866
  }
20875
- var app35 = new Hono2();
20876
- app35.use("*", clientIpMiddleware);
20877
- app35.use("*", async (c, next) => {
20867
+ var app34 = new Hono2();
20868
+ app34.use("*", clientIpMiddleware);
20869
+ app34.use("*", async (c, next) => {
20878
20870
  await next();
20879
20871
  c.header("X-Content-Type-Options", "nosniff");
20880
20872
  c.header("Referrer-Policy", "strict-origin-when-cross-origin");
@@ -20897,7 +20889,7 @@ var PUBLIC_ALLOWED_PREFIXES = [
20897
20889
  "/g/"
20898
20890
  ];
20899
20891
  var PUBLIC_ALLOWED_EXACT = ["/favicon.ico"];
20900
- app35.use("*", async (c, next) => {
20892
+ app34.use("*", async (c, next) => {
20901
20893
  const host = (c.req.header("host") ?? "").split(":")[0];
20902
20894
  if (!isPublicHost(host)) {
20903
20895
  await next();
@@ -20937,7 +20929,7 @@ function resolveRemoteAuthOpts() {
20937
20929
  return brandLoginOpts;
20938
20930
  }
20939
20931
  var MAX_LOGIN_BODY = 8 * 1024;
20940
- app35.post("/__remote-auth/login", async (c) => {
20932
+ app34.post("/__remote-auth/login", async (c) => {
20941
20933
  const clientIp = c.var.clientIp || "unknown";
20942
20934
  const rateLimited = checkRateLimit(clientIp);
20943
20935
  if (rateLimited) {
@@ -20973,10 +20965,7 @@ app35.post("/__remote-auth/login", async (c) => {
20973
20965
  }
20974
20966
  });
20975
20967
  });
20976
- app35.get("/__remote-auth/logout", (c) => {
20977
- const cookieHeader = c.req.header("cookie");
20978
- const token = parseCookie(cookieHeader, "__remote_session");
20979
- if (token) invalidateRemoteSession(token);
20968
+ app34.get("/__remote-auth/logout", () => {
20980
20969
  return new Response(null, {
20981
20970
  status: 302,
20982
20971
  headers: {
@@ -20986,7 +20975,7 @@ app35.get("/__remote-auth/logout", (c) => {
20986
20975
  }
20987
20976
  });
20988
20977
  });
20989
- app35.post("/__remote-auth/change-password", async (c) => {
20978
+ app34.post("/__remote-auth/change-password", async (c) => {
20990
20979
  const clientIp = c.var.clientIp || "unknown";
20991
20980
  const rateLimited = checkRateLimit(clientIp);
20992
20981
  if (rateLimited) {
@@ -21035,13 +21024,13 @@ app35.post("/__remote-auth/change-password", async (c) => {
21035
21024
  return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(), mode: "change", changeError: "Failed to save password", redirect }), 200);
21036
21025
  }
21037
21026
  });
21038
- app35.get("/__remote-auth/setup", (c) => {
21027
+ app34.get("/__remote-auth/setup", (c) => {
21039
21028
  if (isRemoteAuthConfigured()) {
21040
21029
  return c.redirect("/");
21041
21030
  }
21042
21031
  return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(), mode: "setup" }), 200);
21043
21032
  });
21044
- app35.post("/__remote-auth/set-initial-password", async (c) => {
21033
+ app34.post("/__remote-auth/set-initial-password", async (c) => {
21045
21034
  if (isRemoteAuthConfigured()) {
21046
21035
  return c.redirect("/");
21047
21036
  }
@@ -21077,10 +21066,10 @@ app35.post("/__remote-auth/set-initial-password", async (c) => {
21077
21066
  return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(), mode: "setup", setupError: "Failed to save password. Please try again." }), 200);
21078
21067
  }
21079
21068
  });
21080
- app35.get("/api/remote-auth/status", (c) => {
21069
+ app34.get("/api/remote-auth/status", (c) => {
21081
21070
  return c.json({ configured: isRemoteAuthConfigured() });
21082
21071
  });
21083
- app35.post("/api/remote-auth/set-password", async (c) => {
21072
+ app34.post("/api/remote-auth/set-password", async (c) => {
21084
21073
  let body;
21085
21074
  try {
21086
21075
  body = await c.req.json();
@@ -21110,9 +21099,9 @@ app35.post("/api/remote-auth/set-password", async (c) => {
21110
21099
  return c.json({ error: "Failed to save password" }, 500);
21111
21100
  }
21112
21101
  });
21113
- app35.route("/api/_client-error", client_error_default);
21102
+ app34.route("/api/_client-error", client_error_default);
21114
21103
  console.log("[client-error-route] mounted");
21115
- app35.use("*", async (c, next) => {
21104
+ app34.use("*", async (c, next) => {
21116
21105
  const host = (c.req.header("host") ?? "").split(":")[0];
21117
21106
  const path2 = c.req.path;
21118
21107
  if (path2 === "/favicon.ico" || path2.startsWith("/assets/") || path2.startsWith("/brand/")) {
@@ -21142,25 +21131,15 @@ app35.use("*", async (c, next) => {
21142
21131
  console.error(`[remote-auth] login required ip=${clientIp} path=${path2}`);
21143
21132
  return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(), redirect: path2 }), 200);
21144
21133
  });
21145
- function parseCookie(cookieHeader, name) {
21146
- if (!cookieHeader) return null;
21147
- const match2 = cookieHeader.match(new RegExp(`(?:^|;\\s*)${name}=([^;]*)`));
21148
- if (!match2) return null;
21149
- try {
21150
- return decodeURIComponent(match2[1]);
21151
- } catch {
21152
- return null;
21153
- }
21154
- }
21155
- app35.route("/api/health", health_default);
21156
- app35.route("/api/session", session_default);
21157
- app35.route("/api/chat", chat_default);
21158
- app35.route("/api/group", group_default);
21159
- app35.route("/api/access", access_default);
21160
- app35.route("/api/telegram", telegram_default);
21161
- app35.route("/api/whatsapp", whatsapp_default);
21162
- app35.route("/api/onboarding", onboarding_default);
21163
- app35.route("/api/admin", admin_default);
21134
+ app34.route("/api/health", health_default);
21135
+ app34.route("/api/session", session_default);
21136
+ app34.route("/api/chat", chat_default);
21137
+ app34.route("/api/group", group_default);
21138
+ app34.route("/api/access", access_default);
21139
+ app34.route("/api/telegram", telegram_default);
21140
+ app34.route("/api/whatsapp", whatsapp_default);
21141
+ app34.route("/api/onboarding", onboarding_default);
21142
+ app34.route("/api/admin", admin_default);
21164
21143
  var SAFE_SLUG_RE = /^[a-z][a-z0-9-]{2,49}$/;
21165
21144
  var SAFE_FILENAME_RE = /^[a-z0-9_][a-z0-9_.-]{0,99}$/i;
21166
21145
  var IMAGE_MIME = {
@@ -21172,7 +21151,7 @@ var IMAGE_MIME = {
21172
21151
  ".svg": "image/svg+xml",
21173
21152
  ".ico": "image/x-icon"
21174
21153
  };
21175
- app35.get("/agent-assets/:slug/:filename", (c) => {
21154
+ app34.get("/agent-assets/:slug/:filename", (c) => {
21176
21155
  const slug = c.req.param("slug");
21177
21156
  const filename = c.req.param("filename");
21178
21157
  if (!SAFE_SLUG_RE.test(slug)) {
@@ -21201,13 +21180,13 @@ app35.get("/agent-assets/:slug/:filename", (c) => {
21201
21180
  const ext = "." + filename.split(".").pop()?.toLowerCase();
21202
21181
  const contentType = IMAGE_MIME[ext] || "application/octet-stream";
21203
21182
  console.log(`[agent-assets] serve slug=${slug} file=${filename} status=200`);
21204
- const body = readFileSync24(filePath);
21183
+ const body = readFileSync25(filePath);
21205
21184
  return c.body(body, 200, {
21206
21185
  "Content-Type": contentType,
21207
21186
  "Cache-Control": "public, max-age=3600"
21208
21187
  });
21209
21188
  });
21210
- app35.get("/generated/:filename", (c) => {
21189
+ app34.get("/generated/:filename", (c) => {
21211
21190
  const filename = c.req.param("filename");
21212
21191
  if (!SAFE_FILENAME_RE.test(filename) || filename.includes("..")) {
21213
21192
  console.error(`[generated] serve file=${filename} status=403`);
@@ -21231,7 +21210,7 @@ app35.get("/generated/:filename", (c) => {
21231
21210
  const ext = "." + filename.split(".").pop()?.toLowerCase();
21232
21211
  const contentType = IMAGE_MIME[ext] || "application/octet-stream";
21233
21212
  console.log(`[generated] serve file=${filename} status=200`);
21234
- const body = readFileSync24(filePath);
21213
+ const body = readFileSync25(filePath);
21235
21214
  return c.body(body, 200, {
21236
21215
  "Content-Type": contentType,
21237
21216
  "Cache-Control": "public, max-age=86400"
@@ -21242,7 +21221,7 @@ var brandLogoPath = "/brand/maxy-monochrome.png";
21242
21221
  var brandIconPath = "/brand/maxy-monochrome.png";
21243
21222
  if (BRAND_JSON_PATH && existsSync23(BRAND_JSON_PATH)) {
21244
21223
  try {
21245
- const fullBrand = JSON.parse(readFileSync24(BRAND_JSON_PATH, "utf-8"));
21224
+ const fullBrand = JSON.parse(readFileSync25(BRAND_JSON_PATH, "utf-8"));
21246
21225
  if (fullBrand.assets?.logo) brandLogoPath = `/brand/${fullBrand.assets.logo}`;
21247
21226
  brandIconPath = fullBrand.assets?.icon ? `/brand/${fullBrand.assets.icon}` : brandLogoPath;
21248
21227
  } catch {
@@ -21261,7 +21240,7 @@ function readInstalledVersion() {
21261
21240
  if (!PLATFORM_ROOT10) return "unknown";
21262
21241
  const versionFile = join12(PLATFORM_ROOT10, "config", `.${BRAND.hostname}-version`);
21263
21242
  if (!existsSync23(versionFile)) return "unknown";
21264
- const content = readFileSync24(versionFile, "utf-8").trim();
21243
+ const content = readFileSync25(versionFile, "utf-8").trim();
21265
21244
  return content || "unknown";
21266
21245
  } catch {
21267
21246
  return "unknown";
@@ -21302,7 +21281,7 @@ var clientErrorReporterScript = `<script>
21302
21281
  function cachedHtml(file) {
21303
21282
  let html = htmlCache.get(file);
21304
21283
  if (!html) {
21305
- html = readFileSync24(resolve27(process.cwd(), "public", file), "utf-8");
21284
+ html = readFileSync25(resolve27(process.cwd(), "public", file), "utf-8");
21306
21285
  html = html.replace("<title>Maxy</title>", `<title>${escapeHtml(BRAND.productName)}</title>`);
21307
21286
  html = html.replace('href="/favicon.ico"', `href="${escapeHtml(brandFaviconPath)}"`);
21308
21287
  const headInjection = file === "index.html" ? `${brandScript}
@@ -21321,12 +21300,12 @@ function loadBrandingCache(agentSlug) {
21321
21300
  try {
21322
21301
  const accountJsonPath = join12(configDir2, "account.json");
21323
21302
  if (!existsSync23(accountJsonPath)) return null;
21324
- const account = JSON.parse(readFileSync24(accountJsonPath, "utf-8"));
21303
+ const account = JSON.parse(readFileSync25(accountJsonPath, "utf-8"));
21325
21304
  const accountId = account.accountId;
21326
21305
  if (!accountId) return null;
21327
21306
  const cachePath = join12(configDir2, "branding-cache", accountId, `${agentSlug}.json`);
21328
21307
  if (!existsSync23(cachePath)) return null;
21329
- return JSON.parse(readFileSync24(cachePath, "utf-8"));
21308
+ return JSON.parse(readFileSync25(cachePath, "utf-8"));
21330
21309
  } catch {
21331
21310
  return null;
21332
21311
  }
@@ -21336,7 +21315,7 @@ function resolveDefaultSlug() {
21336
21315
  const configDir2 = join12(homedir4(), BRAND.configDir);
21337
21316
  const accountJsonPath = join12(configDir2, "account.json");
21338
21317
  if (!existsSync23(accountJsonPath)) return null;
21339
- const account = JSON.parse(readFileSync24(accountJsonPath, "utf-8"));
21318
+ const account = JSON.parse(readFileSync25(accountJsonPath, "utf-8"));
21340
21319
  return account.defaultAgent || null;
21341
21320
  } catch {
21342
21321
  return null;
@@ -21372,7 +21351,7 @@ function brandedPublicHtml(agentSlug) {
21372
21351
  function escapeHtml(s) {
21373
21352
  return s.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
21374
21353
  }
21375
- app35.get("/", (c) => {
21354
+ app34.get("/", (c) => {
21376
21355
  const host = (c.req.header("host") ?? "").split(":")[0];
21377
21356
  if (isPublicHost(host)) {
21378
21357
  const defaultSlug = resolveDefaultSlug();
@@ -21380,12 +21359,12 @@ app35.get("/", (c) => {
21380
21359
  }
21381
21360
  return c.html(cachedHtml("index.html"));
21382
21361
  });
21383
- app35.get("/public", (c) => {
21362
+ app34.get("/public", (c) => {
21384
21363
  const host = (c.req.header("host") ?? "").split(":")[0];
21385
21364
  if (isPublicHost(host)) return c.text("Not found", 404);
21386
21365
  return c.html(cachedHtml("public.html"));
21387
21366
  });
21388
- app35.get("/chat", (c) => {
21367
+ app34.get("/chat", (c) => {
21389
21368
  const host = (c.req.header("host") ?? "").split(":")[0];
21390
21369
  if (isPublicHost(host)) return c.text("Not found", 404);
21391
21370
  return c.html(cachedHtml("public.html"));
@@ -21404,12 +21383,12 @@ async function logViewerFetch(c, next) {
21404
21383
  duration_ms: Date.now() - start
21405
21384
  });
21406
21385
  }
21407
- app35.use("/vnc-viewer.html", logViewerFetch);
21408
- app35.use("/vnc-popout.html", logViewerFetch);
21409
- app35.get("/vnc-popout.html", (c) => {
21386
+ app34.use("/vnc-viewer.html", logViewerFetch);
21387
+ app34.use("/vnc-popout.html", logViewerFetch);
21388
+ app34.get("/vnc-popout.html", (c) => {
21410
21389
  let html = htmlCache.get("vnc-popout.html");
21411
21390
  if (!html) {
21412
- html = readFileSync24(resolve27(process.cwd(), "public", "vnc-popout.html"), "utf-8");
21391
+ html = readFileSync25(resolve27(process.cwd(), "public", "vnc-popout.html"), "utf-8");
21413
21392
  const name = escapeHtml(BRAND.productName);
21414
21393
  html = html.replace("<title>Browser \u2014 Maxy</title>", `<title>${name}</title>`);
21415
21394
  html = html.replace("</head>", ` ${brandScript}
@@ -21419,7 +21398,7 @@ app35.get("/vnc-popout.html", (c) => {
21419
21398
  }
21420
21399
  return c.html(html);
21421
21400
  });
21422
- app35.post("/api/vnc/client-event", async (c) => {
21401
+ app34.post("/api/vnc/client-event", async (c) => {
21423
21402
  let body;
21424
21403
  try {
21425
21404
  body = await c.req.json();
@@ -21440,20 +21419,20 @@ app35.post("/api/vnc/client-event", async (c) => {
21440
21419
  });
21441
21420
  return c.json({ ok: true });
21442
21421
  });
21443
- app35.get("/g/:slug", (c) => {
21422
+ app34.get("/g/:slug", (c) => {
21444
21423
  return c.html(brandedPublicHtml());
21445
21424
  });
21446
- app35.get("/graph", (c) => {
21425
+ app34.get("/graph", (c) => {
21447
21426
  const host = (c.req.header("host") ?? "").split(":")[0];
21448
21427
  if (isPublicHost(host)) return c.text("Not found", 404);
21449
21428
  return c.html(cachedHtml("graph.html"));
21450
21429
  });
21451
- app35.get("/data", (c) => {
21430
+ app34.get("/data", (c) => {
21452
21431
  const host = (c.req.header("host") ?? "").split(":")[0];
21453
21432
  if (isPublicHost(host)) return c.text("Not found", 404);
21454
21433
  return c.html(cachedHtml("data.html"));
21455
21434
  });
21456
- app35.get("/:slug", async (c, next) => {
21435
+ app34.get("/:slug", async (c, next) => {
21457
21436
  const slug = c.req.param("slug");
21458
21437
  if (AGENT_SLUG_PATTERN.test(`/${slug}`)) {
21459
21438
  const branding = loadBrandingCache(slug);
@@ -21462,10 +21441,10 @@ app35.get("/:slug", async (c, next) => {
21462
21441
  }
21463
21442
  await next();
21464
21443
  });
21465
- app35.use("/*", serveStatic({ root: "./public" }));
21444
+ app34.use("/*", serveStatic({ root: "./public" }));
21466
21445
  var port = parseInt(process.env.PORT ?? "19199", 10);
21467
21446
  var hostname = process.env.HOSTNAME ?? "127.0.0.1";
21468
- var httpServer = serve({ fetch: app35.fetch, port, hostname });
21447
+ var httpServer = serve({ fetch: app34.fetch, port, hostname });
21469
21448
  console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
21470
21449
  var SUBAPP_MANIFEST = [
21471
21450
  { prefix: "/api/health", file: "server/routes/health.ts", subapp: health_default },
@@ -21485,7 +21464,7 @@ for (const m of SUBAPP_MANIFEST) {
21485
21464
  }
21486
21465
  try {
21487
21466
  const registered = [];
21488
- for (const r of app35.routes ?? []) {
21467
+ for (const r of app34.routes ?? []) {
21489
21468
  if (typeof r.path !== "string" || r.path.includes(":") || r.path.includes("*")) continue;
21490
21469
  if (AGENT_SLUG_PATTERN.test(r.path)) {
21491
21470
  registered.push({ method: (r.method ?? "ALL").toUpperCase(), path: r.path });
@@ -21500,7 +21479,7 @@ try {
21500
21479
  try {
21501
21480
  let userId = "";
21502
21481
  if (existsSync23(USERS_FILE)) {
21503
- const users = JSON.parse(readFileSync24(USERS_FILE, "utf-8").trim() || "[]");
21482
+ const users = JSON.parse(readFileSync25(USERS_FILE, "utf-8").trim() || "[]");
21504
21483
  userId = users[0]?.userId ?? "";
21505
21484
  }
21506
21485
  await backfillNullUserIdConversations(userId);