@rubytech/create-realagent 1.0.853 → 1.0.854
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/lib/persistent-components/dist/index.d.ts +21 -0
- package/payload/platform/lib/persistent-components/dist/index.d.ts.map +1 -0
- package/payload/platform/lib/persistent-components/dist/index.js +32 -0
- package/payload/platform/lib/persistent-components/dist/index.js.map +1 -0
- package/payload/platform/lib/persistent-components/src/index.ts +28 -0
- package/payload/platform/lib/persistent-components/tsconfig.json +8 -0
- package/payload/platform/package.json +2 -2
- package/payload/platform/plugins/admin/PLUGIN.md +1 -1
- package/payload/platform/plugins/admin/hooks/__tests__/playwright-file-guard.test.sh +278 -0
- package/payload/platform/plugins/admin/hooks/playwright-file-guard.sh +204 -20
- package/payload/platform/plugins/admin/mcp/dist/index.js +40 -1
- package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/docs/references/deployment.md +2 -0
- package/payload/platform/plugins/docs/references/getting-started.md +2 -0
- package/payload/platform/plugins/docs/references/platform.md +1 -1
- package/payload/platform/plugins/docs/references/troubleshooting.md +10 -0
- package/payload/platform/scripts/admin-persist-audit.ts +191 -0
- package/payload/platform/scripts/component-knowledgedoc-backfill.ts +214 -0
- package/payload/platform/scripts/installer-device-verify.sh +17 -4
- package/payload/platform/templates/specialists/agents/content-producer.md +2 -2
- package/payload/server/chunk-CFNSKDGA.js +667 -0
- package/payload/server/chunk-DC6DWYZJ.js +1603 -0
- package/payload/server/chunk-LTB5SSQW.js +10889 -0
- package/payload/server/chunk-MN2LGNUB.js +2143 -0
- package/payload/server/client-pool-AMT2W3II.js +34 -0
- package/payload/server/cloudflare-task-tracker-LJ4SMK2D.js +20 -0
- package/payload/server/maxy-edge.js +3 -3
- package/payload/server/public/assets/admin-DZ8Ke7t3.js +352 -0
- package/payload/server/public/assets/public-DApUXgoq.js +5 -0
- package/payload/server/public/assets/useVoiceRecorder-CI8GpxfU.js +36 -0
- package/payload/server/public/index.html +2 -2
- package/payload/server/public/public.html +2 -2
- package/payload/server/server.js +535 -351
- package/payload/server/public/assets/admin-Dyl8uNxX.js +0 -352
- package/payload/server/public/assets/public-B_PNZUph.js +0 -5
- package/payload/server/public/assets/useVoiceRecorder-fD0IWzJj.js +0 -36
package/payload/server/server.js
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
|
+
ATTACHMENTS_ROOT,
|
|
2
3
|
Hono,
|
|
4
|
+
MAX_FILES_PER_MESSAGE,
|
|
5
|
+
MAX_FILE_SIZE_BYTES,
|
|
6
|
+
SUPPORTED_MIME_TYPES,
|
|
7
|
+
assertSupportedMime,
|
|
3
8
|
autoDeliverPremiumPlugins,
|
|
4
9
|
buildX11Env,
|
|
5
10
|
callOauthLlm,
|
|
@@ -10,6 +15,10 @@ import {
|
|
|
10
15
|
compactSession,
|
|
11
16
|
computeConstraints,
|
|
12
17
|
createRemoteSession,
|
|
18
|
+
deriveComponentAttachmentId,
|
|
19
|
+
deriveComponentMimeType,
|
|
20
|
+
deriveComponentTitle,
|
|
21
|
+
detectMimeType,
|
|
13
22
|
ensureAuth,
|
|
14
23
|
ensureCdp,
|
|
15
24
|
ensureLogDir,
|
|
@@ -19,11 +28,13 @@ import {
|
|
|
19
28
|
invokeAgent,
|
|
20
29
|
isActionActive,
|
|
21
30
|
isPasswordValid,
|
|
31
|
+
isPersistentComponent,
|
|
22
32
|
isRemoteAuthConfigured,
|
|
23
33
|
keyFilePath,
|
|
24
34
|
launchAction,
|
|
25
35
|
load,
|
|
26
36
|
logPath,
|
|
37
|
+
pickComponentBytes,
|
|
27
38
|
recordFailedAttempt,
|
|
28
39
|
render,
|
|
29
40
|
renderLoginPage,
|
|
@@ -36,9 +47,14 @@ import {
|
|
|
36
47
|
serve,
|
|
37
48
|
setRemotePassword,
|
|
38
49
|
sleep,
|
|
50
|
+
storeAttachment,
|
|
51
|
+
storeComponentArtefact,
|
|
52
|
+
storeGeneratedFile,
|
|
53
|
+
storePublicAttachment,
|
|
39
54
|
streamLogPathFor,
|
|
40
55
|
stripAttachmentMetaSuffix,
|
|
41
56
|
tryCookieBridgeForConversation,
|
|
57
|
+
validateFilePathInAccount,
|
|
42
58
|
validateKey,
|
|
43
59
|
validatePasswordStrength,
|
|
44
60
|
verifyPassword,
|
|
@@ -46,7 +62,7 @@ import {
|
|
|
46
62
|
vncLog,
|
|
47
63
|
waitForExit,
|
|
48
64
|
writeChromiumWrapper
|
|
49
|
-
} from "./chunk-
|
|
65
|
+
} from "./chunk-LTB5SSQW.js";
|
|
50
66
|
import {
|
|
51
67
|
ACCOUNTS_DIR,
|
|
52
68
|
CDP_PORT,
|
|
@@ -91,7 +107,7 @@ import {
|
|
|
91
107
|
unregisterSession,
|
|
92
108
|
validateAgentSlug,
|
|
93
109
|
validateSession
|
|
94
|
-
} from "./chunk-
|
|
110
|
+
} from "./chunk-DC6DWYZJ.js";
|
|
95
111
|
import {
|
|
96
112
|
CLOUDFLARE_TASK_DIAGNOSTICS,
|
|
97
113
|
appendCloudflareSteps,
|
|
@@ -99,7 +115,7 @@ import {
|
|
|
99
115
|
openCloudflareTask,
|
|
100
116
|
readTunnelState,
|
|
101
117
|
resolveUnitGoneVerdict
|
|
102
|
-
} from "./chunk-
|
|
118
|
+
} from "./chunk-CFNSKDGA.js";
|
|
103
119
|
import {
|
|
104
120
|
GREETING_DIRECTIVE,
|
|
105
121
|
HAIKU_MODEL,
|
|
@@ -123,13 +139,14 @@ import {
|
|
|
123
139
|
listAdminSessions,
|
|
124
140
|
loadAdminUserName,
|
|
125
141
|
loadOnboardingStep,
|
|
142
|
+
persistMessage,
|
|
126
143
|
projectAgent,
|
|
127
144
|
renameConversation,
|
|
128
145
|
runAdminUserSelfHeal,
|
|
129
146
|
verifyAndGetConversationUpdatedAt,
|
|
130
147
|
verifyConversationOwnership,
|
|
131
148
|
writeAdminUserAndPerson
|
|
132
|
-
} from "./chunk-
|
|
149
|
+
} from "./chunk-MN2LGNUB.js";
|
|
133
150
|
import {
|
|
134
151
|
__commonJS,
|
|
135
152
|
__toESM
|
|
@@ -632,9 +649,9 @@ var serveStatic = (options = { root: "" }) => {
|
|
|
632
649
|
};
|
|
633
650
|
|
|
634
651
|
// server/index.ts
|
|
635
|
-
import { readFileSync as
|
|
636
|
-
import { resolve as
|
|
637
|
-
import { homedir as
|
|
652
|
+
import { readFileSync as readFileSync18, existsSync as existsSync24, watchFile } from "fs";
|
|
653
|
+
import { resolve as resolve21, join as join12, basename as basename4 } from "path";
|
|
654
|
+
import { homedir as homedir3 } from "os";
|
|
638
655
|
|
|
639
656
|
// app/lib/agent-slug-pattern.ts
|
|
640
657
|
var AGENT_SLUG_PATTERN = /^\/([a-z][a-z0-9-]{2,49})$/;
|
|
@@ -1277,7 +1294,7 @@ var credsSaveQueue = Promise.resolve();
|
|
|
1277
1294
|
async function drainCredsSaveQueue(timeoutMs = 5e3) {
|
|
1278
1295
|
console.error(`${TAG3} draining credential save queue\u2026`);
|
|
1279
1296
|
const timer2 = new Promise(
|
|
1280
|
-
(
|
|
1297
|
+
(resolve22) => setTimeout(() => resolve22("timeout"), timeoutMs)
|
|
1281
1298
|
);
|
|
1282
1299
|
const result = await Promise.race([
|
|
1283
1300
|
credsSaveQueue.then(() => "drained"),
|
|
@@ -1405,11 +1422,11 @@ async function createWaSocket(opts) {
|
|
|
1405
1422
|
return sock;
|
|
1406
1423
|
}
|
|
1407
1424
|
async function waitForConnection(sock) {
|
|
1408
|
-
return new Promise((
|
|
1425
|
+
return new Promise((resolve22, reject) => {
|
|
1409
1426
|
const handler = (update) => {
|
|
1410
1427
|
if (update.connection === "open") {
|
|
1411
1428
|
sock.ev.off("connection.update", handler);
|
|
1412
|
-
|
|
1429
|
+
resolve22();
|
|
1413
1430
|
}
|
|
1414
1431
|
if (update.connection === "close") {
|
|
1415
1432
|
sock.ev.off("connection.update", handler);
|
|
@@ -1523,14 +1540,14 @@ ${inspected}`;
|
|
|
1523
1540
|
return inspect2(err, INSPECT_OPTS2);
|
|
1524
1541
|
}
|
|
1525
1542
|
function withTimeout(label, promise, timeoutMs) {
|
|
1526
|
-
return new Promise((
|
|
1543
|
+
return new Promise((resolve22, reject) => {
|
|
1527
1544
|
const timer2 = setTimeout(() => {
|
|
1528
1545
|
reject(new Error(`${label} timed out after ${timeoutMs}ms`));
|
|
1529
1546
|
}, timeoutMs);
|
|
1530
1547
|
promise.then(
|
|
1531
1548
|
(value) => {
|
|
1532
1549
|
clearTimeout(timer2);
|
|
1533
|
-
|
|
1550
|
+
resolve22(value);
|
|
1534
1551
|
},
|
|
1535
1552
|
(err) => {
|
|
1536
1553
|
clearTimeout(timer2);
|
|
@@ -2065,8 +2082,8 @@ async function persistWhatsAppMessage(input) {
|
|
|
2065
2082
|
const { givenName, familyName } = splitName(input.pushName);
|
|
2066
2083
|
const prev = sessionWriteLocks.get(input.sessionKey);
|
|
2067
2084
|
let release;
|
|
2068
|
-
const mine = new Promise((
|
|
2069
|
-
release =
|
|
2085
|
+
const mine = new Promise((resolve22) => {
|
|
2086
|
+
release = resolve22;
|
|
2070
2087
|
});
|
|
2071
2088
|
const chained = (prev ?? Promise.resolve()).then(() => mine);
|
|
2072
2089
|
sessionWriteLocks.set(input.sessionKey, chained);
|
|
@@ -3091,11 +3108,11 @@ async function connectWithReconnect(conn) {
|
|
|
3091
3108
|
console.error(
|
|
3092
3109
|
`${TAG13} reconnecting account=${conn.accountId} in ${delay}ms (attempt ${decision.nextAttempts}/${maxAttempts})`
|
|
3093
3110
|
);
|
|
3094
|
-
await new Promise((
|
|
3095
|
-
const timer2 = setTimeout(
|
|
3111
|
+
await new Promise((resolve22) => {
|
|
3112
|
+
const timer2 = setTimeout(resolve22, delay);
|
|
3096
3113
|
conn.abortController.signal.addEventListener("abort", () => {
|
|
3097
3114
|
clearTimeout(timer2);
|
|
3098
|
-
|
|
3115
|
+
resolve22();
|
|
3099
3116
|
}, { once: true });
|
|
3100
3117
|
});
|
|
3101
3118
|
}
|
|
@@ -3103,16 +3120,16 @@ async function connectWithReconnect(conn) {
|
|
|
3103
3120
|
}
|
|
3104
3121
|
}
|
|
3105
3122
|
function waitForDisconnectEvent(conn) {
|
|
3106
|
-
return new Promise((
|
|
3123
|
+
return new Promise((resolve22) => {
|
|
3107
3124
|
if (!conn.sock) {
|
|
3108
|
-
|
|
3125
|
+
resolve22();
|
|
3109
3126
|
return;
|
|
3110
3127
|
}
|
|
3111
3128
|
const sock = conn.sock;
|
|
3112
3129
|
const handler = (update) => {
|
|
3113
3130
|
if (update.connection === "close") {
|
|
3114
3131
|
sock.ev.off("connection.update", handler);
|
|
3115
|
-
|
|
3132
|
+
resolve22();
|
|
3116
3133
|
}
|
|
3117
3134
|
};
|
|
3118
3135
|
sock.ev.on("connection.update", handler);
|
|
@@ -3373,8 +3390,8 @@ async function handleInboundMessage(conn, msg) {
|
|
|
3373
3390
|
const conversationKey = isGroup ? remoteJid : senderPhone;
|
|
3374
3391
|
const debounceKey = `${conn.accountId}:${conversationKey}:${senderPhone}`;
|
|
3375
3392
|
let resolvePending;
|
|
3376
|
-
const sttPending = new Promise((
|
|
3377
|
-
resolvePending =
|
|
3393
|
+
const sttPending = new Promise((resolve22) => {
|
|
3394
|
+
resolvePending = resolve22;
|
|
3378
3395
|
});
|
|
3379
3396
|
if (conn.debouncer) conn.debouncer.registerPending(debounceKey, sttPending);
|
|
3380
3397
|
try {
|
|
@@ -3495,20 +3512,20 @@ async function probeApiKey() {
|
|
|
3495
3512
|
return result.status;
|
|
3496
3513
|
}
|
|
3497
3514
|
function checkPort(port2, timeoutMs = 500) {
|
|
3498
|
-
return new Promise((
|
|
3515
|
+
return new Promise((resolve22) => {
|
|
3499
3516
|
const socket = createConnection(port2, "127.0.0.1");
|
|
3500
3517
|
socket.setTimeout(timeoutMs);
|
|
3501
3518
|
socket.once("connect", () => {
|
|
3502
3519
|
socket.destroy();
|
|
3503
|
-
|
|
3520
|
+
resolve22(true);
|
|
3504
3521
|
});
|
|
3505
3522
|
socket.once("error", () => {
|
|
3506
3523
|
socket.destroy();
|
|
3507
|
-
|
|
3524
|
+
resolve22(false);
|
|
3508
3525
|
});
|
|
3509
3526
|
socket.once("timeout", () => {
|
|
3510
3527
|
socket.destroy();
|
|
3511
|
-
|
|
3528
|
+
resolve22(false);
|
|
3512
3529
|
});
|
|
3513
3530
|
});
|
|
3514
3531
|
}
|
|
@@ -3921,142 +3938,31 @@ ${raw}`;
|
|
|
3921
3938
|
return base;
|
|
3922
3939
|
}
|
|
3923
3940
|
|
|
3924
|
-
// app/lib/attachments.ts
|
|
3925
|
-
import { randomUUID as randomUUID3 } from "crypto";
|
|
3926
|
-
import { mkdir as mkdir2, readFile, stat as stat2, writeFile as writeFile2 } from "fs/promises";
|
|
3927
|
-
import { realpathSync } from "fs";
|
|
3928
|
-
import { resolve as resolve4, extname, basename } from "path";
|
|
3929
|
-
var PLATFORM_ROOT2 = process.env.MAXY_PLATFORM_ROOT ?? resolve4(process.cwd(), "../platform");
|
|
3930
|
-
var ATTACHMENTS_ROOT = resolve4(PLATFORM_ROOT2, "..", "data/uploads");
|
|
3931
|
-
var SUPPORTED_MIME_TYPES = /* @__PURE__ */ new Set([
|
|
3932
|
-
"image/jpeg",
|
|
3933
|
-
"image/png",
|
|
3934
|
-
"image/gif",
|
|
3935
|
-
"image/webp",
|
|
3936
|
-
"application/pdf",
|
|
3937
|
-
"text/plain",
|
|
3938
|
-
"text/markdown",
|
|
3939
|
-
"text/csv",
|
|
3940
|
-
"text/html",
|
|
3941
|
-
"text/calendar",
|
|
3942
|
-
"application/zip",
|
|
3943
|
-
"application/x-zip-compressed"
|
|
3944
|
-
]);
|
|
3945
|
-
var MAX_FILE_SIZE_BYTES = 50 * 1024 * 1024;
|
|
3946
|
-
var MAX_FILES_PER_MESSAGE = 5;
|
|
3947
|
-
var MAX_ZIP_UNCOMPRESSED_BYTES = 100 * 1024 * 1024;
|
|
3948
|
-
function assertSupportedMime(mimeType) {
|
|
3949
|
-
if (!SUPPORTED_MIME_TYPES.has(mimeType)) {
|
|
3950
|
-
throw new Error(
|
|
3951
|
-
`Unsupported file type: "${mimeType}". Supported types: ${[...SUPPORTED_MIME_TYPES].join(", ")}.`
|
|
3952
|
-
);
|
|
3953
|
-
}
|
|
3954
|
-
}
|
|
3955
|
-
async function writeAttachment(scope, filename, mimeType, sizeBytes, buffer) {
|
|
3956
|
-
const attachmentId = randomUUID3();
|
|
3957
|
-
const dir = resolve4(ATTACHMENTS_ROOT, scope, attachmentId);
|
|
3958
|
-
await mkdir2(dir, { recursive: true });
|
|
3959
|
-
const ext = extname(filename) || "";
|
|
3960
|
-
const storagePath = resolve4(dir, `${attachmentId}${ext}`);
|
|
3961
|
-
const metaPath = resolve4(dir, `${attachmentId}.meta.json`);
|
|
3962
|
-
const meta = {
|
|
3963
|
-
attachmentId,
|
|
3964
|
-
scope,
|
|
3965
|
-
filename,
|
|
3966
|
-
mimeType,
|
|
3967
|
-
sizeBytes,
|
|
3968
|
-
storedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3969
|
-
};
|
|
3970
|
-
await writeFile2(storagePath, buffer);
|
|
3971
|
-
await writeFile2(metaPath, JSON.stringify(meta, null, 2));
|
|
3972
|
-
return { attachmentId, filename, storagePath, metaPath, mimeType, sizeBytes };
|
|
3973
|
-
}
|
|
3974
|
-
async function storeAttachment(accountId, file) {
|
|
3975
|
-
assertSupportedMime(file.type);
|
|
3976
|
-
if (file.size > MAX_FILE_SIZE_BYTES) {
|
|
3977
|
-
throw new Error(
|
|
3978
|
-
`File "${file.name}" exceeds the 50 MB limit (${(file.size / 1024 / 1024).toFixed(1)} MB).`
|
|
3979
|
-
);
|
|
3980
|
-
}
|
|
3981
|
-
const buffer = Buffer.from(await file.arrayBuffer());
|
|
3982
|
-
return writeAttachment(accountId, file.name, file.type, file.size, buffer);
|
|
3983
|
-
}
|
|
3984
|
-
async function storePublicAttachment(sessionId, file, buffer) {
|
|
3985
|
-
assertSupportedMime(file.type);
|
|
3986
|
-
if (file.size > MAX_FILE_SIZE_BYTES) {
|
|
3987
|
-
throw new Error(
|
|
3988
|
-
`File "${file.name}" exceeds the 50 MB limit (${(file.size / 1024 / 1024).toFixed(1)} MB).`
|
|
3989
|
-
);
|
|
3990
|
-
}
|
|
3991
|
-
return writeAttachment(`public/${sessionId}`, file.name, file.type, file.size, buffer);
|
|
3992
|
-
}
|
|
3993
|
-
var MIME_BY_EXT = {
|
|
3994
|
-
".pdf": "application/pdf",
|
|
3995
|
-
".png": "image/png",
|
|
3996
|
-
".jpg": "image/jpeg",
|
|
3997
|
-
".jpeg": "image/jpeg",
|
|
3998
|
-
".gif": "image/gif",
|
|
3999
|
-
".webp": "image/webp",
|
|
4000
|
-
".svg": "image/svg+xml",
|
|
4001
|
-
".html": "text/html",
|
|
4002
|
-
".htm": "text/html",
|
|
4003
|
-
".css": "text/css",
|
|
4004
|
-
".js": "application/javascript",
|
|
4005
|
-
".json": "application/json",
|
|
4006
|
-
".csv": "text/csv",
|
|
4007
|
-
".txt": "text/plain",
|
|
4008
|
-
".md": "text/markdown",
|
|
4009
|
-
".xml": "text/xml",
|
|
4010
|
-
".zip": "application/zip",
|
|
4011
|
-
".xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
4012
|
-
".docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
4013
|
-
".pptx": "application/vnd.openxmlformats-officedocument.presentationml.presentation",
|
|
4014
|
-
".ics": "text/calendar"
|
|
4015
|
-
};
|
|
4016
|
-
function detectMimeType(filePath) {
|
|
4017
|
-
const ext = extname(filePath).toLowerCase();
|
|
4018
|
-
return MIME_BY_EXT[ext] ?? "application/octet-stream";
|
|
4019
|
-
}
|
|
4020
|
-
function validateFilePathInAccount(filePath, accountDir) {
|
|
4021
|
-
const resolved = realpathSync(filePath);
|
|
4022
|
-
const accountResolved = realpathSync(accountDir);
|
|
4023
|
-
if (!resolved.startsWith(accountResolved + "/")) {
|
|
4024
|
-
throw new Error(`File path is outside the account directory`);
|
|
4025
|
-
}
|
|
4026
|
-
return resolved;
|
|
4027
|
-
}
|
|
4028
|
-
async function storeGeneratedFile(accountId, filePath) {
|
|
4029
|
-
const fileStat = await stat2(filePath);
|
|
4030
|
-
if (fileStat.size > MAX_FILE_SIZE_BYTES) {
|
|
4031
|
-
throw new Error(
|
|
4032
|
-
`File exceeds the 50 MB limit (${(fileStat.size / 1024 / 1024).toFixed(1)} MB).`
|
|
4033
|
-
);
|
|
4034
|
-
}
|
|
4035
|
-
const filename = basename(filePath);
|
|
4036
|
-
const mimeType = detectMimeType(filePath);
|
|
4037
|
-
const buffer = await readFile(filePath);
|
|
4038
|
-
return writeAttachment(accountId, filename, mimeType, fileStat.size, buffer);
|
|
4039
|
-
}
|
|
4040
|
-
|
|
4041
3941
|
// app/lib/stt/voice-note.ts
|
|
4042
|
-
import { writeFile as
|
|
3942
|
+
import { writeFile as writeFile2, mkdtemp, rm } from "fs/promises";
|
|
4043
3943
|
import { tmpdir } from "os";
|
|
4044
3944
|
import { join as join4 } from "path";
|
|
4045
3945
|
var TAG14 = "[voice]";
|
|
4046
3946
|
var AUDIO_MIME_TYPES = /* @__PURE__ */ new Set([
|
|
4047
3947
|
"audio/ogg",
|
|
3948
|
+
"audio/opus",
|
|
4048
3949
|
"audio/webm",
|
|
4049
3950
|
"audio/mp4",
|
|
4050
3951
|
"audio/mpeg",
|
|
4051
3952
|
"audio/wav"
|
|
4052
3953
|
]);
|
|
3954
|
+
function normalizeMime(mimeType) {
|
|
3955
|
+
return mimeType.split(";")[0].trim().toLowerCase();
|
|
3956
|
+
}
|
|
4053
3957
|
function isAudioMime(mimeType) {
|
|
4054
|
-
return AUDIO_MIME_TYPES.has(mimeType);
|
|
3958
|
+
return AUDIO_MIME_TYPES.has(normalizeMime(mimeType));
|
|
4055
3959
|
}
|
|
4056
3960
|
function audioExtension(mimeType) {
|
|
4057
|
-
switch (mimeType) {
|
|
3961
|
+
switch (normalizeMime(mimeType)) {
|
|
4058
3962
|
case "audio/ogg":
|
|
4059
3963
|
return ".ogg";
|
|
3964
|
+
case "audio/opus":
|
|
3965
|
+
return ".opus";
|
|
4060
3966
|
case "audio/webm":
|
|
4061
3967
|
return ".webm";
|
|
4062
3968
|
case "audio/mp4":
|
|
@@ -4083,7 +3989,7 @@ async function transcribeVoiceNote(file, source) {
|
|
|
4083
3989
|
const ext = audioExtension(mimeType);
|
|
4084
3990
|
tempPath = join4(tempDir, `recording${ext}`);
|
|
4085
3991
|
const buffer = Buffer.from(await file.arrayBuffer());
|
|
4086
|
-
await
|
|
3992
|
+
await writeFile2(tempPath, buffer);
|
|
4087
3993
|
} catch (err) {
|
|
4088
3994
|
const reason = err instanceof Error ? err.message : String(err);
|
|
4089
3995
|
console.error(`${TAG14} failed source=${source} error=temp-file-write: ${reason}`);
|
|
@@ -4637,13 +4543,13 @@ var group_default = app4;
|
|
|
4637
4543
|
// app/lib/access-gate.ts
|
|
4638
4544
|
import neo4j from "neo4j-driver";
|
|
4639
4545
|
import { readFileSync as readFileSync4 } from "fs";
|
|
4640
|
-
import { resolve as
|
|
4641
|
-
import { randomUUID as
|
|
4642
|
-
var
|
|
4546
|
+
import { resolve as resolve4 } from "path";
|
|
4547
|
+
import { randomUUID as randomUUID3, randomInt } from "crypto";
|
|
4548
|
+
var PLATFORM_ROOT2 = process.env.MAXY_PLATFORM_ROOT ?? resolve4(process.cwd(), "..");
|
|
4643
4549
|
var driver = null;
|
|
4644
4550
|
function readPassword() {
|
|
4645
4551
|
if (process.env.NEO4J_PASSWORD) return process.env.NEO4J_PASSWORD;
|
|
4646
|
-
const passwordFile =
|
|
4552
|
+
const passwordFile = resolve4(PLATFORM_ROOT2, "config/.neo4j-password");
|
|
4647
4553
|
try {
|
|
4648
4554
|
return readFileSync4(passwordFile, "utf-8").trim();
|
|
4649
4555
|
} catch {
|
|
@@ -4894,7 +4800,7 @@ async function setGrantPassword(grantId, passwordHash) {
|
|
|
4894
4800
|
}
|
|
4895
4801
|
}
|
|
4896
4802
|
async function generateNewMagicToken(grantId) {
|
|
4897
|
-
const token =
|
|
4803
|
+
const token = randomUUID3();
|
|
4898
4804
|
const session = getSession2();
|
|
4899
4805
|
try {
|
|
4900
4806
|
await session.run(
|
|
@@ -4958,15 +4864,15 @@ async function findActiveGrantByContact(contactValue, agentSlug, accountId) {
|
|
|
4958
4864
|
// app/lib/brevo-sms.ts
|
|
4959
4865
|
import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, mkdirSync as mkdirSync2, existsSync as existsSync5, chmodSync } from "fs";
|
|
4960
4866
|
import { dirname } from "path";
|
|
4961
|
-
import { resolve as
|
|
4962
|
-
var BREVO_API_KEY_FILE =
|
|
4867
|
+
import { resolve as resolve5 } from "path";
|
|
4868
|
+
var BREVO_API_KEY_FILE = resolve5(MAXY_DIR, ".brevo-api-key");
|
|
4963
4869
|
var BREVO_API_URL = "https://api.brevo.com/v3/transactionalSMS/sms";
|
|
4964
4870
|
var BREVO_TIMEOUT_MS = 1e4;
|
|
4965
4871
|
var BREVO_SENDER = "Maxy";
|
|
4966
4872
|
var platformRoot = process.env.MAXY_PLATFORM_ROOT;
|
|
4967
4873
|
if (platformRoot) {
|
|
4968
4874
|
try {
|
|
4969
|
-
const brandPath =
|
|
4875
|
+
const brandPath = resolve5(platformRoot, "config", "brand.json");
|
|
4970
4876
|
if (existsSync5(brandPath)) {
|
|
4971
4877
|
const brand = JSON.parse(readFileSync5(brandPath, "utf-8"));
|
|
4972
4878
|
if (brand.productName) BREVO_SENDER = brand.productName;
|
|
@@ -5603,12 +5509,12 @@ app6.post("/webhook", async (c) => {
|
|
|
5603
5509
|
var telegram_default = app6;
|
|
5604
5510
|
|
|
5605
5511
|
// server/routes/whatsapp.ts
|
|
5606
|
-
import { join as join5, resolve as
|
|
5607
|
-
import { readFile
|
|
5608
|
-
import { realpathSync
|
|
5512
|
+
import { join as join5, resolve as resolve6, basename } from "path";
|
|
5513
|
+
import { readFile, stat as stat2 } from "fs/promises";
|
|
5514
|
+
import { realpathSync, readdirSync as readdirSync2, readFileSync as readFileSync7, existsSync as existsSync7 } from "fs";
|
|
5609
5515
|
|
|
5610
5516
|
// app/lib/whatsapp/login.ts
|
|
5611
|
-
import { randomUUID as
|
|
5517
|
+
import { randomUUID as randomUUID4 } from "crypto";
|
|
5612
5518
|
var TAG17 = "[whatsapp:login]";
|
|
5613
5519
|
var ACTIVE_LOGIN_TTL_MS = 3 * 6e4;
|
|
5614
5520
|
var activeLogins = /* @__PURE__ */ new Map();
|
|
@@ -5711,8 +5617,8 @@ async function startLogin(opts) {
|
|
|
5711
5617
|
resetActiveLogin(accountId);
|
|
5712
5618
|
let resolveQr = null;
|
|
5713
5619
|
let rejectQr = null;
|
|
5714
|
-
const qrPromise = new Promise((
|
|
5715
|
-
resolveQr =
|
|
5620
|
+
const qrPromise = new Promise((resolve22, reject) => {
|
|
5621
|
+
resolveQr = resolve22;
|
|
5716
5622
|
rejectQr = reject;
|
|
5717
5623
|
});
|
|
5718
5624
|
const qrTimer = setTimeout(
|
|
@@ -5747,7 +5653,7 @@ async function startLogin(opts) {
|
|
|
5747
5653
|
const login = {
|
|
5748
5654
|
accountId,
|
|
5749
5655
|
authDir,
|
|
5750
|
-
id:
|
|
5656
|
+
id: randomUUID4(),
|
|
5751
5657
|
sock,
|
|
5752
5658
|
startedAt: Date.now(),
|
|
5753
5659
|
connected: false
|
|
@@ -5928,7 +5834,7 @@ function serializeWhatsAppSchema() {
|
|
|
5928
5834
|
|
|
5929
5835
|
// server/routes/whatsapp.ts
|
|
5930
5836
|
var TAG18 = "[whatsapp:api]";
|
|
5931
|
-
var
|
|
5837
|
+
var PLATFORM_ROOT3 = process.env.MAXY_PLATFORM_ROOT || "";
|
|
5932
5838
|
var app7 = new Hono();
|
|
5933
5839
|
app7.get("/status", (c) => {
|
|
5934
5840
|
try {
|
|
@@ -6106,14 +6012,14 @@ app7.post("/config", async (c) => {
|
|
|
6106
6012
|
return c.json(result, result.ok ? 200 : 400);
|
|
6107
6013
|
}
|
|
6108
6014
|
case "list-public-agents": {
|
|
6109
|
-
const agentsDir =
|
|
6015
|
+
const agentsDir = resolve6(account.accountDir, "agents");
|
|
6110
6016
|
const agents = [];
|
|
6111
6017
|
if (existsSync7(agentsDir)) {
|
|
6112
6018
|
try {
|
|
6113
6019
|
const entries = readdirSync2(agentsDir, { withFileTypes: true });
|
|
6114
6020
|
for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
6115
6021
|
if (!entry.isDirectory() || entry.name === "admin") continue;
|
|
6116
|
-
const configPath2 =
|
|
6022
|
+
const configPath2 = resolve6(agentsDir, entry.name, "config.json");
|
|
6117
6023
|
if (!existsSync7(configPath2)) continue;
|
|
6118
6024
|
try {
|
|
6119
6025
|
const config = JSON.parse(readFileSync7(configPath2, "utf-8"));
|
|
@@ -6188,14 +6094,14 @@ app7.post("/send-document", async (c) => {
|
|
|
6188
6094
|
if (!to || !filePath) {
|
|
6189
6095
|
return c.json({ error: "Missing required fields: to, filePath" }, 400);
|
|
6190
6096
|
}
|
|
6191
|
-
if (!maxyAccountId || !
|
|
6097
|
+
if (!maxyAccountId || !PLATFORM_ROOT3) {
|
|
6192
6098
|
return c.json({ error: "Cannot validate file path: missing account or platform context" }, 400);
|
|
6193
6099
|
}
|
|
6194
|
-
const accountDir =
|
|
6100
|
+
const accountDir = resolve6(PLATFORM_ROOT3, "..", "data/accounts", maxyAccountId);
|
|
6195
6101
|
let resolvedPath;
|
|
6196
6102
|
try {
|
|
6197
|
-
resolvedPath =
|
|
6198
|
-
const accountResolved =
|
|
6103
|
+
resolvedPath = realpathSync(filePath);
|
|
6104
|
+
const accountResolved = realpathSync(accountDir);
|
|
6199
6105
|
if (!resolvedPath.startsWith(accountResolved + "/")) {
|
|
6200
6106
|
const sanitised = filePath.replace(accountDir, "<account>/");
|
|
6201
6107
|
console.error(`${TAG18} send-document REJECTED path=${sanitised} reason=outside_account_directory`);
|
|
@@ -6210,12 +6116,12 @@ app7.post("/send-document", async (c) => {
|
|
|
6210
6116
|
console.error(`${TAG18} send-document path error: ${String(err)}`);
|
|
6211
6117
|
return c.json({ error: String(err) }, 500);
|
|
6212
6118
|
}
|
|
6213
|
-
const fileStat = await
|
|
6119
|
+
const fileStat = await stat2(resolvedPath);
|
|
6214
6120
|
if (fileStat.size > MAX_FILE_SIZE_BYTES) {
|
|
6215
6121
|
return c.json({ error: `File exceeds 50 MB limit (${(fileStat.size / 1024 / 1024).toFixed(1)} MB)` }, 400);
|
|
6216
6122
|
}
|
|
6217
|
-
const buffer = Buffer.from(await
|
|
6218
|
-
const filename =
|
|
6123
|
+
const buffer = Buffer.from(await readFile(resolvedPath));
|
|
6124
|
+
const filename = basename(resolvedPath);
|
|
6219
6125
|
const mimetype = detectMimeType(resolvedPath);
|
|
6220
6126
|
const sock = getSocket(accountId);
|
|
6221
6127
|
if (!sock) {
|
|
@@ -6416,8 +6322,8 @@ var whatsapp_default = app7;
|
|
|
6416
6322
|
// server/routes/onboarding.ts
|
|
6417
6323
|
import { spawn, execFileSync } from "child_process";
|
|
6418
6324
|
import { openSync, closeSync, writeFileSync as writeFileSync5, writeSync, existsSync as existsSync9, mkdirSync as mkdirSync4, readFileSync as readFileSync9, unlinkSync } from "fs";
|
|
6419
|
-
import { resolve as
|
|
6420
|
-
import { createHash, randomUUID as
|
|
6325
|
+
import { resolve as resolve7, dirname as dirname3 } from "path";
|
|
6326
|
+
import { createHash, randomUUID as randomUUID5 } from "crypto";
|
|
6421
6327
|
|
|
6422
6328
|
// ../lib/admins-write/src/index.ts
|
|
6423
6329
|
import { existsSync as existsSync8, readFileSync as readFileSync8, writeFileSync as writeFileSync4, renameSync, mkdirSync as mkdirSync3, readdirSync as readdirSync3, statSync as statSync2 } from "fs";
|
|
@@ -6566,7 +6472,7 @@ function checkAdminAuthInvariant(input) {
|
|
|
6566
6472
|
}
|
|
6567
6473
|
|
|
6568
6474
|
// server/routes/onboarding.ts
|
|
6569
|
-
var
|
|
6475
|
+
var PLATFORM_ROOT4 = process.env.MAXY_PLATFORM_ROOT || "";
|
|
6570
6476
|
function hashPin(pin) {
|
|
6571
6477
|
return createHash("sha256").update(pin).digest("hex");
|
|
6572
6478
|
}
|
|
@@ -6693,7 +6599,7 @@ app8.post("/set-pin", async (c) => {
|
|
|
6693
6599
|
const hash = hashPin(body.pin);
|
|
6694
6600
|
const account = resolveAccount();
|
|
6695
6601
|
const existingOwnerUserId = account?.config.admins?.find((a) => a.role === "owner")?.userId;
|
|
6696
|
-
const userId = existingOwnerUserId ??
|
|
6602
|
+
const userId = existingOwnerUserId ?? randomUUID5();
|
|
6697
6603
|
if (existingOwnerUserId) {
|
|
6698
6604
|
console.log(`[set-pin] reusing existing owner userId=${userId.slice(0, 8)}\u2026 (change-PIN preserves identity)`);
|
|
6699
6605
|
} else {
|
|
@@ -6785,7 +6691,7 @@ app8.post("/skip", async (c) => {
|
|
|
6785
6691
|
}
|
|
6786
6692
|
const { accountId, accountDir } = account;
|
|
6787
6693
|
let agentName = "Maxy";
|
|
6788
|
-
const brandPath =
|
|
6694
|
+
const brandPath = PLATFORM_ROOT4 ? resolve7(PLATFORM_ROOT4, "config", "brand.json") : "";
|
|
6789
6695
|
if (brandPath && existsSync9(brandPath)) {
|
|
6790
6696
|
try {
|
|
6791
6697
|
const brand = JSON.parse(readFileSync9(brandPath, "utf-8"));
|
|
@@ -6794,7 +6700,7 @@ app8.post("/skip", async (c) => {
|
|
|
6794
6700
|
console.error(`[onboarding-skip] brand.json read failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
6795
6701
|
}
|
|
6796
6702
|
}
|
|
6797
|
-
const soulPath =
|
|
6703
|
+
const soulPath = resolve7(accountDir, "agents", "admin", "SOUL.md");
|
|
6798
6704
|
try {
|
|
6799
6705
|
mkdirSync4(dirname3(soulPath), { recursive: true });
|
|
6800
6706
|
writeFileSync5(soulPath, `You are ${agentName}, an AI operations manager.
|
|
@@ -7225,7 +7131,7 @@ app10.post("/", async (c) => {
|
|
|
7225
7131
|
var session_default2 = app10;
|
|
7226
7132
|
|
|
7227
7133
|
// server/routes/admin/chat.ts
|
|
7228
|
-
import { resolve as
|
|
7134
|
+
import { resolve as resolve8 } from "path";
|
|
7229
7135
|
import { appendFileSync as appendFileSync3 } from "fs";
|
|
7230
7136
|
|
|
7231
7137
|
// app/lib/script-stream-tailer.ts
|
|
@@ -7516,7 +7422,7 @@ var app11 = new Hono();
|
|
|
7516
7422
|
app11.post("/cancel", requireAdminSession, async (c) => {
|
|
7517
7423
|
const session_key = c.var.sessionKey;
|
|
7518
7424
|
try {
|
|
7519
|
-
const { interruptClient: interruptClient2 } = await import("./client-pool-
|
|
7425
|
+
const { interruptClient: interruptClient2 } = await import("./client-pool-AMT2W3II.js");
|
|
7520
7426
|
await interruptClient2(session_key);
|
|
7521
7427
|
return c.json({ ok: true });
|
|
7522
7428
|
} catch (err) {
|
|
@@ -7680,7 +7586,7 @@ app11.post("/", requireAdminSession, async (c) => {
|
|
|
7680
7586
|
function resolveTeeStreamLogPath() {
|
|
7681
7587
|
const liveConvId = getConversationIdForSession(session_key);
|
|
7682
7588
|
const key = liveConvId ?? preflushStreamLogKey(session_key);
|
|
7683
|
-
return
|
|
7589
|
+
return resolve8(account.accountDir, "logs", `claude-agent-stream-${key}.log`);
|
|
7684
7590
|
}
|
|
7685
7591
|
try {
|
|
7686
7592
|
appendFileSync3(resolveTeeStreamLogPath(), `[${(/* @__PURE__ */ new Date()).toISOString()}] [chat-route-version=task606-tee-path-resolve] sessionKey=${session_key.slice(0, 12)}\u2026
|
|
@@ -7782,7 +7688,7 @@ app11.post("/", requireAdminSession, async (c) => {
|
|
|
7782
7688
|
try {
|
|
7783
7689
|
registerAdminSSE(sseEntry);
|
|
7784
7690
|
if (sseConvId) {
|
|
7785
|
-
const streamLogPath =
|
|
7691
|
+
const streamLogPath = resolve8(account.accountDir, "logs", `claude-agent-stream-${sseConvId}.log`);
|
|
7786
7692
|
tailer = startScriptStreamTailer({
|
|
7787
7693
|
path: streamLogPath,
|
|
7788
7694
|
onEvent: (event) => {
|
|
@@ -7912,7 +7818,7 @@ var compact_default = app12;
|
|
|
7912
7818
|
|
|
7913
7819
|
// server/routes/admin/logs.ts
|
|
7914
7820
|
import { existsSync as existsSync13, readdirSync as readdirSync5, readFileSync as readFileSync11, statSync as statSync6 } from "fs";
|
|
7915
|
-
import { resolve as
|
|
7821
|
+
import { resolve as resolve9, basename as basename2 } from "path";
|
|
7916
7822
|
|
|
7917
7823
|
// app/lib/logs-read-resolve.ts
|
|
7918
7824
|
import { existsSync as existsSync12 } from "fs";
|
|
@@ -7956,15 +7862,15 @@ app13.get("/", async (c) => {
|
|
|
7956
7862
|
const sessionKeyParam = c.req.query("sessionKey");
|
|
7957
7863
|
const download = c.req.query("download") === "1";
|
|
7958
7864
|
const account = resolveAccount();
|
|
7959
|
-
const accountLogDir = account ?
|
|
7865
|
+
const accountLogDir = account ? resolve9(account.accountDir, "logs") : null;
|
|
7960
7866
|
const logDirs = [];
|
|
7961
7867
|
if (accountLogDir) logDirs.push(accountLogDir);
|
|
7962
7868
|
logDirs.push(LOG_DIR);
|
|
7963
7869
|
if (fileParam) {
|
|
7964
|
-
const safe =
|
|
7870
|
+
const safe = basename2(fileParam);
|
|
7965
7871
|
const searched = [];
|
|
7966
7872
|
for (const dir of logDirs) {
|
|
7967
|
-
const filePath =
|
|
7873
|
+
const filePath = resolve9(dir, safe);
|
|
7968
7874
|
searched.push(filePath);
|
|
7969
7875
|
try {
|
|
7970
7876
|
const buffer = readFileSync11(filePath);
|
|
@@ -8051,7 +7957,7 @@ app13.get("/", async (c) => {
|
|
|
8051
7957
|
const hit = hits[0];
|
|
8052
7958
|
console.info(`[admin/logs] resolved sessionKey=${sessionKeySlice} conversationId=${conversationIdSlice} shape=${hit.shape} stalePreflushCount=${stalePreflushCount}`);
|
|
8053
7959
|
try {
|
|
8054
|
-
const filename =
|
|
7960
|
+
const filename = basename2(hit.path);
|
|
8055
7961
|
if (stalePreflushCount > 0 && !download) {
|
|
8056
7962
|
const content = readFileSync11(hit.path, "utf-8");
|
|
8057
7963
|
return c.json({
|
|
@@ -8104,10 +8010,10 @@ app13.get("/", async (c) => {
|
|
|
8104
8010
|
console.warn(`[admin/logs] readdir-fail dir=${dir} reason=${reason}`);
|
|
8105
8011
|
continue;
|
|
8106
8012
|
}
|
|
8107
|
-
files.filter((f) => !seen.has(f)).map((f) => ({ name: f, mtime: statSync6(
|
|
8013
|
+
files.filter((f) => !seen.has(f)).map((f) => ({ name: f, mtime: statSync6(resolve9(dir, f)).mtimeMs })).sort((a, b) => b.mtime - a.mtime).forEach(({ name }) => {
|
|
8108
8014
|
seen.add(name);
|
|
8109
8015
|
try {
|
|
8110
|
-
const content = readFileSync11(
|
|
8016
|
+
const content = readFileSync11(resolve9(dir, name));
|
|
8111
8017
|
const tail = content.length > TAIL_BYTES ? content.subarray(content.length - TAIL_BYTES).toString("utf-8") : content.toString("utf-8");
|
|
8112
8018
|
logs[name] = tail.trim() || "(empty)";
|
|
8113
8019
|
} catch (err) {
|
|
@@ -8145,9 +8051,9 @@ app14.get("/", (c) => {
|
|
|
8145
8051
|
var claude_info_default = app14;
|
|
8146
8052
|
|
|
8147
8053
|
// server/routes/admin/attachment.ts
|
|
8148
|
-
import { readFile as
|
|
8054
|
+
import { readFile as readFile2, readdir } from "fs/promises";
|
|
8149
8055
|
import { existsSync as existsSync14 } from "fs";
|
|
8150
|
-
import { resolve as
|
|
8056
|
+
import { resolve as resolve10 } from "path";
|
|
8151
8057
|
var app15 = new Hono();
|
|
8152
8058
|
app15.get("/:attachmentId", requireAdminSession, async (c) => {
|
|
8153
8059
|
const attachmentId = c.req.param("attachmentId");
|
|
@@ -8159,17 +8065,17 @@ app15.get("/:attachmentId", requireAdminSession, async (c) => {
|
|
|
8159
8065
|
if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/.test(attachmentId)) {
|
|
8160
8066
|
return new Response("Not found", { status: 404 });
|
|
8161
8067
|
}
|
|
8162
|
-
const dir =
|
|
8068
|
+
const dir = resolve10(ATTACHMENTS_ROOT, accountId, attachmentId);
|
|
8163
8069
|
if (!existsSync14(dir)) {
|
|
8164
8070
|
return new Response("Not found", { status: 404 });
|
|
8165
8071
|
}
|
|
8166
|
-
const metaPath =
|
|
8072
|
+
const metaPath = resolve10(dir, `${attachmentId}.meta.json`);
|
|
8167
8073
|
if (!existsSync14(metaPath)) {
|
|
8168
8074
|
return new Response("Not found", { status: 404 });
|
|
8169
8075
|
}
|
|
8170
8076
|
let meta;
|
|
8171
8077
|
try {
|
|
8172
|
-
meta = JSON.parse(await
|
|
8078
|
+
meta = JSON.parse(await readFile2(metaPath, "utf-8"));
|
|
8173
8079
|
} catch {
|
|
8174
8080
|
return new Response("Not found", { status: 404 });
|
|
8175
8081
|
}
|
|
@@ -8178,8 +8084,8 @@ app15.get("/:attachmentId", requireAdminSession, async (c) => {
|
|
|
8178
8084
|
if (!dataFile) {
|
|
8179
8085
|
return new Response("Not found", { status: 404 });
|
|
8180
8086
|
}
|
|
8181
|
-
const filePath =
|
|
8182
|
-
const buffer = await
|
|
8087
|
+
const filePath = resolve10(dir, dataFile);
|
|
8088
|
+
const buffer = await readFile2(filePath);
|
|
8183
8089
|
return new Response(new Uint8Array(buffer), {
|
|
8184
8090
|
headers: {
|
|
8185
8091
|
"Content-Type": meta.mimeType,
|
|
@@ -8191,13 +8097,13 @@ app15.get("/:attachmentId", requireAdminSession, async (c) => {
|
|
|
8191
8097
|
var attachment_default = app15;
|
|
8192
8098
|
|
|
8193
8099
|
// server/routes/admin/agents.ts
|
|
8194
|
-
import { resolve as
|
|
8100
|
+
import { resolve as resolve11 } from "path";
|
|
8195
8101
|
import { readdirSync as readdirSync6, readFileSync as readFileSync12, existsSync as existsSync15, rmSync } from "fs";
|
|
8196
8102
|
var app16 = new Hono();
|
|
8197
8103
|
app16.get("/", (c) => {
|
|
8198
8104
|
const account = resolveAccount();
|
|
8199
8105
|
if (!account) return c.json({ agents: [] });
|
|
8200
|
-
const agentsDir =
|
|
8106
|
+
const agentsDir = resolve11(account.accountDir, "agents");
|
|
8201
8107
|
if (!existsSync15(agentsDir)) return c.json({ agents: [] });
|
|
8202
8108
|
const agents = [];
|
|
8203
8109
|
try {
|
|
@@ -8205,7 +8111,7 @@ app16.get("/", (c) => {
|
|
|
8205
8111
|
for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
8206
8112
|
if (!entry.isDirectory()) continue;
|
|
8207
8113
|
if (entry.name === "admin") continue;
|
|
8208
|
-
const configPath2 =
|
|
8114
|
+
const configPath2 = resolve11(agentsDir, entry.name, "config.json");
|
|
8209
8115
|
if (!existsSync15(configPath2)) continue;
|
|
8210
8116
|
try {
|
|
8211
8117
|
const config = JSON.parse(readFileSync12(configPath2, "utf-8"));
|
|
@@ -8234,7 +8140,7 @@ app16.delete("/:slug", async (c) => {
|
|
|
8234
8140
|
if (slug.includes("/") || slug.includes("..") || slug.includes("\\")) {
|
|
8235
8141
|
return c.json({ error: "Invalid agent slug" }, 400);
|
|
8236
8142
|
}
|
|
8237
|
-
const agentDir =
|
|
8143
|
+
const agentDir = resolve11(account.accountDir, "agents", slug);
|
|
8238
8144
|
if (!existsSync15(agentDir)) {
|
|
8239
8145
|
return c.json({ error: "Agent not found" }, 404);
|
|
8240
8146
|
}
|
|
@@ -8264,7 +8170,7 @@ app16.post("/:slug/project", async (c) => {
|
|
|
8264
8170
|
if (slug.includes("/") || slug.includes("..") || slug.includes("\\")) {
|
|
8265
8171
|
return c.json({ error: "Invalid agent slug" }, 400);
|
|
8266
8172
|
}
|
|
8267
|
-
const agentDir =
|
|
8173
|
+
const agentDir = resolve11(account.accountDir, "agents", slug);
|
|
8268
8174
|
if (!existsSync15(agentDir)) {
|
|
8269
8175
|
return c.json({ error: "Agent not found on disk" }, 404);
|
|
8270
8176
|
}
|
|
@@ -8281,7 +8187,7 @@ var agents_default = app16;
|
|
|
8281
8187
|
// server/routes/admin/sessions.ts
|
|
8282
8188
|
import crypto2 from "crypto";
|
|
8283
8189
|
import { resolve as resolvePath } from "path";
|
|
8284
|
-
import { appendFileSync as appendFileSync4, existsSync as
|
|
8190
|
+
import { appendFileSync as appendFileSync4, existsSync as existsSync17 } from "fs";
|
|
8285
8191
|
|
|
8286
8192
|
// app/lib/synthetic-marker.ts
|
|
8287
8193
|
var CLOUDFLARE_MARKER_PREFIX = "Cloudflare setup completed (actionId: ";
|
|
@@ -8292,7 +8198,135 @@ function isSyntheticUserMarker(content) {
|
|
|
8292
8198
|
return content.startsWith(COMPONENT_DONE_PREFIX) || content.startsWith(LIFECYCLE_PREFIX) || content.startsWith(CLOUDFLARE_MARKER_PREFIX);
|
|
8293
8199
|
}
|
|
8294
8200
|
|
|
8201
|
+
// app/lib/claude-agent/jsonl-replay.ts
|
|
8202
|
+
import { existsSync as existsSync16, readFileSync as readFileSync13 } from "fs";
|
|
8203
|
+
function replayJsonl(jsonlPath) {
|
|
8204
|
+
if (!existsSync16(jsonlPath)) {
|
|
8205
|
+
return { messages: [], jsonlMissing: true, malformedLines: 0 };
|
|
8206
|
+
}
|
|
8207
|
+
let raw;
|
|
8208
|
+
try {
|
|
8209
|
+
raw = readFileSync13(jsonlPath, "utf8");
|
|
8210
|
+
} catch {
|
|
8211
|
+
return { messages: [], jsonlMissing: true, malformedLines: 0 };
|
|
8212
|
+
}
|
|
8213
|
+
const lines = raw.split("\n");
|
|
8214
|
+
const messages = [];
|
|
8215
|
+
let malformedLines = 0;
|
|
8216
|
+
let pendingText = "";
|
|
8217
|
+
let pendingComponents = [];
|
|
8218
|
+
let pendingMessageId;
|
|
8219
|
+
let pendingCreatedAt;
|
|
8220
|
+
const flushAssistant = () => {
|
|
8221
|
+
if (!pendingMessageId) return;
|
|
8222
|
+
if (pendingText.length === 0 && pendingComponents.length === 0) {
|
|
8223
|
+
pendingMessageId = void 0;
|
|
8224
|
+
pendingCreatedAt = void 0;
|
|
8225
|
+
return;
|
|
8226
|
+
}
|
|
8227
|
+
messages.push({
|
|
8228
|
+
messageId: pendingMessageId,
|
|
8229
|
+
role: "assistant",
|
|
8230
|
+
content: pendingText,
|
|
8231
|
+
createdAt: pendingCreatedAt ?? (/* @__PURE__ */ new Date(0)).toISOString(),
|
|
8232
|
+
components: pendingComponents,
|
|
8233
|
+
attachments: []
|
|
8234
|
+
});
|
|
8235
|
+
pendingText = "";
|
|
8236
|
+
pendingComponents = [];
|
|
8237
|
+
pendingMessageId = void 0;
|
|
8238
|
+
pendingCreatedAt = void 0;
|
|
8239
|
+
};
|
|
8240
|
+
for (const line of lines) {
|
|
8241
|
+
if (!line.trim()) continue;
|
|
8242
|
+
let entry;
|
|
8243
|
+
try {
|
|
8244
|
+
entry = JSON.parse(line);
|
|
8245
|
+
} catch {
|
|
8246
|
+
malformedLines += 1;
|
|
8247
|
+
continue;
|
|
8248
|
+
}
|
|
8249
|
+
if (entry.type === "user") {
|
|
8250
|
+
const content = entry.message?.content;
|
|
8251
|
+
if (typeof content === "string" && content.length > 0) {
|
|
8252
|
+
flushAssistant();
|
|
8253
|
+
messages.push({
|
|
8254
|
+
messageId: entry.uuid ?? `jsonl-user-${messages.length}`,
|
|
8255
|
+
role: "user",
|
|
8256
|
+
content,
|
|
8257
|
+
createdAt: entry.timestamp ?? (/* @__PURE__ */ new Date(0)).toISOString(),
|
|
8258
|
+
components: [],
|
|
8259
|
+
attachments: []
|
|
8260
|
+
});
|
|
8261
|
+
}
|
|
8262
|
+
continue;
|
|
8263
|
+
}
|
|
8264
|
+
if (entry.type === "assistant" && Array.isArray(entry.message?.content)) {
|
|
8265
|
+
if (!pendingMessageId) {
|
|
8266
|
+
pendingMessageId = entry.uuid ?? `jsonl-asst-${messages.length}`;
|
|
8267
|
+
pendingCreatedAt = entry.timestamp;
|
|
8268
|
+
}
|
|
8269
|
+
for (const block of entry.message.content) {
|
|
8270
|
+
if (block.type === "text" && typeof block.text === "string" && block.text.length > 0) {
|
|
8271
|
+
pendingText = pendingText.length > 0 ? `${pendingText}
|
|
8272
|
+
|
|
8273
|
+
${block.text}` : block.text;
|
|
8274
|
+
} else if (block.type === "tool_use" && block.name === "mcp__admin__render-component") {
|
|
8275
|
+
const rawInput = block.input ?? {};
|
|
8276
|
+
const rawName = rawInput.name;
|
|
8277
|
+
const rawData = rawInput.data;
|
|
8278
|
+
const componentName = typeof rawName === "string" && rawName.length > 0 ? rawName : "unknown";
|
|
8279
|
+
let dataObj = {};
|
|
8280
|
+
if (rawData !== null && typeof rawData === "object" && !Array.isArray(rawData)) {
|
|
8281
|
+
dataObj = rawData;
|
|
8282
|
+
} else if (typeof rawData === "string") {
|
|
8283
|
+
try {
|
|
8284
|
+
dataObj = JSON.parse(rawData);
|
|
8285
|
+
} catch {
|
|
8286
|
+
}
|
|
8287
|
+
}
|
|
8288
|
+
let healArtefactAttachmentId;
|
|
8289
|
+
let healArtefactMimeType;
|
|
8290
|
+
let healArtefactTitle;
|
|
8291
|
+
if (isPersistentComponent(componentName) && block.id) {
|
|
8292
|
+
const mimeType = deriveComponentMimeType(dataObj);
|
|
8293
|
+
if (mimeType) {
|
|
8294
|
+
healArtefactAttachmentId = deriveComponentAttachmentId(block.id);
|
|
8295
|
+
healArtefactMimeType = mimeType;
|
|
8296
|
+
healArtefactTitle = deriveComponentTitle(componentName, dataObj);
|
|
8297
|
+
}
|
|
8298
|
+
}
|
|
8299
|
+
pendingComponents.push({
|
|
8300
|
+
componentId: block.id ?? `jsonl-comp-${pendingComponents.length}`,
|
|
8301
|
+
name: componentName,
|
|
8302
|
+
data: JSON.stringify(dataObj),
|
|
8303
|
+
ordinal: pendingComponents.length,
|
|
8304
|
+
textOffset: pendingText.length,
|
|
8305
|
+
// Submitted state is a Neo4j projection — JSONL has no notion of
|
|
8306
|
+
// it. The resume route supplements `submitted: true` from the
|
|
8307
|
+
// matching :Component row when present.
|
|
8308
|
+
submitted: false,
|
|
8309
|
+
...healArtefactAttachmentId ? {
|
|
8310
|
+
artefactAttachmentId: healArtefactAttachmentId,
|
|
8311
|
+
artefactMimeType: healArtefactMimeType,
|
|
8312
|
+
artefactTitle: healArtefactTitle
|
|
8313
|
+
} : {}
|
|
8314
|
+
});
|
|
8315
|
+
}
|
|
8316
|
+
}
|
|
8317
|
+
continue;
|
|
8318
|
+
}
|
|
8319
|
+
}
|
|
8320
|
+
flushAssistant();
|
|
8321
|
+
return { messages, jsonlMissing: false, malformedLines };
|
|
8322
|
+
}
|
|
8323
|
+
function resolveJsonlPath(homeDir, accountDir, agentSessionId) {
|
|
8324
|
+
const projectKey = "-" + accountDir.split("/").filter(Boolean).join("-");
|
|
8325
|
+
return `${homeDir}/.claude/projects/${projectKey}/${agentSessionId}.jsonl`;
|
|
8326
|
+
}
|
|
8327
|
+
|
|
8295
8328
|
// server/routes/admin/sessions.ts
|
|
8329
|
+
import { homedir } from "os";
|
|
8296
8330
|
function validateAndShapeAttachments(raws, conversationAccountId, conversationId, messageId, streamLogPath) {
|
|
8297
8331
|
const chips = [];
|
|
8298
8332
|
let valid = 0;
|
|
@@ -8301,7 +8335,7 @@ function validateAndShapeAttachments(raws, conversationAccountId, conversationId
|
|
|
8301
8335
|
let reason = null;
|
|
8302
8336
|
if (!a.attachmentId || !a.filename || !a.mimeType || !a.storagePath) reason = "schema-fail";
|
|
8303
8337
|
else if (a.accountId !== conversationAccountId) reason = "account-mismatch";
|
|
8304
|
-
else if (!
|
|
8338
|
+
else if (!existsSync17(a.storagePath)) reason = "missing-file";
|
|
8305
8339
|
if (reason) {
|
|
8306
8340
|
invalid++;
|
|
8307
8341
|
try {
|
|
@@ -8550,15 +8584,150 @@ app17.post("/:id/resume", async (c) => {
|
|
|
8550
8584
|
if (persistedAgentSessionId) {
|
|
8551
8585
|
setAgentSessionId(sessionKey, persistedAgentSessionId);
|
|
8552
8586
|
}
|
|
8587
|
+
const streamLogPath = resolvePath(ACCOUNTS_DIR, accountId, "logs", `claude-agent-stream-${conversationId}.log`);
|
|
8588
|
+
const tag = persistedAgentSessionId ? `agentSessionId=${persistedAgentSessionId.slice(0, 8)}\u2026` : "agentSessionId=missing";
|
|
8553
8589
|
let messages = [];
|
|
8590
|
+
let neo4jMessages = [];
|
|
8591
|
+
let jsonlReplayMessages = [];
|
|
8592
|
+
let jsonlMissing = false;
|
|
8593
|
+
let jsonlMalformedLines = 0;
|
|
8594
|
+
let jsonlHealMissing = 0;
|
|
8595
|
+
let jsonlHealOk = 0;
|
|
8596
|
+
let jsonlHealFail = 0;
|
|
8554
8597
|
try {
|
|
8555
|
-
|
|
8598
|
+
neo4jMessages = await getRecentMessages(conversationId);
|
|
8556
8599
|
} catch (err) {
|
|
8557
8600
|
console.error(`[admin-resume] ${(/* @__PURE__ */ new Date()).toISOString()} getRecentMessages failed: conversationId=${conversationId.slice(0, 8)}\u2026 error=${err instanceof Error ? err.message : String(err)}`);
|
|
8558
8601
|
return c.json({ error: "Failed to load conversation messages" }, 500);
|
|
8559
8602
|
}
|
|
8560
|
-
|
|
8561
|
-
|
|
8603
|
+
if (persistedAgentSessionId) {
|
|
8604
|
+
const jsonlPath = resolveJsonlPath(homedir(), resolvePath(ACCOUNTS_DIR, accountId), persistedAgentSessionId);
|
|
8605
|
+
const replay = replayJsonl(jsonlPath);
|
|
8606
|
+
jsonlMissing = replay.jsonlMissing;
|
|
8607
|
+
jsonlMalformedLines = replay.malformedLines;
|
|
8608
|
+
jsonlReplayMessages = replay.messages;
|
|
8609
|
+
} else {
|
|
8610
|
+
jsonlMissing = true;
|
|
8611
|
+
}
|
|
8612
|
+
if (jsonlMissing || jsonlReplayMessages.length === 0) {
|
|
8613
|
+
messages = neo4jMessages;
|
|
8614
|
+
} else {
|
|
8615
|
+
const neo4jByKey = /* @__PURE__ */ new Map();
|
|
8616
|
+
for (const n of neo4jMessages) {
|
|
8617
|
+
neo4jByKey.set(`${n.role}${n.content}`, n);
|
|
8618
|
+
}
|
|
8619
|
+
const healQueue = [];
|
|
8620
|
+
messages = jsonlReplayMessages.map((j) => {
|
|
8621
|
+
const match = neo4jByKey.get(`${j.role}${j.content}`);
|
|
8622
|
+
if (match) {
|
|
8623
|
+
const mergedComponents = j.components.map((c2, i) => {
|
|
8624
|
+
const neoComp = match.components?.[i];
|
|
8625
|
+
return {
|
|
8626
|
+
componentId: neoComp?.componentId ?? c2.componentId,
|
|
8627
|
+
name: c2.name,
|
|
8628
|
+
data: c2.data,
|
|
8629
|
+
ordinal: c2.ordinal,
|
|
8630
|
+
textOffset: c2.textOffset,
|
|
8631
|
+
submitted: neoComp?.submitted ?? false
|
|
8632
|
+
};
|
|
8633
|
+
});
|
|
8634
|
+
return {
|
|
8635
|
+
messageId: match.messageId,
|
|
8636
|
+
role: match.role,
|
|
8637
|
+
content: match.content,
|
|
8638
|
+
createdAt: match.createdAt,
|
|
8639
|
+
components: mergedComponents,
|
|
8640
|
+
attachments: match.attachments ?? []
|
|
8641
|
+
};
|
|
8642
|
+
}
|
|
8643
|
+
healQueue.push({ role: j.role, content: j.content, createdAt: j.createdAt, components: j.components });
|
|
8644
|
+
return {
|
|
8645
|
+
messageId: j.messageId,
|
|
8646
|
+
role: j.role,
|
|
8647
|
+
content: j.content,
|
|
8648
|
+
createdAt: j.createdAt,
|
|
8649
|
+
components: j.components.map((c2) => ({
|
|
8650
|
+
componentId: c2.componentId,
|
|
8651
|
+
name: c2.name,
|
|
8652
|
+
data: c2.data,
|
|
8653
|
+
ordinal: c2.ordinal,
|
|
8654
|
+
textOffset: c2.textOffset,
|
|
8655
|
+
submitted: false
|
|
8656
|
+
})),
|
|
8657
|
+
attachments: []
|
|
8658
|
+
};
|
|
8659
|
+
});
|
|
8660
|
+
jsonlHealMissing = healQueue.length;
|
|
8661
|
+
if (healQueue.length > 0) {
|
|
8662
|
+
void (async () => {
|
|
8663
|
+
for (let i = 0; i < healQueue.length; i++) {
|
|
8664
|
+
const q = healQueue[i];
|
|
8665
|
+
try {
|
|
8666
|
+
const healComponents = await Promise.all(q.components.map(async (c2) => {
|
|
8667
|
+
const base = {
|
|
8668
|
+
componentId: c2.componentId,
|
|
8669
|
+
name: c2.name,
|
|
8670
|
+
data: c2.data,
|
|
8671
|
+
ordinal: c2.ordinal,
|
|
8672
|
+
textOffset: c2.textOffset
|
|
8673
|
+
};
|
|
8674
|
+
if (!c2.artefactAttachmentId || !c2.artefactMimeType || !c2.artefactTitle) return base;
|
|
8675
|
+
try {
|
|
8676
|
+
const dataObj = JSON.parse(c2.data);
|
|
8677
|
+
const bytesPick = pickComponentBytes(dataObj);
|
|
8678
|
+
if (!bytesPick) return base;
|
|
8679
|
+
const filename = bytesPick.mimeType === "text/html" ? `${c2.artefactTitle}.html` : `${c2.artefactTitle}.md`;
|
|
8680
|
+
await storeComponentArtefact(accountId, c2.artefactAttachmentId, bytesPick.mimeType, bytesPick.content, filename);
|
|
8681
|
+
appendFileSync4(
|
|
8682
|
+
streamLogPath,
|
|
8683
|
+
`[${(/* @__PURE__ */ new Date()).toISOString()}] [render-component-persist] heal componentName=${c2.name} attachmentId=${c2.artefactAttachmentId.slice(0, 8)} mimeType=${bytesPick.mimeType} bytes=${bytesPick.content.length} outcome=ok
|
|
8684
|
+
`
|
|
8685
|
+
);
|
|
8686
|
+
return {
|
|
8687
|
+
...base,
|
|
8688
|
+
artefactAttachmentId: c2.artefactAttachmentId,
|
|
8689
|
+
artefactMimeType: c2.artefactMimeType,
|
|
8690
|
+
artefactTitle: c2.artefactTitle
|
|
8691
|
+
};
|
|
8692
|
+
} catch (writeErr) {
|
|
8693
|
+
const reason2 = writeErr instanceof Error ? writeErr.message : String(writeErr);
|
|
8694
|
+
appendFileSync4(
|
|
8695
|
+
streamLogPath,
|
|
8696
|
+
`[${(/* @__PURE__ */ new Date()).toISOString()}] [render-component-persist] heal componentName=${c2.name} attachmentId=${c2.artefactAttachmentId.slice(0, 8)} outcome=disk-fail reason=${JSON.stringify(reason2.slice(0, 200))}
|
|
8697
|
+
`
|
|
8698
|
+
);
|
|
8699
|
+
return base;
|
|
8700
|
+
}
|
|
8701
|
+
}));
|
|
8702
|
+
const messageId = await persistMessage(
|
|
8703
|
+
conversationId,
|
|
8704
|
+
q.role,
|
|
8705
|
+
q.content,
|
|
8706
|
+
accountId,
|
|
8707
|
+
void 0,
|
|
8708
|
+
q.createdAt,
|
|
8709
|
+
void 0,
|
|
8710
|
+
healComponents,
|
|
8711
|
+
void 0
|
|
8712
|
+
);
|
|
8713
|
+
jsonlHealOk += 1;
|
|
8714
|
+
try {
|
|
8715
|
+
appendFileSync4(streamLogPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] [admin-persist-heal] convId=${conversationId.slice(0, 8)} turnIndex=${i} role=${q.role} outcome=ok messageId=${(messageId ?? "").slice(0, 8)}
|
|
8716
|
+
`);
|
|
8717
|
+
} catch {
|
|
8718
|
+
}
|
|
8719
|
+
} catch (err) {
|
|
8720
|
+
jsonlHealFail += 1;
|
|
8721
|
+
try {
|
|
8722
|
+
appendFileSync4(streamLogPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] [admin-persist-heal] convId=${conversationId.slice(0, 8)} turnIndex=${i} role=${q.role} outcome=fail reason=${JSON.stringify((err instanceof Error ? err.message : String(err)).slice(0, 200))}
|
|
8723
|
+
`);
|
|
8724
|
+
} catch {
|
|
8725
|
+
}
|
|
8726
|
+
}
|
|
8727
|
+
}
|
|
8728
|
+
})();
|
|
8729
|
+
}
|
|
8730
|
+
}
|
|
8562
8731
|
let totalValid = 0;
|
|
8563
8732
|
let totalInvalid = 0;
|
|
8564
8733
|
let totalComponents = 0;
|
|
@@ -8600,7 +8769,8 @@ app17.post("/:id/resume", async (c) => {
|
|
|
8600
8769
|
const userMessageCount = rehydrated.filter((m) => m.role !== "assistant").length;
|
|
8601
8770
|
const reason = bridged ? "post-restart" : "page-refresh";
|
|
8602
8771
|
try {
|
|
8603
|
-
|
|
8772
|
+
const source = jsonlMissing ? persistedAgentSessionId ? "jsonl-missing" : "neo4j-only" : "jsonl";
|
|
8773
|
+
appendFileSync4(streamLogPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] [admin-resume] reason=${reason} source=${source} sessionKey=${sessionKey.slice(0, 8)} conversationId=${conversationId.slice(0, 8)} ${tag} loadedMessages=${messages.length} jsonlReplayMessages=${jsonlReplayMessages.length} neo4jMessages=${neo4jMessages.length} jsonlMalformed=${jsonlMalformedLines} componentCount=${totalComponents} userAttachmentCount=${totalAttachments} syntheticHidden=${syntheticHidden}
|
|
8604
8774
|
`);
|
|
8605
8775
|
if (totalComponents > 0) {
|
|
8606
8776
|
appendFileSync4(streamLogPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] [component-rehydrate] conversationId=${conversationId.slice(0, 8)} count=${totalComponents} valid=${totalValid} invalid=${totalInvalid} textRuns=${textRuns}
|
|
@@ -8608,13 +8778,27 @@ app17.post("/:id/resume", async (c) => {
|
|
|
8608
8778
|
}
|
|
8609
8779
|
if (totalAttachments > 0 || totalAttachmentInvalid > 0) {
|
|
8610
8780
|
appendFileSync4(streamLogPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] [attachment-rehydrate] conversationId=${conversationId.slice(0, 8)} userMessages=${userMessageCount} attachments=${totalAttachments} invalid=${totalAttachmentInvalid}
|
|
8781
|
+
`);
|
|
8782
|
+
}
|
|
8783
|
+
if (jsonlHealMissing > 0) {
|
|
8784
|
+
appendFileSync4(streamLogPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] [admin-resume-heal] conversationId=${conversationId.slice(0, 8)} missingTurns=${jsonlHealMissing} healOk=${jsonlHealOk} healFail=${jsonlHealFail} note=async-writes-may-still-be-in-flight
|
|
8611
8785
|
`);
|
|
8612
8786
|
}
|
|
8613
8787
|
} catch {
|
|
8614
8788
|
}
|
|
8615
8789
|
const age = formatAge(updatedAt);
|
|
8616
|
-
console.log(`[admin-resume] ${(/* @__PURE__ */ new Date()).toISOString()} reason=${reason} conversationId=${conversationId.slice(0, 8)}\u2026 age=${age} loaded=${messages.length} messages ${tag} components=${totalComponents} attachments=${totalAttachments} syntheticHidden=${syntheticHidden} sessionKey=${sessionKey.slice(0, 8)}\u2026`);
|
|
8617
|
-
return c.json({
|
|
8790
|
+
console.log(`[admin-resume] ${(/* @__PURE__ */ new Date()).toISOString()} reason=${reason} conversationId=${conversationId.slice(0, 8)}\u2026 age=${age} loaded=${messages.length} messages ${tag} components=${totalComponents} attachments=${totalAttachments} syntheticHidden=${syntheticHidden} jsonlMissing=${jsonlMissing} healMissing=${jsonlHealMissing} sessionKey=${sessionKey.slice(0, 8)}\u2026`);
|
|
8791
|
+
return c.json({
|
|
8792
|
+
conversationId,
|
|
8793
|
+
messages: rehydrated,
|
|
8794
|
+
resumeHeal: {
|
|
8795
|
+
missingTurns: jsonlHealMissing,
|
|
8796
|
+
healed: jsonlHealOk,
|
|
8797
|
+
failed: jsonlHealFail,
|
|
8798
|
+
jsonlMissing,
|
|
8799
|
+
jsonlMalformedLines
|
|
8800
|
+
}
|
|
8801
|
+
});
|
|
8618
8802
|
});
|
|
8619
8803
|
app17.post("/:id/label", requireAdminSession, async (c) => {
|
|
8620
8804
|
const conversationId = c.req.param("id");
|
|
@@ -8870,9 +9054,9 @@ app20.post("/", async (c) => {
|
|
|
8870
9054
|
var events_default = app20;
|
|
8871
9055
|
|
|
8872
9056
|
// server/routes/admin/cloudflare.ts
|
|
8873
|
-
import { homedir } from "os";
|
|
8874
|
-
import { resolve as
|
|
8875
|
-
import { readFileSync as
|
|
9057
|
+
import { homedir as homedir2 } from "os";
|
|
9058
|
+
import { resolve as resolve13 } from "path";
|
|
9059
|
+
import { readFileSync as readFileSync15 } from "fs";
|
|
8876
9060
|
|
|
8877
9061
|
// app/lib/dns-label.ts
|
|
8878
9062
|
var VALID_LABEL = /^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$/;
|
|
@@ -8888,14 +9072,14 @@ function isValidDomain(value) {
|
|
|
8888
9072
|
}
|
|
8889
9073
|
|
|
8890
9074
|
// app/lib/alias-domains.ts
|
|
8891
|
-
import { existsSync as
|
|
9075
|
+
import { existsSync as existsSync18, mkdirSync as mkdirSync6, readFileSync as readFileSync14, writeFileSync as writeFileSync7 } from "fs";
|
|
8892
9076
|
import { dirname as dirname5 } from "path";
|
|
8893
|
-
import { resolve as
|
|
8894
|
-
var ALIAS_DOMAINS_PATH =
|
|
9077
|
+
import { resolve as resolve12 } from "path";
|
|
9078
|
+
var ALIAS_DOMAINS_PATH = resolve12(MAXY_DIR, "alias-domains.json");
|
|
8895
9079
|
function readExisting() {
|
|
8896
|
-
if (!
|
|
9080
|
+
if (!existsSync18(ALIAS_DOMAINS_PATH)) return /* @__PURE__ */ new Set();
|
|
8897
9081
|
try {
|
|
8898
|
-
const parsed = JSON.parse(
|
|
9082
|
+
const parsed = JSON.parse(readFileSync14(ALIAS_DOMAINS_PATH, "utf-8"));
|
|
8899
9083
|
if (!Array.isArray(parsed)) return /* @__PURE__ */ new Set();
|
|
8900
9084
|
return new Set(parsed.filter((h) => typeof h === "string"));
|
|
8901
9085
|
} catch {
|
|
@@ -8919,10 +9103,10 @@ var CLOUDFLARE_SETUP_FORM_SCHEMA = {
|
|
|
8919
9103
|
var SETUP_TIMEOUT_MS = 10 * 60 * 1e3;
|
|
8920
9104
|
var DOMAINS_TIMEOUT_MS = 40 * 1e3;
|
|
8921
9105
|
function loadBrandInfo() {
|
|
8922
|
-
const platformRoot2 = process.env.MAXY_PLATFORM_ROOT ??
|
|
8923
|
-
const brandPath =
|
|
9106
|
+
const platformRoot2 = process.env.MAXY_PLATFORM_ROOT ?? resolve13(process.cwd(), "..");
|
|
9107
|
+
const brandPath = resolve13(platformRoot2, "config", "brand.json");
|
|
8924
9108
|
try {
|
|
8925
|
-
const parsed = JSON.parse(
|
|
9109
|
+
const parsed = JSON.parse(readFileSync15(brandPath, "utf-8"));
|
|
8926
9110
|
const hostname2 = typeof parsed.hostname === "string" && parsed.hostname ? parsed.hostname : "maxy";
|
|
8927
9111
|
const configDir2 = typeof parsed.configDir === "string" && parsed.configDir ? parsed.configDir : ".maxy";
|
|
8928
9112
|
return { hostname: hostname2, configDir: configDir2 };
|
|
@@ -9045,7 +9229,7 @@ app21.get("/domains", requireAdminSession, async (c) => {
|
|
|
9045
9229
|
streamLogPath = streamLogPathFor(accountId, correlationId).streamLogPath;
|
|
9046
9230
|
log(`phase=stream-log-resolved path=${streamLogPath}`);
|
|
9047
9231
|
const brand = loadBrandInfo();
|
|
9048
|
-
const scriptPath =
|
|
9232
|
+
const scriptPath = resolve13(homedir2(), "list-cf-domains.sh");
|
|
9049
9233
|
const result = await runFormSpawn({
|
|
9050
9234
|
scriptPath,
|
|
9051
9235
|
args: [brand.hostname],
|
|
@@ -9135,9 +9319,9 @@ app21.get("/tunnels", requireAdminSession, async (c) => {
|
|
|
9135
9319
|
if (!accountId) return err("session", "No account bound to session \u2014 refresh chat.");
|
|
9136
9320
|
if (!correlationId) return err("session", "No active conversation for session \u2014 refresh chat.");
|
|
9137
9321
|
streamLogPath = streamLogPathFor(accountId, correlationId).streamLogPath;
|
|
9138
|
-
const certPath =
|
|
9139
|
-
const { existsSync:
|
|
9140
|
-
if (!
|
|
9322
|
+
const certPath = resolve13(homedir2(), brand.configDir, "cloudflared", "cert.pem");
|
|
9323
|
+
const { existsSync: existsSync25 } = await import("fs");
|
|
9324
|
+
if (!existsSync25(certPath)) {
|
|
9141
9325
|
return err("cert", `Cloudflare origin certificate is not on disk yet (${certPath}). Complete the Cloudflare login first by submitting the form once \u2014 the OAuth flow writes cert.pem.`);
|
|
9142
9326
|
}
|
|
9143
9327
|
const result = await runFormSpawn({
|
|
@@ -9425,22 +9609,22 @@ var cloudflare_default = app21;
|
|
|
9425
9609
|
|
|
9426
9610
|
// server/routes/admin/files.ts
|
|
9427
9611
|
import { createReadStream as createReadStream3 } from "fs";
|
|
9428
|
-
import { readdir as readdir2, readFile as
|
|
9429
|
-
import { realpathSync as
|
|
9430
|
-
import { basename as
|
|
9612
|
+
import { readdir as readdir2, readFile as readFile3, stat as stat3, mkdir as mkdir2, writeFile as writeFile3, unlink as unlink2 } from "fs/promises";
|
|
9613
|
+
import { realpathSync as realpathSync3 } from "fs";
|
|
9614
|
+
import { basename as basename3, dirname as dirname6, join as join10, resolve as resolve15, sep as sep2 } from "path";
|
|
9431
9615
|
import { Readable as Readable2 } from "stream";
|
|
9432
9616
|
|
|
9433
9617
|
// app/lib/data-path.ts
|
|
9434
|
-
import { realpathSync as
|
|
9435
|
-
import { resolve as
|
|
9436
|
-
var
|
|
9437
|
-
var DATA_ROOT =
|
|
9618
|
+
import { realpathSync as realpathSync2 } from "fs";
|
|
9619
|
+
import { resolve as resolve14, normalize, sep, relative } from "path";
|
|
9620
|
+
var PLATFORM_ROOT5 = process.env.MAXY_PLATFORM_ROOT ?? resolve14(process.cwd(), "../platform");
|
|
9621
|
+
var DATA_ROOT = resolve14(PLATFORM_ROOT5, "..", "data");
|
|
9438
9622
|
function resolveDataPath(raw) {
|
|
9439
9623
|
const cleaned = normalize("/" + (raw ?? "").replace(/\\/g, "/")).replace(/^\/+/, "");
|
|
9440
|
-
const absolute =
|
|
9624
|
+
const absolute = resolve14(DATA_ROOT, cleaned);
|
|
9441
9625
|
let dataRootReal;
|
|
9442
9626
|
try {
|
|
9443
|
-
dataRootReal =
|
|
9627
|
+
dataRootReal = realpathSync2(DATA_ROOT);
|
|
9444
9628
|
} catch (err) {
|
|
9445
9629
|
return {
|
|
9446
9630
|
ok: false,
|
|
@@ -9450,7 +9634,7 @@ function resolveDataPath(raw) {
|
|
|
9450
9634
|
}
|
|
9451
9635
|
let resolvedReal;
|
|
9452
9636
|
try {
|
|
9453
|
-
resolvedReal =
|
|
9637
|
+
resolvedReal = realpathSync2(absolute);
|
|
9454
9638
|
} catch (err) {
|
|
9455
9639
|
const code = err.code;
|
|
9456
9640
|
if (code === "ENOENT") {
|
|
@@ -9785,7 +9969,7 @@ async function cascadeDeleteDocument(params) {
|
|
|
9785
9969
|
var UUID_RE4 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
9786
9970
|
async function readMeta(absDir, baseName) {
|
|
9787
9971
|
try {
|
|
9788
|
-
const raw = await
|
|
9972
|
+
const raw = await readFile3(join10(absDir, `${baseName}.meta.json`), "utf8");
|
|
9789
9973
|
const parsed = JSON.parse(raw);
|
|
9790
9974
|
if (typeof parsed?.filename === "string") {
|
|
9791
9975
|
return { filename: parsed.filename, mimeType: typeof parsed.mimeType === "string" ? parsed.mimeType : void 0 };
|
|
@@ -9796,7 +9980,7 @@ async function readMeta(absDir, baseName) {
|
|
|
9796
9980
|
}
|
|
9797
9981
|
async function readAccountNames() {
|
|
9798
9982
|
const map = /* @__PURE__ */ new Map();
|
|
9799
|
-
const accountsDir =
|
|
9983
|
+
const accountsDir = resolve15(DATA_ROOT, "accounts");
|
|
9800
9984
|
let names;
|
|
9801
9985
|
try {
|
|
9802
9986
|
names = await readdir2(accountsDir);
|
|
@@ -9805,9 +9989,9 @@ async function readAccountNames() {
|
|
|
9805
9989
|
}
|
|
9806
9990
|
for (const name of names) {
|
|
9807
9991
|
if (!UUID_RE4.test(name)) continue;
|
|
9808
|
-
const configPath2 =
|
|
9992
|
+
const configPath2 = resolve15(accountsDir, name, "account.json");
|
|
9809
9993
|
try {
|
|
9810
|
-
const raw = await
|
|
9994
|
+
const raw = await readFile3(configPath2, "utf8");
|
|
9811
9995
|
const parsed = JSON.parse(raw);
|
|
9812
9996
|
if (typeof parsed?.name === "string" && parsed.name.length > 0) {
|
|
9813
9997
|
map.set(name, parsed.name);
|
|
@@ -9871,7 +10055,7 @@ app22.get("/", requireAdminSession, async (c) => {
|
|
|
9871
10055
|
}
|
|
9872
10056
|
const { absolute, relative: relPath2 } = resolution;
|
|
9873
10057
|
try {
|
|
9874
|
-
const info = await
|
|
10058
|
+
const info = await stat3(absolute);
|
|
9875
10059
|
if (!info.isDirectory()) {
|
|
9876
10060
|
return c.json({ error: "Path is not a directory \u2014 use /api/admin/files/download for files" }, 400);
|
|
9877
10061
|
}
|
|
@@ -9883,7 +10067,7 @@ app22.get("/", requireAdminSession, async (c) => {
|
|
|
9883
10067
|
}
|
|
9884
10068
|
try {
|
|
9885
10069
|
const entryPath = join10(absolute, name);
|
|
9886
|
-
const s = await
|
|
10070
|
+
const s = await stat3(entryPath);
|
|
9887
10071
|
entries.push({
|
|
9888
10072
|
name,
|
|
9889
10073
|
kind: s.isDirectory() ? "directory" : s.isFile() ? "file" : "other",
|
|
@@ -9933,11 +10117,11 @@ app22.get("/download", requireAdminSession, async (c) => {
|
|
|
9933
10117
|
}
|
|
9934
10118
|
const { absolute, relative: relPath2 } = resolution;
|
|
9935
10119
|
try {
|
|
9936
|
-
const info = await
|
|
10120
|
+
const info = await stat3(absolute);
|
|
9937
10121
|
if (!info.isFile()) {
|
|
9938
10122
|
return c.json({ error: "Path is not a file" }, 400);
|
|
9939
10123
|
}
|
|
9940
|
-
const filename =
|
|
10124
|
+
const filename = basename3(absolute);
|
|
9941
10125
|
const mimeType = detectMimeType(absolute);
|
|
9942
10126
|
const nodeStream = createReadStream3(absolute);
|
|
9943
10127
|
const webStream = Readable2.toWeb(nodeStream);
|
|
@@ -9994,20 +10178,20 @@ app22.post("/upload", requireAdminSession, async (c) => {
|
|
|
9994
10178
|
error: `Unsupported file type: "${file.type}". Supported: ${[...SUPPORTED_MIME_TYPES].join(", ")}.`
|
|
9995
10179
|
}, 422);
|
|
9996
10180
|
}
|
|
9997
|
-
const safeName =
|
|
10181
|
+
const safeName = basename3(file.name).replace(/[\0/\\]/g, "_");
|
|
9998
10182
|
const finalName = `${Date.now()}-${safeName}`;
|
|
9999
|
-
const destDir =
|
|
10000
|
-
const destPath =
|
|
10183
|
+
const destDir = resolve15(DATA_ROOT, "uploads", accountId);
|
|
10184
|
+
const destPath = resolve15(destDir, finalName);
|
|
10001
10185
|
try {
|
|
10002
|
-
await
|
|
10003
|
-
const dataRootReal =
|
|
10004
|
-
const destDirReal =
|
|
10186
|
+
await mkdir2(destDir, { recursive: true });
|
|
10187
|
+
const dataRootReal = realpathSync3(DATA_ROOT);
|
|
10188
|
+
const destDirReal = realpathSync3(destDir);
|
|
10005
10189
|
if (destDirReal !== dataRootReal && !destDirReal.startsWith(dataRootReal + sep2)) {
|
|
10006
10190
|
console.error(`[data] path-traversal-blocked requested="uploads/${accountId}" resolved="${destDirReal}"`);
|
|
10007
10191
|
return c.json({ error: "Upload destination escapes DATA_ROOT" }, 403);
|
|
10008
10192
|
}
|
|
10009
10193
|
const buffer = Buffer.from(await file.arrayBuffer());
|
|
10010
|
-
await
|
|
10194
|
+
await writeFile3(destPath, buffer);
|
|
10011
10195
|
} catch (err) {
|
|
10012
10196
|
const message = err instanceof Error ? err.message : String(err);
|
|
10013
10197
|
console.error(`[data] file-upload error filename="${safeName}" err="${message}"`);
|
|
@@ -10039,14 +10223,14 @@ app22.delete("/", requireAdminSession, async (c) => {
|
|
|
10039
10223
|
return c.json({ error: resolution.error }, resolution.status);
|
|
10040
10224
|
}
|
|
10041
10225
|
const { absolute, relative: relPath2 } = resolution;
|
|
10042
|
-
const base =
|
|
10226
|
+
const base = basename3(absolute);
|
|
10043
10227
|
const segments = relPath2.split("/").filter(Boolean);
|
|
10044
10228
|
if (base === "account.json" || segments.includes(".git")) {
|
|
10045
10229
|
console.error(`[data] file-delete blocked path="${relPath2}" reason="protected"`);
|
|
10046
10230
|
return c.json({ error: "Protected file \u2014 refusing to delete" }, 403);
|
|
10047
10231
|
}
|
|
10048
10232
|
try {
|
|
10049
|
-
const info = await
|
|
10233
|
+
const info = await stat3(absolute);
|
|
10050
10234
|
if (info.isDirectory()) {
|
|
10051
10235
|
return c.json({ error: "Directory deletion not supported" }, 400);
|
|
10052
10236
|
}
|
|
@@ -11675,9 +11859,9 @@ var adherence_default = app30;
|
|
|
11675
11859
|
|
|
11676
11860
|
// server/routes/admin/sidebar-artefacts.ts
|
|
11677
11861
|
import neo4j3 from "neo4j-driver";
|
|
11678
|
-
import { readFile as
|
|
11679
|
-
import { resolve as
|
|
11680
|
-
import { existsSync as
|
|
11862
|
+
import { readFile as readFile4, readdir as readdir3, stat as stat4 } from "fs/promises";
|
|
11863
|
+
import { resolve as resolve16, relative as relative2, isAbsolute } from "path";
|
|
11864
|
+
import { existsSync as existsSync19 } from "fs";
|
|
11681
11865
|
var LIMIT = 50;
|
|
11682
11866
|
var TEXT_MIME_PREFIXES = ["text/", "application/json", "application/markdown"];
|
|
11683
11867
|
var ADMIN_AGENT_FILES = ["IDENTITY.md", "SOUL.md", "KNOWLEDGE.md"];
|
|
@@ -11693,7 +11877,7 @@ app31.get("/", requireAdminSession, async (c) => {
|
|
|
11693
11877
|
if (docs === null) {
|
|
11694
11878
|
return c.json({ error: "Failed to load artefacts" }, 500);
|
|
11695
11879
|
}
|
|
11696
|
-
const accountDir =
|
|
11880
|
+
const accountDir = resolve16(ACCOUNTS_DIR, accountId);
|
|
11697
11881
|
const agents = await fetchAgentTemplateRows(accountDir);
|
|
11698
11882
|
const artefacts = [...docs, ...agents].sort(
|
|
11699
11883
|
(a, b) => (b.updatedAt ?? "").localeCompare(a.updatedAt ?? "")
|
|
@@ -11756,8 +11940,8 @@ async function readArtefactContent(accountId, attachmentId, mimeType, displayNam
|
|
|
11756
11940
|
logSkip(displayName, "non-text-mime", mimeType);
|
|
11757
11941
|
return { content: "", skipReason: "non-text-mime" };
|
|
11758
11942
|
}
|
|
11759
|
-
const accountDir =
|
|
11760
|
-
const dir =
|
|
11943
|
+
const accountDir = resolve16(ATTACHMENTS_ROOT, accountId);
|
|
11944
|
+
const dir = resolve16(accountDir, attachmentId);
|
|
11761
11945
|
try {
|
|
11762
11946
|
validateFilePathInAccount(dir, accountDir);
|
|
11763
11947
|
} catch {
|
|
@@ -11771,7 +11955,7 @@ async function readArtefactContent(accountId, attachmentId, mimeType, displayNam
|
|
|
11771
11955
|
logSkip(displayName, "missing-on-disk", mimeType);
|
|
11772
11956
|
return { content: "", skipReason: "missing-on-disk" };
|
|
11773
11957
|
}
|
|
11774
|
-
return { content: await
|
|
11958
|
+
return { content: await readFile4(resolve16(dir, dataFile), "utf-8"), skipReason: null };
|
|
11775
11959
|
} catch (err) {
|
|
11776
11960
|
const message = err instanceof Error ? err.message : String(err);
|
|
11777
11961
|
console.error(`[admin/sidebar-artefacts] read-failed attachmentId=${attachmentId.slice(0, 8)} error="${message}"`);
|
|
@@ -11787,8 +11971,8 @@ function logSkip(name, reason, mimeType) {
|
|
|
11787
11971
|
async function fetchAgentTemplateRows(accountDir) {
|
|
11788
11972
|
const rows = [];
|
|
11789
11973
|
for (const filename of ADMIN_AGENT_FILES) {
|
|
11790
|
-
const overridePath =
|
|
11791
|
-
const bundledPath =
|
|
11974
|
+
const overridePath = resolve16(accountDir, "agents", "admin", filename);
|
|
11975
|
+
const bundledPath = resolve16(PLATFORM_ROOT, "templates", "agents", "admin", filename);
|
|
11792
11976
|
const labelStem = filename.replace(/\.md$/, "");
|
|
11793
11977
|
const row = await readAgentTemplateRow({
|
|
11794
11978
|
id: `agent-template:admin:${filename}`,
|
|
@@ -11802,12 +11986,12 @@ async function fetchAgentTemplateRows(accountDir) {
|
|
|
11802
11986
|
});
|
|
11803
11987
|
if (row) rows.push(row);
|
|
11804
11988
|
}
|
|
11805
|
-
const overrideDir =
|
|
11806
|
-
const bundledDir =
|
|
11989
|
+
const overrideDir = resolve16(accountDir, "specialists", "agents");
|
|
11990
|
+
const bundledDir = resolve16(PLATFORM_ROOT, "templates", "specialists", "agents");
|
|
11807
11991
|
const specialistNames = await unionSpecialistFilenames(overrideDir, bundledDir);
|
|
11808
11992
|
for (const filename of specialistNames) {
|
|
11809
|
-
const overridePath =
|
|
11810
|
-
const bundledPath =
|
|
11993
|
+
const overridePath = resolve16(overrideDir, filename);
|
|
11994
|
+
const bundledPath = resolve16(bundledDir, filename);
|
|
11811
11995
|
const row = await readAgentTemplateRow({
|
|
11812
11996
|
id: `agent-template:specialist:${filename}`,
|
|
11813
11997
|
displayName: filename.replace(/\.md$/, ""),
|
|
@@ -11825,7 +12009,7 @@ async function fetchAgentTemplateRows(accountDir) {
|
|
|
11825
12009
|
async function unionSpecialistFilenames(overrideDir, bundledDir) {
|
|
11826
12010
|
const names = /* @__PURE__ */ new Set();
|
|
11827
12011
|
for (const dir of [overrideDir, bundledDir]) {
|
|
11828
|
-
if (!
|
|
12012
|
+
if (!existsSync19(dir)) continue;
|
|
11829
12013
|
try {
|
|
11830
12014
|
const entries = await readdir3(dir);
|
|
11831
12015
|
for (const entry of entries) {
|
|
@@ -11840,7 +12024,7 @@ async function unionSpecialistFilenames(overrideDir, bundledDir) {
|
|
|
11840
12024
|
}
|
|
11841
12025
|
async function readAgentTemplateRow(inp) {
|
|
11842
12026
|
let chosenPath = null;
|
|
11843
|
-
if (
|
|
12027
|
+
if (existsSync19(inp.overridePath)) {
|
|
11844
12028
|
try {
|
|
11845
12029
|
validateFilePathInAccount(inp.overridePath, inp.overrideRoot);
|
|
11846
12030
|
chosenPath = inp.overridePath;
|
|
@@ -11851,7 +12035,7 @@ async function readAgentTemplateRow(inp) {
|
|
|
11851
12035
|
);
|
|
11852
12036
|
return null;
|
|
11853
12037
|
}
|
|
11854
|
-
} else if (
|
|
12038
|
+
} else if (existsSync19(inp.bundledPath)) {
|
|
11855
12039
|
if (!isWithin(inp.bundledPath, inp.bundledRoot)) {
|
|
11856
12040
|
console.error(
|
|
11857
12041
|
`[admin/sidebar-artefacts] agent-template-read-failed agent=${inp.displayName} kind=${inp.logName} error="bundled path outside PLATFORM_ROOT"`
|
|
@@ -11863,8 +12047,8 @@ async function readAgentTemplateRow(inp) {
|
|
|
11863
12047
|
if (!chosenPath) return null;
|
|
11864
12048
|
try {
|
|
11865
12049
|
const [content, st] = await Promise.all([
|
|
11866
|
-
|
|
11867
|
-
|
|
12050
|
+
readFile4(chosenPath, "utf-8"),
|
|
12051
|
+
stat4(chosenPath)
|
|
11868
12052
|
]);
|
|
11869
12053
|
return {
|
|
11870
12054
|
id: inp.id,
|
|
@@ -11891,9 +12075,9 @@ function isWithin(target, root) {
|
|
|
11891
12075
|
var sidebar_artefacts_default = app31;
|
|
11892
12076
|
|
|
11893
12077
|
// server/routes/admin/sidebar-artefact-save.ts
|
|
11894
|
-
import { mkdir as
|
|
11895
|
-
import { resolve as
|
|
11896
|
-
import { existsSync as
|
|
12078
|
+
import { mkdir as mkdir3, readdir as readdir4, stat as stat5, writeFile as writeFile4 } from "fs/promises";
|
|
12079
|
+
import { resolve as resolve17 } from "path";
|
|
12080
|
+
import { existsSync as existsSync20 } from "fs";
|
|
11897
12081
|
var ADMIN_AGENT_FILES2 = /* @__PURE__ */ new Set(["IDENTITY.md", "SOUL.md", "KNOWLEDGE.md"]);
|
|
11898
12082
|
var UUID_RE5 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
|
|
11899
12083
|
var app32 = new Hono();
|
|
@@ -11905,7 +12089,7 @@ app32.post("/", requireAdminSession, async (c) => {
|
|
|
11905
12089
|
if (!body || typeof body.id !== "string" || typeof body.content !== "string") {
|
|
11906
12090
|
return c.json({ error: "id and content required" }, 400);
|
|
11907
12091
|
}
|
|
11908
|
-
const accountDir =
|
|
12092
|
+
const accountDir = resolve17(ACCOUNTS_DIR, accountId);
|
|
11909
12093
|
const resolved = await resolveSavePath(body.id, accountId, accountDir);
|
|
11910
12094
|
if (resolved.kind === "reject") {
|
|
11911
12095
|
console.error(
|
|
@@ -11915,7 +12099,7 @@ app32.post("/", requireAdminSession, async (c) => {
|
|
|
11915
12099
|
}
|
|
11916
12100
|
const start = Date.now();
|
|
11917
12101
|
try {
|
|
11918
|
-
await
|
|
12102
|
+
await writeFile4(resolved.path, body.content, "utf-8");
|
|
11919
12103
|
} catch (err) {
|
|
11920
12104
|
const message = err instanceof Error ? err.message : String(err);
|
|
11921
12105
|
console.error(
|
|
@@ -11925,7 +12109,7 @@ app32.post("/", requireAdminSession, async (c) => {
|
|
|
11925
12109
|
}
|
|
11926
12110
|
let updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
11927
12111
|
try {
|
|
11928
|
-
const st = await
|
|
12112
|
+
const st = await stat5(resolved.path);
|
|
11929
12113
|
updatedAt = st.mtime.toISOString();
|
|
11930
12114
|
} catch {
|
|
11931
12115
|
}
|
|
@@ -11946,22 +12130,22 @@ async function resolveSavePath(id, accountId, accountDir) {
|
|
|
11946
12130
|
if (role !== "admin" || !ADMIN_AGENT_FILES2.has(filename)) {
|
|
11947
12131
|
return { kind: "reject", status: 400, reason: "invalid-id" };
|
|
11948
12132
|
}
|
|
11949
|
-
const parent =
|
|
11950
|
-
await
|
|
12133
|
+
const parent = resolve17(accountDir, "agents", "admin");
|
|
12134
|
+
await mkdir3(parent, { recursive: true });
|
|
11951
12135
|
try {
|
|
11952
12136
|
validateFilePathInAccount(parent, accountDir);
|
|
11953
12137
|
} catch {
|
|
11954
12138
|
return { kind: "reject", status: 400, reason: "containment-rejected" };
|
|
11955
12139
|
}
|
|
11956
|
-
return { kind: "admin-template", path:
|
|
12140
|
+
return { kind: "admin-template", path: resolve17(parent, filename) };
|
|
11957
12141
|
}
|
|
11958
12142
|
if (UUID_RE5.test(id)) {
|
|
11959
|
-
const dir =
|
|
11960
|
-
if (!
|
|
12143
|
+
const dir = resolve17(ATTACHMENTS_ROOT, accountId, id);
|
|
12144
|
+
if (!existsSync20(dir)) {
|
|
11961
12145
|
return { kind: "reject", status: 400, reason: "not-found" };
|
|
11962
12146
|
}
|
|
11963
12147
|
try {
|
|
11964
|
-
validateFilePathInAccount(dir,
|
|
12148
|
+
validateFilePathInAccount(dir, resolve17(ATTACHMENTS_ROOT, accountId));
|
|
11965
12149
|
} catch {
|
|
11966
12150
|
return { kind: "reject", status: 400, reason: "containment-rejected" };
|
|
11967
12151
|
}
|
|
@@ -11970,7 +12154,7 @@ async function resolveSavePath(id, accountId, accountDir) {
|
|
|
11970
12154
|
if (!dataFile) {
|
|
11971
12155
|
return { kind: "reject", status: 400, reason: "not-found" };
|
|
11972
12156
|
}
|
|
11973
|
-
return { kind: "knowledge-doc", path:
|
|
12157
|
+
return { kind: "knowledge-doc", path: resolve17(dir, dataFile) };
|
|
11974
12158
|
}
|
|
11975
12159
|
return { kind: "reject", status: 400, reason: "invalid-id" };
|
|
11976
12160
|
}
|
|
@@ -11980,9 +12164,9 @@ function relPath(absPath, root) {
|
|
|
11980
12164
|
var sidebar_artefact_save_default = app32;
|
|
11981
12165
|
|
|
11982
12166
|
// server/routes/admin/sidebar-artefact-content.ts
|
|
11983
|
-
import { readFile as
|
|
11984
|
-
import { existsSync as
|
|
11985
|
-
import { resolve as
|
|
12167
|
+
import { readFile as readFile5, readdir as readdir5 } from "fs/promises";
|
|
12168
|
+
import { existsSync as existsSync21 } from "fs";
|
|
12169
|
+
import { resolve as resolve18 } from "path";
|
|
11986
12170
|
var UUID_RE6 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
|
|
11987
12171
|
var app33 = new Hono();
|
|
11988
12172
|
app33.get("/", requireAdminSession, async (c) => {
|
|
@@ -11994,14 +12178,14 @@ app33.get("/", requireAdminSession, async (c) => {
|
|
|
11994
12178
|
console.error(`[admin/sidebar-artefact-content] not-found id=${id.slice(0, 8)}`);
|
|
11995
12179
|
return new Response("Not found", { status: 404 });
|
|
11996
12180
|
}
|
|
11997
|
-
const dir =
|
|
11998
|
-
if (!
|
|
12181
|
+
const dir = resolve18(ATTACHMENTS_ROOT, accountId, id);
|
|
12182
|
+
if (!existsSync21(dir)) {
|
|
11999
12183
|
console.error(`[admin/sidebar-artefact-content] not-found id=${id.slice(0, 8)}`);
|
|
12000
12184
|
return new Response("Not found", { status: 404 });
|
|
12001
12185
|
}
|
|
12002
12186
|
let meta;
|
|
12003
12187
|
try {
|
|
12004
|
-
meta = JSON.parse(await
|
|
12188
|
+
meta = JSON.parse(await readFile5(resolve18(dir, `${id}.meta.json`), "utf-8"));
|
|
12005
12189
|
} catch {
|
|
12006
12190
|
console.error(`[admin/sidebar-artefact-content] not-found id=${id.slice(0, 8)}`);
|
|
12007
12191
|
return new Response("Not found", { status: 404 });
|
|
@@ -12013,7 +12197,7 @@ app33.get("/", requireAdminSession, async (c) => {
|
|
|
12013
12197
|
return new Response("Not found", { status: 404 });
|
|
12014
12198
|
}
|
|
12015
12199
|
const start = Date.now();
|
|
12016
|
-
const buffer = await
|
|
12200
|
+
const buffer = await readFile5(resolve18(dir, dataFile));
|
|
12017
12201
|
const ms = Date.now() - start;
|
|
12018
12202
|
console.log(
|
|
12019
12203
|
`[admin/sidebar-artefact-content] account=${accountId} id=${id.slice(0, 8)} mime=${meta.mimeType} bytes=${buffer.length} ms=${ms}`
|
|
@@ -12029,24 +12213,24 @@ app33.get("/", requireAdminSession, async (c) => {
|
|
|
12029
12213
|
var sidebar_artefact_content_default = app33;
|
|
12030
12214
|
|
|
12031
12215
|
// server/routes/admin/health.ts
|
|
12032
|
-
import { existsSync as
|
|
12033
|
-
import { resolve as
|
|
12034
|
-
var
|
|
12216
|
+
import { existsSync as existsSync22, readFileSync as readFileSync16 } from "fs";
|
|
12217
|
+
import { resolve as resolve19, join as join11 } from "path";
|
|
12218
|
+
var PLATFORM_ROOT6 = process.env.MAXY_PLATFORM_ROOT ?? resolve19(process.cwd(), "..");
|
|
12035
12219
|
var brandHostname = "maxy";
|
|
12036
|
-
var brandJsonPath = join11(
|
|
12037
|
-
if (
|
|
12220
|
+
var brandJsonPath = join11(PLATFORM_ROOT6, "config", "brand.json");
|
|
12221
|
+
if (existsSync22(brandJsonPath)) {
|
|
12038
12222
|
try {
|
|
12039
|
-
const brand = JSON.parse(
|
|
12223
|
+
const brand = JSON.parse(readFileSync16(brandJsonPath, "utf-8"));
|
|
12040
12224
|
if (brand.hostname) brandHostname = brand.hostname;
|
|
12041
12225
|
} catch {
|
|
12042
12226
|
}
|
|
12043
12227
|
}
|
|
12044
|
-
var VERSION_FILE =
|
|
12228
|
+
var VERSION_FILE = resolve19(PLATFORM_ROOT6, `config/.${brandHostname}-version`);
|
|
12045
12229
|
var PROCESS_STARTED_AT = (/* @__PURE__ */ new Date()).toISOString();
|
|
12046
12230
|
var PROBE_TIMEOUT_MS = 1e3;
|
|
12047
12231
|
function readVersion() {
|
|
12048
|
-
if (!
|
|
12049
|
-
return
|
|
12232
|
+
if (!existsSync22(VERSION_FILE)) return "unknown";
|
|
12233
|
+
return readFileSync16(VERSION_FILE, "utf-8").trim() || "unknown";
|
|
12050
12234
|
}
|
|
12051
12235
|
async function probeConversationDb() {
|
|
12052
12236
|
let session;
|
|
@@ -12126,8 +12310,8 @@ app35.route("/health-brand", health_default2);
|
|
|
12126
12310
|
var admin_default = app35;
|
|
12127
12311
|
|
|
12128
12312
|
// server/routes/sites.ts
|
|
12129
|
-
import { existsSync as
|
|
12130
|
-
import { resolve as
|
|
12313
|
+
import { existsSync as existsSync23, readFileSync as readFileSync17, realpathSync as realpathSync4, statSync as statSync7 } from "fs";
|
|
12314
|
+
import { resolve as resolve20 } from "path";
|
|
12131
12315
|
var SAFE_SEG_RE = /^[a-z0-9_][a-z0-9_.-]{0,99}$/i;
|
|
12132
12316
|
var MIME = {
|
|
12133
12317
|
".html": "text/html; charset=utf-8",
|
|
@@ -12184,19 +12368,19 @@ app36.get("/:rel{.*}", (c) => {
|
|
|
12184
12368
|
}
|
|
12185
12369
|
segments.push(seg);
|
|
12186
12370
|
}
|
|
12187
|
-
const rootDir =
|
|
12188
|
-
let filePath = segments.length === 0 ? rootDir :
|
|
12371
|
+
const rootDir = resolve20(account.accountDir, "sites");
|
|
12372
|
+
let filePath = segments.length === 0 ? rootDir : resolve20(rootDir, ...segments);
|
|
12189
12373
|
if (filePath !== rootDir && !filePath.startsWith(rootDir + "/")) {
|
|
12190
12374
|
console.error(`[sites] path-traversal-rejected path=${reqPath} reason=escape status=403`);
|
|
12191
12375
|
return c.text("Forbidden", 403);
|
|
12192
12376
|
}
|
|
12193
|
-
let
|
|
12377
|
+
let stat6;
|
|
12194
12378
|
try {
|
|
12195
|
-
|
|
12379
|
+
stat6 = existsSync23(filePath) ? statSync7(filePath) : null;
|
|
12196
12380
|
} catch {
|
|
12197
|
-
|
|
12381
|
+
stat6 = null;
|
|
12198
12382
|
}
|
|
12199
|
-
if (
|
|
12383
|
+
if (stat6?.isDirectory() && !reqPath.endsWith("/")) {
|
|
12200
12384
|
const search = new URL(c.req.url).search;
|
|
12201
12385
|
const target = `${reqPath}/${search}`;
|
|
12202
12386
|
console.log(
|
|
@@ -12204,22 +12388,22 @@ app36.get("/:rel{.*}", (c) => {
|
|
|
12204
12388
|
);
|
|
12205
12389
|
return c.redirect(target, 301);
|
|
12206
12390
|
}
|
|
12207
|
-
if (
|
|
12208
|
-
filePath =
|
|
12391
|
+
if (stat6?.isDirectory()) {
|
|
12392
|
+
filePath = resolve20(filePath, "index.html");
|
|
12209
12393
|
}
|
|
12210
12394
|
if (!filePath.startsWith(rootDir + "/")) {
|
|
12211
12395
|
console.error(`[sites] path-traversal-rejected path=${reqPath} reason=escape status=403`);
|
|
12212
12396
|
return c.text("Forbidden", 403);
|
|
12213
12397
|
}
|
|
12214
|
-
if (!
|
|
12398
|
+
if (!existsSync23(filePath)) {
|
|
12215
12399
|
console.error(`[sites] not-found path=${reqPath} status=404`);
|
|
12216
12400
|
return c.text("Not found", 404);
|
|
12217
12401
|
}
|
|
12218
12402
|
let realPath;
|
|
12219
12403
|
let realRoot;
|
|
12220
12404
|
try {
|
|
12221
|
-
realPath =
|
|
12222
|
-
realRoot =
|
|
12405
|
+
realPath = realpathSync4(filePath);
|
|
12406
|
+
realRoot = realpathSync4(rootDir);
|
|
12223
12407
|
} catch {
|
|
12224
12408
|
console.error(`[sites] not-found path=${reqPath} status=404`);
|
|
12225
12409
|
return c.text("Not found", 404);
|
|
@@ -12230,7 +12414,7 @@ app36.get("/:rel{.*}", (c) => {
|
|
|
12230
12414
|
}
|
|
12231
12415
|
let body;
|
|
12232
12416
|
try {
|
|
12233
|
-
body =
|
|
12417
|
+
body = readFileSync17(realPath);
|
|
12234
12418
|
} catch (err) {
|
|
12235
12419
|
const code = err?.code;
|
|
12236
12420
|
if (code === "EISDIR") {
|
|
@@ -12361,15 +12545,15 @@ function clientFrom(c) {
|
|
|
12361
12545
|
c.req.header("x-forwarded-for")
|
|
12362
12546
|
);
|
|
12363
12547
|
}
|
|
12364
|
-
var
|
|
12365
|
-
var BRAND_JSON_PATH =
|
|
12548
|
+
var PLATFORM_ROOT7 = process.env.MAXY_PLATFORM_ROOT || "";
|
|
12549
|
+
var BRAND_JSON_PATH = PLATFORM_ROOT7 ? join12(PLATFORM_ROOT7, "config", "brand.json") : "";
|
|
12366
12550
|
var BRAND = { productName: "Maxy", hostname: "maxy", configDir: ".maxy", domain: "getmaxy.com" };
|
|
12367
|
-
if (BRAND_JSON_PATH && !
|
|
12551
|
+
if (BRAND_JSON_PATH && !existsSync24(BRAND_JSON_PATH)) {
|
|
12368
12552
|
console.error(`[brand] WARNING: brand.json not found at ${BRAND_JSON_PATH} \u2014 using Maxy defaults`);
|
|
12369
12553
|
}
|
|
12370
|
-
if (BRAND_JSON_PATH &&
|
|
12554
|
+
if (BRAND_JSON_PATH && existsSync24(BRAND_JSON_PATH)) {
|
|
12371
12555
|
try {
|
|
12372
|
-
const parsed = JSON.parse(
|
|
12556
|
+
const parsed = JSON.parse(readFileSync18(BRAND_JSON_PATH, "utf-8"));
|
|
12373
12557
|
BRAND = { ...BRAND, ...parsed };
|
|
12374
12558
|
} catch (err) {
|
|
12375
12559
|
console.error(`[brand] Failed to parse brand.json: ${err.message}`);
|
|
@@ -12388,11 +12572,11 @@ var brandLoginOpts = {
|
|
|
12388
12572
|
bodyFont: BRAND.defaultFonts?.body,
|
|
12389
12573
|
logoContainsName: !!BRAND.logoContainsName
|
|
12390
12574
|
};
|
|
12391
|
-
var ALIAS_DOMAINS_PATH2 = join12(
|
|
12575
|
+
var ALIAS_DOMAINS_PATH2 = join12(homedir3(), BRAND.configDir, "alias-domains.json");
|
|
12392
12576
|
function loadAliasDomains() {
|
|
12393
12577
|
try {
|
|
12394
|
-
if (!
|
|
12395
|
-
const parsed = JSON.parse(
|
|
12578
|
+
if (!existsSync24(ALIAS_DOMAINS_PATH2)) return null;
|
|
12579
|
+
const parsed = JSON.parse(readFileSync18(ALIAS_DOMAINS_PATH2, "utf-8"));
|
|
12396
12580
|
if (!Array.isArray(parsed)) {
|
|
12397
12581
|
console.error("[alias-domains] malformed alias-domains.json \u2014 expected array");
|
|
12398
12582
|
return null;
|
|
@@ -12732,20 +12916,20 @@ app37.get("/agent-assets/:slug/:filename", (c) => {
|
|
|
12732
12916
|
console.error(`[agent-assets] no-account slug=${slug} file=${filename}`);
|
|
12733
12917
|
return c.text("Not found", 404);
|
|
12734
12918
|
}
|
|
12735
|
-
const filePath =
|
|
12736
|
-
const expectedDir =
|
|
12919
|
+
const filePath = resolve21(account.accountDir, "agents", slug, "assets", filename);
|
|
12920
|
+
const expectedDir = resolve21(account.accountDir, "agents", slug, "assets");
|
|
12737
12921
|
if (!filePath.startsWith(expectedDir + "/")) {
|
|
12738
12922
|
console.error(`[agent-assets] path-traversal-rejected slug=${slug} file=${filename}`);
|
|
12739
12923
|
return c.text("Forbidden", 403);
|
|
12740
12924
|
}
|
|
12741
|
-
if (!
|
|
12925
|
+
if (!existsSync24(filePath)) {
|
|
12742
12926
|
console.error(`[agent-assets] serve slug=${slug} file=${filename} status=404`);
|
|
12743
12927
|
return c.text("Not found", 404);
|
|
12744
12928
|
}
|
|
12745
12929
|
const ext = "." + filename.split(".").pop()?.toLowerCase();
|
|
12746
12930
|
const contentType = IMAGE_MIME[ext] || "application/octet-stream";
|
|
12747
12931
|
console.log(`[agent-assets] serve slug=${slug} file=${filename} status=200`);
|
|
12748
|
-
const body =
|
|
12932
|
+
const body = readFileSync18(filePath);
|
|
12749
12933
|
return c.body(body, 200, {
|
|
12750
12934
|
"Content-Type": contentType,
|
|
12751
12935
|
"Cache-Control": "public, max-age=3600"
|
|
@@ -12762,20 +12946,20 @@ app37.get("/generated/:filename", (c) => {
|
|
|
12762
12946
|
console.error(`[generated] serve file=${filename} status=404`);
|
|
12763
12947
|
return c.text("Not found", 404);
|
|
12764
12948
|
}
|
|
12765
|
-
const filePath =
|
|
12766
|
-
const expectedDir =
|
|
12949
|
+
const filePath = resolve21(account.accountDir, "generated", filename);
|
|
12950
|
+
const expectedDir = resolve21(account.accountDir, "generated");
|
|
12767
12951
|
if (!filePath.startsWith(expectedDir + "/")) {
|
|
12768
12952
|
console.error(`[generated] serve file=${filename} status=403`);
|
|
12769
12953
|
return c.text("Forbidden", 403);
|
|
12770
12954
|
}
|
|
12771
|
-
if (!
|
|
12955
|
+
if (!existsSync24(filePath)) {
|
|
12772
12956
|
console.error(`[generated] serve file=${filename} status=404`);
|
|
12773
12957
|
return c.text("Not found", 404);
|
|
12774
12958
|
}
|
|
12775
12959
|
const ext = "." + filename.split(".").pop()?.toLowerCase();
|
|
12776
12960
|
const contentType = IMAGE_MIME[ext] || "application/octet-stream";
|
|
12777
12961
|
console.log(`[generated] serve file=${filename} status=200`);
|
|
12778
|
-
const body =
|
|
12962
|
+
const body = readFileSync18(filePath);
|
|
12779
12963
|
return c.body(body, 200, {
|
|
12780
12964
|
"Content-Type": contentType,
|
|
12781
12965
|
"Cache-Control": "public, max-age=86400"
|
|
@@ -12785,9 +12969,9 @@ app37.route("/sites", sites_default);
|
|
|
12785
12969
|
var htmlCache = /* @__PURE__ */ new Map();
|
|
12786
12970
|
var brandLogoPath = "/brand/maxy-monochrome.png";
|
|
12787
12971
|
var brandIconPath = "/brand/maxy-monochrome.png";
|
|
12788
|
-
if (BRAND_JSON_PATH &&
|
|
12972
|
+
if (BRAND_JSON_PATH && existsSync24(BRAND_JSON_PATH)) {
|
|
12789
12973
|
try {
|
|
12790
|
-
const fullBrand = JSON.parse(
|
|
12974
|
+
const fullBrand = JSON.parse(readFileSync18(BRAND_JSON_PATH, "utf-8"));
|
|
12791
12975
|
if (fullBrand.assets?.logo) brandLogoPath = `/brand/${fullBrand.assets.logo}`;
|
|
12792
12976
|
brandIconPath = fullBrand.assets?.icon ? `/brand/${fullBrand.assets.icon}` : brandLogoPath;
|
|
12793
12977
|
} catch {
|
|
@@ -12803,10 +12987,10 @@ var brandScript = `<script>window.__BRAND__=${JSON.stringify({
|
|
|
12803
12987
|
})}</script>`;
|
|
12804
12988
|
function readInstalledVersion() {
|
|
12805
12989
|
try {
|
|
12806
|
-
if (!
|
|
12807
|
-
const versionFile = join12(
|
|
12808
|
-
if (!
|
|
12809
|
-
const content =
|
|
12990
|
+
if (!PLATFORM_ROOT7) return "unknown";
|
|
12991
|
+
const versionFile = join12(PLATFORM_ROOT7, "config", `.${BRAND.hostname}-version`);
|
|
12992
|
+
if (!existsSync24(versionFile)) return "unknown";
|
|
12993
|
+
const content = readFileSync18(versionFile, "utf-8").trim();
|
|
12810
12994
|
return content || "unknown";
|
|
12811
12995
|
} catch {
|
|
12812
12996
|
return "unknown";
|
|
@@ -12847,7 +13031,7 @@ var clientErrorReporterScript = `<script>
|
|
|
12847
13031
|
function cachedHtml(file) {
|
|
12848
13032
|
let html = htmlCache.get(file);
|
|
12849
13033
|
if (!html) {
|
|
12850
|
-
html =
|
|
13034
|
+
html = readFileSync18(resolve21(process.cwd(), "public", file), "utf-8");
|
|
12851
13035
|
const productNameEsc = escapeHtml(BRAND.productName);
|
|
12852
13036
|
html = html.replace(/<title>([^<]*)<\/title>/, (_match, inner) => `<title>${escapeHtml(inner).replace(/Maxy/g, productNameEsc)}</title>`);
|
|
12853
13037
|
html = html.replace('href="/favicon.ico"', `href="${escapeHtml(brandFaviconPath)}"`);
|
|
@@ -12863,26 +13047,26 @@ ${clientErrorReporterScript}
|
|
|
12863
13047
|
}
|
|
12864
13048
|
var brandedHtmlCache = /* @__PURE__ */ new Map();
|
|
12865
13049
|
function loadBrandingCache(agentSlug) {
|
|
12866
|
-
const configDir2 = join12(
|
|
13050
|
+
const configDir2 = join12(homedir3(), BRAND.configDir);
|
|
12867
13051
|
try {
|
|
12868
13052
|
const accountJsonPath = join12(configDir2, "account.json");
|
|
12869
|
-
if (!
|
|
12870
|
-
const account = JSON.parse(
|
|
13053
|
+
if (!existsSync24(accountJsonPath)) return null;
|
|
13054
|
+
const account = JSON.parse(readFileSync18(accountJsonPath, "utf-8"));
|
|
12871
13055
|
const accountId = account.accountId;
|
|
12872
13056
|
if (!accountId) return null;
|
|
12873
13057
|
const cachePath = join12(configDir2, "branding-cache", accountId, `${agentSlug}.json`);
|
|
12874
|
-
if (!
|
|
12875
|
-
return JSON.parse(
|
|
13058
|
+
if (!existsSync24(cachePath)) return null;
|
|
13059
|
+
return JSON.parse(readFileSync18(cachePath, "utf-8"));
|
|
12876
13060
|
} catch {
|
|
12877
13061
|
return null;
|
|
12878
13062
|
}
|
|
12879
13063
|
}
|
|
12880
13064
|
function resolveDefaultSlug() {
|
|
12881
13065
|
try {
|
|
12882
|
-
const configDir2 = join12(
|
|
13066
|
+
const configDir2 = join12(homedir3(), BRAND.configDir);
|
|
12883
13067
|
const accountJsonPath = join12(configDir2, "account.json");
|
|
12884
|
-
if (!
|
|
12885
|
-
const account = JSON.parse(
|
|
13068
|
+
if (!existsSync24(accountJsonPath)) return null;
|
|
13069
|
+
const account = JSON.parse(readFileSync18(accountJsonPath, "utf-8"));
|
|
12886
13070
|
return account.defaultAgent || null;
|
|
12887
13071
|
} catch {
|
|
12888
13072
|
return null;
|
|
@@ -12955,7 +13139,7 @@ app37.use("/vnc-popout.html", logViewerFetch);
|
|
|
12955
13139
|
app37.get("/vnc-popout.html", (c) => {
|
|
12956
13140
|
let html = htmlCache.get("vnc-popout.html");
|
|
12957
13141
|
if (!html) {
|
|
12958
|
-
html =
|
|
13142
|
+
html = readFileSync18(resolve21(process.cwd(), "public", "vnc-popout.html"), "utf-8");
|
|
12959
13143
|
const name = escapeHtml(BRAND.productName);
|
|
12960
13144
|
html = html.replace("<title>Browser \u2014 Maxy</title>", `<title>${name}</title>`);
|
|
12961
13145
|
html = html.replace("</head>", ` ${brandScript}
|
|
@@ -13061,8 +13245,8 @@ try {
|
|
|
13061
13245
|
(async () => {
|
|
13062
13246
|
try {
|
|
13063
13247
|
let userId = "";
|
|
13064
|
-
if (
|
|
13065
|
-
const users = JSON.parse(
|
|
13248
|
+
if (existsSync24(USERS_FILE)) {
|
|
13249
|
+
const users = JSON.parse(readFileSync18(USERS_FILE, "utf-8").trim() || "[]");
|
|
13066
13250
|
userId = users[0]?.userId ?? "";
|
|
13067
13251
|
}
|
|
13068
13252
|
await backfillNullUserIdConversations(userId);
|
|
@@ -13072,8 +13256,8 @@ try {
|
|
|
13072
13256
|
})();
|
|
13073
13257
|
(async () => {
|
|
13074
13258
|
try {
|
|
13075
|
-
if (!
|
|
13076
|
-
const usersRaw =
|
|
13259
|
+
if (!existsSync24(USERS_FILE)) return;
|
|
13260
|
+
const usersRaw = readFileSync18(USERS_FILE, "utf-8").trim();
|
|
13077
13261
|
if (!usersRaw) return;
|
|
13078
13262
|
const users = JSON.parse(usersRaw);
|
|
13079
13263
|
const userId = users[0]?.userId;
|
|
@@ -13095,7 +13279,7 @@ try {
|
|
|
13095
13279
|
} catch (err) {
|
|
13096
13280
|
console.error(`[graph-health] account-enumeration unavailable reason=${err instanceof Error ? err.message : String(err)}`);
|
|
13097
13281
|
}
|
|
13098
|
-
var configDirForWhatsApp =
|
|
13282
|
+
var configDirForWhatsApp = basename4(MAXY_DIR) || ".maxy";
|
|
13099
13283
|
var bootAccount = resolveAccount();
|
|
13100
13284
|
var bootAccountConfig = bootAccount?.config;
|
|
13101
13285
|
var bootPublicAgent = bootAccount ? resolvePublicAgent(bootAccount.accountDir, { accountId: bootAccount.accountId })?.slug ?? null : null;
|
|
@@ -13112,7 +13296,7 @@ autoDeliverPremiumPlugins(bootEntitlement?.purchasedPlugins ?? void 0);
|
|
|
13112
13296
|
(async () => {
|
|
13113
13297
|
if (!bootAccount) return;
|
|
13114
13298
|
try {
|
|
13115
|
-
const { recoverRunningCloudflareTasks } = await import("./cloudflare-task-tracker-
|
|
13299
|
+
const { recoverRunningCloudflareTasks } = await import("./cloudflare-task-tracker-LJ4SMK2D.js");
|
|
13116
13300
|
const result = await recoverRunningCloudflareTasks(
|
|
13117
13301
|
bootAccount.accountId,
|
|
13118
13302
|
configDirForWhatsApp,
|
|
@@ -13162,7 +13346,7 @@ if (bootAccountConfig?.whatsapp) {
|
|
|
13162
13346
|
}
|
|
13163
13347
|
init({
|
|
13164
13348
|
configDir: configDirForWhatsApp,
|
|
13165
|
-
platformRoot:
|
|
13349
|
+
platformRoot: resolve21(process.env.MAXY_PLATFORM_ROOT ?? join12(__dirname, "..")),
|
|
13166
13350
|
accountConfig: bootAccountConfig,
|
|
13167
13351
|
onMessage: async (msg) => {
|
|
13168
13352
|
try {
|