@rubytech/create-maxy-code 0.1.238 → 0.1.240

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.
@@ -19,7 +19,9 @@ import {
19
19
  VNC_DISPLAY,
20
20
  WEBSOCKIFY_PORT,
21
21
  autoDeliverPremiumPlugins,
22
+ bindVisitorToGroup,
22
23
  canAccessAdmin,
24
+ checkGroupMembership,
23
25
  checkRateLimit,
24
26
  cleanupLeakedPremiumSubs,
25
27
  clearRateLimit,
@@ -31,11 +33,15 @@ import {
31
33
  emitMissingOnResolve,
32
34
  ensureConversation,
33
35
  fetchBranding,
36
+ findGroupBySlug,
34
37
  findMissingPlugins,
38
+ findRecentConversation,
35
39
  fingerprintSessionKey,
36
40
  getAccountIdForSession,
37
41
  getAgentSessionIdForConversation,
38
42
  getConversationClaudeSessionIds,
43
+ getDefaultAccountId,
44
+ getGroupParticipants,
39
45
  getRecentMessages,
40
46
  getRoleForSession,
41
47
  getSession,
@@ -76,6 +82,7 @@ import {
76
82
  stripAttachmentMetaSuffix,
77
83
  tryCookieBridgeForConversation,
78
84
  unregisterSession,
85
+ validateAgentSlug,
79
86
  validatePasswordStrength,
80
87
  validateSession,
81
88
  verifyAndGetConversationUpdatedAt,
@@ -84,7 +91,7 @@ import {
84
91
  vncLog,
85
92
  walkPremiumBundles,
86
93
  writeAdminUserAndPerson
87
- } from "./chunk-63M3ILJ6.js";
94
+ } from "./chunk-ZY6W3UA2.js";
88
95
  import {
89
96
  __commonJS,
90
97
  __toESM
@@ -816,14 +823,37 @@ var serveStatic = (options = { root: "" }) => {
816
823
  };
817
824
 
818
825
  // server/index.ts
819
- import { readFileSync as readFileSync22, existsSync as existsSync22, watchFile } from "fs";
820
- import { resolve as resolve24, join as join17, basename as basename6 } from "path";
826
+ import { readFileSync as readFileSync22, existsSync as existsSync23, watchFile } from "fs";
827
+ import { resolve as resolve25, join as join17, basename as basename6 } from "path";
821
828
  import { homedir as homedir2 } from "os";
822
829
  import { monitorEventLoopDelay } from "perf_hooks";
823
830
 
824
831
  // app/lib/agent-slug-pattern.ts
825
832
  var AGENT_SLUG_PATTERN = /^\/([a-z][a-z0-9-]{2,49})$/;
826
833
 
834
+ // server/public-host-gate.ts
835
+ var PUBLIC_ALLOWED_PREFIXES = [
836
+ "/api/session",
837
+ "/api/chat",
838
+ "/api/access/",
839
+ "/api/health",
840
+ "/api/group/",
841
+ "/api/telegram/",
842
+ "/assets/",
843
+ "/brand/",
844
+ "/agent-assets/",
845
+ "/generated/",
846
+ "/g/",
847
+ "/sites/"
848
+ ];
849
+ var PUBLIC_ALLOWED_EXACT = ["/favicon.ico", "/brand-defaults.css"];
850
+ function isPublicPathAllowed(path2) {
851
+ if (path2 === "/") return true;
852
+ if (PUBLIC_ALLOWED_EXACT.includes(path2)) return true;
853
+ if (PUBLIC_ALLOWED_PREFIXES.some((prefix) => path2.startsWith(prefix))) return true;
854
+ return AGENT_SLUG_PATTERN.test(path2);
855
+ }
856
+
827
857
  // app/lib/claude-auth.ts
828
858
  import { readFileSync, writeFileSync } from "fs";
829
859
  import { sep } from "path";
@@ -1317,7 +1347,7 @@ var credsSaveQueue = Promise.resolve();
1317
1347
  async function drainCredsSaveQueue(timeoutMs = 5e3) {
1318
1348
  console.error(`${TAG2} draining credential save queue\u2026`);
1319
1349
  const timer2 = new Promise(
1320
- (resolve25) => setTimeout(() => resolve25("timeout"), timeoutMs)
1350
+ (resolve26) => setTimeout(() => resolve26("timeout"), timeoutMs)
1321
1351
  );
1322
1352
  const result = await Promise.race([
1323
1353
  credsSaveQueue.then(() => "drained"),
@@ -1445,11 +1475,11 @@ async function createWaSocket(opts) {
1445
1475
  return sock;
1446
1476
  }
1447
1477
  async function waitForConnection(sock) {
1448
- return new Promise((resolve25, reject) => {
1478
+ return new Promise((resolve26, reject) => {
1449
1479
  const handler = (update) => {
1450
1480
  if (update.connection === "open") {
1451
1481
  sock.ev.off("connection.update", handler);
1452
- resolve25();
1482
+ resolve26();
1453
1483
  }
1454
1484
  if (update.connection === "close") {
1455
1485
  sock.ev.off("connection.update", handler);
@@ -1563,14 +1593,14 @@ ${inspected}`;
1563
1593
  return inspect2(err, INSPECT_OPTS2);
1564
1594
  }
1565
1595
  function withTimeout(label, promise, timeoutMs) {
1566
- return new Promise((resolve25, reject) => {
1596
+ return new Promise((resolve26, reject) => {
1567
1597
  const timer2 = setTimeout(() => {
1568
1598
  reject(new Error(`${label} timed out after ${timeoutMs}ms`));
1569
1599
  }, timeoutMs);
1570
1600
  promise.then(
1571
1601
  (value) => {
1572
1602
  clearTimeout(timer2);
1573
- resolve25(value);
1603
+ resolve26(value);
1574
1604
  },
1575
1605
  (err) => {
1576
1606
  clearTimeout(timer2);
@@ -2105,8 +2135,8 @@ async function persistWhatsAppMessage(input) {
2105
2135
  const { givenName, familyName } = splitName(input.pushName);
2106
2136
  const prev = sessionWriteLocks.get(input.cacheKey);
2107
2137
  let release;
2108
- const mine = new Promise((resolve25) => {
2109
- release = resolve25;
2138
+ const mine = new Promise((resolve26) => {
2139
+ release = resolve26;
2110
2140
  });
2111
2141
  const chained = (prev ?? Promise.resolve()).then(() => mine);
2112
2142
  sessionWriteLocks.set(input.cacheKey, chained);
@@ -3142,11 +3172,11 @@ async function connectWithReconnect(conn) {
3142
3172
  console.error(
3143
3173
  `${TAG12} reconnecting account=${conn.accountId} in ${delay}ms (attempt ${decision.nextAttempts}/${maxAttempts})`
3144
3174
  );
3145
- await new Promise((resolve25) => {
3146
- const timer2 = setTimeout(resolve25, delay);
3175
+ await new Promise((resolve26) => {
3176
+ const timer2 = setTimeout(resolve26, delay);
3147
3177
  conn.abortController.signal.addEventListener("abort", () => {
3148
3178
  clearTimeout(timer2);
3149
- resolve25();
3179
+ resolve26();
3150
3180
  }, { once: true });
3151
3181
  });
3152
3182
  }
@@ -3154,16 +3184,16 @@ async function connectWithReconnect(conn) {
3154
3184
  }
3155
3185
  }
3156
3186
  function waitForDisconnectEvent(conn) {
3157
- return new Promise((resolve25) => {
3187
+ return new Promise((resolve26) => {
3158
3188
  if (!conn.sock) {
3159
- resolve25();
3189
+ resolve26();
3160
3190
  return;
3161
3191
  }
3162
3192
  const sock = conn.sock;
3163
3193
  const handler = (update) => {
3164
3194
  if (update.connection === "close") {
3165
3195
  sock.ev.off("connection.update", handler);
3166
- resolve25();
3196
+ resolve26();
3167
3197
  }
3168
3198
  };
3169
3199
  sock.ev.on("connection.update", handler);
@@ -3425,8 +3455,8 @@ async function handleInboundMessage(conn, msg) {
3425
3455
  const conversationKey = isGroup ? remoteJid : senderPhone;
3426
3456
  const debounceKey = `${conn.accountId}:${conversationKey}:${senderPhone}`;
3427
3457
  let resolvePending;
3428
- const sttPending = new Promise((resolve25) => {
3429
- resolvePending = resolve25;
3458
+ const sttPending = new Promise((resolve26) => {
3459
+ resolvePending = resolve26;
3430
3460
  });
3431
3461
  if (conn.debouncer) conn.debouncer.registerPending(debounceKey, sttPending);
3432
3462
  try {
@@ -3830,20 +3860,20 @@ function buildX11Env(chromiumWrapperPath, transport = "vnc") {
3830
3860
 
3831
3861
  // server/routes/health.ts
3832
3862
  function checkPort(port2, timeoutMs = 500) {
3833
- return new Promise((resolve25) => {
3863
+ return new Promise((resolve26) => {
3834
3864
  const socket = createConnection2(port2, "127.0.0.1");
3835
3865
  socket.setTimeout(timeoutMs);
3836
3866
  socket.once("connect", () => {
3837
3867
  socket.destroy();
3838
- resolve25(true);
3868
+ resolve26(true);
3839
3869
  });
3840
3870
  socket.once("error", () => {
3841
3871
  socket.destroy();
3842
- resolve25(false);
3872
+ resolve26(false);
3843
3873
  });
3844
3874
  socket.once("timeout", () => {
3845
3875
  socket.destroy();
3846
- resolve25(false);
3876
+ resolve26(false);
3847
3877
  });
3848
3878
  });
3849
3879
  }
@@ -4579,12 +4609,12 @@ async function dispatchOnce(input) {
4579
4609
  });
4580
4610
  if (!entry) return { error: "spawn-failed" };
4581
4611
  entry.lastInboundAt = Date.now();
4582
- let resolve25;
4612
+ let resolve26;
4583
4613
  const turnPromise = new Promise((r) => {
4584
- resolve25 = r;
4614
+ resolve26 = r;
4585
4615
  });
4586
4616
  const listener = (text) => {
4587
- resolve25(text);
4617
+ resolve26(text);
4588
4618
  };
4589
4619
  entry.subscribers.add(listener);
4590
4620
  const writeOk = await writeInput(entry, input.text);
@@ -5026,8 +5056,8 @@ async function startLogin(opts) {
5026
5056
  resetActiveLogin(accountId);
5027
5057
  let resolveQr = null;
5028
5058
  let rejectQr = null;
5029
- const qrPromise = new Promise((resolve25, reject) => {
5030
- resolveQr = resolve25;
5059
+ const qrPromise = new Promise((resolve26, reject) => {
5060
+ resolveQr = resolve26;
5031
5061
  rejectQr = reject;
5032
5062
  });
5033
5063
  const qrTimer = setTimeout(
@@ -7147,7 +7177,7 @@ app10.post("/:slug/project", async (c) => {
7147
7177
  var agents_default = app10;
7148
7178
 
7149
7179
  // server/routes/admin/sessions.ts
7150
- import crypto from "crypto";
7180
+ import crypto2 from "crypto";
7151
7181
  import { resolve as resolvePath } from "path";
7152
7182
  import { existsSync as existsSync13, mkdirSync as mkdirSync3, createWriteStream } from "fs";
7153
7183
 
@@ -7456,7 +7486,7 @@ app11.post("/new", requireAdminSession, async (c) => {
7456
7486
  console.error(`[admin-chat] new-conversation outcome=fail reason=missing-account-or-user cacheKey=${oldCacheKey.slice(0, 8)}`);
7457
7487
  return c.json({ error: "Session missing account or user context" }, 400);
7458
7488
  }
7459
- const newSignedSessionToken = crypto.randomUUID();
7489
+ const newSignedSessionToken = crypto2.randomUUID();
7460
7490
  const newCacheKey = fingerprintSessionKey(newSignedSessionToken);
7461
7491
  const userName = getUserNameForSession(oldCacheKey);
7462
7492
  registerSession(newCacheKey, "admin", accountId, void 0, userId, userName);
@@ -12055,7 +12085,7 @@ function isPidAlive(pid) {
12055
12085
  }
12056
12086
  }
12057
12087
  function sleep2(ms) {
12058
- return new Promise((resolve25) => setTimeout(resolve25, ms));
12088
+ return new Promise((resolve26) => setTimeout(resolve26, ms));
12059
12089
  }
12060
12090
  function sendSignal(pid, signal) {
12061
12091
  try {
@@ -14037,6 +14067,244 @@ async function writeEvent(opts) {
14037
14067
  }
14038
14068
  var visitor_event_default = app40;
14039
14069
 
14070
+ // server/routes/session.ts
14071
+ import { resolve as resolve22 } from "path";
14072
+ import { existsSync as existsSync21, writeFileSync as writeFileSync7, mkdirSync as mkdirSync5 } from "fs";
14073
+ var UUID_RE4 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
14074
+ function writeBrandingCache(accountId, agentSlug, branding) {
14075
+ try {
14076
+ const cacheDir = resolve22(MAXY_DIR, "branding-cache", accountId);
14077
+ mkdirSync5(cacheDir, { recursive: true });
14078
+ writeFileSync7(resolve22(cacheDir, `${agentSlug}.json`), JSON.stringify(branding), "utf-8");
14079
+ } catch (err) {
14080
+ console.error(`[branding] cache write failed: ${err instanceof Error ? err.message : String(err)}`);
14081
+ }
14082
+ }
14083
+ function parseVisitorCookie(cookieHeader) {
14084
+ if (!cookieHeader) return null;
14085
+ const match = cookieHeader.match(/(?:^|;\s*)maxy_visitor=([^;]*)/);
14086
+ if (!match) return null;
14087
+ const value = decodeURIComponent(match[1]).trim();
14088
+ return UUID_RE4.test(value) ? value : null;
14089
+ }
14090
+ function parseAccessSessionId(cookieHeader) {
14091
+ if (!cookieHeader) return null;
14092
+ const part = cookieHeader.split(";").map((p) => p.trim()).find((p) => p.startsWith("__access_session="));
14093
+ return part ? part.slice("__access_session=".length) : null;
14094
+ }
14095
+ function withVisitorCookie(response, visitorId) {
14096
+ if (!visitorId) return response;
14097
+ const headers = new Headers(response.headers);
14098
+ headers.set("Set-Cookie", `maxy_visitor=${visitorId}; Path=/; Max-Age=86400; SameSite=Lax`);
14099
+ return new Response(response.body, {
14100
+ status: response.status,
14101
+ statusText: response.statusText,
14102
+ headers
14103
+ });
14104
+ }
14105
+ var app41 = new Hono();
14106
+ app41.post("/", async (c) => {
14107
+ let body;
14108
+ try {
14109
+ body = await c.req.json();
14110
+ } catch {
14111
+ return c.json({ error: "Invalid request" }, 400);
14112
+ }
14113
+ if (!body.session_id || typeof body.session_id !== "string") {
14114
+ return c.json({ error: "session_id required" }, 400);
14115
+ }
14116
+ const accountId = getDefaultAccountId();
14117
+ if (!accountId) {
14118
+ console.error("[session] getDefaultAccountId() returned null \u2014 no account configured");
14119
+ return c.json({ error: "No account configured" }, 503);
14120
+ }
14121
+ const account = resolveAccount();
14122
+ const cookieHeader = c.req.header("cookie");
14123
+ const visitorId = parseVisitorCookie(cookieHeader);
14124
+ const groupSlug = body.group_slug && typeof body.group_slug === "string" ? body.group_slug : null;
14125
+ let groupInfo = null;
14126
+ if (groupSlug) {
14127
+ groupInfo = await findGroupBySlug(groupSlug, accountId).catch(() => null);
14128
+ if (!groupInfo) {
14129
+ console.error(`[session] group not found: slug=${groupSlug} account=${accountId.slice(0, 8)}\u2026`);
14130
+ return c.json({ error: "Group not found" }, 404);
14131
+ }
14132
+ }
14133
+ let agentSlug = null;
14134
+ if (groupInfo) {
14135
+ agentSlug = groupInfo.agentSlug;
14136
+ } else if (body.agent) {
14137
+ if (!validateAgentSlug(body.agent)) {
14138
+ return c.json({ error: "Invalid agent name" }, 400);
14139
+ }
14140
+ agentSlug = body.agent;
14141
+ } else {
14142
+ agentSlug = account ? resolveDefaultAgentSlug(account.accountDir) : null;
14143
+ if (agentSlug) {
14144
+ console.log(`[session] using defaultAgent=${agentSlug} (no agent param)`);
14145
+ }
14146
+ }
14147
+ if (!agentSlug) {
14148
+ console.error("[session] no agent resolved");
14149
+ return c.json({ error: "No agents configured" }, 404);
14150
+ }
14151
+ let agentConfig = null;
14152
+ if (account) {
14153
+ const agentDir = resolve22(account.accountDir, "agents", agentSlug);
14154
+ if (!existsSync21(agentDir) || !existsSync21(resolve22(agentDir, "config.json"))) {
14155
+ return c.json({ error: "Agent not found" }, 404);
14156
+ }
14157
+ agentConfig = resolveAgentConfig(account.accountDir, agentSlug);
14158
+ }
14159
+ const agentIdentity = agentConfig ? {
14160
+ ...agentConfig.displayName ? { displayName: agentConfig.displayName } : {},
14161
+ ...agentConfig.image ? { agentImage: agentConfig.image } : {},
14162
+ ...agentConfig.imageShape ? { agentImageShape: agentConfig.imageShape } : {},
14163
+ ...agentConfig.showAgentName ? { showAgentName: agentConfig.showAgentName } : {}
14164
+ } : {};
14165
+ const groupFields = groupInfo ? {
14166
+ group: true,
14167
+ groupName: groupInfo.groupName,
14168
+ groupSlug
14169
+ } : {};
14170
+ const branding = await fetchBranding(accountId).catch(() => null);
14171
+ if (branding) writeBrandingCache(accountId, agentSlug, branding);
14172
+ let accessSession = null;
14173
+ if (agentConfig && agentConfig.accessMode !== "open") {
14174
+ const accessSessionId = parseAccessSessionId(cookieHeader);
14175
+ accessSession = accessSessionId ? getAccessSession(accessSessionId) : null;
14176
+ if (!accessSession || accessSession.agentSlug !== agentSlug) {
14177
+ console.log(`[session] agent=${agentSlug} accessMode=${agentConfig.accessMode} \u2014 auth required`);
14178
+ return withVisitorCookie(
14179
+ Response.json({
14180
+ auth_required: true,
14181
+ agent_id: agentSlug,
14182
+ ...groupFields,
14183
+ ...branding ? { branding } : {},
14184
+ ...agentIdentity
14185
+ }),
14186
+ null
14187
+ );
14188
+ }
14189
+ }
14190
+ if (body.session_key && typeof body.session_key === "string" && UUID_RE4.test(body.session_key)) {
14191
+ if (visitorId) {
14192
+ const recent = await findRecentConversation(visitorId, accountId, agentSlug).catch(() => null);
14193
+ if (recent) {
14194
+ const neo4jMessages = await getRecentMessages(recent.sessionId).catch(() => []);
14195
+ const uiMessages = neo4jMessages.filter((m) => m.content !== GREETING_DIRECTIVE).map((m) => ({
14196
+ role: m.role === "user" ? "visitor" : "maxy",
14197
+ content: m.content,
14198
+ timestamp: new Date(m.createdAt).getTime() || Date.now()
14199
+ }));
14200
+ const resumed = uiMessages.length > 0;
14201
+ console.log(`[session] hot-resume session=${body.session_key.slice(0, 8)}\u2026 messages=${uiMessages.length}`);
14202
+ return c.json({
14203
+ session_key: body.session_key,
14204
+ agent_id: agentSlug,
14205
+ resumed,
14206
+ ...resumed ? { messages: uiMessages } : {},
14207
+ ...groupFields,
14208
+ ...branding ? { branding } : {},
14209
+ ...agentIdentity
14210
+ });
14211
+ }
14212
+ }
14213
+ console.log(`[session] hot-resume session=${body.session_key.slice(0, 8)}\u2026 (no cookie/conversation)`);
14214
+ return c.json({
14215
+ session_key: body.session_key,
14216
+ agent_id: agentSlug,
14217
+ resumed: false,
14218
+ ...groupFields,
14219
+ ...branding ? { branding } : {},
14220
+ ...agentIdentity
14221
+ });
14222
+ }
14223
+ if (groupInfo && visitorId) {
14224
+ let memberName = await checkGroupMembership(groupInfo.sessionId, visitorId).catch(() => null);
14225
+ if (!memberName && accessSession) {
14226
+ const contactValue = accessSession.contactValue;
14227
+ const isEmail = contactValue?.includes("@");
14228
+ const bindResult = await bindVisitorToGroup(
14229
+ groupInfo.sessionId,
14230
+ visitorId,
14231
+ isEmail ? contactValue : void 0,
14232
+ !isEmail ? contactValue : void 0
14233
+ ).catch(() => null);
14234
+ memberName = bindResult?.displayName ?? null;
14235
+ }
14236
+ if (memberName) {
14237
+ const neo4jMessages = await getRecentMessages(groupInfo.sessionId).catch(() => []);
14238
+ const uiMessages = neo4jMessages.filter((m) => m.content !== GREETING_DIRECTIVE).map((m) => ({
14239
+ role: m.role === "user" ? "visitor" : "maxy",
14240
+ content: m.content,
14241
+ timestamp: new Date(m.createdAt).getTime() || Date.now()
14242
+ }));
14243
+ const participants = await getGroupParticipants(groupInfo.sessionId).catch(() => []);
14244
+ const cacheKey2 = crypto.randomUUID();
14245
+ const resumed = uiMessages.length > 0;
14246
+ console.log(`[session] cold-resume group=${groupSlug} visitor=${visitorId.slice(0, 8)}\u2026 messages=${uiMessages.length}`);
14247
+ return withVisitorCookie(
14248
+ Response.json({
14249
+ session_key: cacheKey2,
14250
+ agent_id: agentSlug,
14251
+ resumed,
14252
+ ...resumed ? { messages: uiMessages } : {},
14253
+ ...groupFields,
14254
+ participants: participants.map((p) => ({ displayName: p.displayName, joinedAt: p.joinedAt })),
14255
+ ...branding ? { branding } : {},
14256
+ ...agentIdentity
14257
+ }),
14258
+ visitorId
14259
+ );
14260
+ }
14261
+ console.log(`[session] group cold-resume denied: visitor=${visitorId.slice(0, 8)}\u2026 not a member of group=${groupSlug}`);
14262
+ return c.json({ error: "Not a member of this group" }, 403);
14263
+ }
14264
+ if (groupInfo) {
14265
+ console.error(`[session] group=${groupSlug} requires visitor cookie`);
14266
+ return c.json({ error: "Groups require member authentication" }, 400);
14267
+ }
14268
+ if (visitorId) {
14269
+ const recent = await findRecentConversation(visitorId, accountId, agentSlug).catch(() => null);
14270
+ if (recent) {
14271
+ const neo4jMessages = await getRecentMessages(recent.sessionId).catch(() => []);
14272
+ const uiMessages = neo4jMessages.filter((m) => m.content !== GREETING_DIRECTIVE).map((m) => ({
14273
+ role: m.role === "user" ? "visitor" : "maxy",
14274
+ content: m.content,
14275
+ timestamp: new Date(m.createdAt).getTime() || Date.now()
14276
+ }));
14277
+ const cacheKey2 = crypto.randomUUID();
14278
+ const resumed = uiMessages.length > 0;
14279
+ console.log(`[session] cold-resume visitor=${visitorId.slice(0, 8)}\u2026 session=${recent.sessionId.slice(0, 8)}\u2026 messages=${uiMessages.length}`);
14280
+ return withVisitorCookie(
14281
+ Response.json({
14282
+ session_key: cacheKey2,
14283
+ agent_id: agentSlug,
14284
+ resumed,
14285
+ ...resumed ? { messages: uiMessages } : {},
14286
+ ...branding ? { branding } : {},
14287
+ ...agentIdentity
14288
+ }),
14289
+ visitorId
14290
+ );
14291
+ }
14292
+ }
14293
+ const newVisitorId = visitorId ?? crypto.randomUUID();
14294
+ const cacheKey = crypto.randomUUID();
14295
+ console.log(`[session] new-session visitor=${newVisitorId.slice(0, 8)}\u2026 session=${cacheKey.slice(0, 8)}\u2026 agent=${agentSlug} image=${agentConfig?.image ? "yes" : "no"}`);
14296
+ return withVisitorCookie(
14297
+ Response.json({
14298
+ session_key: cacheKey,
14299
+ agent_id: agentSlug,
14300
+ ...branding ? { branding } : {},
14301
+ ...agentIdentity
14302
+ }),
14303
+ newVisitorId
14304
+ );
14305
+ });
14306
+ var session_default2 = app41;
14307
+
14040
14308
  // app/lib/graph-health.ts
14041
14309
  var HOUR_MS = 60 * 60 * 1e3;
14042
14310
  var timer = null;
@@ -14120,7 +14388,7 @@ function startGraphHealthTimer() {
14120
14388
 
14121
14389
  // app/lib/file-watcher.ts
14122
14390
  import * as fsp2 from "fs/promises";
14123
- import { resolve as resolve22, sep as sep6 } from "path";
14391
+ import { resolve as resolve23, sep as sep6 } from "path";
14124
14392
  var ACCOUNT_UUID_RE2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
14125
14393
  var DEFAULT_COALESCE_MS = 500;
14126
14394
  var ROOTS = ["uploads", "accounts"];
@@ -14139,7 +14407,7 @@ async function startFileWatcher(opts = {}) {
14139
14407
  const dropFn = opts.drop ?? dropFileIndex;
14140
14408
  for (const r of ROOTS) {
14141
14409
  try {
14142
- await fsp2.mkdir(resolve22(dataRoot, r), { recursive: true });
14410
+ await fsp2.mkdir(resolve23(dataRoot, r), { recursive: true });
14143
14411
  } catch (err) {
14144
14412
  console.error(
14145
14413
  `[file-watcher] start-failed root="${r}" err="${err.message}" \u2014 index will be maintained by the 5-min reconcile backstop only`
@@ -14160,7 +14428,7 @@ async function startFileWatcher(opts = {}) {
14160
14428
  timers.set(relativePath, t);
14161
14429
  }
14162
14430
  async function runHook(relativePath, accountId) {
14163
- const absolute = resolve22(dataRoot, relativePath);
14431
+ const absolute = resolve23(dataRoot, relativePath);
14164
14432
  let exists = false;
14165
14433
  try {
14166
14434
  const st = await fsp2.stat(absolute);
@@ -14184,7 +14452,7 @@ async function startFileWatcher(opts = {}) {
14184
14452
  }
14185
14453
  }
14186
14454
  async function watchRoot(rootName) {
14187
- const absRoot = resolve22(dataRoot, rootName);
14455
+ const absRoot = resolve23(dataRoot, rootName);
14188
14456
  try {
14189
14457
  const iter = fsp2.watch(absRoot, { recursive: true, signal: controller.signal });
14190
14458
  for await (const event of iter) {
@@ -14282,8 +14550,8 @@ function broadcastAdminShutdown(reason) {
14282
14550
 
14283
14551
  // ../lib/entitlement/src/index.ts
14284
14552
  import { createPublicKey, createHash as createHash4, verify as cryptoVerify } from "crypto";
14285
- import { existsSync as existsSync21, readFileSync as readFileSync21, statSync as statSync9 } from "fs";
14286
- import { resolve as resolve23 } from "path";
14553
+ import { existsSync as existsSync22, readFileSync as readFileSync21, statSync as statSync9 } from "fs";
14554
+ import { resolve as resolve24 } from "path";
14287
14555
 
14288
14556
  // ../lib/entitlement/src/canonicalize.ts
14289
14557
  function canonicalize(value) {
@@ -14318,7 +14586,7 @@ var PUBKEY_SHA256 = "8eee6bcb33545fd13b16d3199a5735ca5db5062834c7b49dfe4f23801d9
14318
14586
  var GRACE_DAYS = 7;
14319
14587
  var GRACE_MS = GRACE_DAYS * 24 * 60 * 60 * 1e3;
14320
14588
  function pubkeyPath(brand) {
14321
- return resolve23(brand.platformRoot, "lib", "entitlement", "rubytech-pubkey.pem");
14589
+ return resolve24(brand.platformRoot, "lib", "entitlement", "rubytech-pubkey.pem");
14322
14590
  }
14323
14591
  var memo = null;
14324
14592
  function memoKey(mtimeMs, account) {
@@ -14330,8 +14598,8 @@ function resolveEntitlement(brand, account) {
14330
14598
  if (brand.commercialMode !== true) {
14331
14599
  return logResolved(implicitTrust(account), null);
14332
14600
  }
14333
- const entitlementPath = resolve23(brand.configDir, "entitlement.json");
14334
- if (!existsSync21(entitlementPath)) {
14601
+ const entitlementPath = resolve24(brand.configDir, "entitlement.json");
14602
+ if (!existsSync22(entitlementPath)) {
14335
14603
  return logResolved(anonymousFallback("missing"), { reason: "missing" });
14336
14604
  }
14337
14605
  const stat7 = statSync9(entitlementPath);
@@ -14523,10 +14791,10 @@ function clientFrom(c) {
14523
14791
  var PLATFORM_ROOT9 = process.env.MAXY_PLATFORM_ROOT || "";
14524
14792
  var BRAND_JSON_PATH = PLATFORM_ROOT9 ? join17(PLATFORM_ROOT9, "config", "brand.json") : "";
14525
14793
  var BRAND = { productName: "Maxy", hostname: "maxy", configDir: ".maxy", marketingUrl: "https://getmaxy.com" };
14526
- if (BRAND_JSON_PATH && !existsSync22(BRAND_JSON_PATH)) {
14794
+ if (BRAND_JSON_PATH && !existsSync23(BRAND_JSON_PATH)) {
14527
14795
  console.error(`[brand] WARNING: brand.json not found at ${BRAND_JSON_PATH} \u2014 using Maxy defaults`);
14528
14796
  }
14529
- if (BRAND_JSON_PATH && existsSync22(BRAND_JSON_PATH)) {
14797
+ if (BRAND_JSON_PATH && existsSync23(BRAND_JSON_PATH)) {
14530
14798
  try {
14531
14799
  const parsed = JSON.parse(readFileSync22(BRAND_JSON_PATH, "utf-8"));
14532
14800
  BRAND = { ...BRAND, ...parsed };
@@ -14550,7 +14818,7 @@ var brandLoginOpts = {
14550
14818
  var ALIAS_DOMAINS_PATH = join17(homedir2(), BRAND.configDir, "alias-domains.json");
14551
14819
  function loadAliasDomains() {
14552
14820
  try {
14553
- if (!existsSync22(ALIAS_DOMAINS_PATH)) return null;
14821
+ if (!existsSync23(ALIAS_DOMAINS_PATH)) return null;
14554
14822
  const parsed = JSON.parse(readFileSync22(ALIAS_DOMAINS_PATH, "utf-8"));
14555
14823
  if (!Array.isArray(parsed)) {
14556
14824
  console.error("[alias-domains] malformed alias-domains.json \u2014 expected array");
@@ -14575,9 +14843,9 @@ watchFile(ALIAS_DOMAINS_PATH, { interval: 2e3 }, () => {
14575
14843
  function isPublicHost(host) {
14576
14844
  return host.startsWith("public.") || aliasDomains.has(host);
14577
14845
  }
14578
- var app41 = new Hono();
14579
- app41.use("*", clientIpMiddleware);
14580
- app41.use("*", async (c, next) => {
14846
+ var app42 = new Hono();
14847
+ app42.use("*", clientIpMiddleware);
14848
+ app42.use("*", async (c, next) => {
14581
14849
  await next();
14582
14850
  c.header("X-Content-Type-Options", "nosniff");
14583
14851
  c.header("Referrer-Policy", "strict-origin-when-cross-origin");
@@ -14587,7 +14855,7 @@ app41.use("*", async (c, next) => {
14587
14855
  );
14588
14856
  });
14589
14857
  var HTTP_LOG_PATHS = /* @__PURE__ */ new Set(["/vnc-viewer.html", "/vnc-popout.html"]);
14590
- app41.use("*", async (c, next) => {
14858
+ app42.use("*", async (c, next) => {
14591
14859
  if (!HTTP_LOG_PATHS.has(c.req.path)) {
14592
14860
  await next();
14593
14861
  return;
@@ -14605,38 +14873,14 @@ app41.use("*", async (c, next) => {
14605
14873
  });
14606
14874
  }
14607
14875
  });
14608
- var PUBLIC_ALLOWED_PREFIXES = [
14609
- "/api/session",
14610
- "/api/chat",
14611
- "/api/access/",
14612
- "/api/health",
14613
- "/api/group/",
14614
- "/api/telegram/",
14615
- "/assets/",
14616
- "/brand/",
14617
- "/agent-assets/",
14618
- "/generated/",
14619
- "/g/",
14620
- "/sites/"
14621
- ];
14622
- var PUBLIC_ALLOWED_EXACT = ["/favicon.ico"];
14623
- app41.use("*", async (c, next) => {
14876
+ app42.use("*", async (c, next) => {
14624
14877
  const host = (c.req.header("host") ?? "").split(":")[0];
14625
14878
  if (!isPublicHost(host)) {
14626
14879
  await next();
14627
14880
  return;
14628
14881
  }
14629
14882
  const path2 = c.req.path;
14630
- if (path2 === "/") {
14631
- await next();
14632
- return;
14633
- }
14634
- const allowed = PUBLIC_ALLOWED_EXACT.includes(path2) || PUBLIC_ALLOWED_PREFIXES.some((prefix) => path2.startsWith(prefix));
14635
- if (allowed) {
14636
- await next();
14637
- return;
14638
- }
14639
- if (AGENT_SLUG_PATTERN.test(path2)) {
14883
+ if (isPublicPathAllowed(path2)) {
14640
14884
  await next();
14641
14885
  return;
14642
14886
  }
@@ -14660,7 +14904,7 @@ function resolveRemoteAuthOpts() {
14660
14904
  return brandLoginOpts;
14661
14905
  }
14662
14906
  var MAX_LOGIN_BODY = 8 * 1024;
14663
- app41.post("/__remote-auth/login", async (c) => {
14907
+ app42.post("/__remote-auth/login", async (c) => {
14664
14908
  const client = clientFrom(c);
14665
14909
  const clientIp = client.ip || "unknown";
14666
14910
  if (!requestIsTlsTerminated(c)) {
@@ -14705,7 +14949,7 @@ app41.post("/__remote-auth/login", async (c) => {
14705
14949
  }
14706
14950
  });
14707
14951
  });
14708
- app41.get("/__remote-auth/logout", (c) => {
14952
+ app42.get("/__remote-auth/logout", (c) => {
14709
14953
  const client = clientFrom(c);
14710
14954
  const clientIp = client.ip || "unknown";
14711
14955
  console.error(`[remote-auth] logout ip=${clientIp}`);
@@ -14718,7 +14962,7 @@ app41.get("/__remote-auth/logout", (c) => {
14718
14962
  }
14719
14963
  });
14720
14964
  });
14721
- app41.post("/__remote-auth/change-password", async (c) => {
14965
+ app42.post("/__remote-auth/change-password", async (c) => {
14722
14966
  const client = clientFrom(c);
14723
14967
  const clientIp = client.ip || "unknown";
14724
14968
  const rateLimited = checkRateLimit(client);
@@ -14769,13 +15013,13 @@ app41.post("/__remote-auth/change-password", async (c) => {
14769
15013
  return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(), mode: "change", changeError: "Failed to save password", redirect }), 200);
14770
15014
  }
14771
15015
  });
14772
- app41.get("/__remote-auth/setup", (c) => {
15016
+ app42.get("/__remote-auth/setup", (c) => {
14773
15017
  if (isRemoteAuthConfigured()) {
14774
15018
  return c.redirect("/");
14775
15019
  }
14776
15020
  return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(), mode: "setup" }), 200);
14777
15021
  });
14778
- app41.post("/__remote-auth/set-initial-password", async (c) => {
15022
+ app42.post("/__remote-auth/set-initial-password", async (c) => {
14779
15023
  if (isRemoteAuthConfigured()) {
14780
15024
  return c.redirect("/");
14781
15025
  }
@@ -14813,10 +15057,10 @@ app41.post("/__remote-auth/set-initial-password", async (c) => {
14813
15057
  return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(), mode: "setup", setupError: "Failed to save password. Please try again." }), 200);
14814
15058
  }
14815
15059
  });
14816
- app41.get("/api/remote-auth/status", (c) => {
15060
+ app42.get("/api/remote-auth/status", (c) => {
14817
15061
  return c.json({ configured: isRemoteAuthConfigured() });
14818
15062
  });
14819
- app41.post("/api/remote-auth/set-password", async (c) => {
15063
+ app42.post("/api/remote-auth/set-password", async (c) => {
14820
15064
  let body;
14821
15065
  try {
14822
15066
  body = await c.req.json();
@@ -14847,9 +15091,9 @@ app41.post("/api/remote-auth/set-password", async (c) => {
14847
15091
  return c.json({ error: "Failed to save password" }, 500);
14848
15092
  }
14849
15093
  });
14850
- app41.route("/api/_client-error", client_error_default);
15094
+ app42.route("/api/_client-error", client_error_default);
14851
15095
  console.log("[client-error-route] mounted");
14852
- app41.use("*", async (c, next) => {
15096
+ app42.use("*", async (c, next) => {
14853
15097
  const host = (c.req.header("host") ?? "").split(":")[0];
14854
15098
  const path2 = c.req.path;
14855
15099
  if (path2 === "/favicon.ico" || path2.startsWith("/assets/") || path2.startsWith("/brand/")) {
@@ -14882,12 +15126,13 @@ app41.use("*", async (c, next) => {
14882
15126
  console.error(`[remote-auth] login required ip=${clientIp} path=${path2} ${disambig}`);
14883
15127
  return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(), redirect: path2 }), 200);
14884
15128
  });
14885
- app41.route("/api/health", health_default);
14886
- app41.route("/api/chat", chat_default);
14887
- app41.route("/api/whatsapp", whatsapp_default);
14888
- app41.route("/api/onboarding", onboarding_default);
14889
- app41.route("/api/admin", admin_default);
14890
- app41.route("/api/access", access_default);
15129
+ app42.route("/api/health", health_default);
15130
+ app42.route("/api/chat", chat_default);
15131
+ app42.route("/api/whatsapp", whatsapp_default);
15132
+ app42.route("/api/onboarding", onboarding_default);
15133
+ app42.route("/api/admin", admin_default);
15134
+ app42.route("/api/access", access_default);
15135
+ app42.route("/api/session", session_default2);
14891
15136
  var SAFE_SLUG_RE2 = /^[a-z][a-z0-9-]{2,49}$/;
14892
15137
  var SAFE_FILENAME_RE = /^[a-z0-9_][a-z0-9_.-]{0,99}$/i;
14893
15138
  var IMAGE_MIME = {
@@ -14899,7 +15144,7 @@ var IMAGE_MIME = {
14899
15144
  ".svg": "image/svg+xml",
14900
15145
  ".ico": "image/x-icon"
14901
15146
  };
14902
- app41.get("/agent-assets/:slug/:filename", (c) => {
15147
+ app42.get("/agent-assets/:slug/:filename", (c) => {
14903
15148
  const slug = c.req.param("slug");
14904
15149
  const filename = c.req.param("filename");
14905
15150
  if (!SAFE_SLUG_RE2.test(slug)) {
@@ -14915,13 +15160,13 @@ app41.get("/agent-assets/:slug/:filename", (c) => {
14915
15160
  console.error(`[agent-assets] no-account slug=${slug} file=${filename}`);
14916
15161
  return c.text("Not found", 404);
14917
15162
  }
14918
- const filePath = resolve24(account.accountDir, "agents", slug, "assets", filename);
14919
- const expectedDir = resolve24(account.accountDir, "agents", slug, "assets");
15163
+ const filePath = resolve25(account.accountDir, "agents", slug, "assets", filename);
15164
+ const expectedDir = resolve25(account.accountDir, "agents", slug, "assets");
14920
15165
  if (!filePath.startsWith(expectedDir + "/")) {
14921
15166
  console.error(`[agent-assets] path-traversal-rejected slug=${slug} file=${filename}`);
14922
15167
  return c.text("Forbidden", 403);
14923
15168
  }
14924
- if (!existsSync22(filePath)) {
15169
+ if (!existsSync23(filePath)) {
14925
15170
  console.error(`[agent-assets] serve slug=${slug} file=${filename} status=404`);
14926
15171
  return c.text("Not found", 404);
14927
15172
  }
@@ -14934,7 +15179,7 @@ app41.get("/agent-assets/:slug/:filename", (c) => {
14934
15179
  "Cache-Control": "public, max-age=3600"
14935
15180
  });
14936
15181
  });
14937
- app41.get("/generated/:filename", (c) => {
15182
+ app42.get("/generated/:filename", (c) => {
14938
15183
  const filename = c.req.param("filename");
14939
15184
  if (!SAFE_FILENAME_RE.test(filename) || filename.includes("..")) {
14940
15185
  console.error(`[generated] serve file=${filename} status=403`);
@@ -14945,13 +15190,13 @@ app41.get("/generated/:filename", (c) => {
14945
15190
  console.error(`[generated] serve file=${filename} status=404`);
14946
15191
  return c.text("Not found", 404);
14947
15192
  }
14948
- const filePath = resolve24(account.accountDir, "generated", filename);
14949
- const expectedDir = resolve24(account.accountDir, "generated");
15193
+ const filePath = resolve25(account.accountDir, "generated", filename);
15194
+ const expectedDir = resolve25(account.accountDir, "generated");
14950
15195
  if (!filePath.startsWith(expectedDir + "/")) {
14951
15196
  console.error(`[generated] serve file=${filename} status=403`);
14952
15197
  return c.text("Forbidden", 403);
14953
15198
  }
14954
- if (!existsSync22(filePath)) {
15199
+ if (!existsSync23(filePath)) {
14955
15200
  console.error(`[generated] serve file=${filename} status=404`);
14956
15201
  return c.text("Not found", 404);
14957
15202
  }
@@ -14964,14 +15209,14 @@ app41.get("/generated/:filename", (c) => {
14964
15209
  "Cache-Control": "public, max-age=86400"
14965
15210
  });
14966
15211
  });
14967
- app41.route("/sites", sites_default);
14968
- app41.route("/listings", listings_default);
14969
- app41.route("/v", visitor_event_default);
14970
- app41.route("/v", visitor_consent_default);
15212
+ app42.route("/sites", sites_default);
15213
+ app42.route("/listings", listings_default);
15214
+ app42.route("/v", visitor_event_default);
15215
+ app42.route("/v", visitor_consent_default);
14971
15216
  var htmlCache = /* @__PURE__ */ new Map();
14972
15217
  var brandLogoPath = "/brand/maxy-monochrome.png";
14973
15218
  var brandIconPath = "/brand/maxy-monochrome.png";
14974
- if (BRAND_JSON_PATH && existsSync22(BRAND_JSON_PATH)) {
15219
+ if (BRAND_JSON_PATH && existsSync23(BRAND_JSON_PATH)) {
14975
15220
  try {
14976
15221
  const fullBrand = JSON.parse(readFileSync22(BRAND_JSON_PATH, "utf-8"));
14977
15222
  if (fullBrand.assets?.logo) brandLogoPath = `/brand/${fullBrand.assets.logo}`;
@@ -14991,7 +15236,7 @@ function readInstalledVersion() {
14991
15236
  try {
14992
15237
  if (!PLATFORM_ROOT9) return "unknown";
14993
15238
  const versionFile = join17(PLATFORM_ROOT9, "config", `.${BRAND.hostname}-version`);
14994
- if (!existsSync22(versionFile)) return "unknown";
15239
+ if (!existsSync23(versionFile)) return "unknown";
14995
15240
  const content = readFileSync22(versionFile, "utf-8").trim();
14996
15241
  return content || "unknown";
14997
15242
  } catch {
@@ -15033,7 +15278,7 @@ var clientErrorReporterScript = `<script>
15033
15278
  function cachedHtml(file) {
15034
15279
  let html = htmlCache.get(file);
15035
15280
  if (!html) {
15036
- html = readFileSync22(resolve24(process.cwd(), "public", file), "utf-8");
15281
+ html = readFileSync22(resolve25(process.cwd(), "public", file), "utf-8");
15037
15282
  const productNameEsc = escapeHtml(BRAND.productName);
15038
15283
  html = html.replace(/<title>([^<]*)<\/title>/, (_match, inner) => `<title>${escapeHtml(inner).replace(/Maxy/g, productNameEsc)}</title>`);
15039
15284
  html = html.replace('href="/favicon.ico"', `href="${escapeHtml(brandFaviconPath)}"`);
@@ -15052,12 +15297,12 @@ function loadBrandingCache(agentSlug) {
15052
15297
  const configDir2 = join17(homedir2(), BRAND.configDir);
15053
15298
  try {
15054
15299
  const accountJsonPath = join17(configDir2, "account.json");
15055
- if (!existsSync22(accountJsonPath)) return null;
15300
+ if (!existsSync23(accountJsonPath)) return null;
15056
15301
  const account = JSON.parse(readFileSync22(accountJsonPath, "utf-8"));
15057
15302
  const accountId = account.accountId;
15058
15303
  if (!accountId) return null;
15059
15304
  const cachePath = join17(configDir2, "branding-cache", accountId, `${agentSlug}.json`);
15060
- if (!existsSync22(cachePath)) return null;
15305
+ if (!existsSync23(cachePath)) return null;
15061
15306
  return JSON.parse(readFileSync22(cachePath, "utf-8"));
15062
15307
  } catch {
15063
15308
  return null;
@@ -15067,7 +15312,7 @@ function resolveDefaultSlug() {
15067
15312
  try {
15068
15313
  const configDir2 = join17(homedir2(), BRAND.configDir);
15069
15314
  const accountJsonPath = join17(configDir2, "account.json");
15070
- if (!existsSync22(accountJsonPath)) return null;
15315
+ if (!existsSync23(accountJsonPath)) return null;
15071
15316
  const account = JSON.parse(readFileSync22(accountJsonPath, "utf-8"));
15072
15317
  return account.defaultAgent || null;
15073
15318
  } catch {
@@ -15104,7 +15349,7 @@ function brandedPublicHtml(agentSlug) {
15104
15349
  function escapeHtml(s) {
15105
15350
  return s.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
15106
15351
  }
15107
- app41.get("/", (c) => {
15352
+ app42.get("/", (c) => {
15108
15353
  const host = (c.req.header("host") ?? "").split(":")[0];
15109
15354
  if (isPublicHost(host)) {
15110
15355
  const defaultSlug = resolveDefaultSlug();
@@ -15112,12 +15357,12 @@ app41.get("/", (c) => {
15112
15357
  }
15113
15358
  return c.html(cachedHtml("index.html"));
15114
15359
  });
15115
- app41.get("/public", (c) => {
15360
+ app42.get("/public", (c) => {
15116
15361
  const host = (c.req.header("host") ?? "").split(":")[0];
15117
15362
  if (isPublicHost(host)) return c.text("Not found", 404);
15118
15363
  return c.html(cachedHtml("public.html"));
15119
15364
  });
15120
- app41.get("/chat", (c) => {
15365
+ app42.get("/chat", (c) => {
15121
15366
  const host = (c.req.header("host") ?? "").split(":")[0];
15122
15367
  if (isPublicHost(host)) return c.text("Not found", 404);
15123
15368
  return c.html(cachedHtml("public.html"));
@@ -15136,12 +15381,12 @@ async function logViewerFetch(c, next) {
15136
15381
  duration_ms: Date.now() - start
15137
15382
  });
15138
15383
  }
15139
- app41.use("/vnc-viewer.html", logViewerFetch);
15140
- app41.use("/vnc-popout.html", logViewerFetch);
15141
- app41.get("/vnc-popout.html", (c) => {
15384
+ app42.use("/vnc-viewer.html", logViewerFetch);
15385
+ app42.use("/vnc-popout.html", logViewerFetch);
15386
+ app42.get("/vnc-popout.html", (c) => {
15142
15387
  let html = htmlCache.get("vnc-popout.html");
15143
15388
  if (!html) {
15144
- html = readFileSync22(resolve24(process.cwd(), "public", "vnc-popout.html"), "utf-8");
15389
+ html = readFileSync22(resolve25(process.cwd(), "public", "vnc-popout.html"), "utf-8");
15145
15390
  const name = escapeHtml(BRAND.productName);
15146
15391
  html = html.replace("<title>Browser \u2014 Maxy</title>", `<title>${name}</title>`);
15147
15392
  html = html.replace("</head>", ` ${brandScript}
@@ -15151,7 +15396,7 @@ app41.get("/vnc-popout.html", (c) => {
15151
15396
  }
15152
15397
  return c.html(html);
15153
15398
  });
15154
- app41.post("/api/vnc/client-event", async (c) => {
15399
+ app42.post("/api/vnc/client-event", async (c) => {
15155
15400
  let body;
15156
15401
  try {
15157
15402
  body = await c.req.json();
@@ -15172,25 +15417,25 @@ app41.post("/api/vnc/client-event", async (c) => {
15172
15417
  });
15173
15418
  return c.json({ ok: true });
15174
15419
  });
15175
- app41.get("/g/:slug", (c) => {
15420
+ app42.get("/g/:slug", (c) => {
15176
15421
  return c.html(brandedPublicHtml());
15177
15422
  });
15178
- app41.get("/graph", (c) => {
15423
+ app42.get("/graph", (c) => {
15179
15424
  const host = (c.req.header("host") ?? "").split(":")[0];
15180
15425
  if (isPublicHost(host)) return c.text("Not found", 404);
15181
15426
  return c.html(cachedHtml("graph.html"));
15182
15427
  });
15183
- app41.get("/sessions", (c) => {
15428
+ app42.get("/sessions", (c) => {
15184
15429
  const host = (c.req.header("host") ?? "").split(":")[0];
15185
15430
  if (isPublicHost(host)) return c.text("Not found", 404);
15186
15431
  return c.html(cachedHtml("sessions.html"));
15187
15432
  });
15188
- app41.get("/data", (c) => {
15433
+ app42.get("/data", (c) => {
15189
15434
  const host = (c.req.header("host") ?? "").split(":")[0];
15190
15435
  if (isPublicHost(host)) return c.text("Not found", 404);
15191
15436
  return c.html(cachedHtml("data.html"));
15192
15437
  });
15193
- app41.get("/:slug", async (c, next) => {
15438
+ app42.get("/:slug", async (c, next) => {
15194
15439
  const slug = c.req.param("slug");
15195
15440
  if (AGENT_SLUG_PATTERN.test(`/${slug}`)) {
15196
15441
  const branding = loadBrandingCache(slug);
@@ -15200,15 +15445,27 @@ app41.get("/:slug", async (c, next) => {
15200
15445
  await next();
15201
15446
  });
15202
15447
  if (brandFaviconPath !== "/favicon.ico") {
15203
- app41.get("/favicon.ico", (c) => {
15448
+ app42.get("/favicon.ico", (c) => {
15204
15449
  c.header("Cache-Control", "public, max-age=300");
15205
15450
  return c.redirect(brandFaviconPath, 302);
15206
15451
  });
15207
15452
  }
15208
- app41.use("/*", serveStatic({ root: "./public" }));
15453
+ app42.use("/*", serveStatic({ root: "./public" }));
15454
+ app42.all("*", (c) => {
15455
+ const host = (c.req.header("host") ?? "").split(":")[0];
15456
+ const path2 = c.req.path;
15457
+ if (isPublicHost(host)) {
15458
+ const inAllowedList = PUBLIC_ALLOWED_EXACT.includes(path2) || PUBLIC_ALLOWED_PREFIXES.some((prefix) => path2.startsWith(prefix));
15459
+ if (inAllowedList) {
15460
+ console.error(`[public-host] unmatched-allowed path=${path2}`);
15461
+ return c.json({ error: "not-found" }, 404);
15462
+ }
15463
+ }
15464
+ return c.text("Not found", 404);
15465
+ });
15209
15466
  var port = requirePortEnv("MAXY_UI_INTERNAL_PORT", { tag: "ui-server" });
15210
15467
  var hostname = process.env.HOSTNAME ?? "127.0.0.1";
15211
- var httpServer = serve({ fetch: app41.fetch, port, hostname });
15468
+ var httpServer = serve({ fetch: app42.fetch, port, hostname });
15212
15469
  console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
15213
15470
  startAuthHealthHeartbeat();
15214
15471
  {
@@ -15234,6 +15491,7 @@ var SUBAPP_MANIFEST = [
15234
15491
  { prefix: "/api/onboarding", file: "server/routes/onboarding.ts", subapp: onboarding_default },
15235
15492
  { prefix: "/api/admin", file: "server/routes/admin/index.ts", subapp: admin_default },
15236
15493
  { prefix: "/api/access", file: "server/routes/access/index.ts", subapp: access_default },
15494
+ { prefix: "/api/session", file: "server/routes/session.ts", subapp: session_default2 },
15237
15495
  { prefix: "/api/_client-error", file: "server/routes/client-error.ts", subapp: client_error_default },
15238
15496
  { prefix: "/listings", file: "server/routes/listings.ts", subapp: listings_default },
15239
15497
  { prefix: "/v", file: "server/routes/visitor-event.ts", subapp: visitor_event_default },
@@ -15245,7 +15503,7 @@ for (const m of SUBAPP_MANIFEST) {
15245
15503
  }
15246
15504
  try {
15247
15505
  const registered = [];
15248
- for (const r of app41.routes ?? []) {
15506
+ for (const r of app42.routes ?? []) {
15249
15507
  if (typeof r.path !== "string" || r.path.includes(":") || r.path.includes("*")) continue;
15250
15508
  if (AGENT_SLUG_PATTERN.test(r.path)) {
15251
15509
  registered.push({ method: (r.method ?? "ALL").toUpperCase(), path: r.path });
@@ -15267,7 +15525,7 @@ try {
15267
15525
  }
15268
15526
  (async () => {
15269
15527
  try {
15270
- if (!existsSync22(USERS_FILE)) return;
15528
+ if (!existsSync23(USERS_FILE)) return;
15271
15529
  const usersRaw = readFileSync22(USERS_FILE, "utf-8").trim();
15272
15530
  if (!usersRaw) return;
15273
15531
  const users = JSON.parse(usersRaw);
@@ -15363,7 +15621,7 @@ if (bootAccountConfig?.whatsapp) {
15363
15621
  }
15364
15622
  init({
15365
15623
  configDir: configDirForWhatsApp,
15366
- platformRoot: resolve24(process.env.MAXY_PLATFORM_ROOT ?? join17(__dirname, "..")),
15624
+ platformRoot: resolve25(process.env.MAXY_PLATFORM_ROOT ?? join17(__dirname, "..")),
15367
15625
  accountConfig: bootAccountConfig,
15368
15626
  onMessage: async (msg) => {
15369
15627
  if (process.env.WHATSAPP_PTY_BRIDGE_ENABLED === "true" && msg.text && !msg.isOwnerMirror) {