@rubytech/create-maxy-code 0.1.126 → 0.1.129

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 (29) hide show
  1. package/dist/index.js +15 -0
  2. package/package.json +1 -1
  3. package/payload/platform/config/brand.json +20 -1
  4. package/payload/platform/lib/aeo-llms-txt-writer/dist/index.d.ts +33 -0
  5. package/payload/platform/lib/aeo-llms-txt-writer/dist/index.d.ts.map +1 -0
  6. package/payload/platform/lib/aeo-llms-txt-writer/dist/index.js +119 -0
  7. package/payload/platform/lib/aeo-llms-txt-writer/dist/index.js.map +1 -0
  8. package/payload/platform/lib/aeo-llms-txt-writer/src/index.ts +172 -0
  9. package/payload/platform/lib/aeo-llms-txt-writer/tsconfig.json +8 -0
  10. package/payload/platform/package.json +2 -2
  11. package/payload/platform/plugins/admin/.claude-plugin/plugin.json +1 -1
  12. package/payload/platform/plugins/admin/PLUGIN.md +4 -1
  13. package/payload/platform/plugins/admin/mcp/dist/index.js +73 -0
  14. package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -1
  15. package/payload/platform/plugins/admin/mcp/dist/tools/publish-site.d.ts +34 -0
  16. package/payload/platform/plugins/admin/mcp/dist/tools/publish-site.d.ts.map +1 -0
  17. package/payload/platform/plugins/admin/mcp/dist/tools/publish-site.js +176 -0
  18. package/payload/platform/plugins/admin/mcp/dist/tools/publish-site.js.map +1 -0
  19. package/payload/platform/plugins/admin/skills/publish-site/SKILL.md +19 -57
  20. package/payload/platform/plugins/aeo/mcp/dist/tools/aeo-write-llms-txt.d.ts +5 -38
  21. package/payload/platform/plugins/aeo/mcp/dist/tools/aeo-write-llms-txt.d.ts.map +1 -1
  22. package/payload/platform/plugins/aeo/mcp/dist/tools/aeo-write-llms-txt.js +7 -114
  23. package/payload/platform/plugins/aeo/mcp/dist/tools/aeo-write-llms-txt.js.map +1 -1
  24. package/payload/platform/templates/specialists/agents/content-producer.md +3 -3
  25. package/payload/server/public/consent.css +97 -0
  26. package/payload/server/public/consent.js +259 -0
  27. package/payload/server/public/privacy.html +68 -5
  28. package/payload/server/public/v.js +53 -0
  29. package/payload/server/server.js +229 -84
@@ -12396,11 +12396,161 @@ function verifyVisitorToken(token, now = Date.now()) {
12396
12396
  var VISITOR_COOKIE_NAME = "mxy_v";
12397
12397
  var VISITOR_COOKIE_MAX_AGE_SECONDS = Math.floor(DEFAULT_TTL_MS / 1e3);
12398
12398
 
12399
+ // app/lib/brand-config.ts
12400
+ import { existsSync as existsSync22, readFileSync as readFileSync19 } from "fs";
12401
+ import { join as join14 } from "path";
12402
+ var cached2 = null;
12403
+ var cachedAttempted = false;
12404
+ function readBrandConfig() {
12405
+ if (cachedAttempted) return cached2;
12406
+ cachedAttempted = true;
12407
+ const platformRoot = process.env.MAXY_PLATFORM_ROOT;
12408
+ if (!platformRoot) return null;
12409
+ const brandPath = join14(platformRoot, "config", "brand.json");
12410
+ if (!existsSync22(brandPath)) return null;
12411
+ try {
12412
+ cached2 = JSON.parse(readFileSync19(brandPath, "utf-8"));
12413
+ return cached2;
12414
+ } catch {
12415
+ return null;
12416
+ }
12417
+ }
12418
+
12419
+ // server/routes/visitor-consent.ts
12420
+ var app35 = new Hono();
12421
+ var CONSENT_COOKIE_NAME = "mxy_consent";
12422
+ var CONSENT_COOKIE_MAX_AGE_SECONDS = 60 * 60 * 24 * 365;
12423
+ var DEFAULT_CONSENT_COPY = {
12424
+ title: "Before you browse",
12425
+ body: "We use a small first-party script to count page views, scrolls and clicks on this listing. Nothing is shared with third parties. You can change your mind at any time.",
12426
+ accept: "Accept",
12427
+ reject: "Reject",
12428
+ manage: "Manage cookies",
12429
+ policyLinkText: "Read the cookie policy",
12430
+ policyHref: "/privacy.html#cookies"
12431
+ };
12432
+ var DEFAULT_CONSENT_PALETTE = {
12433
+ bg: "#FFFFFF",
12434
+ fg: "#1F1F1F",
12435
+ accent: "#1F1F1F",
12436
+ accentFg: "#FFFFFF",
12437
+ border: "#E5E5E2"
12438
+ };
12439
+ function getOrigin(c) {
12440
+ return c.req.header("origin") ?? c.req.header("referer") ?? "";
12441
+ }
12442
+ function setCorsHeaders(c, origin) {
12443
+ if (origin) c.header("Access-Control-Allow-Origin", origin);
12444
+ c.header("Access-Control-Allow-Credentials", "true");
12445
+ c.header("Access-Control-Allow-Methods", "POST, GET, OPTIONS");
12446
+ c.header("Access-Control-Allow-Headers", "Content-Type");
12447
+ c.header("Vary", "Origin");
12448
+ }
12449
+ function parseConsent(body) {
12450
+ if (!body || typeof body !== "object") return null;
12451
+ const b = body;
12452
+ if (b.decision !== "yes" && b.decision !== "no") return null;
12453
+ const v = typeof b.v === "string" && b.v.length > 0 && b.v.length <= 2048 ? b.v : null;
12454
+ return { decision: b.decision, v };
12455
+ }
12456
+ function siteSlugFromReferer(referer) {
12457
+ try {
12458
+ const u = new URL(referer);
12459
+ const parts = u.pathname.split("/").filter(Boolean);
12460
+ return parts[parts.length - 1]?.slice(0, 120) ?? "";
12461
+ } catch {
12462
+ return "";
12463
+ }
12464
+ }
12465
+ app35.options("/consent", (c) => {
12466
+ const origin = getOrigin(c);
12467
+ setCorsHeaders(c, origin);
12468
+ return c.body(null, 204);
12469
+ });
12470
+ app35.options("/brand-config", (c) => {
12471
+ const origin = getOrigin(c);
12472
+ setCorsHeaders(c, origin);
12473
+ return c.body(null, 204);
12474
+ });
12475
+ app35.post("/consent", async (c) => {
12476
+ const origin = getOrigin(c);
12477
+ setCorsHeaders(c, origin);
12478
+ let raw;
12479
+ try {
12480
+ raw = await c.req.json();
12481
+ } catch {
12482
+ console.error("[consent-error] reason=bad-json");
12483
+ return c.text("Bad Request", 400);
12484
+ }
12485
+ const parsed = parseConsent(raw);
12486
+ if (!parsed) {
12487
+ console.error("[consent-error] reason=bad-body");
12488
+ return c.text("Bad Request", 400);
12489
+ }
12490
+ const referer = c.req.header("referer") ?? "";
12491
+ const site = siteSlugFromReferer(referer);
12492
+ const brand = readBrandConfig();
12493
+ const brandName = brand?.productName ?? "-";
12494
+ const cookies = [];
12495
+ cookies.push(
12496
+ `${CONSENT_COOKIE_NAME}=${parsed.decision}; Max-Age=${CONSENT_COOKIE_MAX_AGE_SECONDS}; Path=/; SameSite=Lax; Secure`
12497
+ );
12498
+ let tokenBound = false;
12499
+ if (parsed.decision === "yes" && parsed.v) {
12500
+ const verify = verifyVisitorToken(parsed.v);
12501
+ if (verify.ok) {
12502
+ cookies.push(
12503
+ `${VISITOR_COOKIE_NAME}=${parsed.v}; Max-Age=${VISITOR_COOKIE_MAX_AGE_SECONDS}; Path=/; SameSite=Lax; Secure; HttpOnly`
12504
+ );
12505
+ tokenBound = true;
12506
+ console.log(`[token-bind] via=consent site=${site} person=${verify.personId}`);
12507
+ } else {
12508
+ console.error(`[token-bind] reject reason=${verify.reason} via=consent site=${site}`);
12509
+ }
12510
+ }
12511
+ for (const cookie of cookies) c.header("Set-Cookie", cookie, { append: true });
12512
+ console.log(`[consent] ${parsed.decision} site=${site} brand=${brandName} tokenBound=${tokenBound}`);
12513
+ return c.body(null, 204);
12514
+ });
12515
+ app35.get("/brand-config", (c) => {
12516
+ const origin = getOrigin(c);
12517
+ setCorsHeaders(c, origin);
12518
+ const brand = readBrandConfig();
12519
+ const consent = brand?.consent ?? {};
12520
+ const copy = { ...DEFAULT_CONSENT_COPY, ...consent.copy ?? {} };
12521
+ const palette = { ...DEFAULT_CONSENT_PALETTE, ...consent.palette ?? {} };
12522
+ c.header("Cache-Control", "public, max-age=300");
12523
+ return c.json({ consent: { copy, palette } });
12524
+ });
12525
+ var visitor_consent_default = app35;
12526
+
12399
12527
  // server/routes/listings.ts
12528
+ function getCookie(headerValue, name) {
12529
+ if (!headerValue) return null;
12530
+ for (const part of headerValue.split(";")) {
12531
+ const [k, ...rest] = part.trim().split("=");
12532
+ if (k === name) return rest.join("=");
12533
+ }
12534
+ return null;
12535
+ }
12536
+ function appendConsentParams(pageUrl, token) {
12537
+ try {
12538
+ const u = new URL(pageUrl);
12539
+ u.searchParams.set("consent", "needed");
12540
+ u.searchParams.set("v", token);
12541
+ return u.toString();
12542
+ } catch {
12543
+ const hashIdx = pageUrl.indexOf("#");
12544
+ const hash = hashIdx >= 0 ? pageUrl.slice(hashIdx) : "";
12545
+ const base = hashIdx >= 0 ? pageUrl.slice(0, hashIdx) : pageUrl;
12546
+ const sep4 = base.indexOf("?") >= 0 ? "&" : "?";
12547
+ return base + sep4 + `consent=needed&v=${encodeURIComponent(token)}` + hash;
12548
+ }
12549
+ }
12400
12550
  var SAFE_SLUG_RE = /^[a-z0-9](?:[a-z0-9-]{0,118}[a-z0-9])?$/;
12401
12551
  var CHAT_SESSION_KEY_RE = /^[A-Za-z0-9][A-Za-z0-9_-]{7,127}$/;
12402
- var app35 = new Hono();
12403
- app35.get("/:slug/click", async (c) => {
12552
+ var app36 = new Hono();
12553
+ app36.get("/:slug/click", async (c) => {
12404
12554
  const slug = c.req.param("slug") ?? "";
12405
12555
  const rawSession = c.req.query("session") ?? "";
12406
12556
  const sessionKey = CHAT_SESSION_KEY_RE.test(rawSession) ? rawSession : "invalid";
@@ -12441,45 +12591,33 @@ app35.get("/:slug/click", async (c) => {
12441
12591
  console.error(`[property-card-click] reject reason=listing-not-found slug=${slug} sessionKey=${sessionKey} accountId=${account.accountId} status=404`);
12442
12592
  return c.text("Not Found", 404);
12443
12593
  }
12594
+ let redirectUrl = pageUrl;
12595
+ const consentCookie = getCookie(c.req.header("cookie"), CONSENT_COOKIE_NAME);
12596
+ const consented = consentCookie === "yes";
12444
12597
  if (tokenParam) {
12445
12598
  const verify = verifyVisitorToken(tokenParam);
12446
12599
  if (verify.ok) {
12447
- c.header(
12448
- "Set-Cookie",
12449
- `${VISITOR_COOKIE_NAME}=${tokenParam}; Max-Age=${VISITOR_COOKIE_MAX_AGE_SECONDS}; Path=/; SameSite=Lax; Secure; HttpOnly`
12450
- );
12451
- console.log(`[token-bind] sessionKey=${sessionKey} person=${verify.personId} listingSlug=${slug}`);
12600
+ if (consented) {
12601
+ c.header(
12602
+ "Set-Cookie",
12603
+ `${VISITOR_COOKIE_NAME}=${tokenParam}; Max-Age=${VISITOR_COOKIE_MAX_AGE_SECONDS}; Path=/; SameSite=Lax; Secure; HttpOnly`
12604
+ );
12605
+ console.log(`[token-bind] sessionKey=${sessionKey} person=${verify.personId} listingSlug=${slug}`);
12606
+ } else {
12607
+ redirectUrl = appendConsentParams(pageUrl, tokenParam);
12608
+ console.error(`[token-bind] reject reason=no-consent sessionKey=${sessionKey} person=${verify.personId} listingSlug=${slug}`);
12609
+ }
12452
12610
  } else {
12453
12611
  console.error(`[token-bind] reject reason=${verify.reason} sessionKey=${sessionKey} listingSlug=${slug}`);
12454
12612
  }
12455
12613
  }
12456
- console.log(`[property-card-click] sessionKey=${sessionKey} listingSlug=${slug} ts=${(/* @__PURE__ */ new Date()).toISOString()}`);
12457
- return c.redirect(pageUrl, 302);
12614
+ console.log(`[property-card-click] sessionKey=${sessionKey} listingSlug=${slug} consent=${consentCookie ?? "absent"} ts=${(/* @__PURE__ */ new Date()).toISOString()}`);
12615
+ return c.redirect(redirectUrl, 302);
12458
12616
  });
12459
- var listings_default = app35;
12460
-
12461
- // app/lib/brand-config.ts
12462
- import { existsSync as existsSync22, readFileSync as readFileSync19 } from "fs";
12463
- import { join as join14 } from "path";
12464
- var cached2 = null;
12465
- var cachedAttempted = false;
12466
- function readBrandConfig() {
12467
- if (cachedAttempted) return cached2;
12468
- cachedAttempted = true;
12469
- const platformRoot = process.env.MAXY_PLATFORM_ROOT;
12470
- if (!platformRoot) return null;
12471
- const brandPath = join14(platformRoot, "config", "brand.json");
12472
- if (!existsSync22(brandPath)) return null;
12473
- try {
12474
- cached2 = JSON.parse(readFileSync19(brandPath, "utf-8"));
12475
- return cached2;
12476
- } catch {
12477
- return null;
12478
- }
12479
- }
12617
+ var listings_default = app36;
12480
12618
 
12481
12619
  // server/routes/visitor-event.ts
12482
- var app36 = new Hono();
12620
+ var app37 = new Hono();
12483
12621
  var BOT_UA_RE = /\b(bot|crawl|spider|slurp|headlesschrome|phantomjs|googlebot|bingbot|yandex|baiduspider|ahrefsbot|semrushbot|mj12bot|dotbot|petalbot)\b/i;
12484
12622
  var buckets = /* @__PURE__ */ new Map();
12485
12623
  var RATE_LIMIT = 60;
@@ -12512,7 +12650,7 @@ function parseEvent(body) {
12512
12650
  if (typeof e.ts !== "number" || !Number.isFinite(e.ts)) return null;
12513
12651
  return e;
12514
12652
  }
12515
- function getCookie(c, name) {
12653
+ function getCookie2(c, name) {
12516
12654
  const raw = c.req.header("cookie");
12517
12655
  if (!raw) return null;
12518
12656
  for (const part of raw.split(";")) {
@@ -12521,10 +12659,10 @@ function getCookie(c, name) {
12521
12659
  }
12522
12660
  return null;
12523
12661
  }
12524
- function getOrigin(c) {
12662
+ function getOrigin2(c) {
12525
12663
  return c.req.header("origin") ?? c.req.header("referer") ?? "";
12526
12664
  }
12527
- function setCorsHeaders(c, origin) {
12665
+ function setCorsHeaders2(c, origin) {
12528
12666
  if (origin) c.header("Access-Control-Allow-Origin", origin);
12529
12667
  c.header("Access-Control-Allow-Credentials", "true");
12530
12668
  c.header("Access-Control-Allow-Methods", "POST, OPTIONS");
@@ -12546,14 +12684,14 @@ function originAllowed(origin, allowlist) {
12546
12684
  return false;
12547
12685
  }
12548
12686
  }
12549
- app36.options("/event", (c) => {
12550
- const origin = getOrigin(c);
12551
- setCorsHeaders(c, origin);
12687
+ app37.options("/event", (c) => {
12688
+ const origin = getOrigin2(c);
12689
+ setCorsHeaders2(c, origin);
12552
12690
  return c.body(null, 204);
12553
12691
  });
12554
- app36.post("/event", async (c) => {
12555
- const origin = getOrigin(c);
12556
- setCorsHeaders(c, origin);
12692
+ app37.post("/event", async (c) => {
12693
+ const origin = getOrigin2(c);
12694
+ setCorsHeaders2(c, origin);
12557
12695
  const ua = c.req.header("user-agent") ?? "";
12558
12696
  if (BOT_UA_RE.test(ua)) {
12559
12697
  console.error(`[v-event-error] reason=bot-ua ua=${JSON.stringify(ua.slice(0, 120))}`);
@@ -12576,6 +12714,11 @@ app36.post("/event", async (c) => {
12576
12714
  console.error(`[v-event-error] reason=origin-mismatch origin=${JSON.stringify(origin)} ip=${ip}`);
12577
12715
  return c.text("Forbidden", 403);
12578
12716
  }
12717
+ const consentCookie = getCookie2(c, CONSENT_COOKIE_NAME);
12718
+ if (consentCookie !== "yes") {
12719
+ console.log(`[v-event] drop reason=no-consent consent=${consentCookie ?? "absent"} ip=${ip} origin=${JSON.stringify(origin)}`);
12720
+ return c.body(null, 204);
12721
+ }
12579
12722
  let raw;
12580
12723
  try {
12581
12724
  raw = await c.req.json();
@@ -12588,7 +12731,7 @@ app36.post("/event", async (c) => {
12588
12731
  console.error(`[v-event-error] reason=bad-event ip=${ip}`);
12589
12732
  return c.text("Bad Request", 400);
12590
12733
  }
12591
- const cookieVal = getCookie(c, VISITOR_COOKIE_NAME);
12734
+ const cookieVal = getCookie2(c, VISITOR_COOKIE_NAME);
12592
12735
  let personId = null;
12593
12736
  if (cookieVal) {
12594
12737
  const v = verifyVisitorToken(cookieVal);
@@ -12618,7 +12761,7 @@ app36.post("/event", async (c) => {
12618
12761
  });
12619
12762
  }
12620
12763
  console.log(
12621
- `[visitor-event] type=${event.type} sessionId=${event.sid} visitorId=${event.vid} personId=${personId ?? "-"} path=${event.path ?? ""} label=${event.label ?? "-"} depth=${event.depth ?? "-"} ms=${event.ms ?? "-"}`
12764
+ `[visitor-event] type=${event.type} sessionId=${event.sid} visitorId=${event.vid} personId=${personId ?? "-"} consent=${consentCookie} path=${event.path ?? ""} label=${event.label ?? "-"} depth=${event.depth ?? "-"} ms=${event.ms ?? "-"}`
12622
12765
  );
12623
12766
  return c.body(null, 204);
12624
12767
  });
@@ -12751,7 +12894,7 @@ async function writeEvent(opts) {
12751
12894
  );
12752
12895
  }
12753
12896
  }
12754
- var visitor_event_default = app36;
12897
+ var visitor_event_default = app37;
12755
12898
 
12756
12899
  // app/lib/graph-health.ts
12757
12900
  var HOUR_MS = 60 * 60 * 1e3;
@@ -13188,9 +13331,9 @@ watchFile(ALIAS_DOMAINS_PATH, { interval: 2e3 }, () => {
13188
13331
  function isPublicHost(host) {
13189
13332
  return host.startsWith("public.") || aliasDomains.has(host);
13190
13333
  }
13191
- var app37 = new Hono();
13192
- app37.use("*", clientIpMiddleware);
13193
- app37.use("*", async (c, next) => {
13334
+ var app38 = new Hono();
13335
+ app38.use("*", clientIpMiddleware);
13336
+ app38.use("*", async (c, next) => {
13194
13337
  await next();
13195
13338
  c.header("X-Content-Type-Options", "nosniff");
13196
13339
  c.header("Referrer-Policy", "strict-origin-when-cross-origin");
@@ -13200,7 +13343,7 @@ app37.use("*", async (c, next) => {
13200
13343
  );
13201
13344
  });
13202
13345
  var HTTP_LOG_PATHS = /* @__PURE__ */ new Set(["/vnc-viewer.html", "/vnc-popout.html"]);
13203
- app37.use("*", async (c, next) => {
13346
+ app38.use("*", async (c, next) => {
13204
13347
  if (!HTTP_LOG_PATHS.has(c.req.path)) {
13205
13348
  await next();
13206
13349
  return;
@@ -13233,7 +13376,7 @@ var PUBLIC_ALLOWED_PREFIXES = [
13233
13376
  "/sites/"
13234
13377
  ];
13235
13378
  var PUBLIC_ALLOWED_EXACT = ["/favicon.ico"];
13236
- app37.use("*", async (c, next) => {
13379
+ app38.use("*", async (c, next) => {
13237
13380
  const host = (c.req.header("host") ?? "").split(":")[0];
13238
13381
  if (!isPublicHost(host)) {
13239
13382
  await next();
@@ -13273,7 +13416,7 @@ function resolveRemoteAuthOpts() {
13273
13416
  return brandLoginOpts;
13274
13417
  }
13275
13418
  var MAX_LOGIN_BODY = 8 * 1024;
13276
- app37.post("/__remote-auth/login", async (c) => {
13419
+ app38.post("/__remote-auth/login", async (c) => {
13277
13420
  const client = clientFrom(c);
13278
13421
  const clientIp = client.ip || "unknown";
13279
13422
  if (!requestIsTlsTerminated(c)) {
@@ -13318,7 +13461,7 @@ app37.post("/__remote-auth/login", async (c) => {
13318
13461
  }
13319
13462
  });
13320
13463
  });
13321
- app37.get("/__remote-auth/logout", (c) => {
13464
+ app38.get("/__remote-auth/logout", (c) => {
13322
13465
  const client = clientFrom(c);
13323
13466
  const clientIp = client.ip || "unknown";
13324
13467
  console.error(`[remote-auth] logout ip=${clientIp}`);
@@ -13331,7 +13474,7 @@ app37.get("/__remote-auth/logout", (c) => {
13331
13474
  }
13332
13475
  });
13333
13476
  });
13334
- app37.post("/__remote-auth/change-password", async (c) => {
13477
+ app38.post("/__remote-auth/change-password", async (c) => {
13335
13478
  const client = clientFrom(c);
13336
13479
  const clientIp = client.ip || "unknown";
13337
13480
  const rateLimited = checkRateLimit(client);
@@ -13382,13 +13525,13 @@ app37.post("/__remote-auth/change-password", async (c) => {
13382
13525
  return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(), mode: "change", changeError: "Failed to save password", redirect }), 200);
13383
13526
  }
13384
13527
  });
13385
- app37.get("/__remote-auth/setup", (c) => {
13528
+ app38.get("/__remote-auth/setup", (c) => {
13386
13529
  if (isRemoteAuthConfigured()) {
13387
13530
  return c.redirect("/");
13388
13531
  }
13389
13532
  return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(), mode: "setup" }), 200);
13390
13533
  });
13391
- app37.post("/__remote-auth/set-initial-password", async (c) => {
13534
+ app38.post("/__remote-auth/set-initial-password", async (c) => {
13392
13535
  if (isRemoteAuthConfigured()) {
13393
13536
  return c.redirect("/");
13394
13537
  }
@@ -13426,10 +13569,10 @@ app37.post("/__remote-auth/set-initial-password", async (c) => {
13426
13569
  return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(), mode: "setup", setupError: "Failed to save password. Please try again." }), 200);
13427
13570
  }
13428
13571
  });
13429
- app37.get("/api/remote-auth/status", (c) => {
13572
+ app38.get("/api/remote-auth/status", (c) => {
13430
13573
  return c.json({ configured: isRemoteAuthConfigured() });
13431
13574
  });
13432
- app37.post("/api/remote-auth/set-password", async (c) => {
13575
+ app38.post("/api/remote-auth/set-password", async (c) => {
13433
13576
  let body;
13434
13577
  try {
13435
13578
  body = await c.req.json();
@@ -13460,9 +13603,9 @@ app37.post("/api/remote-auth/set-password", async (c) => {
13460
13603
  return c.json({ error: "Failed to save password" }, 500);
13461
13604
  }
13462
13605
  });
13463
- app37.route("/api/_client-error", client_error_default);
13606
+ app38.route("/api/_client-error", client_error_default);
13464
13607
  console.log("[client-error-route] mounted");
13465
- app37.use("*", async (c, next) => {
13608
+ app38.use("*", async (c, next) => {
13466
13609
  const host = (c.req.header("host") ?? "").split(":")[0];
13467
13610
  const path2 = c.req.path;
13468
13611
  if (path2 === "/favicon.ico" || path2.startsWith("/assets/") || path2.startsWith("/brand/")) {
@@ -13495,11 +13638,11 @@ app37.use("*", async (c, next) => {
13495
13638
  console.error(`[remote-auth] login required ip=${clientIp} path=${path2} ${disambig}`);
13496
13639
  return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(), redirect: path2 }), 200);
13497
13640
  });
13498
- app37.route("/api/health", health_default);
13499
- app37.route("/api/chat", chat_default);
13500
- app37.route("/api/whatsapp", whatsapp_default);
13501
- app37.route("/api/onboarding", onboarding_default);
13502
- app37.route("/api/admin", admin_default);
13641
+ app38.route("/api/health", health_default);
13642
+ app38.route("/api/chat", chat_default);
13643
+ app38.route("/api/whatsapp", whatsapp_default);
13644
+ app38.route("/api/onboarding", onboarding_default);
13645
+ app38.route("/api/admin", admin_default);
13503
13646
  var SAFE_SLUG_RE2 = /^[a-z][a-z0-9-]{2,49}$/;
13504
13647
  var SAFE_FILENAME_RE = /^[a-z0-9_][a-z0-9_.-]{0,99}$/i;
13505
13648
  var IMAGE_MIME = {
@@ -13511,7 +13654,7 @@ var IMAGE_MIME = {
13511
13654
  ".svg": "image/svg+xml",
13512
13655
  ".ico": "image/x-icon"
13513
13656
  };
13514
- app37.get("/agent-assets/:slug/:filename", (c) => {
13657
+ app38.get("/agent-assets/:slug/:filename", (c) => {
13515
13658
  const slug = c.req.param("slug");
13516
13659
  const filename = c.req.param("filename");
13517
13660
  if (!SAFE_SLUG_RE2.test(slug)) {
@@ -13546,7 +13689,7 @@ app37.get("/agent-assets/:slug/:filename", (c) => {
13546
13689
  "Cache-Control": "public, max-age=3600"
13547
13690
  });
13548
13691
  });
13549
- app37.get("/generated/:filename", (c) => {
13692
+ app38.get("/generated/:filename", (c) => {
13550
13693
  const filename = c.req.param("filename");
13551
13694
  if (!SAFE_FILENAME_RE.test(filename) || filename.includes("..")) {
13552
13695
  console.error(`[generated] serve file=${filename} status=403`);
@@ -13576,9 +13719,10 @@ app37.get("/generated/:filename", (c) => {
13576
13719
  "Cache-Control": "public, max-age=86400"
13577
13720
  });
13578
13721
  });
13579
- app37.route("/sites", sites_default);
13580
- app37.route("/listings", listings_default);
13581
- app37.route("/v", visitor_event_default);
13722
+ app38.route("/sites", sites_default);
13723
+ app38.route("/listings", listings_default);
13724
+ app38.route("/v", visitor_event_default);
13725
+ app38.route("/v", visitor_consent_default);
13582
13726
  var htmlCache = /* @__PURE__ */ new Map();
13583
13727
  var brandLogoPath = "/brand/maxy-monochrome.png";
13584
13728
  var brandIconPath = "/brand/maxy-monochrome.png";
@@ -13715,7 +13859,7 @@ function brandedPublicHtml(agentSlug) {
13715
13859
  function escapeHtml(s) {
13716
13860
  return s.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
13717
13861
  }
13718
- app37.get("/", (c) => {
13862
+ app38.get("/", (c) => {
13719
13863
  const host = (c.req.header("host") ?? "").split(":")[0];
13720
13864
  if (isPublicHost(host)) {
13721
13865
  const defaultSlug = resolveDefaultSlug();
@@ -13723,12 +13867,12 @@ app37.get("/", (c) => {
13723
13867
  }
13724
13868
  return c.html(cachedHtml("index.html"));
13725
13869
  });
13726
- app37.get("/public", (c) => {
13870
+ app38.get("/public", (c) => {
13727
13871
  const host = (c.req.header("host") ?? "").split(":")[0];
13728
13872
  if (isPublicHost(host)) return c.text("Not found", 404);
13729
13873
  return c.html(cachedHtml("public.html"));
13730
13874
  });
13731
- app37.get("/chat", (c) => {
13875
+ app38.get("/chat", (c) => {
13732
13876
  const host = (c.req.header("host") ?? "").split(":")[0];
13733
13877
  if (isPublicHost(host)) return c.text("Not found", 404);
13734
13878
  return c.html(cachedHtml("public.html"));
@@ -13747,9 +13891,9 @@ async function logViewerFetch(c, next) {
13747
13891
  duration_ms: Date.now() - start
13748
13892
  });
13749
13893
  }
13750
- app37.use("/vnc-viewer.html", logViewerFetch);
13751
- app37.use("/vnc-popout.html", logViewerFetch);
13752
- app37.get("/vnc-popout.html", (c) => {
13894
+ app38.use("/vnc-viewer.html", logViewerFetch);
13895
+ app38.use("/vnc-popout.html", logViewerFetch);
13896
+ app38.get("/vnc-popout.html", (c) => {
13753
13897
  let html = htmlCache.get("vnc-popout.html");
13754
13898
  if (!html) {
13755
13899
  html = readFileSync21(resolve21(process.cwd(), "public", "vnc-popout.html"), "utf-8");
@@ -13762,7 +13906,7 @@ app37.get("/vnc-popout.html", (c) => {
13762
13906
  }
13763
13907
  return c.html(html);
13764
13908
  });
13765
- app37.post("/api/vnc/client-event", async (c) => {
13909
+ app38.post("/api/vnc/client-event", async (c) => {
13766
13910
  let body;
13767
13911
  try {
13768
13912
  body = await c.req.json();
@@ -13783,25 +13927,25 @@ app37.post("/api/vnc/client-event", async (c) => {
13783
13927
  });
13784
13928
  return c.json({ ok: true });
13785
13929
  });
13786
- app37.get("/g/:slug", (c) => {
13930
+ app38.get("/g/:slug", (c) => {
13787
13931
  return c.html(brandedPublicHtml());
13788
13932
  });
13789
- app37.get("/graph", (c) => {
13933
+ app38.get("/graph", (c) => {
13790
13934
  const host = (c.req.header("host") ?? "").split(":")[0];
13791
13935
  if (isPublicHost(host)) return c.text("Not found", 404);
13792
13936
  return c.html(cachedHtml("graph.html"));
13793
13937
  });
13794
- app37.get("/sessions", (c) => {
13938
+ app38.get("/sessions", (c) => {
13795
13939
  const host = (c.req.header("host") ?? "").split(":")[0];
13796
13940
  if (isPublicHost(host)) return c.text("Not found", 404);
13797
13941
  return c.html(cachedHtml("sessions.html"));
13798
13942
  });
13799
- app37.get("/data", (c) => {
13943
+ app38.get("/data", (c) => {
13800
13944
  const host = (c.req.header("host") ?? "").split(":")[0];
13801
13945
  if (isPublicHost(host)) return c.text("Not found", 404);
13802
13946
  return c.html(cachedHtml("data.html"));
13803
13947
  });
13804
- app37.get("/:slug", async (c, next) => {
13948
+ app38.get("/:slug", async (c, next) => {
13805
13949
  const slug = c.req.param("slug");
13806
13950
  if (AGENT_SLUG_PATTERN.test(`/${slug}`)) {
13807
13951
  const branding = loadBrandingCache(slug);
@@ -13811,15 +13955,15 @@ app37.get("/:slug", async (c, next) => {
13811
13955
  await next();
13812
13956
  });
13813
13957
  if (brandFaviconPath !== "/favicon.ico") {
13814
- app37.get("/favicon.ico", (c) => {
13958
+ app38.get("/favicon.ico", (c) => {
13815
13959
  c.header("Cache-Control", "public, max-age=300");
13816
13960
  return c.redirect(brandFaviconPath, 302);
13817
13961
  });
13818
13962
  }
13819
- app37.use("/*", serveStatic({ root: "./public" }));
13963
+ app38.use("/*", serveStatic({ root: "./public" }));
13820
13964
  var port = requirePortEnv("MAXY_UI_INTERNAL_PORT", { tag: "ui-server" });
13821
13965
  var hostname = process.env.HOSTNAME ?? "127.0.0.1";
13822
- var httpServer = serve({ fetch: app37.fetch, port, hostname });
13966
+ var httpServer = serve({ fetch: app38.fetch, port, hostname });
13823
13967
  console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
13824
13968
  {
13825
13969
  const loopHist = monitorEventLoopDelay({ resolution: 50 });
@@ -13845,7 +13989,8 @@ var SUBAPP_MANIFEST = [
13845
13989
  { prefix: "/api/admin", file: "server/routes/admin/index.ts", subapp: admin_default },
13846
13990
  { prefix: "/api/_client-error", file: "server/routes/client-error.ts", subapp: client_error_default },
13847
13991
  { prefix: "/listings", file: "server/routes/listings.ts", subapp: listings_default },
13848
- { prefix: "/v", file: "server/routes/visitor-event.ts", subapp: visitor_event_default }
13992
+ { prefix: "/v", file: "server/routes/visitor-event.ts", subapp: visitor_event_default },
13993
+ { prefix: "/v", file: "server/routes/visitor-consent.ts", subapp: visitor_consent_default }
13849
13994
  ];
13850
13995
  for (const m of SUBAPP_MANIFEST) {
13851
13996
  const routeCount = Array.isArray(m.subapp.routes) ? m.subapp.routes.length : 0;
@@ -13853,7 +13998,7 @@ for (const m of SUBAPP_MANIFEST) {
13853
13998
  }
13854
13999
  try {
13855
14000
  const registered = [];
13856
- for (const r of app37.routes ?? []) {
14001
+ for (const r of app38.routes ?? []) {
13857
14002
  if (typeof r.path !== "string" || r.path.includes(":") || r.path.includes("*")) continue;
13858
14003
  if (AGENT_SLUG_PATTERN.test(r.path)) {
13859
14004
  registered.push({ method: (r.method ?? "ALL").toUpperCase(), path: r.path });