@rubytech/create-maxy 1.0.797 → 1.0.799
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/neo4j/schema.cypher +34 -0
- package/payload/platform/plugins/docs/references/internals.md +4 -0
- package/payload/platform/plugins/whatsapp/mcp/dist/call-api.d.ts +14 -0
- package/payload/platform/plugins/whatsapp/mcp/dist/call-api.d.ts.map +1 -0
- package/payload/platform/plugins/whatsapp/mcp/dist/call-api.js +42 -0
- package/payload/platform/plugins/whatsapp/mcp/dist/call-api.js.map +1 -0
- package/payload/platform/plugins/whatsapp/mcp/dist/index.js +50 -19
- package/payload/platform/plugins/whatsapp/mcp/dist/index.js.map +1 -1
- package/payload/server/chunk-SBHI2NMF.js +9910 -0
- package/payload/server/chunk-WHF6YXJ5.js +3456 -0
- package/payload/server/client-pool-4YDRTKAT.js +29 -0
- package/payload/server/maxy-edge.js +2 -2
- package/payload/server/public/assets/{admin-Cz8hUAqx.js → admin-C0lKk6WM.js} +1 -1
- package/payload/server/public/index.html +1 -1
- package/payload/server/server.js +434 -122
package/payload/server/server.js
CHANGED
|
@@ -42,6 +42,7 @@ import {
|
|
|
42
42
|
setRemotePassword,
|
|
43
43
|
sleep,
|
|
44
44
|
streamLogPathFor,
|
|
45
|
+
stripAttachmentMetaSuffix,
|
|
45
46
|
validateKey,
|
|
46
47
|
validatePasswordStrength,
|
|
47
48
|
verifyPassword,
|
|
@@ -49,7 +50,7 @@ import {
|
|
|
49
50
|
vncLog,
|
|
50
51
|
waitForExit,
|
|
51
52
|
writeChromiumWrapper
|
|
52
|
-
} from "./chunk-
|
|
53
|
+
} from "./chunk-SBHI2NMF.js";
|
|
53
54
|
import {
|
|
54
55
|
ACCOUNTS_DIR,
|
|
55
56
|
GREETING_DIRECTIVE,
|
|
@@ -112,7 +113,7 @@ import {
|
|
|
112
113
|
verifyAndGetConversationUpdatedAt,
|
|
113
114
|
verifyConversationOwnership,
|
|
114
115
|
writeAdminUserAndPerson
|
|
115
|
-
} from "./chunk-
|
|
116
|
+
} from "./chunk-WHF6YXJ5.js";
|
|
116
117
|
import {
|
|
117
118
|
__commonJS,
|
|
118
119
|
__toESM
|
|
@@ -615,8 +616,8 @@ var serveStatic = (options = { root: "" }) => {
|
|
|
615
616
|
};
|
|
616
617
|
|
|
617
618
|
// server/index.ts
|
|
618
|
-
import { readFileSync as
|
|
619
|
-
import { resolve as
|
|
619
|
+
import { readFileSync as readFileSync17, existsSync as existsSync24, watchFile } from "fs";
|
|
620
|
+
import { resolve as resolve24, join as join10, basename as basename7 } from "path";
|
|
620
621
|
import { homedir as homedir2 } from "os";
|
|
621
622
|
|
|
622
623
|
// app/lib/agent-slug-pattern.ts
|
|
@@ -2196,9 +2197,10 @@ var WhatsAppAccountSchema = z.object({
|
|
|
2196
2197
|
groups: z.record(
|
|
2197
2198
|
z.string(),
|
|
2198
2199
|
z.object({
|
|
2199
|
-
activation: z.enum(["always", "mention", "off"]).optional()
|
|
2200
|
+
activation: z.enum(["always", "mention", "off"]).optional(),
|
|
2201
|
+
publicAgent: z.string().optional().describe("Per-group public-agent slug override. When set, inbound messages in this group route to this agent instead of the per-account or top-level publicAgent.")
|
|
2200
2202
|
}).strict().optional()
|
|
2201
|
-
).optional().describe("Per-group settings
|
|
2203
|
+
).optional().describe("Per-group settings: 'activation' controls when the agent responds ('always', 'mention', 'off'); 'publicAgent' optionally overrides the public-agent slug for this group (precedence: group \u2192 account \u2192 top-level)."),
|
|
2202
2204
|
ackReaction: z.object({
|
|
2203
2205
|
emoji: z.string().optional(),
|
|
2204
2206
|
direct: z.boolean().optional().default(true),
|
|
@@ -2452,20 +2454,107 @@ function setPublicAgent(accountDir, slug) {
|
|
|
2452
2454
|
return { ok: false, error: msg };
|
|
2453
2455
|
}
|
|
2454
2456
|
}
|
|
2455
|
-
function
|
|
2457
|
+
function resolvePublicAgent(accountDir, opts) {
|
|
2456
2458
|
try {
|
|
2457
2459
|
const config = readConfig(accountDir);
|
|
2458
2460
|
const wa = config.whatsapp;
|
|
2459
2461
|
if (!wa) return null;
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2462
|
+
if (opts.groupJid) {
|
|
2463
|
+
const accounts2 = wa.accounts;
|
|
2464
|
+
const account2 = accounts2?.[opts.accountId];
|
|
2465
|
+
const groups = account2?.groups;
|
|
2466
|
+
const group = groups?.[opts.groupJid];
|
|
2467
|
+
const groupSlug = group?.publicAgent;
|
|
2468
|
+
if (typeof groupSlug === "string" && groupSlug.trim()) {
|
|
2469
|
+
return { slug: groupSlug.trim(), source: "group" };
|
|
2470
|
+
}
|
|
2471
|
+
}
|
|
2472
|
+
const accounts = wa.accounts;
|
|
2473
|
+
const account = accounts?.[opts.accountId];
|
|
2474
|
+
const accountSlug = account?.publicAgent;
|
|
2475
|
+
if (typeof accountSlug === "string" && accountSlug.trim()) {
|
|
2476
|
+
return { slug: accountSlug.trim(), source: "account" };
|
|
2477
|
+
}
|
|
2478
|
+
const topSlug = wa.publicAgent;
|
|
2479
|
+
if (typeof topSlug === "string" && topSlug.trim()) {
|
|
2480
|
+
return { slug: topSlug.trim(), source: "top-level" };
|
|
2463
2481
|
}
|
|
2464
2482
|
return null;
|
|
2465
2483
|
} catch {
|
|
2466
2484
|
return null;
|
|
2467
2485
|
}
|
|
2468
2486
|
}
|
|
2487
|
+
function setGroupPublicAgent(accountDir, accountId, groupJid, slug) {
|
|
2488
|
+
const trimmedSlug = slug.trim();
|
|
2489
|
+
const trimmedGroup = groupJid.trim();
|
|
2490
|
+
const trimmedAccount = accountId.trim();
|
|
2491
|
+
if (!trimmedSlug) return { ok: false, error: "Agent slug cannot be empty." };
|
|
2492
|
+
if (!trimmedGroup) return { ok: false, error: "Group JID cannot be empty." };
|
|
2493
|
+
if (!trimmedAccount) return { ok: false, error: "Account ID cannot be empty." };
|
|
2494
|
+
const agentConfigPath = join3(accountDir, "agents", trimmedSlug, "config.json");
|
|
2495
|
+
if (!existsSync5(agentConfigPath)) {
|
|
2496
|
+
return { ok: false, error: `Agent "${trimmedSlug}" not found \u2014 no config.json at ${agentConfigPath}. Check the agent slug and try again.` };
|
|
2497
|
+
}
|
|
2498
|
+
try {
|
|
2499
|
+
const config = readConfig(accountDir);
|
|
2500
|
+
if (!config.whatsapp || typeof config.whatsapp !== "object") config.whatsapp = {};
|
|
2501
|
+
const wa = config.whatsapp;
|
|
2502
|
+
if (!wa.accounts || typeof wa.accounts !== "object") wa.accounts = {};
|
|
2503
|
+
const accounts = wa.accounts;
|
|
2504
|
+
if (!accounts[trimmedAccount] || typeof accounts[trimmedAccount] !== "object") accounts[trimmedAccount] = {};
|
|
2505
|
+
const account = accounts[trimmedAccount];
|
|
2506
|
+
if (!account.groups || typeof account.groups !== "object") account.groups = {};
|
|
2507
|
+
const groups = account.groups;
|
|
2508
|
+
const existing = groups[trimmedGroup] && typeof groups[trimmedGroup] === "object" ? groups[trimmedGroup] : {};
|
|
2509
|
+
groups[trimmedGroup] = { ...existing, publicAgent: trimmedSlug };
|
|
2510
|
+
const parsed = WhatsAppConfigSchema.safeParse(wa);
|
|
2511
|
+
if (!parsed.success) {
|
|
2512
|
+
const msg = parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ");
|
|
2513
|
+
return { ok: false, error: `Validation failed: ${msg}` };
|
|
2514
|
+
}
|
|
2515
|
+
config.whatsapp = parsed.data;
|
|
2516
|
+
writeConfig(accountDir, config);
|
|
2517
|
+
console.error(`${TAG} setGroupPublicAgent account=${trimmedAccount} groupJid=${trimmedGroup} slug=${trimmedSlug}`);
|
|
2518
|
+
reloadManagerConfig(accountDir);
|
|
2519
|
+
return { ok: true, message: `Per-group public agent set to "${trimmedSlug}" for group ${trimmedGroup}.` };
|
|
2520
|
+
} catch (err) {
|
|
2521
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2522
|
+
console.error(`${TAG} setGroupPublicAgent failed: ${msg}`);
|
|
2523
|
+
return { ok: false, error: msg };
|
|
2524
|
+
}
|
|
2525
|
+
}
|
|
2526
|
+
function unsetGroupPublicAgent(accountDir, accountId, groupJid) {
|
|
2527
|
+
const trimmedGroup = groupJid.trim();
|
|
2528
|
+
const trimmedAccount = accountId.trim();
|
|
2529
|
+
if (!trimmedGroup) return { ok: false, error: "Group JID cannot be empty." };
|
|
2530
|
+
if (!trimmedAccount) return { ok: false, error: "Account ID cannot be empty." };
|
|
2531
|
+
try {
|
|
2532
|
+
const config = readConfig(accountDir);
|
|
2533
|
+
const wa = config.whatsapp ?? {};
|
|
2534
|
+
const accounts = wa.accounts;
|
|
2535
|
+
const account = accounts?.[trimmedAccount];
|
|
2536
|
+
const groups = account?.groups;
|
|
2537
|
+
const group = groups?.[trimmedGroup];
|
|
2538
|
+
if (!group || typeof group.publicAgent !== "string") {
|
|
2539
|
+
return { ok: true, message: `No per-group publicAgent override for group ${trimmedGroup}; nothing to remove.` };
|
|
2540
|
+
}
|
|
2541
|
+
delete group.publicAgent;
|
|
2542
|
+
const parsed = WhatsAppConfigSchema.safeParse(wa);
|
|
2543
|
+
if (!parsed.success) {
|
|
2544
|
+
const msg = parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ");
|
|
2545
|
+
return { ok: false, error: `Validation failed: ${msg}` };
|
|
2546
|
+
}
|
|
2547
|
+
config.whatsapp = parsed.data;
|
|
2548
|
+
writeConfig(accountDir, config);
|
|
2549
|
+
console.error(`${TAG} unsetGroupPublicAgent account=${trimmedAccount} groupJid=${trimmedGroup}`);
|
|
2550
|
+
reloadManagerConfig(accountDir);
|
|
2551
|
+
return { ok: true, message: `Per-group public agent override removed for group ${trimmedGroup}.` };
|
|
2552
|
+
} catch (err) {
|
|
2553
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2554
|
+
console.error(`${TAG} unsetGroupPublicAgent failed: ${msg}`);
|
|
2555
|
+
return { ok: false, error: msg };
|
|
2556
|
+
}
|
|
2557
|
+
}
|
|
2469
2558
|
function updateConfig(accountDir, fields) {
|
|
2470
2559
|
const fieldNames = Object.keys(fields);
|
|
2471
2560
|
if (fieldNames.length === 0) {
|
|
@@ -2701,7 +2790,7 @@ var credsSaveQueue = Promise.resolve();
|
|
|
2701
2790
|
async function drainCredsSaveQueue(timeoutMs = 5e3) {
|
|
2702
2791
|
console.error(`${TAG3} draining credential save queue\u2026`);
|
|
2703
2792
|
const timer2 = new Promise(
|
|
2704
|
-
(
|
|
2793
|
+
(resolve25) => setTimeout(() => resolve25("timeout"), timeoutMs)
|
|
2705
2794
|
);
|
|
2706
2795
|
const result = await Promise.race([
|
|
2707
2796
|
credsSaveQueue.then(() => "drained"),
|
|
@@ -2829,11 +2918,11 @@ async function createWaSocket(opts) {
|
|
|
2829
2918
|
return sock;
|
|
2830
2919
|
}
|
|
2831
2920
|
async function waitForConnection(sock) {
|
|
2832
|
-
return new Promise((
|
|
2921
|
+
return new Promise((resolve25, reject) => {
|
|
2833
2922
|
const handler = (update) => {
|
|
2834
2923
|
if (update.connection === "open") {
|
|
2835
2924
|
sock.ev.off("connection.update", handler);
|
|
2836
|
-
|
|
2925
|
+
resolve25();
|
|
2837
2926
|
}
|
|
2838
2927
|
if (update.connection === "close") {
|
|
2839
2928
|
sock.ev.off("connection.update", handler);
|
|
@@ -2947,14 +3036,14 @@ ${inspected}`;
|
|
|
2947
3036
|
return inspect2(err, INSPECT_OPTS2);
|
|
2948
3037
|
}
|
|
2949
3038
|
function withTimeout(label, promise, timeoutMs) {
|
|
2950
|
-
return new Promise((
|
|
3039
|
+
return new Promise((resolve25, reject) => {
|
|
2951
3040
|
const timer2 = setTimeout(() => {
|
|
2952
3041
|
reject(new Error(`${label} timed out after ${timeoutMs}ms`));
|
|
2953
3042
|
}, timeoutMs);
|
|
2954
3043
|
promise.then(
|
|
2955
3044
|
(value) => {
|
|
2956
3045
|
clearTimeout(timer2);
|
|
2957
|
-
|
|
3046
|
+
resolve25(value);
|
|
2958
3047
|
},
|
|
2959
3048
|
(err) => {
|
|
2960
3049
|
clearTimeout(timer2);
|
|
@@ -4168,11 +4257,11 @@ async function connectWithReconnect(conn) {
|
|
|
4168
4257
|
console.error(
|
|
4169
4258
|
`${TAG11} reconnecting account=${conn.accountId} in ${delay}ms (attempt ${decision.nextAttempts}/${maxAttempts})`
|
|
4170
4259
|
);
|
|
4171
|
-
await new Promise((
|
|
4172
|
-
const timer2 = setTimeout(
|
|
4260
|
+
await new Promise((resolve25) => {
|
|
4261
|
+
const timer2 = setTimeout(resolve25, delay);
|
|
4173
4262
|
conn.abortController.signal.addEventListener("abort", () => {
|
|
4174
4263
|
clearTimeout(timer2);
|
|
4175
|
-
|
|
4264
|
+
resolve25();
|
|
4176
4265
|
}, { once: true });
|
|
4177
4266
|
});
|
|
4178
4267
|
}
|
|
@@ -4180,16 +4269,16 @@ async function connectWithReconnect(conn) {
|
|
|
4180
4269
|
}
|
|
4181
4270
|
}
|
|
4182
4271
|
function waitForDisconnectEvent(conn) {
|
|
4183
|
-
return new Promise((
|
|
4272
|
+
return new Promise((resolve25) => {
|
|
4184
4273
|
if (!conn.sock) {
|
|
4185
|
-
|
|
4274
|
+
resolve25();
|
|
4186
4275
|
return;
|
|
4187
4276
|
}
|
|
4188
4277
|
const sock = conn.sock;
|
|
4189
4278
|
const handler = (update) => {
|
|
4190
4279
|
if (update.connection === "close") {
|
|
4191
4280
|
sock.ev.off("connection.update", handler);
|
|
4192
|
-
|
|
4281
|
+
resolve25();
|
|
4193
4282
|
}
|
|
4194
4283
|
};
|
|
4195
4284
|
sock.ev.on("connection.update", handler);
|
|
@@ -4406,8 +4495,8 @@ async function handleInboundMessage(conn, msg) {
|
|
|
4406
4495
|
const conversationKey = isGroup ? remoteJid : senderPhone;
|
|
4407
4496
|
const debounceKey = `${conn.accountId}:${conversationKey}:${senderPhone}`;
|
|
4408
4497
|
let resolvePending;
|
|
4409
|
-
const sttPending = new Promise((
|
|
4410
|
-
resolvePending =
|
|
4498
|
+
const sttPending = new Promise((resolve25) => {
|
|
4499
|
+
resolvePending = resolve25;
|
|
4411
4500
|
});
|
|
4412
4501
|
if (conn.debouncer) conn.debouncer.registerPending(debounceKey, sttPending);
|
|
4413
4502
|
try {
|
|
@@ -4520,20 +4609,20 @@ async function probeApiKey() {
|
|
|
4520
4609
|
return result.status;
|
|
4521
4610
|
}
|
|
4522
4611
|
function checkPort(port2, timeoutMs = 500) {
|
|
4523
|
-
return new Promise((
|
|
4612
|
+
return new Promise((resolve25) => {
|
|
4524
4613
|
const socket = createConnection(port2, "127.0.0.1");
|
|
4525
4614
|
socket.setTimeout(timeoutMs);
|
|
4526
4615
|
socket.once("connect", () => {
|
|
4527
4616
|
socket.destroy();
|
|
4528
|
-
|
|
4617
|
+
resolve25(true);
|
|
4529
4618
|
});
|
|
4530
4619
|
socket.once("error", () => {
|
|
4531
4620
|
socket.destroy();
|
|
4532
|
-
|
|
4621
|
+
resolve25(false);
|
|
4533
4622
|
});
|
|
4534
4623
|
socket.once("timeout", () => {
|
|
4535
4624
|
socket.destroy();
|
|
4536
|
-
|
|
4625
|
+
resolve25(false);
|
|
4537
4626
|
});
|
|
4538
4627
|
});
|
|
4539
4628
|
}
|
|
@@ -6746,8 +6835,8 @@ async function startLogin(opts) {
|
|
|
6746
6835
|
resetActiveLogin(accountId);
|
|
6747
6836
|
let resolveQr = null;
|
|
6748
6837
|
let rejectQr = null;
|
|
6749
|
-
const qrPromise = new Promise((
|
|
6750
|
-
resolveQr =
|
|
6838
|
+
const qrPromise = new Promise((resolve25, reject) => {
|
|
6839
|
+
resolveQr = resolve25;
|
|
6751
6840
|
rejectQr = reject;
|
|
6752
6841
|
});
|
|
6753
6842
|
const qrTimer = setTimeout(
|
|
@@ -7074,7 +7163,7 @@ app7.post("/send", async (c) => {
|
|
|
7074
7163
|
app7.post("/config", async (c) => {
|
|
7075
7164
|
try {
|
|
7076
7165
|
const body = await c.req.json().catch(() => ({}));
|
|
7077
|
-
const { action, phone, slug, fields, accountId } = body;
|
|
7166
|
+
const { action, phone, slug, groupJid, fields, accountId } = body;
|
|
7078
7167
|
if (!action || typeof action !== "string") {
|
|
7079
7168
|
return c.json({ ok: false, error: 'Missing required field "action".' }, 400);
|
|
7080
7169
|
}
|
|
@@ -7113,9 +7202,32 @@ app7.post("/config", async (c) => {
|
|
|
7113
7202
|
return c.json(result, result.ok ? 200 : 400);
|
|
7114
7203
|
}
|
|
7115
7204
|
case "get-public-agent": {
|
|
7116
|
-
const
|
|
7117
|
-
|
|
7118
|
-
|
|
7205
|
+
const targetAccount = typeof accountId === "string" && accountId.trim() ? accountId.trim() : "default";
|
|
7206
|
+
const targetGroup = typeof groupJid === "string" && groupJid.trim() ? groupJid.trim() : void 0;
|
|
7207
|
+
const resolved = resolvePublicAgent(account.accountDir, { accountId: targetAccount, groupJid: targetGroup });
|
|
7208
|
+
console.error(`${TAG16} config action=get-public-agent accountId=${targetAccount} groupJid=${targetGroup ?? "none"} slug=${resolved?.slug ?? "none"} source=${resolved?.source ?? "none"}`);
|
|
7209
|
+
return c.json({ ok: true, slug: resolved?.slug ?? null, source: resolved?.source ?? null });
|
|
7210
|
+
}
|
|
7211
|
+
case "set-group-public-agent": {
|
|
7212
|
+
if (!slug || typeof slug !== "string") {
|
|
7213
|
+
return c.json({ ok: false, error: 'Missing required field "slug" (the public-agent directory name, e.g. "my-agent").' }, 400);
|
|
7214
|
+
}
|
|
7215
|
+
if (!groupJid || typeof groupJid !== "string") {
|
|
7216
|
+
return c.json({ ok: false, error: 'Missing required field "groupJid" (group JID ending in @g.us).' }, 400);
|
|
7217
|
+
}
|
|
7218
|
+
const targetAccount = typeof accountId === "string" && accountId.trim() ? accountId.trim() : "default";
|
|
7219
|
+
const result = setGroupPublicAgent(account.accountDir, targetAccount, groupJid, slug);
|
|
7220
|
+
console.error(`${TAG16} config action=set-group-public-agent accountId=${targetAccount} groupJid=${groupJid} slug=${slug} ok=${result.ok}`);
|
|
7221
|
+
return c.json(result, result.ok ? 200 : 400);
|
|
7222
|
+
}
|
|
7223
|
+
case "unset-group-public-agent": {
|
|
7224
|
+
if (!groupJid || typeof groupJid !== "string") {
|
|
7225
|
+
return c.json({ ok: false, error: 'Missing required field "groupJid" (group JID ending in @g.us).' }, 400);
|
|
7226
|
+
}
|
|
7227
|
+
const targetAccount = typeof accountId === "string" && accountId.trim() ? accountId.trim() : "default";
|
|
7228
|
+
const result = unsetGroupPublicAgent(account.accountDir, targetAccount, groupJid);
|
|
7229
|
+
console.error(`${TAG16} config action=unset-group-public-agent accountId=${targetAccount} groupJid=${groupJid} ok=${result.ok}`);
|
|
7230
|
+
return c.json(result, result.ok ? 200 : 400);
|
|
7119
7231
|
}
|
|
7120
7232
|
case "list-public-agents": {
|
|
7121
7233
|
const agentsDir = resolve10(account.accountDir, "agents");
|
|
@@ -7183,7 +7295,7 @@ app7.post("/config", async (c) => {
|
|
|
7183
7295
|
}
|
|
7184
7296
|
default:
|
|
7185
7297
|
return c.json(
|
|
7186
|
-
{ ok: false, error: `Unknown action "${action}". Valid actions: add-admin-phone, remove-admin-phone, list-admin-phones, set-public-agent, get-public-agent, list-public-agents, update-config, get-config, schema, list-groups.` },
|
|
7298
|
+
{ ok: false, error: `Unknown action "${action}". Valid actions: add-admin-phone, remove-admin-phone, list-admin-phones, set-public-agent, get-public-agent, set-group-public-agent, unset-group-public-agent, list-public-agents, update-config, get-config, schema, list-groups.` },
|
|
7187
7299
|
400
|
|
7188
7300
|
);
|
|
7189
7301
|
}
|
|
@@ -8213,7 +8325,7 @@ var app11 = new Hono();
|
|
|
8213
8325
|
app11.post("/cancel", requireAdminSession, async (c) => {
|
|
8214
8326
|
const session_key = c.var.sessionKey;
|
|
8215
8327
|
try {
|
|
8216
|
-
const { interruptClient: interruptClient2 } = await import("./client-pool-
|
|
8328
|
+
const { interruptClient: interruptClient2 } = await import("./client-pool-4YDRTKAT.js");
|
|
8217
8329
|
await interruptClient2(session_key);
|
|
8218
8330
|
return c.json({ ok: true });
|
|
8219
8331
|
} catch (err) {
|
|
@@ -8967,7 +9079,35 @@ var agents_default = app16;
|
|
|
8967
9079
|
// server/routes/admin/sessions.ts
|
|
8968
9080
|
import crypto2 from "crypto";
|
|
8969
9081
|
import { resolve as resolvePath } from "path";
|
|
8970
|
-
import { appendFileSync as appendFileSync5 } from "fs";
|
|
9082
|
+
import { appendFileSync as appendFileSync5, existsSync as existsSync18 } from "fs";
|
|
9083
|
+
function validateAndShapeAttachments(raws, conversationAccountId, conversationId, messageId, streamLogPath) {
|
|
9084
|
+
const chips = [];
|
|
9085
|
+
let valid = 0;
|
|
9086
|
+
let invalid = 0;
|
|
9087
|
+
for (const a of raws) {
|
|
9088
|
+
let reason = null;
|
|
9089
|
+
if (!a.attachmentId || !a.filename || !a.mimeType || !a.storagePath) reason = "schema-fail";
|
|
9090
|
+
else if (a.accountId !== conversationAccountId) reason = "account-mismatch";
|
|
9091
|
+
else if (!existsSync18(a.storagePath)) reason = "missing-file";
|
|
9092
|
+
if (reason) {
|
|
9093
|
+
invalid++;
|
|
9094
|
+
try {
|
|
9095
|
+
appendFileSync5(streamLogPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] [attachment-rehydrate-invalid] conversationId=${conversationId.slice(0, 8)} messageId=${messageId.slice(0, 8)} attachmentId=${(a.attachmentId || "").slice(0, 8)} reason=${reason}
|
|
9096
|
+
`);
|
|
9097
|
+
} catch {
|
|
9098
|
+
}
|
|
9099
|
+
continue;
|
|
9100
|
+
}
|
|
9101
|
+
valid++;
|
|
9102
|
+
chips.push({
|
|
9103
|
+
attachmentId: a.attachmentId,
|
|
9104
|
+
filename: a.filename,
|
|
9105
|
+
mimeType: a.mimeType,
|
|
9106
|
+
sizeBytes: a.sizeBytes
|
|
9107
|
+
});
|
|
9108
|
+
}
|
|
9109
|
+
return { chips, valid, invalid };
|
|
9110
|
+
}
|
|
8971
9111
|
function reconstructAssistantEvents(content, components, conversationId, messageId, streamLogPath) {
|
|
8972
9112
|
if (components.length === 0) {
|
|
8973
9113
|
return {
|
|
@@ -9120,7 +9260,17 @@ app17.get("/:id/messages", requireAdminSession, async (c) => {
|
|
|
9120
9260
|
if (!owned) return c.json({ error: "Conversation not found" }, 404);
|
|
9121
9261
|
try {
|
|
9122
9262
|
const messages = await getRecentMessages(conversationId, 50);
|
|
9123
|
-
|
|
9263
|
+
const sanitised = messages.map((m) => ({
|
|
9264
|
+
...m,
|
|
9265
|
+
attachments: m.attachments.map((a) => ({
|
|
9266
|
+
attachmentId: a.attachmentId,
|
|
9267
|
+
filename: a.filename,
|
|
9268
|
+
mimeType: a.mimeType,
|
|
9269
|
+
sizeBytes: a.sizeBytes,
|
|
9270
|
+
ordinal: a.ordinal
|
|
9271
|
+
}))
|
|
9272
|
+
}));
|
|
9273
|
+
return c.json({ messages: sanitised });
|
|
9124
9274
|
} catch (err) {
|
|
9125
9275
|
console.error(`[sessions-messages] Failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
9126
9276
|
return c.json({ error: "Failed to fetch messages" }, 500);
|
|
@@ -9153,10 +9303,23 @@ app17.post("/:id/resume", requireAdminSession, async (c) => {
|
|
|
9153
9303
|
let totalValid = 0;
|
|
9154
9304
|
let totalInvalid = 0;
|
|
9155
9305
|
let totalComponents = 0;
|
|
9306
|
+
let totalAttachments = 0;
|
|
9307
|
+
let totalAttachmentInvalid = 0;
|
|
9156
9308
|
const rehydrated = messages.map((m) => {
|
|
9157
9309
|
const components = m.components ?? [];
|
|
9310
|
+
const rawAttachments = m.attachments ?? [];
|
|
9158
9311
|
if (m.role !== "assistant") {
|
|
9159
|
-
|
|
9312
|
+
const { chips, valid: valid2, invalid: invalid2 } = validateAndShapeAttachments(rawAttachments, accountId, conversationId, m.messageId, streamLogPath);
|
|
9313
|
+
totalAttachments += valid2;
|
|
9314
|
+
totalAttachmentInvalid += invalid2;
|
|
9315
|
+
const cleanedContent = chips.length > 0 ? stripAttachmentMetaSuffix(m.content) : m.content;
|
|
9316
|
+
return {
|
|
9317
|
+
messageId: m.messageId,
|
|
9318
|
+
role: m.role,
|
|
9319
|
+
content: cleanedContent,
|
|
9320
|
+
createdAt: m.createdAt,
|
|
9321
|
+
...chips.length > 0 ? { attachments: chips } : {}
|
|
9322
|
+
};
|
|
9160
9323
|
}
|
|
9161
9324
|
const { events, valid, invalid, submittedEventIndices } = reconstructAssistantEvents(m.content, components, conversationId, m.messageId, streamLogPath);
|
|
9162
9325
|
totalValid += valid;
|
|
@@ -9173,17 +9336,22 @@ app17.post("/:id/resume", requireAdminSession, async (c) => {
|
|
|
9173
9336
|
};
|
|
9174
9337
|
});
|
|
9175
9338
|
const textRuns = rehydrated.reduce((n, m) => n + (m.events?.filter((e) => e.type === "text").length ?? 0), 0);
|
|
9339
|
+
const userMessageCount = rehydrated.filter((m) => m.role !== "assistant").length;
|
|
9176
9340
|
try {
|
|
9177
|
-
appendFileSync5(streamLogPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] [admin-resume] sessionKey=${sessionKey.slice(0, 8)} conversationId=${conversationId.slice(0, 8)} ${tag} loadedMessages=${messages.length} componentCount=${totalComponents}
|
|
9341
|
+
appendFileSync5(streamLogPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] [admin-resume] sessionKey=${sessionKey.slice(0, 8)} conversationId=${conversationId.slice(0, 8)} ${tag} loadedMessages=${messages.length} componentCount=${totalComponents} userAttachmentCount=${totalAttachments}
|
|
9178
9342
|
`);
|
|
9179
9343
|
if (totalComponents > 0) {
|
|
9180
9344
|
appendFileSync5(streamLogPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] [component-rehydrate] conversationId=${conversationId.slice(0, 8)} count=${totalComponents} valid=${totalValid} invalid=${totalInvalid} textRuns=${textRuns}
|
|
9345
|
+
`);
|
|
9346
|
+
}
|
|
9347
|
+
if (totalAttachments > 0 || totalAttachmentInvalid > 0) {
|
|
9348
|
+
appendFileSync5(streamLogPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] [attachment-rehydrate] conversationId=${conversationId.slice(0, 8)} userMessages=${userMessageCount} attachments=${totalAttachments} invalid=${totalAttachmentInvalid}
|
|
9181
9349
|
`);
|
|
9182
9350
|
}
|
|
9183
9351
|
} catch {
|
|
9184
9352
|
}
|
|
9185
9353
|
const age = formatAge(updatedAt);
|
|
9186
|
-
console.log(`[admin-resume] ${(/* @__PURE__ */ new Date()).toISOString()} conversationId=${conversationId.slice(0, 8)}\u2026 age=${age} loaded=${messages.length} messages ${tag} components=${totalComponents} sessionKey=${sessionKey.slice(0, 8)}\u2026`);
|
|
9354
|
+
console.log(`[admin-resume] ${(/* @__PURE__ */ new Date()).toISOString()} conversationId=${conversationId.slice(0, 8)}\u2026 age=${age} loaded=${messages.length} messages ${tag} components=${totalComponents} attachments=${totalAttachments} sessionKey=${sessionKey.slice(0, 8)}\u2026`);
|
|
9187
9355
|
return c.json({ conversationId, messages: rehydrated });
|
|
9188
9356
|
});
|
|
9189
9357
|
app17.post("/:id/label", requireAdminSession, async (c) => {
|
|
@@ -9458,12 +9626,12 @@ function isValidDomain(value) {
|
|
|
9458
9626
|
}
|
|
9459
9627
|
|
|
9460
9628
|
// app/lib/alias-domains.ts
|
|
9461
|
-
import { existsSync as
|
|
9629
|
+
import { existsSync as existsSync19, mkdirSync as mkdirSync8, readFileSync as readFileSync14, writeFileSync as writeFileSync9 } from "fs";
|
|
9462
9630
|
import { dirname as dirname7 } from "path";
|
|
9463
9631
|
import { resolve as resolve16 } from "path";
|
|
9464
9632
|
var ALIAS_DOMAINS_PATH = resolve16(MAXY_DIR, "alias-domains.json");
|
|
9465
9633
|
function readExisting() {
|
|
9466
|
-
if (!
|
|
9634
|
+
if (!existsSync19(ALIAS_DOMAINS_PATH)) return /* @__PURE__ */ new Set();
|
|
9467
9635
|
try {
|
|
9468
9636
|
const parsed = JSON.parse(readFileSync14(ALIAS_DOMAINS_PATH, "utf-8"));
|
|
9469
9637
|
if (!Array.isArray(parsed)) return /* @__PURE__ */ new Set();
|
|
@@ -11800,7 +11968,7 @@ var adherence_default = app30;
|
|
|
11800
11968
|
import neo4j3 from "neo4j-driver";
|
|
11801
11969
|
import { readFile as readFile5, readdir as readdir3, stat as stat5 } from "fs/promises";
|
|
11802
11970
|
import { resolve as resolve20, relative as relative2, isAbsolute } from "path";
|
|
11803
|
-
import { existsSync as
|
|
11971
|
+
import { existsSync as existsSync20 } from "fs";
|
|
11804
11972
|
var LIMIT = 50;
|
|
11805
11973
|
var TEXT_MIME_PREFIXES = ["text/", "application/json", "application/markdown"];
|
|
11806
11974
|
var ADMIN_AGENT_FILES = ["IDENTITY.md", "SOUL.md", "KNOWLEDGE.md"];
|
|
@@ -11948,7 +12116,7 @@ async function fetchAgentTemplateRows(accountDir) {
|
|
|
11948
12116
|
async function unionSpecialistFilenames(overrideDir, bundledDir) {
|
|
11949
12117
|
const names = /* @__PURE__ */ new Set();
|
|
11950
12118
|
for (const dir of [overrideDir, bundledDir]) {
|
|
11951
|
-
if (!
|
|
12119
|
+
if (!existsSync20(dir)) continue;
|
|
11952
12120
|
try {
|
|
11953
12121
|
const entries = await readdir3(dir);
|
|
11954
12122
|
for (const entry of entries) {
|
|
@@ -11963,7 +12131,7 @@ async function unionSpecialistFilenames(overrideDir, bundledDir) {
|
|
|
11963
12131
|
}
|
|
11964
12132
|
async function readAgentTemplateRow(inp) {
|
|
11965
12133
|
let chosenPath = null;
|
|
11966
|
-
if (
|
|
12134
|
+
if (existsSync20(inp.overridePath)) {
|
|
11967
12135
|
try {
|
|
11968
12136
|
validateFilePathInAccount(inp.overridePath, inp.overrideRoot);
|
|
11969
12137
|
chosenPath = inp.overridePath;
|
|
@@ -11974,7 +12142,7 @@ async function readAgentTemplateRow(inp) {
|
|
|
11974
12142
|
);
|
|
11975
12143
|
return null;
|
|
11976
12144
|
}
|
|
11977
|
-
} else if (
|
|
12145
|
+
} else if (existsSync20(inp.bundledPath)) {
|
|
11978
12146
|
if (!isWithin(inp.bundledPath, inp.bundledRoot)) {
|
|
11979
12147
|
console.error(
|
|
11980
12148
|
`[admin/sidebar-artefacts] agent-template-read-failed agent=${inp.displayName} kind=${inp.logName} error="bundled path outside PLATFORM_ROOT"`
|
|
@@ -12016,7 +12184,7 @@ var sidebar_artefacts_default = app31;
|
|
|
12016
12184
|
// server/routes/admin/sidebar-artefact-save.ts
|
|
12017
12185
|
import { mkdir as mkdir4, readdir as readdir4, stat as stat6, writeFile as writeFile5 } from "fs/promises";
|
|
12018
12186
|
import { resolve as resolve21 } from "path";
|
|
12019
|
-
import { existsSync as
|
|
12187
|
+
import { existsSync as existsSync21 } from "fs";
|
|
12020
12188
|
var ADMIN_AGENT_FILES2 = /* @__PURE__ */ new Set(["IDENTITY.md", "SOUL.md", "KNOWLEDGE.md"]);
|
|
12021
12189
|
var UUID_RE4 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
|
|
12022
12190
|
var app32 = new Hono();
|
|
@@ -12080,7 +12248,7 @@ async function resolveSavePath(id, accountId, accountDir) {
|
|
|
12080
12248
|
}
|
|
12081
12249
|
if (UUID_RE4.test(id)) {
|
|
12082
12250
|
const dir = resolve21(ATTACHMENTS_ROOT, accountId, id);
|
|
12083
|
-
if (!
|
|
12251
|
+
if (!existsSync21(dir)) {
|
|
12084
12252
|
return { kind: "reject", status: 400, reason: "not-found" };
|
|
12085
12253
|
}
|
|
12086
12254
|
try {
|
|
@@ -12104,7 +12272,7 @@ var sidebar_artefact_save_default = app32;
|
|
|
12104
12272
|
|
|
12105
12273
|
// server/routes/admin/sidebar-artefact-content.ts
|
|
12106
12274
|
import { readFile as readFile6, readdir as readdir5 } from "fs/promises";
|
|
12107
|
-
import { existsSync as
|
|
12275
|
+
import { existsSync as existsSync22 } from "fs";
|
|
12108
12276
|
import { resolve as resolve22 } from "path";
|
|
12109
12277
|
var UUID_RE5 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
|
|
12110
12278
|
var app33 = new Hono();
|
|
@@ -12118,7 +12286,7 @@ app33.get("/", requireAdminSession, async (c) => {
|
|
|
12118
12286
|
return new Response("Not found", { status: 404 });
|
|
12119
12287
|
}
|
|
12120
12288
|
const dir = resolve22(ATTACHMENTS_ROOT, accountId, id);
|
|
12121
|
-
if (!
|
|
12289
|
+
if (!existsSync22(dir)) {
|
|
12122
12290
|
console.error(`[admin/sidebar-artefact-content] not-found id=${id.slice(0, 8)}`);
|
|
12123
12291
|
return new Response("Not found", { status: 404 });
|
|
12124
12292
|
}
|
|
@@ -12179,6 +12347,140 @@ app34.route("/sidebar-artefact-save", sidebar_artefact_save_default);
|
|
|
12179
12347
|
app34.route("/sidebar-artefact-content", sidebar_artefact_content_default);
|
|
12180
12348
|
var admin_default = app34;
|
|
12181
12349
|
|
|
12350
|
+
// server/routes/sites.ts
|
|
12351
|
+
import { existsSync as existsSync23, readFileSync as readFileSync16, realpathSync as realpathSync5, statSync as statSync8 } from "fs";
|
|
12352
|
+
import { resolve as resolve23 } from "path";
|
|
12353
|
+
var SAFE_SEG_RE = /^[a-z0-9_][a-z0-9_.-]{0,99}$/i;
|
|
12354
|
+
var MIME = {
|
|
12355
|
+
".html": "text/html; charset=utf-8",
|
|
12356
|
+
".htm": "text/html; charset=utf-8",
|
|
12357
|
+
".css": "text/css; charset=utf-8",
|
|
12358
|
+
".js": "text/javascript; charset=utf-8",
|
|
12359
|
+
".mjs": "text/javascript; charset=utf-8",
|
|
12360
|
+
".json": "application/json; charset=utf-8",
|
|
12361
|
+
".txt": "text/plain; charset=utf-8",
|
|
12362
|
+
".md": "text/plain; charset=utf-8",
|
|
12363
|
+
".png": "image/png",
|
|
12364
|
+
".jpg": "image/jpeg",
|
|
12365
|
+
".jpeg": "image/jpeg",
|
|
12366
|
+
".gif": "image/gif",
|
|
12367
|
+
".webp": "image/webp",
|
|
12368
|
+
".svg": "image/svg+xml",
|
|
12369
|
+
".ico": "image/x-icon",
|
|
12370
|
+
".woff": "font/woff",
|
|
12371
|
+
".woff2": "font/woff2",
|
|
12372
|
+
".ttf": "font/ttf",
|
|
12373
|
+
".otf": "font/otf"
|
|
12374
|
+
};
|
|
12375
|
+
var HTML_EXTS = /* @__PURE__ */ new Set([".html", ".htm"]);
|
|
12376
|
+
var CSP_HTML = "default-src 'self' https: data:; script-src 'none'";
|
|
12377
|
+
function getExt(p) {
|
|
12378
|
+
const idx = p.lastIndexOf(".");
|
|
12379
|
+
if (idx < 0) return "";
|
|
12380
|
+
if (idx < p.lastIndexOf("/")) return "";
|
|
12381
|
+
return p.slice(idx).toLowerCase();
|
|
12382
|
+
}
|
|
12383
|
+
var app35 = new Hono();
|
|
12384
|
+
app35.get("/:rel{.*}", (c) => {
|
|
12385
|
+
const reqPath = c.req.path;
|
|
12386
|
+
const rawRel = c.req.param("rel") ?? "";
|
|
12387
|
+
const trimmed = rawRel.replace(/^\/+/, "").replace(/\/+$/, "");
|
|
12388
|
+
const isDirRequest = rawRel === "" || rawRel.endsWith("/") || reqPath.endsWith("/");
|
|
12389
|
+
const account = resolveAccount();
|
|
12390
|
+
if (!account) {
|
|
12391
|
+
console.error(`[sites] no-account path=${reqPath} status=404`);
|
|
12392
|
+
return c.text("Not found", 404);
|
|
12393
|
+
}
|
|
12394
|
+
const rawSegments = trimmed === "" ? [] : trimmed.split("/");
|
|
12395
|
+
const segments = [];
|
|
12396
|
+
for (const raw of rawSegments) {
|
|
12397
|
+
let seg;
|
|
12398
|
+
try {
|
|
12399
|
+
seg = decodeURIComponent(raw);
|
|
12400
|
+
} catch {
|
|
12401
|
+
console.error(`[sites] path-traversal-rejected path=${reqPath} reason=segment status=403`);
|
|
12402
|
+
return c.text("Forbidden", 403);
|
|
12403
|
+
}
|
|
12404
|
+
if (seg === ".." || seg === "." || !SAFE_SEG_RE.test(seg)) {
|
|
12405
|
+
console.error(`[sites] path-traversal-rejected path=${reqPath} reason=segment status=403`);
|
|
12406
|
+
return c.text("Forbidden", 403);
|
|
12407
|
+
}
|
|
12408
|
+
segments.push(seg);
|
|
12409
|
+
}
|
|
12410
|
+
const rootDir = resolve23(account.accountDir, "sites");
|
|
12411
|
+
let filePath = segments.length === 0 ? rootDir : resolve23(rootDir, ...segments);
|
|
12412
|
+
if (filePath !== rootDir && !filePath.startsWith(rootDir + "/")) {
|
|
12413
|
+
console.error(`[sites] path-traversal-rejected path=${reqPath} reason=escape status=403`);
|
|
12414
|
+
return c.text("Forbidden", 403);
|
|
12415
|
+
}
|
|
12416
|
+
let stat7;
|
|
12417
|
+
try {
|
|
12418
|
+
stat7 = existsSync23(filePath) ? statSync8(filePath) : null;
|
|
12419
|
+
} catch {
|
|
12420
|
+
stat7 = null;
|
|
12421
|
+
}
|
|
12422
|
+
if (stat7?.isDirectory()) {
|
|
12423
|
+
filePath = resolve23(filePath, "index.html");
|
|
12424
|
+
} else if (stat7 === null && isDirRequest) {
|
|
12425
|
+
filePath = resolve23(filePath, "index.html");
|
|
12426
|
+
}
|
|
12427
|
+
if (!filePath.startsWith(rootDir + "/")) {
|
|
12428
|
+
console.error(`[sites] path-traversal-rejected path=${reqPath} reason=escape status=403`);
|
|
12429
|
+
return c.text("Forbidden", 403);
|
|
12430
|
+
}
|
|
12431
|
+
if (!existsSync23(filePath)) {
|
|
12432
|
+
console.error(`[sites] not-found path=${reqPath} status=404`);
|
|
12433
|
+
return c.text("Not found", 404);
|
|
12434
|
+
}
|
|
12435
|
+
let realPath;
|
|
12436
|
+
let realRoot;
|
|
12437
|
+
try {
|
|
12438
|
+
realPath = realpathSync5(filePath);
|
|
12439
|
+
realRoot = realpathSync5(rootDir);
|
|
12440
|
+
} catch {
|
|
12441
|
+
console.error(`[sites] not-found path=${reqPath} status=404`);
|
|
12442
|
+
return c.text("Not found", 404);
|
|
12443
|
+
}
|
|
12444
|
+
if (realPath !== realRoot && !realPath.startsWith(realRoot + "/")) {
|
|
12445
|
+
console.error(`[sites] symlink-escape-rejected path=${reqPath} real=${realPath} status=403`);
|
|
12446
|
+
return c.text("Forbidden", 403);
|
|
12447
|
+
}
|
|
12448
|
+
let body;
|
|
12449
|
+
try {
|
|
12450
|
+
body = readFileSync16(realPath);
|
|
12451
|
+
} catch (err) {
|
|
12452
|
+
const code = err?.code;
|
|
12453
|
+
if (code === "EISDIR") {
|
|
12454
|
+
console.error(`[sites] not-found path=${reqPath} reason=is-directory status=404`);
|
|
12455
|
+
return c.text("Not found", 404);
|
|
12456
|
+
}
|
|
12457
|
+
if (code === "ENOENT") {
|
|
12458
|
+
console.error(`[sites] not-found path=${reqPath} status=404`);
|
|
12459
|
+
return c.text("Not found", 404);
|
|
12460
|
+
}
|
|
12461
|
+
throw err;
|
|
12462
|
+
}
|
|
12463
|
+
const ext = getExt(realPath);
|
|
12464
|
+
const contentType = MIME[ext] ?? "application/octet-stream";
|
|
12465
|
+
const isHtml = HTML_EXTS.has(ext);
|
|
12466
|
+
console.log(`[sites] serve path=${reqPath} ext=${ext || "(none)"} status=200 bytes=${body.length}`);
|
|
12467
|
+
const view = Uint8Array.from(body);
|
|
12468
|
+
if (isHtml) {
|
|
12469
|
+
return c.body(view, 200, {
|
|
12470
|
+
"Content-Type": contentType,
|
|
12471
|
+
"Cache-Control": "no-cache",
|
|
12472
|
+
"X-Content-Type-Options": "nosniff",
|
|
12473
|
+
"Content-Security-Policy": CSP_HTML
|
|
12474
|
+
});
|
|
12475
|
+
}
|
|
12476
|
+
return c.body(view, 200, {
|
|
12477
|
+
"Content-Type": contentType,
|
|
12478
|
+
"Cache-Control": "public, max-age=3600",
|
|
12479
|
+
"X-Content-Type-Options": "nosniff"
|
|
12480
|
+
});
|
|
12481
|
+
});
|
|
12482
|
+
var sites_default = app35;
|
|
12483
|
+
|
|
12182
12484
|
// app/lib/graph-health.ts
|
|
12183
12485
|
var HOUR_MS = 60 * 60 * 1e3;
|
|
12184
12486
|
var timer = null;
|
|
@@ -12279,12 +12581,12 @@ function clientFrom(c) {
|
|
|
12279
12581
|
var PLATFORM_ROOT7 = process.env.MAXY_PLATFORM_ROOT || "";
|
|
12280
12582
|
var BRAND_JSON_PATH = PLATFORM_ROOT7 ? join10(PLATFORM_ROOT7, "config", "brand.json") : "";
|
|
12281
12583
|
var BRAND = { productName: "Maxy", hostname: "maxy", configDir: ".maxy", domain: "getmaxy.com" };
|
|
12282
|
-
if (BRAND_JSON_PATH && !
|
|
12584
|
+
if (BRAND_JSON_PATH && !existsSync24(BRAND_JSON_PATH)) {
|
|
12283
12585
|
console.error(`[brand] WARNING: brand.json not found at ${BRAND_JSON_PATH} \u2014 using Maxy defaults`);
|
|
12284
12586
|
}
|
|
12285
|
-
if (BRAND_JSON_PATH &&
|
|
12587
|
+
if (BRAND_JSON_PATH && existsSync24(BRAND_JSON_PATH)) {
|
|
12286
12588
|
try {
|
|
12287
|
-
const parsed = JSON.parse(
|
|
12589
|
+
const parsed = JSON.parse(readFileSync17(BRAND_JSON_PATH, "utf-8"));
|
|
12288
12590
|
BRAND = { ...BRAND, ...parsed };
|
|
12289
12591
|
} catch (err) {
|
|
12290
12592
|
console.error(`[brand] Failed to parse brand.json: ${err.message}`);
|
|
@@ -12306,8 +12608,8 @@ var brandLoginOpts = {
|
|
|
12306
12608
|
var ALIAS_DOMAINS_PATH2 = join10(homedir2(), BRAND.configDir, "alias-domains.json");
|
|
12307
12609
|
function loadAliasDomains() {
|
|
12308
12610
|
try {
|
|
12309
|
-
if (!
|
|
12310
|
-
const parsed = JSON.parse(
|
|
12611
|
+
if (!existsSync24(ALIAS_DOMAINS_PATH2)) return null;
|
|
12612
|
+
const parsed = JSON.parse(readFileSync17(ALIAS_DOMAINS_PATH2, "utf-8"));
|
|
12311
12613
|
if (!Array.isArray(parsed)) {
|
|
12312
12614
|
console.error("[alias-domains] malformed alias-domains.json \u2014 expected array");
|
|
12313
12615
|
return null;
|
|
@@ -12331,9 +12633,9 @@ watchFile(ALIAS_DOMAINS_PATH2, { interval: 2e3 }, () => {
|
|
|
12331
12633
|
function isPublicHost(host) {
|
|
12332
12634
|
return host.startsWith("public.") || aliasDomains.has(host);
|
|
12333
12635
|
}
|
|
12334
|
-
var
|
|
12335
|
-
|
|
12336
|
-
|
|
12636
|
+
var app36 = new Hono();
|
|
12637
|
+
app36.use("*", clientIpMiddleware);
|
|
12638
|
+
app36.use("*", async (c, next) => {
|
|
12337
12639
|
await next();
|
|
12338
12640
|
c.header("X-Content-Type-Options", "nosniff");
|
|
12339
12641
|
c.header("Referrer-Policy", "strict-origin-when-cross-origin");
|
|
@@ -12353,10 +12655,11 @@ var PUBLIC_ALLOWED_PREFIXES = [
|
|
|
12353
12655
|
"/brand/",
|
|
12354
12656
|
"/agent-assets/",
|
|
12355
12657
|
"/generated/",
|
|
12356
|
-
"/g/"
|
|
12658
|
+
"/g/",
|
|
12659
|
+
"/sites/"
|
|
12357
12660
|
];
|
|
12358
12661
|
var PUBLIC_ALLOWED_EXACT = ["/favicon.ico"];
|
|
12359
|
-
|
|
12662
|
+
app36.use("*", async (c, next) => {
|
|
12360
12663
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
12361
12664
|
if (!isPublicHost(host)) {
|
|
12362
12665
|
await next();
|
|
@@ -12396,7 +12699,7 @@ function resolveRemoteAuthOpts() {
|
|
|
12396
12699
|
return brandLoginOpts;
|
|
12397
12700
|
}
|
|
12398
12701
|
var MAX_LOGIN_BODY = 8 * 1024;
|
|
12399
|
-
|
|
12702
|
+
app36.post("/__remote-auth/login", async (c) => {
|
|
12400
12703
|
const client = clientFrom(c);
|
|
12401
12704
|
const clientIp = client.ip || "unknown";
|
|
12402
12705
|
if (!requestIsTlsTerminated(c)) {
|
|
@@ -12440,7 +12743,7 @@ app35.post("/__remote-auth/login", async (c) => {
|
|
|
12440
12743
|
}
|
|
12441
12744
|
});
|
|
12442
12745
|
});
|
|
12443
|
-
|
|
12746
|
+
app36.get("/__remote-auth/logout", (c) => {
|
|
12444
12747
|
return new Response(null, {
|
|
12445
12748
|
status: 302,
|
|
12446
12749
|
headers: {
|
|
@@ -12450,7 +12753,7 @@ app35.get("/__remote-auth/logout", (c) => {
|
|
|
12450
12753
|
}
|
|
12451
12754
|
});
|
|
12452
12755
|
});
|
|
12453
|
-
|
|
12756
|
+
app36.post("/__remote-auth/change-password", async (c) => {
|
|
12454
12757
|
const client = clientFrom(c);
|
|
12455
12758
|
const clientIp = client.ip || "unknown";
|
|
12456
12759
|
const rateLimited = checkRateLimit(client);
|
|
@@ -12500,13 +12803,13 @@ app35.post("/__remote-auth/change-password", async (c) => {
|
|
|
12500
12803
|
return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(), mode: "change", changeError: "Failed to save password", redirect }), 200);
|
|
12501
12804
|
}
|
|
12502
12805
|
});
|
|
12503
|
-
|
|
12806
|
+
app36.get("/__remote-auth/setup", (c) => {
|
|
12504
12807
|
if (isRemoteAuthConfigured()) {
|
|
12505
12808
|
return c.redirect("/");
|
|
12506
12809
|
}
|
|
12507
12810
|
return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(), mode: "setup" }), 200);
|
|
12508
12811
|
});
|
|
12509
|
-
|
|
12812
|
+
app36.post("/__remote-auth/set-initial-password", async (c) => {
|
|
12510
12813
|
if (isRemoteAuthConfigured()) {
|
|
12511
12814
|
return c.redirect("/");
|
|
12512
12815
|
}
|
|
@@ -12542,10 +12845,10 @@ app35.post("/__remote-auth/set-initial-password", async (c) => {
|
|
|
12542
12845
|
return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(), mode: "setup", setupError: "Failed to save password. Please try again." }), 200);
|
|
12543
12846
|
}
|
|
12544
12847
|
});
|
|
12545
|
-
|
|
12848
|
+
app36.get("/api/remote-auth/status", (c) => {
|
|
12546
12849
|
return c.json({ configured: isRemoteAuthConfigured() });
|
|
12547
12850
|
});
|
|
12548
|
-
|
|
12851
|
+
app36.post("/api/remote-auth/set-password", async (c) => {
|
|
12549
12852
|
let body;
|
|
12550
12853
|
try {
|
|
12551
12854
|
body = await c.req.json();
|
|
@@ -12575,9 +12878,9 @@ app35.post("/api/remote-auth/set-password", async (c) => {
|
|
|
12575
12878
|
return c.json({ error: "Failed to save password" }, 500);
|
|
12576
12879
|
}
|
|
12577
12880
|
});
|
|
12578
|
-
|
|
12881
|
+
app36.route("/api/_client-error", client_error_default);
|
|
12579
12882
|
console.log("[client-error-route] mounted");
|
|
12580
|
-
|
|
12883
|
+
app36.use("*", async (c, next) => {
|
|
12581
12884
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
12582
12885
|
const path2 = c.req.path;
|
|
12583
12886
|
if (path2 === "/favicon.ico" || path2.startsWith("/assets/") || path2.startsWith("/brand/")) {
|
|
@@ -12610,15 +12913,15 @@ app35.use("*", async (c, next) => {
|
|
|
12610
12913
|
console.error(`[remote-auth] login required ip=${clientIp} path=${path2} ${disambig}`);
|
|
12611
12914
|
return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(), redirect: path2 }), 200);
|
|
12612
12915
|
});
|
|
12613
|
-
|
|
12614
|
-
|
|
12615
|
-
|
|
12616
|
-
|
|
12617
|
-
|
|
12618
|
-
|
|
12619
|
-
|
|
12620
|
-
|
|
12621
|
-
|
|
12916
|
+
app36.route("/api/health", health_default);
|
|
12917
|
+
app36.route("/api/session", session_default);
|
|
12918
|
+
app36.route("/api/chat", chat_default);
|
|
12919
|
+
app36.route("/api/group", group_default);
|
|
12920
|
+
app36.route("/api/access", access_default);
|
|
12921
|
+
app36.route("/api/telegram", telegram_default);
|
|
12922
|
+
app36.route("/api/whatsapp", whatsapp_default);
|
|
12923
|
+
app36.route("/api/onboarding", onboarding_default);
|
|
12924
|
+
app36.route("/api/admin", admin_default);
|
|
12622
12925
|
var SAFE_SLUG_RE = /^[a-z][a-z0-9-]{2,49}$/;
|
|
12623
12926
|
var SAFE_FILENAME_RE = /^[a-z0-9_][a-z0-9_.-]{0,99}$/i;
|
|
12624
12927
|
var IMAGE_MIME = {
|
|
@@ -12630,7 +12933,7 @@ var IMAGE_MIME = {
|
|
|
12630
12933
|
".svg": "image/svg+xml",
|
|
12631
12934
|
".ico": "image/x-icon"
|
|
12632
12935
|
};
|
|
12633
|
-
|
|
12936
|
+
app36.get("/agent-assets/:slug/:filename", (c) => {
|
|
12634
12937
|
const slug = c.req.param("slug");
|
|
12635
12938
|
const filename = c.req.param("filename");
|
|
12636
12939
|
if (!SAFE_SLUG_RE.test(slug)) {
|
|
@@ -12646,26 +12949,26 @@ app35.get("/agent-assets/:slug/:filename", (c) => {
|
|
|
12646
12949
|
console.error(`[agent-assets] no-account slug=${slug} file=${filename}`);
|
|
12647
12950
|
return c.text("Not found", 404);
|
|
12648
12951
|
}
|
|
12649
|
-
const filePath =
|
|
12650
|
-
const expectedDir =
|
|
12952
|
+
const filePath = resolve24(account.accountDir, "agents", slug, "assets", filename);
|
|
12953
|
+
const expectedDir = resolve24(account.accountDir, "agents", slug, "assets");
|
|
12651
12954
|
if (!filePath.startsWith(expectedDir + "/")) {
|
|
12652
12955
|
console.error(`[agent-assets] path-traversal-rejected slug=${slug} file=${filename}`);
|
|
12653
12956
|
return c.text("Forbidden", 403);
|
|
12654
12957
|
}
|
|
12655
|
-
if (!
|
|
12958
|
+
if (!existsSync24(filePath)) {
|
|
12656
12959
|
console.error(`[agent-assets] serve slug=${slug} file=${filename} status=404`);
|
|
12657
12960
|
return c.text("Not found", 404);
|
|
12658
12961
|
}
|
|
12659
12962
|
const ext = "." + filename.split(".").pop()?.toLowerCase();
|
|
12660
12963
|
const contentType = IMAGE_MIME[ext] || "application/octet-stream";
|
|
12661
12964
|
console.log(`[agent-assets] serve slug=${slug} file=${filename} status=200`);
|
|
12662
|
-
const body =
|
|
12965
|
+
const body = readFileSync17(filePath);
|
|
12663
12966
|
return c.body(body, 200, {
|
|
12664
12967
|
"Content-Type": contentType,
|
|
12665
12968
|
"Cache-Control": "public, max-age=3600"
|
|
12666
12969
|
});
|
|
12667
12970
|
});
|
|
12668
|
-
|
|
12971
|
+
app36.get("/generated/:filename", (c) => {
|
|
12669
12972
|
const filename = c.req.param("filename");
|
|
12670
12973
|
if (!SAFE_FILENAME_RE.test(filename) || filename.includes("..")) {
|
|
12671
12974
|
console.error(`[generated] serve file=${filename} status=403`);
|
|
@@ -12676,31 +12979,32 @@ app35.get("/generated/:filename", (c) => {
|
|
|
12676
12979
|
console.error(`[generated] serve file=${filename} status=404`);
|
|
12677
12980
|
return c.text("Not found", 404);
|
|
12678
12981
|
}
|
|
12679
|
-
const filePath =
|
|
12680
|
-
const expectedDir =
|
|
12982
|
+
const filePath = resolve24(account.accountDir, "generated", filename);
|
|
12983
|
+
const expectedDir = resolve24(account.accountDir, "generated");
|
|
12681
12984
|
if (!filePath.startsWith(expectedDir + "/")) {
|
|
12682
12985
|
console.error(`[generated] serve file=${filename} status=403`);
|
|
12683
12986
|
return c.text("Forbidden", 403);
|
|
12684
12987
|
}
|
|
12685
|
-
if (!
|
|
12988
|
+
if (!existsSync24(filePath)) {
|
|
12686
12989
|
console.error(`[generated] serve file=${filename} status=404`);
|
|
12687
12990
|
return c.text("Not found", 404);
|
|
12688
12991
|
}
|
|
12689
12992
|
const ext = "." + filename.split(".").pop()?.toLowerCase();
|
|
12690
12993
|
const contentType = IMAGE_MIME[ext] || "application/octet-stream";
|
|
12691
12994
|
console.log(`[generated] serve file=${filename} status=200`);
|
|
12692
|
-
const body =
|
|
12995
|
+
const body = readFileSync17(filePath);
|
|
12693
12996
|
return c.body(body, 200, {
|
|
12694
12997
|
"Content-Type": contentType,
|
|
12695
12998
|
"Cache-Control": "public, max-age=86400"
|
|
12696
12999
|
});
|
|
12697
13000
|
});
|
|
13001
|
+
app36.route("/sites", sites_default);
|
|
12698
13002
|
var htmlCache = /* @__PURE__ */ new Map();
|
|
12699
13003
|
var brandLogoPath = "/brand/maxy-monochrome.png";
|
|
12700
13004
|
var brandIconPath = "/brand/maxy-monochrome.png";
|
|
12701
|
-
if (BRAND_JSON_PATH &&
|
|
13005
|
+
if (BRAND_JSON_PATH && existsSync24(BRAND_JSON_PATH)) {
|
|
12702
13006
|
try {
|
|
12703
|
-
const fullBrand = JSON.parse(
|
|
13007
|
+
const fullBrand = JSON.parse(readFileSync17(BRAND_JSON_PATH, "utf-8"));
|
|
12704
13008
|
if (fullBrand.assets?.logo) brandLogoPath = `/brand/${fullBrand.assets.logo}`;
|
|
12705
13009
|
brandIconPath = fullBrand.assets?.icon ? `/brand/${fullBrand.assets.icon}` : brandLogoPath;
|
|
12706
13010
|
} catch {
|
|
@@ -12718,8 +13022,8 @@ function readInstalledVersion() {
|
|
|
12718
13022
|
try {
|
|
12719
13023
|
if (!PLATFORM_ROOT7) return "unknown";
|
|
12720
13024
|
const versionFile = join10(PLATFORM_ROOT7, "config", `.${BRAND.hostname}-version`);
|
|
12721
|
-
if (!
|
|
12722
|
-
const content =
|
|
13025
|
+
if (!existsSync24(versionFile)) return "unknown";
|
|
13026
|
+
const content = readFileSync17(versionFile, "utf-8").trim();
|
|
12723
13027
|
return content || "unknown";
|
|
12724
13028
|
} catch {
|
|
12725
13029
|
return "unknown";
|
|
@@ -12760,7 +13064,7 @@ var clientErrorReporterScript = `<script>
|
|
|
12760
13064
|
function cachedHtml(file) {
|
|
12761
13065
|
let html = htmlCache.get(file);
|
|
12762
13066
|
if (!html) {
|
|
12763
|
-
html =
|
|
13067
|
+
html = readFileSync17(resolve24(process.cwd(), "public", file), "utf-8");
|
|
12764
13068
|
const productNameEsc = escapeHtml(BRAND.productName);
|
|
12765
13069
|
html = html.replace(/<title>([^<]*)<\/title>/, (_match, inner) => `<title>${escapeHtml(inner).replace(/Maxy/g, productNameEsc)}</title>`);
|
|
12766
13070
|
html = html.replace('href="/favicon.ico"', `href="${escapeHtml(brandFaviconPath)}"`);
|
|
@@ -12779,13 +13083,13 @@ function loadBrandingCache(agentSlug) {
|
|
|
12779
13083
|
const configDir2 = join10(homedir2(), BRAND.configDir);
|
|
12780
13084
|
try {
|
|
12781
13085
|
const accountJsonPath = join10(configDir2, "account.json");
|
|
12782
|
-
if (!
|
|
12783
|
-
const account = JSON.parse(
|
|
13086
|
+
if (!existsSync24(accountJsonPath)) return null;
|
|
13087
|
+
const account = JSON.parse(readFileSync17(accountJsonPath, "utf-8"));
|
|
12784
13088
|
const accountId = account.accountId;
|
|
12785
13089
|
if (!accountId) return null;
|
|
12786
13090
|
const cachePath = join10(configDir2, "branding-cache", accountId, `${agentSlug}.json`);
|
|
12787
|
-
if (!
|
|
12788
|
-
return JSON.parse(
|
|
13091
|
+
if (!existsSync24(cachePath)) return null;
|
|
13092
|
+
return JSON.parse(readFileSync17(cachePath, "utf-8"));
|
|
12789
13093
|
} catch {
|
|
12790
13094
|
return null;
|
|
12791
13095
|
}
|
|
@@ -12794,8 +13098,8 @@ function resolveDefaultSlug() {
|
|
|
12794
13098
|
try {
|
|
12795
13099
|
const configDir2 = join10(homedir2(), BRAND.configDir);
|
|
12796
13100
|
const accountJsonPath = join10(configDir2, "account.json");
|
|
12797
|
-
if (!
|
|
12798
|
-
const account = JSON.parse(
|
|
13101
|
+
if (!existsSync24(accountJsonPath)) return null;
|
|
13102
|
+
const account = JSON.parse(readFileSync17(accountJsonPath, "utf-8"));
|
|
12799
13103
|
return account.defaultAgent || null;
|
|
12800
13104
|
} catch {
|
|
12801
13105
|
return null;
|
|
@@ -12831,7 +13135,7 @@ function brandedPublicHtml(agentSlug) {
|
|
|
12831
13135
|
function escapeHtml(s) {
|
|
12832
13136
|
return s.replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<").replace(/>/g, ">");
|
|
12833
13137
|
}
|
|
12834
|
-
|
|
13138
|
+
app36.get("/", (c) => {
|
|
12835
13139
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
12836
13140
|
if (isPublicHost(host)) {
|
|
12837
13141
|
const defaultSlug = resolveDefaultSlug();
|
|
@@ -12839,12 +13143,12 @@ app35.get("/", (c) => {
|
|
|
12839
13143
|
}
|
|
12840
13144
|
return c.html(cachedHtml("index.html"));
|
|
12841
13145
|
});
|
|
12842
|
-
|
|
13146
|
+
app36.get("/public", (c) => {
|
|
12843
13147
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
12844
13148
|
if (isPublicHost(host)) return c.text("Not found", 404);
|
|
12845
13149
|
return c.html(cachedHtml("public.html"));
|
|
12846
13150
|
});
|
|
12847
|
-
|
|
13151
|
+
app36.get("/chat", (c) => {
|
|
12848
13152
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
12849
13153
|
if (isPublicHost(host)) return c.text("Not found", 404);
|
|
12850
13154
|
return c.html(cachedHtml("public.html"));
|
|
@@ -12863,12 +13167,12 @@ async function logViewerFetch(c, next) {
|
|
|
12863
13167
|
duration_ms: Date.now() - start
|
|
12864
13168
|
});
|
|
12865
13169
|
}
|
|
12866
|
-
|
|
12867
|
-
|
|
12868
|
-
|
|
13170
|
+
app36.use("/vnc-viewer.html", logViewerFetch);
|
|
13171
|
+
app36.use("/vnc-popout.html", logViewerFetch);
|
|
13172
|
+
app36.get("/vnc-popout.html", (c) => {
|
|
12869
13173
|
let html = htmlCache.get("vnc-popout.html");
|
|
12870
13174
|
if (!html) {
|
|
12871
|
-
html =
|
|
13175
|
+
html = readFileSync17(resolve24(process.cwd(), "public", "vnc-popout.html"), "utf-8");
|
|
12872
13176
|
const name = escapeHtml(BRAND.productName);
|
|
12873
13177
|
html = html.replace("<title>Browser \u2014 Maxy</title>", `<title>${name}</title>`);
|
|
12874
13178
|
html = html.replace("</head>", ` ${brandScript}
|
|
@@ -12878,7 +13182,7 @@ app35.get("/vnc-popout.html", (c) => {
|
|
|
12878
13182
|
}
|
|
12879
13183
|
return c.html(html);
|
|
12880
13184
|
});
|
|
12881
|
-
|
|
13185
|
+
app36.post("/api/vnc/client-event", async (c) => {
|
|
12882
13186
|
let body;
|
|
12883
13187
|
try {
|
|
12884
13188
|
body = await c.req.json();
|
|
@@ -12899,20 +13203,20 @@ app35.post("/api/vnc/client-event", async (c) => {
|
|
|
12899
13203
|
});
|
|
12900
13204
|
return c.json({ ok: true });
|
|
12901
13205
|
});
|
|
12902
|
-
|
|
13206
|
+
app36.get("/g/:slug", (c) => {
|
|
12903
13207
|
return c.html(brandedPublicHtml());
|
|
12904
13208
|
});
|
|
12905
|
-
|
|
13209
|
+
app36.get("/graph", (c) => {
|
|
12906
13210
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
12907
13211
|
if (isPublicHost(host)) return c.text("Not found", 404);
|
|
12908
13212
|
return c.html(cachedHtml("graph.html"));
|
|
12909
13213
|
});
|
|
12910
|
-
|
|
13214
|
+
app36.get("/data", (c) => {
|
|
12911
13215
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
12912
13216
|
if (isPublicHost(host)) return c.text("Not found", 404);
|
|
12913
13217
|
return c.html(cachedHtml("data.html"));
|
|
12914
13218
|
});
|
|
12915
|
-
|
|
13219
|
+
app36.get("/:slug", async (c, next) => {
|
|
12916
13220
|
const slug = c.req.param("slug");
|
|
12917
13221
|
if (AGENT_SLUG_PATTERN.test(`/${slug}`)) {
|
|
12918
13222
|
const branding = loadBrandingCache(slug);
|
|
@@ -12921,10 +13225,10 @@ app35.get("/:slug", async (c, next) => {
|
|
|
12921
13225
|
}
|
|
12922
13226
|
await next();
|
|
12923
13227
|
});
|
|
12924
|
-
|
|
13228
|
+
app36.use("/*", serveStatic({ root: "./public" }));
|
|
12925
13229
|
var port = parseInt(process.env.MAXY_UI_INTERNAL_PORT ?? process.env.PORT ?? "19199", 10);
|
|
12926
13230
|
var hostname = process.env.HOSTNAME ?? "127.0.0.1";
|
|
12927
|
-
var httpServer = serve({ fetch:
|
|
13231
|
+
var httpServer = serve({ fetch: app36.fetch, port, hostname });
|
|
12928
13232
|
console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
12929
13233
|
var SUBAPP_MANIFEST = [
|
|
12930
13234
|
{ prefix: "/api/health", file: "server/routes/health.ts", subapp: health_default },
|
|
@@ -12944,7 +13248,7 @@ for (const m of SUBAPP_MANIFEST) {
|
|
|
12944
13248
|
}
|
|
12945
13249
|
try {
|
|
12946
13250
|
const registered = [];
|
|
12947
|
-
for (const r of
|
|
13251
|
+
for (const r of app36.routes ?? []) {
|
|
12948
13252
|
if (typeof r.path !== "string" || r.path.includes(":") || r.path.includes("*")) continue;
|
|
12949
13253
|
if (AGENT_SLUG_PATTERN.test(r.path)) {
|
|
12950
13254
|
registered.push({ method: (r.method ?? "ALL").toUpperCase(), path: r.path });
|
|
@@ -12958,8 +13262,8 @@ try {
|
|
|
12958
13262
|
(async () => {
|
|
12959
13263
|
try {
|
|
12960
13264
|
let userId = "";
|
|
12961
|
-
if (
|
|
12962
|
-
const users = JSON.parse(
|
|
13265
|
+
if (existsSync24(USERS_FILE)) {
|
|
13266
|
+
const users = JSON.parse(readFileSync17(USERS_FILE, "utf-8").trim() || "[]");
|
|
12963
13267
|
userId = users[0]?.userId ?? "";
|
|
12964
13268
|
}
|
|
12965
13269
|
await backfillNullUserIdConversations(userId);
|
|
@@ -12985,7 +13289,7 @@ startGraphHealthTimer();
|
|
|
12985
13289
|
var configDirForWhatsApp = basename7(MAXY_DIR) || ".maxy";
|
|
12986
13290
|
var bootAccount = resolveAccount();
|
|
12987
13291
|
var bootAccountConfig = bootAccount?.config;
|
|
12988
|
-
var bootPublicAgent = bootAccount ?
|
|
13292
|
+
var bootPublicAgent = bootAccount ? resolvePublicAgent(bootAccount.accountDir, { accountId: bootAccount.accountId })?.slug ?? null : null;
|
|
12989
13293
|
var bootEntitlement = bootAccountConfig ? resolveEntitlement(
|
|
12990
13294
|
{ configDir: MAXY_DIR, platformRoot: PLATFORM_ROOT, commercialMode: COMMERCIAL_MODE },
|
|
12991
13295
|
{
|
|
@@ -13026,7 +13330,7 @@ if (bootAccountConfig?.whatsapp) {
|
|
|
13026
13330
|
}
|
|
13027
13331
|
init({
|
|
13028
13332
|
configDir: configDirForWhatsApp,
|
|
13029
|
-
platformRoot:
|
|
13333
|
+
platformRoot: resolve24(process.env.MAXY_PLATFORM_ROOT ?? join10(__dirname, "..")),
|
|
13030
13334
|
accountConfig: bootAccountConfig,
|
|
13031
13335
|
onMessage: async (msg) => {
|
|
13032
13336
|
try {
|
|
@@ -13050,12 +13354,20 @@ init({
|
|
|
13050
13354
|
if (msg.agentType === "admin") {
|
|
13051
13355
|
agentName = "admin";
|
|
13052
13356
|
} else {
|
|
13053
|
-
const
|
|
13054
|
-
|
|
13055
|
-
|
|
13357
|
+
const resolved = bootAccount ? resolvePublicAgent(bootAccount.accountDir, { accountId: msg.accountId, groupJid: msg.groupJid }) : null;
|
|
13358
|
+
console.error(`[whatsapp:route] resolved publicAgent account=${msg.accountId} groupJid=${msg.groupJid ?? "none"} source=${resolved?.source ?? "null"} slug=${resolved?.slug ?? "none"}`);
|
|
13359
|
+
if (!resolved) {
|
|
13360
|
+
console.error(`[whatsapp:route] rejected: no publicAgent configured account=${msg.accountId} groupJid=${msg.groupJid ?? "none"} from=${msg.senderPhone}`);
|
|
13056
13361
|
return;
|
|
13057
13362
|
}
|
|
13058
|
-
|
|
13363
|
+
if (bootAccount) {
|
|
13364
|
+
const agentConfigPath = resolveBoot(bootAccount.accountDir, "agents", resolved.slug, "config.json");
|
|
13365
|
+
if (!existsSyncBoot(agentConfigPath)) {
|
|
13366
|
+
console.error(`[whatsapp:route] rejected: slug-missing-agent-dir account=${msg.accountId} groupJid=${msg.groupJid ?? "none"} slug=${resolved.slug} source=${resolved.source} expected=${agentConfigPath}`);
|
|
13367
|
+
return;
|
|
13368
|
+
}
|
|
13369
|
+
}
|
|
13370
|
+
agentName = resolved.slug;
|
|
13059
13371
|
}
|
|
13060
13372
|
let enrichedText = msg.text || msg.mediaPath || msg.mediaType || msg.replyContext ? buildEnrichedText2(msg.text ?? "") : "";
|
|
13061
13373
|
if (msg.sessionKey) {
|