@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.
- package/package.json +1 -1
- package/payload/platform/services/claude-session-manager/dist/http-server.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/http-server.js +10 -0
- package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.js +25 -16
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/rc-daemon.d.ts +10 -0
- package/payload/platform/services/claude-session-manager/dist/rc-daemon.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/rc-daemon.js +6 -0
- package/payload/platform/services/claude-session-manager/dist/rc-daemon.js.map +1 -1
- package/payload/server/{chunk-63M3ILJ6.js → chunk-ZY6W3UA2.js} +140 -0
- package/payload/server/maxy-edge.js +1 -1
- package/payload/server/server.js +382 -124
package/payload/server/server.js
CHANGED
|
@@ -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-
|
|
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
|
|
820
|
-
import { resolve as
|
|
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
|
-
(
|
|
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((
|
|
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
|
-
|
|
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((
|
|
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
|
-
|
|
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((
|
|
2109
|
-
release =
|
|
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((
|
|
3146
|
-
const timer2 = setTimeout(
|
|
3175
|
+
await new Promise((resolve26) => {
|
|
3176
|
+
const timer2 = setTimeout(resolve26, delay);
|
|
3147
3177
|
conn.abortController.signal.addEventListener("abort", () => {
|
|
3148
3178
|
clearTimeout(timer2);
|
|
3149
|
-
|
|
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((
|
|
3187
|
+
return new Promise((resolve26) => {
|
|
3158
3188
|
if (!conn.sock) {
|
|
3159
|
-
|
|
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
|
-
|
|
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((
|
|
3429
|
-
resolvePending =
|
|
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((
|
|
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
|
-
|
|
3868
|
+
resolve26(true);
|
|
3839
3869
|
});
|
|
3840
3870
|
socket.once("error", () => {
|
|
3841
3871
|
socket.destroy();
|
|
3842
|
-
|
|
3872
|
+
resolve26(false);
|
|
3843
3873
|
});
|
|
3844
3874
|
socket.once("timeout", () => {
|
|
3845
3875
|
socket.destroy();
|
|
3846
|
-
|
|
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
|
|
4612
|
+
let resolve26;
|
|
4583
4613
|
const turnPromise = new Promise((r) => {
|
|
4584
|
-
|
|
4614
|
+
resolve26 = r;
|
|
4585
4615
|
});
|
|
4586
4616
|
const listener = (text) => {
|
|
4587
|
-
|
|
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((
|
|
5030
|
-
resolveQr =
|
|
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
|
|
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 =
|
|
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((
|
|
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
|
|
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(
|
|
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 =
|
|
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 =
|
|
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
|
|
14286
|
-
import { resolve as
|
|
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
|
|
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 =
|
|
14334
|
-
if (!
|
|
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 && !
|
|
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 &&
|
|
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 (!
|
|
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
|
|
14579
|
-
|
|
14580
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
15060
|
+
app42.get("/api/remote-auth/status", (c) => {
|
|
14817
15061
|
return c.json({ configured: isRemoteAuthConfigured() });
|
|
14818
15062
|
});
|
|
14819
|
-
|
|
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
|
-
|
|
15094
|
+
app42.route("/api/_client-error", client_error_default);
|
|
14851
15095
|
console.log("[client-error-route] mounted");
|
|
14852
|
-
|
|
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
|
-
|
|
14886
|
-
|
|
14887
|
-
|
|
14888
|
-
|
|
14889
|
-
|
|
14890
|
-
|
|
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
|
-
|
|
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 =
|
|
14919
|
-
const expectedDir =
|
|
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 (!
|
|
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
|
-
|
|
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 =
|
|
14949
|
-
const expectedDir =
|
|
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 (!
|
|
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
|
-
|
|
14968
|
-
|
|
14969
|
-
|
|
14970
|
-
|
|
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 &&
|
|
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 (!
|
|
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(
|
|
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 (!
|
|
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 (!
|
|
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 (!
|
|
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, "&").replace(/"/g, """).replace(/</g, "<").replace(/>/g, ">");
|
|
15106
15351
|
}
|
|
15107
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
15140
|
-
|
|
15141
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
15420
|
+
app42.get("/g/:slug", (c) => {
|
|
15176
15421
|
return c.html(brandedPublicHtml());
|
|
15177
15422
|
});
|
|
15178
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
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
|
|
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 (!
|
|
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:
|
|
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) {
|