@rubytech/create-maxy-code 0.1.238 → 0.1.239
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
CHANGED
|
@@ -3591,6 +3591,13 @@ function readAgentFile(accountDir, agentName, filename) {
|
|
|
3591
3591
|
}
|
|
3592
3592
|
return raw2;
|
|
3593
3593
|
}
|
|
3594
|
+
var RESERVED_SLUGS = /* @__PURE__ */ new Set(["admin", "api", "assets", "brand", "bot", "privacy"]);
|
|
3595
|
+
var SLUG_PATTERN = /^[a-z][a-z0-9-]{2,49}$/;
|
|
3596
|
+
function validateAgentSlug(slug) {
|
|
3597
|
+
if (!SLUG_PATTERN.test(slug)) return false;
|
|
3598
|
+
if (RESERVED_SLUGS.has(slug)) return false;
|
|
3599
|
+
return true;
|
|
3600
|
+
}
|
|
3594
3601
|
function resolveDefaultAgentSlug(accountDir) {
|
|
3595
3602
|
const configPath = resolve3(accountDir, "account.json");
|
|
3596
3603
|
if (!existsSync4(configPath)) {
|
|
@@ -3720,6 +3727,9 @@ function resolveAgentConfig(accountDir, agentName) {
|
|
|
3720
3727
|
}
|
|
3721
3728
|
return { model, plugins, status, displayName, image, imageShape, showAgentName, knowledge, knowledgeBaked, liveMemory, knowledgeKeywords, budget, accessMode };
|
|
3722
3729
|
}
|
|
3730
|
+
function getDefaultAccountId() {
|
|
3731
|
+
return resolveAccount()?.accountId ?? null;
|
|
3732
|
+
}
|
|
3723
3733
|
function resolveUserAccounts(userId) {
|
|
3724
3734
|
if (!existsSync4(ACCOUNTS_DIR)) return [];
|
|
3725
3735
|
const results = [];
|
|
@@ -4195,6 +4205,129 @@ async function ensureConversation(accountId, agentType, cacheKey2, visitorId, ag
|
|
|
4195
4205
|
await session.close();
|
|
4196
4206
|
}
|
|
4197
4207
|
}
|
|
4208
|
+
async function findRecentConversation(visitorId, accountId, agentSlug, maxAgeHours = 24) {
|
|
4209
|
+
const session = getSession();
|
|
4210
|
+
try {
|
|
4211
|
+
const result = await session.run(
|
|
4212
|
+
`MATCH (c:Conversation {visitorId: $visitorId, accountId: $accountId, agentType: 'public'})
|
|
4213
|
+
WHERE c.agentSlug = $agentSlug
|
|
4214
|
+
AND c.updatedAt > datetime() - duration({hours: $maxAgeHours})
|
|
4215
|
+
AND NOT c:Trashed
|
|
4216
|
+
RETURN c.sessionId AS sessionId
|
|
4217
|
+
ORDER BY c.updatedAt DESC
|
|
4218
|
+
LIMIT 1`,
|
|
4219
|
+
{ visitorId, accountId, agentSlug, maxAgeHours: neo4j.int(maxAgeHours) },
|
|
4220
|
+
{ timeout: 2e3 }
|
|
4221
|
+
);
|
|
4222
|
+
const record = result.records[0];
|
|
4223
|
+
if (!record) return null;
|
|
4224
|
+
const sessionId = record.get("sessionId");
|
|
4225
|
+
if (!sessionId) return null;
|
|
4226
|
+
console.log(`[persist] found recent conversation ${sessionId.slice(0, 8)}\u2026 for visitor ${visitorId.slice(0, 8)}\u2026`);
|
|
4227
|
+
return { sessionId };
|
|
4228
|
+
} catch (err) {
|
|
4229
|
+
console.error(`[persist] findRecentConversation failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
4230
|
+
return null;
|
|
4231
|
+
} finally {
|
|
4232
|
+
await session.close();
|
|
4233
|
+
}
|
|
4234
|
+
}
|
|
4235
|
+
async function findGroupBySlug(groupSlug, accountId) {
|
|
4236
|
+
const session = getSession();
|
|
4237
|
+
try {
|
|
4238
|
+
const result = await session.run(
|
|
4239
|
+
`MATCH (c:Conversation {groupSlug: $groupSlug, accountId: $accountId, type: 'group'})
|
|
4240
|
+
WHERE NOT c:Trashed
|
|
4241
|
+
RETURN c.sessionId AS sessionId, c.groupName AS groupName, c.agentSlug AS agentSlug`,
|
|
4242
|
+
{ groupSlug, accountId },
|
|
4243
|
+
{ timeout: 2e3 }
|
|
4244
|
+
);
|
|
4245
|
+
const record = result.records[0];
|
|
4246
|
+
if (!record) return null;
|
|
4247
|
+
return {
|
|
4248
|
+
sessionId: record.get("sessionId"),
|
|
4249
|
+
groupName: record.get("groupName"),
|
|
4250
|
+
agentSlug: record.get("agentSlug")
|
|
4251
|
+
};
|
|
4252
|
+
} catch (err) {
|
|
4253
|
+
console.error(`[group] findGroupBySlug failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
4254
|
+
return null;
|
|
4255
|
+
} finally {
|
|
4256
|
+
await session.close();
|
|
4257
|
+
}
|
|
4258
|
+
}
|
|
4259
|
+
async function getGroupParticipants(sessionId) {
|
|
4260
|
+
const session = getSession();
|
|
4261
|
+
try {
|
|
4262
|
+
const result = await session.run(
|
|
4263
|
+
`MATCH (p:Person)-[r:PARTICIPATES_IN]->(c:Conversation {sessionId: $sessionId})
|
|
4264
|
+
RETURN p.givenName AS givenName, p.familyName AS familyName,
|
|
4265
|
+
r.displayName AS displayName, r.joinedAt AS joinedAt, r.visitorId AS visitorId`,
|
|
4266
|
+
{ sessionId }
|
|
4267
|
+
);
|
|
4268
|
+
return result.records.map((r) => ({
|
|
4269
|
+
displayName: r.get("displayName") || r.get("givenName"),
|
|
4270
|
+
givenName: r.get("givenName"),
|
|
4271
|
+
familyName: r.get("familyName"),
|
|
4272
|
+
joinedAt: String(r.get("joinedAt")),
|
|
4273
|
+
visitorId: r.get("visitorId")
|
|
4274
|
+
}));
|
|
4275
|
+
} catch (err) {
|
|
4276
|
+
console.error(`[group] getGroupParticipants failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
4277
|
+
return [];
|
|
4278
|
+
} finally {
|
|
4279
|
+
await session.close();
|
|
4280
|
+
}
|
|
4281
|
+
}
|
|
4282
|
+
async function checkGroupMembership(sessionId, visitorId) {
|
|
4283
|
+
const session = getSession();
|
|
4284
|
+
try {
|
|
4285
|
+
const result = await session.run(
|
|
4286
|
+
`MATCH (p:Person)-[r:PARTICIPATES_IN]->(c:Conversation {sessionId: $sessionId})
|
|
4287
|
+
WHERE r.visitorId = $visitorId
|
|
4288
|
+
RETURN r.displayName AS displayName
|
|
4289
|
+
LIMIT 1`,
|
|
4290
|
+
{ sessionId, visitorId }
|
|
4291
|
+
);
|
|
4292
|
+
return result.records[0]?.get("displayName") ?? null;
|
|
4293
|
+
} catch (err) {
|
|
4294
|
+
console.error(`[group] checkGroupMembership failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
4295
|
+
return null;
|
|
4296
|
+
} finally {
|
|
4297
|
+
await session.close();
|
|
4298
|
+
}
|
|
4299
|
+
}
|
|
4300
|
+
async function bindVisitorToGroup(sessionId, visitorId, personEmail, personPhone) {
|
|
4301
|
+
const session = getSession();
|
|
4302
|
+
try {
|
|
4303
|
+
const result = await session.run(
|
|
4304
|
+
`MATCH (p:Person)-[r:PARTICIPATES_IN]->(c:Conversation {sessionId: $sessionId})
|
|
4305
|
+
WHERE ($email IS NOT NULL AND p.email = $email)
|
|
4306
|
+
OR ($phone IS NOT NULL AND p.telephone = $phone)
|
|
4307
|
+
SET r.visitorId = $visitorId
|
|
4308
|
+
RETURN r.displayName AS displayName
|
|
4309
|
+
LIMIT 1`,
|
|
4310
|
+
{
|
|
4311
|
+
sessionId,
|
|
4312
|
+
visitorId,
|
|
4313
|
+
email: personEmail ?? null,
|
|
4314
|
+
phone: personPhone ?? null
|
|
4315
|
+
}
|
|
4316
|
+
);
|
|
4317
|
+
const name = result.records[0]?.get("displayName");
|
|
4318
|
+
if (name) {
|
|
4319
|
+
console.error(`[group] joined id=${sessionId.slice(0, 8)}\u2026 visitor=${visitorId.slice(0, 8)}\u2026`);
|
|
4320
|
+
} else {
|
|
4321
|
+
console.error(`[group] auth-denied id=${sessionId.slice(0, 8)}\u2026 visitor=${visitorId.slice(0, 8)}\u2026`);
|
|
4322
|
+
}
|
|
4323
|
+
return name ? { displayName: name } : null;
|
|
4324
|
+
} catch (err) {
|
|
4325
|
+
console.error(`[group] bindVisitorToGroup failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
4326
|
+
return null;
|
|
4327
|
+
} finally {
|
|
4328
|
+
await session.close();
|
|
4329
|
+
}
|
|
4330
|
+
}
|
|
4198
4331
|
var HEX_COLOR_RE = /^#[0-9a-fA-F]{3,8}$/;
|
|
4199
4332
|
async function fetchBranding(accountId) {
|
|
4200
4333
|
const session = getSession();
|
|
@@ -5482,8 +5615,10 @@ export {
|
|
|
5482
5615
|
ACCOUNTS_DIR,
|
|
5483
5616
|
listValidAccounts,
|
|
5484
5617
|
resolveAccount,
|
|
5618
|
+
validateAgentSlug,
|
|
5485
5619
|
resolveDefaultAgentSlug,
|
|
5486
5620
|
resolveAgentConfig,
|
|
5621
|
+
getDefaultAccountId,
|
|
5487
5622
|
resolveUserAccounts,
|
|
5488
5623
|
fingerprintSessionKey,
|
|
5489
5624
|
registerSession,
|
|
@@ -5510,6 +5645,11 @@ export {
|
|
|
5510
5645
|
embed,
|
|
5511
5646
|
GREETING_DIRECTIVE,
|
|
5512
5647
|
ensureConversation,
|
|
5648
|
+
findRecentConversation,
|
|
5649
|
+
findGroupBySlug,
|
|
5650
|
+
getGroupParticipants,
|
|
5651
|
+
checkGroupMembership,
|
|
5652
|
+
bindVisitorToGroup,
|
|
5513
5653
|
fetchBranding,
|
|
5514
5654
|
persistMessage,
|
|
5515
5655
|
getAgentSessionIdForConversation,
|
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,8 +823,8 @@ 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
|
|
|
@@ -1317,7 +1324,7 @@ var credsSaveQueue = Promise.resolve();
|
|
|
1317
1324
|
async function drainCredsSaveQueue(timeoutMs = 5e3) {
|
|
1318
1325
|
console.error(`${TAG2} draining credential save queue\u2026`);
|
|
1319
1326
|
const timer2 = new Promise(
|
|
1320
|
-
(
|
|
1327
|
+
(resolve26) => setTimeout(() => resolve26("timeout"), timeoutMs)
|
|
1321
1328
|
);
|
|
1322
1329
|
const result = await Promise.race([
|
|
1323
1330
|
credsSaveQueue.then(() => "drained"),
|
|
@@ -1445,11 +1452,11 @@ async function createWaSocket(opts) {
|
|
|
1445
1452
|
return sock;
|
|
1446
1453
|
}
|
|
1447
1454
|
async function waitForConnection(sock) {
|
|
1448
|
-
return new Promise((
|
|
1455
|
+
return new Promise((resolve26, reject) => {
|
|
1449
1456
|
const handler = (update) => {
|
|
1450
1457
|
if (update.connection === "open") {
|
|
1451
1458
|
sock.ev.off("connection.update", handler);
|
|
1452
|
-
|
|
1459
|
+
resolve26();
|
|
1453
1460
|
}
|
|
1454
1461
|
if (update.connection === "close") {
|
|
1455
1462
|
sock.ev.off("connection.update", handler);
|
|
@@ -1563,14 +1570,14 @@ ${inspected}`;
|
|
|
1563
1570
|
return inspect2(err, INSPECT_OPTS2);
|
|
1564
1571
|
}
|
|
1565
1572
|
function withTimeout(label, promise, timeoutMs) {
|
|
1566
|
-
return new Promise((
|
|
1573
|
+
return new Promise((resolve26, reject) => {
|
|
1567
1574
|
const timer2 = setTimeout(() => {
|
|
1568
1575
|
reject(new Error(`${label} timed out after ${timeoutMs}ms`));
|
|
1569
1576
|
}, timeoutMs);
|
|
1570
1577
|
promise.then(
|
|
1571
1578
|
(value) => {
|
|
1572
1579
|
clearTimeout(timer2);
|
|
1573
|
-
|
|
1580
|
+
resolve26(value);
|
|
1574
1581
|
},
|
|
1575
1582
|
(err) => {
|
|
1576
1583
|
clearTimeout(timer2);
|
|
@@ -2105,8 +2112,8 @@ async function persistWhatsAppMessage(input) {
|
|
|
2105
2112
|
const { givenName, familyName } = splitName(input.pushName);
|
|
2106
2113
|
const prev = sessionWriteLocks.get(input.cacheKey);
|
|
2107
2114
|
let release;
|
|
2108
|
-
const mine = new Promise((
|
|
2109
|
-
release =
|
|
2115
|
+
const mine = new Promise((resolve26) => {
|
|
2116
|
+
release = resolve26;
|
|
2110
2117
|
});
|
|
2111
2118
|
const chained = (prev ?? Promise.resolve()).then(() => mine);
|
|
2112
2119
|
sessionWriteLocks.set(input.cacheKey, chained);
|
|
@@ -3142,11 +3149,11 @@ async function connectWithReconnect(conn) {
|
|
|
3142
3149
|
console.error(
|
|
3143
3150
|
`${TAG12} reconnecting account=${conn.accountId} in ${delay}ms (attempt ${decision.nextAttempts}/${maxAttempts})`
|
|
3144
3151
|
);
|
|
3145
|
-
await new Promise((
|
|
3146
|
-
const timer2 = setTimeout(
|
|
3152
|
+
await new Promise((resolve26) => {
|
|
3153
|
+
const timer2 = setTimeout(resolve26, delay);
|
|
3147
3154
|
conn.abortController.signal.addEventListener("abort", () => {
|
|
3148
3155
|
clearTimeout(timer2);
|
|
3149
|
-
|
|
3156
|
+
resolve26();
|
|
3150
3157
|
}, { once: true });
|
|
3151
3158
|
});
|
|
3152
3159
|
}
|
|
@@ -3154,16 +3161,16 @@ async function connectWithReconnect(conn) {
|
|
|
3154
3161
|
}
|
|
3155
3162
|
}
|
|
3156
3163
|
function waitForDisconnectEvent(conn) {
|
|
3157
|
-
return new Promise((
|
|
3164
|
+
return new Promise((resolve26) => {
|
|
3158
3165
|
if (!conn.sock) {
|
|
3159
|
-
|
|
3166
|
+
resolve26();
|
|
3160
3167
|
return;
|
|
3161
3168
|
}
|
|
3162
3169
|
const sock = conn.sock;
|
|
3163
3170
|
const handler = (update) => {
|
|
3164
3171
|
if (update.connection === "close") {
|
|
3165
3172
|
sock.ev.off("connection.update", handler);
|
|
3166
|
-
|
|
3173
|
+
resolve26();
|
|
3167
3174
|
}
|
|
3168
3175
|
};
|
|
3169
3176
|
sock.ev.on("connection.update", handler);
|
|
@@ -3425,8 +3432,8 @@ async function handleInboundMessage(conn, msg) {
|
|
|
3425
3432
|
const conversationKey = isGroup ? remoteJid : senderPhone;
|
|
3426
3433
|
const debounceKey = `${conn.accountId}:${conversationKey}:${senderPhone}`;
|
|
3427
3434
|
let resolvePending;
|
|
3428
|
-
const sttPending = new Promise((
|
|
3429
|
-
resolvePending =
|
|
3435
|
+
const sttPending = new Promise((resolve26) => {
|
|
3436
|
+
resolvePending = resolve26;
|
|
3430
3437
|
});
|
|
3431
3438
|
if (conn.debouncer) conn.debouncer.registerPending(debounceKey, sttPending);
|
|
3432
3439
|
try {
|
|
@@ -3830,20 +3837,20 @@ function buildX11Env(chromiumWrapperPath, transport = "vnc") {
|
|
|
3830
3837
|
|
|
3831
3838
|
// server/routes/health.ts
|
|
3832
3839
|
function checkPort(port2, timeoutMs = 500) {
|
|
3833
|
-
return new Promise((
|
|
3840
|
+
return new Promise((resolve26) => {
|
|
3834
3841
|
const socket = createConnection2(port2, "127.0.0.1");
|
|
3835
3842
|
socket.setTimeout(timeoutMs);
|
|
3836
3843
|
socket.once("connect", () => {
|
|
3837
3844
|
socket.destroy();
|
|
3838
|
-
|
|
3845
|
+
resolve26(true);
|
|
3839
3846
|
});
|
|
3840
3847
|
socket.once("error", () => {
|
|
3841
3848
|
socket.destroy();
|
|
3842
|
-
|
|
3849
|
+
resolve26(false);
|
|
3843
3850
|
});
|
|
3844
3851
|
socket.once("timeout", () => {
|
|
3845
3852
|
socket.destroy();
|
|
3846
|
-
|
|
3853
|
+
resolve26(false);
|
|
3847
3854
|
});
|
|
3848
3855
|
});
|
|
3849
3856
|
}
|
|
@@ -4579,12 +4586,12 @@ async function dispatchOnce(input) {
|
|
|
4579
4586
|
});
|
|
4580
4587
|
if (!entry) return { error: "spawn-failed" };
|
|
4581
4588
|
entry.lastInboundAt = Date.now();
|
|
4582
|
-
let
|
|
4589
|
+
let resolve26;
|
|
4583
4590
|
const turnPromise = new Promise((r) => {
|
|
4584
|
-
|
|
4591
|
+
resolve26 = r;
|
|
4585
4592
|
});
|
|
4586
4593
|
const listener = (text) => {
|
|
4587
|
-
|
|
4594
|
+
resolve26(text);
|
|
4588
4595
|
};
|
|
4589
4596
|
entry.subscribers.add(listener);
|
|
4590
4597
|
const writeOk = await writeInput(entry, input.text);
|
|
@@ -5026,8 +5033,8 @@ async function startLogin(opts) {
|
|
|
5026
5033
|
resetActiveLogin(accountId);
|
|
5027
5034
|
let resolveQr = null;
|
|
5028
5035
|
let rejectQr = null;
|
|
5029
|
-
const qrPromise = new Promise((
|
|
5030
|
-
resolveQr =
|
|
5036
|
+
const qrPromise = new Promise((resolve26, reject) => {
|
|
5037
|
+
resolveQr = resolve26;
|
|
5031
5038
|
rejectQr = reject;
|
|
5032
5039
|
});
|
|
5033
5040
|
const qrTimer = setTimeout(
|
|
@@ -7147,7 +7154,7 @@ app10.post("/:slug/project", async (c) => {
|
|
|
7147
7154
|
var agents_default = app10;
|
|
7148
7155
|
|
|
7149
7156
|
// server/routes/admin/sessions.ts
|
|
7150
|
-
import
|
|
7157
|
+
import crypto2 from "crypto";
|
|
7151
7158
|
import { resolve as resolvePath } from "path";
|
|
7152
7159
|
import { existsSync as existsSync13, mkdirSync as mkdirSync3, createWriteStream } from "fs";
|
|
7153
7160
|
|
|
@@ -7456,7 +7463,7 @@ app11.post("/new", requireAdminSession, async (c) => {
|
|
|
7456
7463
|
console.error(`[admin-chat] new-conversation outcome=fail reason=missing-account-or-user cacheKey=${oldCacheKey.slice(0, 8)}`);
|
|
7457
7464
|
return c.json({ error: "Session missing account or user context" }, 400);
|
|
7458
7465
|
}
|
|
7459
|
-
const newSignedSessionToken =
|
|
7466
|
+
const newSignedSessionToken = crypto2.randomUUID();
|
|
7460
7467
|
const newCacheKey = fingerprintSessionKey(newSignedSessionToken);
|
|
7461
7468
|
const userName = getUserNameForSession(oldCacheKey);
|
|
7462
7469
|
registerSession(newCacheKey, "admin", accountId, void 0, userId, userName);
|
|
@@ -12055,7 +12062,7 @@ function isPidAlive(pid) {
|
|
|
12055
12062
|
}
|
|
12056
12063
|
}
|
|
12057
12064
|
function sleep2(ms) {
|
|
12058
|
-
return new Promise((
|
|
12065
|
+
return new Promise((resolve26) => setTimeout(resolve26, ms));
|
|
12059
12066
|
}
|
|
12060
12067
|
function sendSignal(pid, signal) {
|
|
12061
12068
|
try {
|
|
@@ -14037,6 +14044,244 @@ async function writeEvent(opts) {
|
|
|
14037
14044
|
}
|
|
14038
14045
|
var visitor_event_default = app40;
|
|
14039
14046
|
|
|
14047
|
+
// server/routes/session.ts
|
|
14048
|
+
import { resolve as resolve22 } from "path";
|
|
14049
|
+
import { existsSync as existsSync21, writeFileSync as writeFileSync7, mkdirSync as mkdirSync5 } from "fs";
|
|
14050
|
+
var UUID_RE4 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
14051
|
+
function writeBrandingCache(accountId, agentSlug, branding) {
|
|
14052
|
+
try {
|
|
14053
|
+
const cacheDir = resolve22(MAXY_DIR, "branding-cache", accountId);
|
|
14054
|
+
mkdirSync5(cacheDir, { recursive: true });
|
|
14055
|
+
writeFileSync7(resolve22(cacheDir, `${agentSlug}.json`), JSON.stringify(branding), "utf-8");
|
|
14056
|
+
} catch (err) {
|
|
14057
|
+
console.error(`[branding] cache write failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
14058
|
+
}
|
|
14059
|
+
}
|
|
14060
|
+
function parseVisitorCookie(cookieHeader) {
|
|
14061
|
+
if (!cookieHeader) return null;
|
|
14062
|
+
const match = cookieHeader.match(/(?:^|;\s*)maxy_visitor=([^;]*)/);
|
|
14063
|
+
if (!match) return null;
|
|
14064
|
+
const value = decodeURIComponent(match[1]).trim();
|
|
14065
|
+
return UUID_RE4.test(value) ? value : null;
|
|
14066
|
+
}
|
|
14067
|
+
function parseAccessSessionId(cookieHeader) {
|
|
14068
|
+
if (!cookieHeader) return null;
|
|
14069
|
+
const part = cookieHeader.split(";").map((p) => p.trim()).find((p) => p.startsWith("__access_session="));
|
|
14070
|
+
return part ? part.slice("__access_session=".length) : null;
|
|
14071
|
+
}
|
|
14072
|
+
function withVisitorCookie(response, visitorId) {
|
|
14073
|
+
if (!visitorId) return response;
|
|
14074
|
+
const headers = new Headers(response.headers);
|
|
14075
|
+
headers.set("Set-Cookie", `maxy_visitor=${visitorId}; Path=/; Max-Age=86400; SameSite=Lax`);
|
|
14076
|
+
return new Response(response.body, {
|
|
14077
|
+
status: response.status,
|
|
14078
|
+
statusText: response.statusText,
|
|
14079
|
+
headers
|
|
14080
|
+
});
|
|
14081
|
+
}
|
|
14082
|
+
var app41 = new Hono();
|
|
14083
|
+
app41.post("/", async (c) => {
|
|
14084
|
+
let body;
|
|
14085
|
+
try {
|
|
14086
|
+
body = await c.req.json();
|
|
14087
|
+
} catch {
|
|
14088
|
+
return c.json({ error: "Invalid request" }, 400);
|
|
14089
|
+
}
|
|
14090
|
+
if (!body.session_id || typeof body.session_id !== "string") {
|
|
14091
|
+
return c.json({ error: "session_id required" }, 400);
|
|
14092
|
+
}
|
|
14093
|
+
const accountId = getDefaultAccountId();
|
|
14094
|
+
if (!accountId) {
|
|
14095
|
+
console.error("[session] getDefaultAccountId() returned null \u2014 no account configured");
|
|
14096
|
+
return c.json({ error: "No account configured" }, 503);
|
|
14097
|
+
}
|
|
14098
|
+
const account = resolveAccount();
|
|
14099
|
+
const cookieHeader = c.req.header("cookie");
|
|
14100
|
+
const visitorId = parseVisitorCookie(cookieHeader);
|
|
14101
|
+
const groupSlug = body.group_slug && typeof body.group_slug === "string" ? body.group_slug : null;
|
|
14102
|
+
let groupInfo = null;
|
|
14103
|
+
if (groupSlug) {
|
|
14104
|
+
groupInfo = await findGroupBySlug(groupSlug, accountId).catch(() => null);
|
|
14105
|
+
if (!groupInfo) {
|
|
14106
|
+
console.error(`[session] group not found: slug=${groupSlug} account=${accountId.slice(0, 8)}\u2026`);
|
|
14107
|
+
return c.json({ error: "Group not found" }, 404);
|
|
14108
|
+
}
|
|
14109
|
+
}
|
|
14110
|
+
let agentSlug = null;
|
|
14111
|
+
if (groupInfo) {
|
|
14112
|
+
agentSlug = groupInfo.agentSlug;
|
|
14113
|
+
} else if (body.agent) {
|
|
14114
|
+
if (!validateAgentSlug(body.agent)) {
|
|
14115
|
+
return c.json({ error: "Invalid agent name" }, 400);
|
|
14116
|
+
}
|
|
14117
|
+
agentSlug = body.agent;
|
|
14118
|
+
} else {
|
|
14119
|
+
agentSlug = account ? resolveDefaultAgentSlug(account.accountDir) : null;
|
|
14120
|
+
if (agentSlug) {
|
|
14121
|
+
console.log(`[session] using defaultAgent=${agentSlug} (no agent param)`);
|
|
14122
|
+
}
|
|
14123
|
+
}
|
|
14124
|
+
if (!agentSlug) {
|
|
14125
|
+
console.error("[session] no agent resolved");
|
|
14126
|
+
return c.json({ error: "No agents configured" }, 404);
|
|
14127
|
+
}
|
|
14128
|
+
let agentConfig = null;
|
|
14129
|
+
if (account) {
|
|
14130
|
+
const agentDir = resolve22(account.accountDir, "agents", agentSlug);
|
|
14131
|
+
if (!existsSync21(agentDir) || !existsSync21(resolve22(agentDir, "config.json"))) {
|
|
14132
|
+
return c.json({ error: "Agent not found" }, 404);
|
|
14133
|
+
}
|
|
14134
|
+
agentConfig = resolveAgentConfig(account.accountDir, agentSlug);
|
|
14135
|
+
}
|
|
14136
|
+
const agentIdentity = agentConfig ? {
|
|
14137
|
+
...agentConfig.displayName ? { displayName: agentConfig.displayName } : {},
|
|
14138
|
+
...agentConfig.image ? { agentImage: agentConfig.image } : {},
|
|
14139
|
+
...agentConfig.imageShape ? { agentImageShape: agentConfig.imageShape } : {},
|
|
14140
|
+
...agentConfig.showAgentName ? { showAgentName: agentConfig.showAgentName } : {}
|
|
14141
|
+
} : {};
|
|
14142
|
+
const groupFields = groupInfo ? {
|
|
14143
|
+
group: true,
|
|
14144
|
+
groupName: groupInfo.groupName,
|
|
14145
|
+
groupSlug
|
|
14146
|
+
} : {};
|
|
14147
|
+
const branding = await fetchBranding(accountId).catch(() => null);
|
|
14148
|
+
if (branding) writeBrandingCache(accountId, agentSlug, branding);
|
|
14149
|
+
let accessSession = null;
|
|
14150
|
+
if (agentConfig && agentConfig.accessMode !== "open") {
|
|
14151
|
+
const accessSessionId = parseAccessSessionId(cookieHeader);
|
|
14152
|
+
accessSession = accessSessionId ? getAccessSession(accessSessionId) : null;
|
|
14153
|
+
if (!accessSession || accessSession.agentSlug !== agentSlug) {
|
|
14154
|
+
console.log(`[session] agent=${agentSlug} accessMode=${agentConfig.accessMode} \u2014 auth required`);
|
|
14155
|
+
return withVisitorCookie(
|
|
14156
|
+
Response.json({
|
|
14157
|
+
auth_required: true,
|
|
14158
|
+
agent_id: agentSlug,
|
|
14159
|
+
...groupFields,
|
|
14160
|
+
...branding ? { branding } : {},
|
|
14161
|
+
...agentIdentity
|
|
14162
|
+
}),
|
|
14163
|
+
null
|
|
14164
|
+
);
|
|
14165
|
+
}
|
|
14166
|
+
}
|
|
14167
|
+
if (body.session_key && typeof body.session_key === "string" && UUID_RE4.test(body.session_key)) {
|
|
14168
|
+
if (visitorId) {
|
|
14169
|
+
const recent = await findRecentConversation(visitorId, accountId, agentSlug).catch(() => null);
|
|
14170
|
+
if (recent) {
|
|
14171
|
+
const neo4jMessages = await getRecentMessages(recent.sessionId).catch(() => []);
|
|
14172
|
+
const uiMessages = neo4jMessages.filter((m) => m.content !== GREETING_DIRECTIVE).map((m) => ({
|
|
14173
|
+
role: m.role === "user" ? "visitor" : "maxy",
|
|
14174
|
+
content: m.content,
|
|
14175
|
+
timestamp: new Date(m.createdAt).getTime() || Date.now()
|
|
14176
|
+
}));
|
|
14177
|
+
const resumed = uiMessages.length > 0;
|
|
14178
|
+
console.log(`[session] hot-resume session=${body.session_key.slice(0, 8)}\u2026 messages=${uiMessages.length}`);
|
|
14179
|
+
return c.json({
|
|
14180
|
+
session_key: body.session_key,
|
|
14181
|
+
agent_id: agentSlug,
|
|
14182
|
+
resumed,
|
|
14183
|
+
...resumed ? { messages: uiMessages } : {},
|
|
14184
|
+
...groupFields,
|
|
14185
|
+
...branding ? { branding } : {},
|
|
14186
|
+
...agentIdentity
|
|
14187
|
+
});
|
|
14188
|
+
}
|
|
14189
|
+
}
|
|
14190
|
+
console.log(`[session] hot-resume session=${body.session_key.slice(0, 8)}\u2026 (no cookie/conversation)`);
|
|
14191
|
+
return c.json({
|
|
14192
|
+
session_key: body.session_key,
|
|
14193
|
+
agent_id: agentSlug,
|
|
14194
|
+
resumed: false,
|
|
14195
|
+
...groupFields,
|
|
14196
|
+
...branding ? { branding } : {},
|
|
14197
|
+
...agentIdentity
|
|
14198
|
+
});
|
|
14199
|
+
}
|
|
14200
|
+
if (groupInfo && visitorId) {
|
|
14201
|
+
let memberName = await checkGroupMembership(groupInfo.sessionId, visitorId).catch(() => null);
|
|
14202
|
+
if (!memberName && accessSession) {
|
|
14203
|
+
const contactValue = accessSession.contactValue;
|
|
14204
|
+
const isEmail = contactValue?.includes("@");
|
|
14205
|
+
const bindResult = await bindVisitorToGroup(
|
|
14206
|
+
groupInfo.sessionId,
|
|
14207
|
+
visitorId,
|
|
14208
|
+
isEmail ? contactValue : void 0,
|
|
14209
|
+
!isEmail ? contactValue : void 0
|
|
14210
|
+
).catch(() => null);
|
|
14211
|
+
memberName = bindResult?.displayName ?? null;
|
|
14212
|
+
}
|
|
14213
|
+
if (memberName) {
|
|
14214
|
+
const neo4jMessages = await getRecentMessages(groupInfo.sessionId).catch(() => []);
|
|
14215
|
+
const uiMessages = neo4jMessages.filter((m) => m.content !== GREETING_DIRECTIVE).map((m) => ({
|
|
14216
|
+
role: m.role === "user" ? "visitor" : "maxy",
|
|
14217
|
+
content: m.content,
|
|
14218
|
+
timestamp: new Date(m.createdAt).getTime() || Date.now()
|
|
14219
|
+
}));
|
|
14220
|
+
const participants = await getGroupParticipants(groupInfo.sessionId).catch(() => []);
|
|
14221
|
+
const cacheKey2 = crypto.randomUUID();
|
|
14222
|
+
const resumed = uiMessages.length > 0;
|
|
14223
|
+
console.log(`[session] cold-resume group=${groupSlug} visitor=${visitorId.slice(0, 8)}\u2026 messages=${uiMessages.length}`);
|
|
14224
|
+
return withVisitorCookie(
|
|
14225
|
+
Response.json({
|
|
14226
|
+
session_key: cacheKey2,
|
|
14227
|
+
agent_id: agentSlug,
|
|
14228
|
+
resumed,
|
|
14229
|
+
...resumed ? { messages: uiMessages } : {},
|
|
14230
|
+
...groupFields,
|
|
14231
|
+
participants: participants.map((p) => ({ displayName: p.displayName, joinedAt: p.joinedAt })),
|
|
14232
|
+
...branding ? { branding } : {},
|
|
14233
|
+
...agentIdentity
|
|
14234
|
+
}),
|
|
14235
|
+
visitorId
|
|
14236
|
+
);
|
|
14237
|
+
}
|
|
14238
|
+
console.log(`[session] group cold-resume denied: visitor=${visitorId.slice(0, 8)}\u2026 not a member of group=${groupSlug}`);
|
|
14239
|
+
return c.json({ error: "Not a member of this group" }, 403);
|
|
14240
|
+
}
|
|
14241
|
+
if (groupInfo) {
|
|
14242
|
+
console.error(`[session] group=${groupSlug} requires visitor cookie`);
|
|
14243
|
+
return c.json({ error: "Groups require member authentication" }, 400);
|
|
14244
|
+
}
|
|
14245
|
+
if (visitorId) {
|
|
14246
|
+
const recent = await findRecentConversation(visitorId, accountId, agentSlug).catch(() => null);
|
|
14247
|
+
if (recent) {
|
|
14248
|
+
const neo4jMessages = await getRecentMessages(recent.sessionId).catch(() => []);
|
|
14249
|
+
const uiMessages = neo4jMessages.filter((m) => m.content !== GREETING_DIRECTIVE).map((m) => ({
|
|
14250
|
+
role: m.role === "user" ? "visitor" : "maxy",
|
|
14251
|
+
content: m.content,
|
|
14252
|
+
timestamp: new Date(m.createdAt).getTime() || Date.now()
|
|
14253
|
+
}));
|
|
14254
|
+
const cacheKey2 = crypto.randomUUID();
|
|
14255
|
+
const resumed = uiMessages.length > 0;
|
|
14256
|
+
console.log(`[session] cold-resume visitor=${visitorId.slice(0, 8)}\u2026 session=${recent.sessionId.slice(0, 8)}\u2026 messages=${uiMessages.length}`);
|
|
14257
|
+
return withVisitorCookie(
|
|
14258
|
+
Response.json({
|
|
14259
|
+
session_key: cacheKey2,
|
|
14260
|
+
agent_id: agentSlug,
|
|
14261
|
+
resumed,
|
|
14262
|
+
...resumed ? { messages: uiMessages } : {},
|
|
14263
|
+
...branding ? { branding } : {},
|
|
14264
|
+
...agentIdentity
|
|
14265
|
+
}),
|
|
14266
|
+
visitorId
|
|
14267
|
+
);
|
|
14268
|
+
}
|
|
14269
|
+
}
|
|
14270
|
+
const newVisitorId = visitorId ?? crypto.randomUUID();
|
|
14271
|
+
const cacheKey = crypto.randomUUID();
|
|
14272
|
+
console.log(`[session] new-session visitor=${newVisitorId.slice(0, 8)}\u2026 session=${cacheKey.slice(0, 8)}\u2026 agent=${agentSlug} image=${agentConfig?.image ? "yes" : "no"}`);
|
|
14273
|
+
return withVisitorCookie(
|
|
14274
|
+
Response.json({
|
|
14275
|
+
session_key: cacheKey,
|
|
14276
|
+
agent_id: agentSlug,
|
|
14277
|
+
...branding ? { branding } : {},
|
|
14278
|
+
...agentIdentity
|
|
14279
|
+
}),
|
|
14280
|
+
newVisitorId
|
|
14281
|
+
);
|
|
14282
|
+
});
|
|
14283
|
+
var session_default2 = app41;
|
|
14284
|
+
|
|
14040
14285
|
// app/lib/graph-health.ts
|
|
14041
14286
|
var HOUR_MS = 60 * 60 * 1e3;
|
|
14042
14287
|
var timer = null;
|
|
@@ -14120,7 +14365,7 @@ function startGraphHealthTimer() {
|
|
|
14120
14365
|
|
|
14121
14366
|
// app/lib/file-watcher.ts
|
|
14122
14367
|
import * as fsp2 from "fs/promises";
|
|
14123
|
-
import { resolve as
|
|
14368
|
+
import { resolve as resolve23, sep as sep6 } from "path";
|
|
14124
14369
|
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
14370
|
var DEFAULT_COALESCE_MS = 500;
|
|
14126
14371
|
var ROOTS = ["uploads", "accounts"];
|
|
@@ -14139,7 +14384,7 @@ async function startFileWatcher(opts = {}) {
|
|
|
14139
14384
|
const dropFn = opts.drop ?? dropFileIndex;
|
|
14140
14385
|
for (const r of ROOTS) {
|
|
14141
14386
|
try {
|
|
14142
|
-
await fsp2.mkdir(
|
|
14387
|
+
await fsp2.mkdir(resolve23(dataRoot, r), { recursive: true });
|
|
14143
14388
|
} catch (err) {
|
|
14144
14389
|
console.error(
|
|
14145
14390
|
`[file-watcher] start-failed root="${r}" err="${err.message}" \u2014 index will be maintained by the 5-min reconcile backstop only`
|
|
@@ -14160,7 +14405,7 @@ async function startFileWatcher(opts = {}) {
|
|
|
14160
14405
|
timers.set(relativePath, t);
|
|
14161
14406
|
}
|
|
14162
14407
|
async function runHook(relativePath, accountId) {
|
|
14163
|
-
const absolute =
|
|
14408
|
+
const absolute = resolve23(dataRoot, relativePath);
|
|
14164
14409
|
let exists = false;
|
|
14165
14410
|
try {
|
|
14166
14411
|
const st = await fsp2.stat(absolute);
|
|
@@ -14184,7 +14429,7 @@ async function startFileWatcher(opts = {}) {
|
|
|
14184
14429
|
}
|
|
14185
14430
|
}
|
|
14186
14431
|
async function watchRoot(rootName) {
|
|
14187
|
-
const absRoot =
|
|
14432
|
+
const absRoot = resolve23(dataRoot, rootName);
|
|
14188
14433
|
try {
|
|
14189
14434
|
const iter = fsp2.watch(absRoot, { recursive: true, signal: controller.signal });
|
|
14190
14435
|
for await (const event of iter) {
|
|
@@ -14282,8 +14527,8 @@ function broadcastAdminShutdown(reason) {
|
|
|
14282
14527
|
|
|
14283
14528
|
// ../lib/entitlement/src/index.ts
|
|
14284
14529
|
import { createPublicKey, createHash as createHash4, verify as cryptoVerify } from "crypto";
|
|
14285
|
-
import { existsSync as
|
|
14286
|
-
import { resolve as
|
|
14530
|
+
import { existsSync as existsSync22, readFileSync as readFileSync21, statSync as statSync9 } from "fs";
|
|
14531
|
+
import { resolve as resolve24 } from "path";
|
|
14287
14532
|
|
|
14288
14533
|
// ../lib/entitlement/src/canonicalize.ts
|
|
14289
14534
|
function canonicalize(value) {
|
|
@@ -14318,7 +14563,7 @@ var PUBKEY_SHA256 = "8eee6bcb33545fd13b16d3199a5735ca5db5062834c7b49dfe4f23801d9
|
|
|
14318
14563
|
var GRACE_DAYS = 7;
|
|
14319
14564
|
var GRACE_MS = GRACE_DAYS * 24 * 60 * 60 * 1e3;
|
|
14320
14565
|
function pubkeyPath(brand) {
|
|
14321
|
-
return
|
|
14566
|
+
return resolve24(brand.platformRoot, "lib", "entitlement", "rubytech-pubkey.pem");
|
|
14322
14567
|
}
|
|
14323
14568
|
var memo = null;
|
|
14324
14569
|
function memoKey(mtimeMs, account) {
|
|
@@ -14330,8 +14575,8 @@ function resolveEntitlement(brand, account) {
|
|
|
14330
14575
|
if (brand.commercialMode !== true) {
|
|
14331
14576
|
return logResolved(implicitTrust(account), null);
|
|
14332
14577
|
}
|
|
14333
|
-
const entitlementPath =
|
|
14334
|
-
if (!
|
|
14578
|
+
const entitlementPath = resolve24(brand.configDir, "entitlement.json");
|
|
14579
|
+
if (!existsSync22(entitlementPath)) {
|
|
14335
14580
|
return logResolved(anonymousFallback("missing"), { reason: "missing" });
|
|
14336
14581
|
}
|
|
14337
14582
|
const stat7 = statSync9(entitlementPath);
|
|
@@ -14523,10 +14768,10 @@ function clientFrom(c) {
|
|
|
14523
14768
|
var PLATFORM_ROOT9 = process.env.MAXY_PLATFORM_ROOT || "";
|
|
14524
14769
|
var BRAND_JSON_PATH = PLATFORM_ROOT9 ? join17(PLATFORM_ROOT9, "config", "brand.json") : "";
|
|
14525
14770
|
var BRAND = { productName: "Maxy", hostname: "maxy", configDir: ".maxy", marketingUrl: "https://getmaxy.com" };
|
|
14526
|
-
if (BRAND_JSON_PATH && !
|
|
14771
|
+
if (BRAND_JSON_PATH && !existsSync23(BRAND_JSON_PATH)) {
|
|
14527
14772
|
console.error(`[brand] WARNING: brand.json not found at ${BRAND_JSON_PATH} \u2014 using Maxy defaults`);
|
|
14528
14773
|
}
|
|
14529
|
-
if (BRAND_JSON_PATH &&
|
|
14774
|
+
if (BRAND_JSON_PATH && existsSync23(BRAND_JSON_PATH)) {
|
|
14530
14775
|
try {
|
|
14531
14776
|
const parsed = JSON.parse(readFileSync22(BRAND_JSON_PATH, "utf-8"));
|
|
14532
14777
|
BRAND = { ...BRAND, ...parsed };
|
|
@@ -14550,7 +14795,7 @@ var brandLoginOpts = {
|
|
|
14550
14795
|
var ALIAS_DOMAINS_PATH = join17(homedir2(), BRAND.configDir, "alias-domains.json");
|
|
14551
14796
|
function loadAliasDomains() {
|
|
14552
14797
|
try {
|
|
14553
|
-
if (!
|
|
14798
|
+
if (!existsSync23(ALIAS_DOMAINS_PATH)) return null;
|
|
14554
14799
|
const parsed = JSON.parse(readFileSync22(ALIAS_DOMAINS_PATH, "utf-8"));
|
|
14555
14800
|
if (!Array.isArray(parsed)) {
|
|
14556
14801
|
console.error("[alias-domains] malformed alias-domains.json \u2014 expected array");
|
|
@@ -14575,9 +14820,9 @@ watchFile(ALIAS_DOMAINS_PATH, { interval: 2e3 }, () => {
|
|
|
14575
14820
|
function isPublicHost(host) {
|
|
14576
14821
|
return host.startsWith("public.") || aliasDomains.has(host);
|
|
14577
14822
|
}
|
|
14578
|
-
var
|
|
14579
|
-
|
|
14580
|
-
|
|
14823
|
+
var app42 = new Hono();
|
|
14824
|
+
app42.use("*", clientIpMiddleware);
|
|
14825
|
+
app42.use("*", async (c, next) => {
|
|
14581
14826
|
await next();
|
|
14582
14827
|
c.header("X-Content-Type-Options", "nosniff");
|
|
14583
14828
|
c.header("Referrer-Policy", "strict-origin-when-cross-origin");
|
|
@@ -14587,7 +14832,7 @@ app41.use("*", async (c, next) => {
|
|
|
14587
14832
|
);
|
|
14588
14833
|
});
|
|
14589
14834
|
var HTTP_LOG_PATHS = /* @__PURE__ */ new Set(["/vnc-viewer.html", "/vnc-popout.html"]);
|
|
14590
|
-
|
|
14835
|
+
app42.use("*", async (c, next) => {
|
|
14591
14836
|
if (!HTTP_LOG_PATHS.has(c.req.path)) {
|
|
14592
14837
|
await next();
|
|
14593
14838
|
return;
|
|
@@ -14620,7 +14865,7 @@ var PUBLIC_ALLOWED_PREFIXES = [
|
|
|
14620
14865
|
"/sites/"
|
|
14621
14866
|
];
|
|
14622
14867
|
var PUBLIC_ALLOWED_EXACT = ["/favicon.ico"];
|
|
14623
|
-
|
|
14868
|
+
app42.use("*", async (c, next) => {
|
|
14624
14869
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
14625
14870
|
if (!isPublicHost(host)) {
|
|
14626
14871
|
await next();
|
|
@@ -14660,7 +14905,7 @@ function resolveRemoteAuthOpts() {
|
|
|
14660
14905
|
return brandLoginOpts;
|
|
14661
14906
|
}
|
|
14662
14907
|
var MAX_LOGIN_BODY = 8 * 1024;
|
|
14663
|
-
|
|
14908
|
+
app42.post("/__remote-auth/login", async (c) => {
|
|
14664
14909
|
const client = clientFrom(c);
|
|
14665
14910
|
const clientIp = client.ip || "unknown";
|
|
14666
14911
|
if (!requestIsTlsTerminated(c)) {
|
|
@@ -14705,7 +14950,7 @@ app41.post("/__remote-auth/login", async (c) => {
|
|
|
14705
14950
|
}
|
|
14706
14951
|
});
|
|
14707
14952
|
});
|
|
14708
|
-
|
|
14953
|
+
app42.get("/__remote-auth/logout", (c) => {
|
|
14709
14954
|
const client = clientFrom(c);
|
|
14710
14955
|
const clientIp = client.ip || "unknown";
|
|
14711
14956
|
console.error(`[remote-auth] logout ip=${clientIp}`);
|
|
@@ -14718,7 +14963,7 @@ app41.get("/__remote-auth/logout", (c) => {
|
|
|
14718
14963
|
}
|
|
14719
14964
|
});
|
|
14720
14965
|
});
|
|
14721
|
-
|
|
14966
|
+
app42.post("/__remote-auth/change-password", async (c) => {
|
|
14722
14967
|
const client = clientFrom(c);
|
|
14723
14968
|
const clientIp = client.ip || "unknown";
|
|
14724
14969
|
const rateLimited = checkRateLimit(client);
|
|
@@ -14769,13 +15014,13 @@ app41.post("/__remote-auth/change-password", async (c) => {
|
|
|
14769
15014
|
return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(), mode: "change", changeError: "Failed to save password", redirect }), 200);
|
|
14770
15015
|
}
|
|
14771
15016
|
});
|
|
14772
|
-
|
|
15017
|
+
app42.get("/__remote-auth/setup", (c) => {
|
|
14773
15018
|
if (isRemoteAuthConfigured()) {
|
|
14774
15019
|
return c.redirect("/");
|
|
14775
15020
|
}
|
|
14776
15021
|
return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(), mode: "setup" }), 200);
|
|
14777
15022
|
});
|
|
14778
|
-
|
|
15023
|
+
app42.post("/__remote-auth/set-initial-password", async (c) => {
|
|
14779
15024
|
if (isRemoteAuthConfigured()) {
|
|
14780
15025
|
return c.redirect("/");
|
|
14781
15026
|
}
|
|
@@ -14813,10 +15058,10 @@ app41.post("/__remote-auth/set-initial-password", async (c) => {
|
|
|
14813
15058
|
return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(), mode: "setup", setupError: "Failed to save password. Please try again." }), 200);
|
|
14814
15059
|
}
|
|
14815
15060
|
});
|
|
14816
|
-
|
|
15061
|
+
app42.get("/api/remote-auth/status", (c) => {
|
|
14817
15062
|
return c.json({ configured: isRemoteAuthConfigured() });
|
|
14818
15063
|
});
|
|
14819
|
-
|
|
15064
|
+
app42.post("/api/remote-auth/set-password", async (c) => {
|
|
14820
15065
|
let body;
|
|
14821
15066
|
try {
|
|
14822
15067
|
body = await c.req.json();
|
|
@@ -14847,9 +15092,9 @@ app41.post("/api/remote-auth/set-password", async (c) => {
|
|
|
14847
15092
|
return c.json({ error: "Failed to save password" }, 500);
|
|
14848
15093
|
}
|
|
14849
15094
|
});
|
|
14850
|
-
|
|
15095
|
+
app42.route("/api/_client-error", client_error_default);
|
|
14851
15096
|
console.log("[client-error-route] mounted");
|
|
14852
|
-
|
|
15097
|
+
app42.use("*", async (c, next) => {
|
|
14853
15098
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
14854
15099
|
const path2 = c.req.path;
|
|
14855
15100
|
if (path2 === "/favicon.ico" || path2.startsWith("/assets/") || path2.startsWith("/brand/")) {
|
|
@@ -14882,12 +15127,13 @@ app41.use("*", async (c, next) => {
|
|
|
14882
15127
|
console.error(`[remote-auth] login required ip=${clientIp} path=${path2} ${disambig}`);
|
|
14883
15128
|
return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(), redirect: path2 }), 200);
|
|
14884
15129
|
});
|
|
14885
|
-
|
|
14886
|
-
|
|
14887
|
-
|
|
14888
|
-
|
|
14889
|
-
|
|
14890
|
-
|
|
15130
|
+
app42.route("/api/health", health_default);
|
|
15131
|
+
app42.route("/api/chat", chat_default);
|
|
15132
|
+
app42.route("/api/whatsapp", whatsapp_default);
|
|
15133
|
+
app42.route("/api/onboarding", onboarding_default);
|
|
15134
|
+
app42.route("/api/admin", admin_default);
|
|
15135
|
+
app42.route("/api/access", access_default);
|
|
15136
|
+
app42.route("/api/session", session_default2);
|
|
14891
15137
|
var SAFE_SLUG_RE2 = /^[a-z][a-z0-9-]{2,49}$/;
|
|
14892
15138
|
var SAFE_FILENAME_RE = /^[a-z0-9_][a-z0-9_.-]{0,99}$/i;
|
|
14893
15139
|
var IMAGE_MIME = {
|
|
@@ -14899,7 +15145,7 @@ var IMAGE_MIME = {
|
|
|
14899
15145
|
".svg": "image/svg+xml",
|
|
14900
15146
|
".ico": "image/x-icon"
|
|
14901
15147
|
};
|
|
14902
|
-
|
|
15148
|
+
app42.get("/agent-assets/:slug/:filename", (c) => {
|
|
14903
15149
|
const slug = c.req.param("slug");
|
|
14904
15150
|
const filename = c.req.param("filename");
|
|
14905
15151
|
if (!SAFE_SLUG_RE2.test(slug)) {
|
|
@@ -14915,13 +15161,13 @@ app41.get("/agent-assets/:slug/:filename", (c) => {
|
|
|
14915
15161
|
console.error(`[agent-assets] no-account slug=${slug} file=${filename}`);
|
|
14916
15162
|
return c.text("Not found", 404);
|
|
14917
15163
|
}
|
|
14918
|
-
const filePath =
|
|
14919
|
-
const expectedDir =
|
|
15164
|
+
const filePath = resolve25(account.accountDir, "agents", slug, "assets", filename);
|
|
15165
|
+
const expectedDir = resolve25(account.accountDir, "agents", slug, "assets");
|
|
14920
15166
|
if (!filePath.startsWith(expectedDir + "/")) {
|
|
14921
15167
|
console.error(`[agent-assets] path-traversal-rejected slug=${slug} file=${filename}`);
|
|
14922
15168
|
return c.text("Forbidden", 403);
|
|
14923
15169
|
}
|
|
14924
|
-
if (!
|
|
15170
|
+
if (!existsSync23(filePath)) {
|
|
14925
15171
|
console.error(`[agent-assets] serve slug=${slug} file=${filename} status=404`);
|
|
14926
15172
|
return c.text("Not found", 404);
|
|
14927
15173
|
}
|
|
@@ -14934,7 +15180,7 @@ app41.get("/agent-assets/:slug/:filename", (c) => {
|
|
|
14934
15180
|
"Cache-Control": "public, max-age=3600"
|
|
14935
15181
|
});
|
|
14936
15182
|
});
|
|
14937
|
-
|
|
15183
|
+
app42.get("/generated/:filename", (c) => {
|
|
14938
15184
|
const filename = c.req.param("filename");
|
|
14939
15185
|
if (!SAFE_FILENAME_RE.test(filename) || filename.includes("..")) {
|
|
14940
15186
|
console.error(`[generated] serve file=${filename} status=403`);
|
|
@@ -14945,13 +15191,13 @@ app41.get("/generated/:filename", (c) => {
|
|
|
14945
15191
|
console.error(`[generated] serve file=${filename} status=404`);
|
|
14946
15192
|
return c.text("Not found", 404);
|
|
14947
15193
|
}
|
|
14948
|
-
const filePath =
|
|
14949
|
-
const expectedDir =
|
|
15194
|
+
const filePath = resolve25(account.accountDir, "generated", filename);
|
|
15195
|
+
const expectedDir = resolve25(account.accountDir, "generated");
|
|
14950
15196
|
if (!filePath.startsWith(expectedDir + "/")) {
|
|
14951
15197
|
console.error(`[generated] serve file=${filename} status=403`);
|
|
14952
15198
|
return c.text("Forbidden", 403);
|
|
14953
15199
|
}
|
|
14954
|
-
if (!
|
|
15200
|
+
if (!existsSync23(filePath)) {
|
|
14955
15201
|
console.error(`[generated] serve file=${filename} status=404`);
|
|
14956
15202
|
return c.text("Not found", 404);
|
|
14957
15203
|
}
|
|
@@ -14964,14 +15210,14 @@ app41.get("/generated/:filename", (c) => {
|
|
|
14964
15210
|
"Cache-Control": "public, max-age=86400"
|
|
14965
15211
|
});
|
|
14966
15212
|
});
|
|
14967
|
-
|
|
14968
|
-
|
|
14969
|
-
|
|
14970
|
-
|
|
15213
|
+
app42.route("/sites", sites_default);
|
|
15214
|
+
app42.route("/listings", listings_default);
|
|
15215
|
+
app42.route("/v", visitor_event_default);
|
|
15216
|
+
app42.route("/v", visitor_consent_default);
|
|
14971
15217
|
var htmlCache = /* @__PURE__ */ new Map();
|
|
14972
15218
|
var brandLogoPath = "/brand/maxy-monochrome.png";
|
|
14973
15219
|
var brandIconPath = "/brand/maxy-monochrome.png";
|
|
14974
|
-
if (BRAND_JSON_PATH &&
|
|
15220
|
+
if (BRAND_JSON_PATH && existsSync23(BRAND_JSON_PATH)) {
|
|
14975
15221
|
try {
|
|
14976
15222
|
const fullBrand = JSON.parse(readFileSync22(BRAND_JSON_PATH, "utf-8"));
|
|
14977
15223
|
if (fullBrand.assets?.logo) brandLogoPath = `/brand/${fullBrand.assets.logo}`;
|
|
@@ -14991,7 +15237,7 @@ function readInstalledVersion() {
|
|
|
14991
15237
|
try {
|
|
14992
15238
|
if (!PLATFORM_ROOT9) return "unknown";
|
|
14993
15239
|
const versionFile = join17(PLATFORM_ROOT9, "config", `.${BRAND.hostname}-version`);
|
|
14994
|
-
if (!
|
|
15240
|
+
if (!existsSync23(versionFile)) return "unknown";
|
|
14995
15241
|
const content = readFileSync22(versionFile, "utf-8").trim();
|
|
14996
15242
|
return content || "unknown";
|
|
14997
15243
|
} catch {
|
|
@@ -15033,7 +15279,7 @@ var clientErrorReporterScript = `<script>
|
|
|
15033
15279
|
function cachedHtml(file) {
|
|
15034
15280
|
let html = htmlCache.get(file);
|
|
15035
15281
|
if (!html) {
|
|
15036
|
-
html = readFileSync22(
|
|
15282
|
+
html = readFileSync22(resolve25(process.cwd(), "public", file), "utf-8");
|
|
15037
15283
|
const productNameEsc = escapeHtml(BRAND.productName);
|
|
15038
15284
|
html = html.replace(/<title>([^<]*)<\/title>/, (_match, inner) => `<title>${escapeHtml(inner).replace(/Maxy/g, productNameEsc)}</title>`);
|
|
15039
15285
|
html = html.replace('href="/favicon.ico"', `href="${escapeHtml(brandFaviconPath)}"`);
|
|
@@ -15052,12 +15298,12 @@ function loadBrandingCache(agentSlug) {
|
|
|
15052
15298
|
const configDir2 = join17(homedir2(), BRAND.configDir);
|
|
15053
15299
|
try {
|
|
15054
15300
|
const accountJsonPath = join17(configDir2, "account.json");
|
|
15055
|
-
if (!
|
|
15301
|
+
if (!existsSync23(accountJsonPath)) return null;
|
|
15056
15302
|
const account = JSON.parse(readFileSync22(accountJsonPath, "utf-8"));
|
|
15057
15303
|
const accountId = account.accountId;
|
|
15058
15304
|
if (!accountId) return null;
|
|
15059
15305
|
const cachePath = join17(configDir2, "branding-cache", accountId, `${agentSlug}.json`);
|
|
15060
|
-
if (!
|
|
15306
|
+
if (!existsSync23(cachePath)) return null;
|
|
15061
15307
|
return JSON.parse(readFileSync22(cachePath, "utf-8"));
|
|
15062
15308
|
} catch {
|
|
15063
15309
|
return null;
|
|
@@ -15067,7 +15313,7 @@ function resolveDefaultSlug() {
|
|
|
15067
15313
|
try {
|
|
15068
15314
|
const configDir2 = join17(homedir2(), BRAND.configDir);
|
|
15069
15315
|
const accountJsonPath = join17(configDir2, "account.json");
|
|
15070
|
-
if (!
|
|
15316
|
+
if (!existsSync23(accountJsonPath)) return null;
|
|
15071
15317
|
const account = JSON.parse(readFileSync22(accountJsonPath, "utf-8"));
|
|
15072
15318
|
return account.defaultAgent || null;
|
|
15073
15319
|
} catch {
|
|
@@ -15104,7 +15350,7 @@ function brandedPublicHtml(agentSlug) {
|
|
|
15104
15350
|
function escapeHtml(s) {
|
|
15105
15351
|
return s.replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<").replace(/>/g, ">");
|
|
15106
15352
|
}
|
|
15107
|
-
|
|
15353
|
+
app42.get("/", (c) => {
|
|
15108
15354
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
15109
15355
|
if (isPublicHost(host)) {
|
|
15110
15356
|
const defaultSlug = resolveDefaultSlug();
|
|
@@ -15112,12 +15358,12 @@ app41.get("/", (c) => {
|
|
|
15112
15358
|
}
|
|
15113
15359
|
return c.html(cachedHtml("index.html"));
|
|
15114
15360
|
});
|
|
15115
|
-
|
|
15361
|
+
app42.get("/public", (c) => {
|
|
15116
15362
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
15117
15363
|
if (isPublicHost(host)) return c.text("Not found", 404);
|
|
15118
15364
|
return c.html(cachedHtml("public.html"));
|
|
15119
15365
|
});
|
|
15120
|
-
|
|
15366
|
+
app42.get("/chat", (c) => {
|
|
15121
15367
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
15122
15368
|
if (isPublicHost(host)) return c.text("Not found", 404);
|
|
15123
15369
|
return c.html(cachedHtml("public.html"));
|
|
@@ -15136,12 +15382,12 @@ async function logViewerFetch(c, next) {
|
|
|
15136
15382
|
duration_ms: Date.now() - start
|
|
15137
15383
|
});
|
|
15138
15384
|
}
|
|
15139
|
-
|
|
15140
|
-
|
|
15141
|
-
|
|
15385
|
+
app42.use("/vnc-viewer.html", logViewerFetch);
|
|
15386
|
+
app42.use("/vnc-popout.html", logViewerFetch);
|
|
15387
|
+
app42.get("/vnc-popout.html", (c) => {
|
|
15142
15388
|
let html = htmlCache.get("vnc-popout.html");
|
|
15143
15389
|
if (!html) {
|
|
15144
|
-
html = readFileSync22(
|
|
15390
|
+
html = readFileSync22(resolve25(process.cwd(), "public", "vnc-popout.html"), "utf-8");
|
|
15145
15391
|
const name = escapeHtml(BRAND.productName);
|
|
15146
15392
|
html = html.replace("<title>Browser \u2014 Maxy</title>", `<title>${name}</title>`);
|
|
15147
15393
|
html = html.replace("</head>", ` ${brandScript}
|
|
@@ -15151,7 +15397,7 @@ app41.get("/vnc-popout.html", (c) => {
|
|
|
15151
15397
|
}
|
|
15152
15398
|
return c.html(html);
|
|
15153
15399
|
});
|
|
15154
|
-
|
|
15400
|
+
app42.post("/api/vnc/client-event", async (c) => {
|
|
15155
15401
|
let body;
|
|
15156
15402
|
try {
|
|
15157
15403
|
body = await c.req.json();
|
|
@@ -15172,25 +15418,25 @@ app41.post("/api/vnc/client-event", async (c) => {
|
|
|
15172
15418
|
});
|
|
15173
15419
|
return c.json({ ok: true });
|
|
15174
15420
|
});
|
|
15175
|
-
|
|
15421
|
+
app42.get("/g/:slug", (c) => {
|
|
15176
15422
|
return c.html(brandedPublicHtml());
|
|
15177
15423
|
});
|
|
15178
|
-
|
|
15424
|
+
app42.get("/graph", (c) => {
|
|
15179
15425
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
15180
15426
|
if (isPublicHost(host)) return c.text("Not found", 404);
|
|
15181
15427
|
return c.html(cachedHtml("graph.html"));
|
|
15182
15428
|
});
|
|
15183
|
-
|
|
15429
|
+
app42.get("/sessions", (c) => {
|
|
15184
15430
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
15185
15431
|
if (isPublicHost(host)) return c.text("Not found", 404);
|
|
15186
15432
|
return c.html(cachedHtml("sessions.html"));
|
|
15187
15433
|
});
|
|
15188
|
-
|
|
15434
|
+
app42.get("/data", (c) => {
|
|
15189
15435
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
15190
15436
|
if (isPublicHost(host)) return c.text("Not found", 404);
|
|
15191
15437
|
return c.html(cachedHtml("data.html"));
|
|
15192
15438
|
});
|
|
15193
|
-
|
|
15439
|
+
app42.get("/:slug", async (c, next) => {
|
|
15194
15440
|
const slug = c.req.param("slug");
|
|
15195
15441
|
if (AGENT_SLUG_PATTERN.test(`/${slug}`)) {
|
|
15196
15442
|
const branding = loadBrandingCache(slug);
|
|
@@ -15200,15 +15446,27 @@ app41.get("/:slug", async (c, next) => {
|
|
|
15200
15446
|
await next();
|
|
15201
15447
|
});
|
|
15202
15448
|
if (brandFaviconPath !== "/favicon.ico") {
|
|
15203
|
-
|
|
15449
|
+
app42.get("/favicon.ico", (c) => {
|
|
15204
15450
|
c.header("Cache-Control", "public, max-age=300");
|
|
15205
15451
|
return c.redirect(brandFaviconPath, 302);
|
|
15206
15452
|
});
|
|
15207
15453
|
}
|
|
15208
|
-
|
|
15454
|
+
app42.use("/*", serveStatic({ root: "./public" }));
|
|
15455
|
+
app42.all("*", (c) => {
|
|
15456
|
+
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
15457
|
+
const path2 = c.req.path;
|
|
15458
|
+
if (isPublicHost(host)) {
|
|
15459
|
+
const inAllowedList = PUBLIC_ALLOWED_EXACT.includes(path2) || PUBLIC_ALLOWED_PREFIXES.some((prefix) => path2.startsWith(prefix));
|
|
15460
|
+
if (inAllowedList) {
|
|
15461
|
+
console.error(`[public-host] unmatched-allowed path=${path2}`);
|
|
15462
|
+
return c.json({ error: "not-found" }, 404);
|
|
15463
|
+
}
|
|
15464
|
+
}
|
|
15465
|
+
return c.text("Not found", 404);
|
|
15466
|
+
});
|
|
15209
15467
|
var port = requirePortEnv("MAXY_UI_INTERNAL_PORT", { tag: "ui-server" });
|
|
15210
15468
|
var hostname = process.env.HOSTNAME ?? "127.0.0.1";
|
|
15211
|
-
var httpServer = serve({ fetch:
|
|
15469
|
+
var httpServer = serve({ fetch: app42.fetch, port, hostname });
|
|
15212
15470
|
console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
15213
15471
|
startAuthHealthHeartbeat();
|
|
15214
15472
|
{
|
|
@@ -15234,6 +15492,7 @@ var SUBAPP_MANIFEST = [
|
|
|
15234
15492
|
{ prefix: "/api/onboarding", file: "server/routes/onboarding.ts", subapp: onboarding_default },
|
|
15235
15493
|
{ prefix: "/api/admin", file: "server/routes/admin/index.ts", subapp: admin_default },
|
|
15236
15494
|
{ prefix: "/api/access", file: "server/routes/access/index.ts", subapp: access_default },
|
|
15495
|
+
{ prefix: "/api/session", file: "server/routes/session.ts", subapp: session_default2 },
|
|
15237
15496
|
{ prefix: "/api/_client-error", file: "server/routes/client-error.ts", subapp: client_error_default },
|
|
15238
15497
|
{ prefix: "/listings", file: "server/routes/listings.ts", subapp: listings_default },
|
|
15239
15498
|
{ prefix: "/v", file: "server/routes/visitor-event.ts", subapp: visitor_event_default },
|
|
@@ -15245,7 +15504,7 @@ for (const m of SUBAPP_MANIFEST) {
|
|
|
15245
15504
|
}
|
|
15246
15505
|
try {
|
|
15247
15506
|
const registered = [];
|
|
15248
|
-
for (const r of
|
|
15507
|
+
for (const r of app42.routes ?? []) {
|
|
15249
15508
|
if (typeof r.path !== "string" || r.path.includes(":") || r.path.includes("*")) continue;
|
|
15250
15509
|
if (AGENT_SLUG_PATTERN.test(r.path)) {
|
|
15251
15510
|
registered.push({ method: (r.method ?? "ALL").toUpperCase(), path: r.path });
|
|
@@ -15267,7 +15526,7 @@ try {
|
|
|
15267
15526
|
}
|
|
15268
15527
|
(async () => {
|
|
15269
15528
|
try {
|
|
15270
|
-
if (!
|
|
15529
|
+
if (!existsSync23(USERS_FILE)) return;
|
|
15271
15530
|
const usersRaw = readFileSync22(USERS_FILE, "utf-8").trim();
|
|
15272
15531
|
if (!usersRaw) return;
|
|
15273
15532
|
const users = JSON.parse(usersRaw);
|
|
@@ -15363,7 +15622,7 @@ if (bootAccountConfig?.whatsapp) {
|
|
|
15363
15622
|
}
|
|
15364
15623
|
init({
|
|
15365
15624
|
configDir: configDirForWhatsApp,
|
|
15366
|
-
platformRoot:
|
|
15625
|
+
platformRoot: resolve25(process.env.MAXY_PLATFORM_ROOT ?? join17(__dirname, "..")),
|
|
15367
15626
|
accountConfig: bootAccountConfig,
|
|
15368
15627
|
onMessage: async (msg) => {
|
|
15369
15628
|
if (process.env.WHATSAPP_PTY_BRIDGE_ENABLED === "true" && msg.text && !msg.isOwnerMirror) {
|