@rubytech/create-maxy-code 0.1.190 → 0.1.191
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/plugins/docs/references/admin-ui.md +16 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest-extract.d.ts +9 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest-extract.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest-extract.js +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest-extract.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.js +9 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.js.map +1 -1
- package/payload/platform/plugins/memory/skills/document-ingest/SKILL.md +2 -0
- package/payload/platform/services/claude-session-manager/dist/rc-daemon.d.ts +21 -0
- package/payload/platform/services/claude-session-manager/dist/rc-daemon.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/rc-daemon.js +106 -1
- package/payload/platform/services/claude-session-manager/dist/rc-daemon.js.map +1 -1
- package/payload/server/public/assets/{admin-Cv2A5vnQ.js → admin-DFxXLXfg.js} +1 -1
- package/payload/server/public/assets/{data-DIa-MKNj.js → data-DtYcqL1G.js} +1 -1
- package/payload/server/public/assets/{graph-Y5y3kw6J.js → graph-C-XPyGsO.js} +1 -1
- package/payload/server/public/assets/graph-labels-CZin3myX.js +1 -0
- package/payload/server/public/assets/{page-BCCqlOmj.js → page-CKlvbjKn.js} +1 -1
- package/payload/server/public/assets/{page-DwNvJOvr.js → page-DSj21hpG.js} +1 -1
- package/payload/server/public/data.html +3 -3
- package/payload/server/public/graph.html +3 -3
- package/payload/server/public/index.html +4 -4
- package/payload/server/server.js +263 -119
- package/payload/server/public/assets/graph-labels-BIRCh328.js +0 -1
package/payload/server/server.js
CHANGED
|
@@ -816,7 +816,7 @@ var serveStatic = (options = { root: "" }) => {
|
|
|
816
816
|
|
|
817
817
|
// server/index.ts
|
|
818
818
|
import { readFileSync as readFileSync20, existsSync as existsSync23, watchFile } from "fs";
|
|
819
|
-
import { resolve as
|
|
819
|
+
import { resolve as resolve24, join as join15, basename as basename5 } from "path";
|
|
820
820
|
import { homedir as homedir2 } from "os";
|
|
821
821
|
import { monitorEventLoopDelay } from "perf_hooks";
|
|
822
822
|
|
|
@@ -1274,7 +1274,7 @@ var credsSaveQueue = Promise.resolve();
|
|
|
1274
1274
|
async function drainCredsSaveQueue(timeoutMs = 5e3) {
|
|
1275
1275
|
console.error(`${TAG2} draining credential save queue\u2026`);
|
|
1276
1276
|
const timer2 = new Promise(
|
|
1277
|
-
(
|
|
1277
|
+
(resolve25) => setTimeout(() => resolve25("timeout"), timeoutMs)
|
|
1278
1278
|
);
|
|
1279
1279
|
const result = await Promise.race([
|
|
1280
1280
|
credsSaveQueue.then(() => "drained"),
|
|
@@ -1402,11 +1402,11 @@ async function createWaSocket(opts) {
|
|
|
1402
1402
|
return sock;
|
|
1403
1403
|
}
|
|
1404
1404
|
async function waitForConnection(sock) {
|
|
1405
|
-
return new Promise((
|
|
1405
|
+
return new Promise((resolve25, reject) => {
|
|
1406
1406
|
const handler = (update) => {
|
|
1407
1407
|
if (update.connection === "open") {
|
|
1408
1408
|
sock.ev.off("connection.update", handler);
|
|
1409
|
-
|
|
1409
|
+
resolve25();
|
|
1410
1410
|
}
|
|
1411
1411
|
if (update.connection === "close") {
|
|
1412
1412
|
sock.ev.off("connection.update", handler);
|
|
@@ -1520,14 +1520,14 @@ ${inspected}`;
|
|
|
1520
1520
|
return inspect2(err, INSPECT_OPTS2);
|
|
1521
1521
|
}
|
|
1522
1522
|
function withTimeout(label, promise, timeoutMs) {
|
|
1523
|
-
return new Promise((
|
|
1523
|
+
return new Promise((resolve25, reject) => {
|
|
1524
1524
|
const timer2 = setTimeout(() => {
|
|
1525
1525
|
reject(new Error(`${label} timed out after ${timeoutMs}ms`));
|
|
1526
1526
|
}, timeoutMs);
|
|
1527
1527
|
promise.then(
|
|
1528
1528
|
(value) => {
|
|
1529
1529
|
clearTimeout(timer2);
|
|
1530
|
-
|
|
1530
|
+
resolve25(value);
|
|
1531
1531
|
},
|
|
1532
1532
|
(err) => {
|
|
1533
1533
|
clearTimeout(timer2);
|
|
@@ -2062,8 +2062,8 @@ async function persistWhatsAppMessage(input) {
|
|
|
2062
2062
|
const { givenName, familyName } = splitName(input.pushName);
|
|
2063
2063
|
const prev = sessionWriteLocks.get(input.cacheKey);
|
|
2064
2064
|
let release;
|
|
2065
|
-
const mine = new Promise((
|
|
2066
|
-
release =
|
|
2065
|
+
const mine = new Promise((resolve25) => {
|
|
2066
|
+
release = resolve25;
|
|
2067
2067
|
});
|
|
2068
2068
|
const chained = (prev ?? Promise.resolve()).then(() => mine);
|
|
2069
2069
|
sessionWriteLocks.set(input.cacheKey, chained);
|
|
@@ -3099,11 +3099,11 @@ async function connectWithReconnect(conn) {
|
|
|
3099
3099
|
console.error(
|
|
3100
3100
|
`${TAG12} reconnecting account=${conn.accountId} in ${delay}ms (attempt ${decision.nextAttempts}/${maxAttempts})`
|
|
3101
3101
|
);
|
|
3102
|
-
await new Promise((
|
|
3103
|
-
const timer2 = setTimeout(
|
|
3102
|
+
await new Promise((resolve25) => {
|
|
3103
|
+
const timer2 = setTimeout(resolve25, delay);
|
|
3104
3104
|
conn.abortController.signal.addEventListener("abort", () => {
|
|
3105
3105
|
clearTimeout(timer2);
|
|
3106
|
-
|
|
3106
|
+
resolve25();
|
|
3107
3107
|
}, { once: true });
|
|
3108
3108
|
});
|
|
3109
3109
|
}
|
|
@@ -3111,16 +3111,16 @@ async function connectWithReconnect(conn) {
|
|
|
3111
3111
|
}
|
|
3112
3112
|
}
|
|
3113
3113
|
function waitForDisconnectEvent(conn) {
|
|
3114
|
-
return new Promise((
|
|
3114
|
+
return new Promise((resolve25) => {
|
|
3115
3115
|
if (!conn.sock) {
|
|
3116
|
-
|
|
3116
|
+
resolve25();
|
|
3117
3117
|
return;
|
|
3118
3118
|
}
|
|
3119
3119
|
const sock = conn.sock;
|
|
3120
3120
|
const handler = (update) => {
|
|
3121
3121
|
if (update.connection === "close") {
|
|
3122
3122
|
sock.ev.off("connection.update", handler);
|
|
3123
|
-
|
|
3123
|
+
resolve25();
|
|
3124
3124
|
}
|
|
3125
3125
|
};
|
|
3126
3126
|
sock.ev.on("connection.update", handler);
|
|
@@ -3382,8 +3382,8 @@ async function handleInboundMessage(conn, msg) {
|
|
|
3382
3382
|
const conversationKey = isGroup ? remoteJid : senderPhone;
|
|
3383
3383
|
const debounceKey = `${conn.accountId}:${conversationKey}:${senderPhone}`;
|
|
3384
3384
|
let resolvePending;
|
|
3385
|
-
const sttPending = new Promise((
|
|
3386
|
-
resolvePending =
|
|
3385
|
+
const sttPending = new Promise((resolve25) => {
|
|
3386
|
+
resolvePending = resolve25;
|
|
3387
3387
|
});
|
|
3388
3388
|
if (conn.debouncer) conn.debouncer.registerPending(debounceKey, sttPending);
|
|
3389
3389
|
try {
|
|
@@ -3787,20 +3787,20 @@ function buildX11Env(chromiumWrapperPath, transport = "vnc") {
|
|
|
3787
3787
|
|
|
3788
3788
|
// server/routes/health.ts
|
|
3789
3789
|
function checkPort(port2, timeoutMs = 500) {
|
|
3790
|
-
return new Promise((
|
|
3790
|
+
return new Promise((resolve25) => {
|
|
3791
3791
|
const socket = createConnection2(port2, "127.0.0.1");
|
|
3792
3792
|
socket.setTimeout(timeoutMs);
|
|
3793
3793
|
socket.once("connect", () => {
|
|
3794
3794
|
socket.destroy();
|
|
3795
|
-
|
|
3795
|
+
resolve25(true);
|
|
3796
3796
|
});
|
|
3797
3797
|
socket.once("error", () => {
|
|
3798
3798
|
socket.destroy();
|
|
3799
|
-
|
|
3799
|
+
resolve25(false);
|
|
3800
3800
|
});
|
|
3801
3801
|
socket.once("timeout", () => {
|
|
3802
3802
|
socket.destroy();
|
|
3803
|
-
|
|
3803
|
+
resolve25(false);
|
|
3804
3804
|
});
|
|
3805
3805
|
});
|
|
3806
3806
|
}
|
|
@@ -4536,12 +4536,12 @@ async function dispatchOnce(input) {
|
|
|
4536
4536
|
});
|
|
4537
4537
|
if (!entry) return { error: "spawn-failed" };
|
|
4538
4538
|
entry.lastInboundAt = Date.now();
|
|
4539
|
-
let
|
|
4539
|
+
let resolve25;
|
|
4540
4540
|
const turnPromise = new Promise((r) => {
|
|
4541
|
-
|
|
4541
|
+
resolve25 = r;
|
|
4542
4542
|
});
|
|
4543
4543
|
const listener = (text) => {
|
|
4544
|
-
|
|
4544
|
+
resolve25(text);
|
|
4545
4545
|
};
|
|
4546
4546
|
entry.subscribers.add(listener);
|
|
4547
4547
|
const writeOk = await writeInput(entry, input.text);
|
|
@@ -4983,8 +4983,8 @@ async function startLogin(opts) {
|
|
|
4983
4983
|
resetActiveLogin(accountId);
|
|
4984
4984
|
let resolveQr = null;
|
|
4985
4985
|
let rejectQr = null;
|
|
4986
|
-
const qrPromise = new Promise((
|
|
4987
|
-
resolveQr =
|
|
4986
|
+
const qrPromise = new Promise((resolve25, reject) => {
|
|
4987
|
+
resolveQr = resolve25;
|
|
4988
4988
|
rejectQr = reject;
|
|
4989
4989
|
});
|
|
4990
4990
|
const qrTimer = setTimeout(
|
|
@@ -8004,10 +8004,10 @@ function computeSpecialistDomains(accountId) {
|
|
|
8004
8004
|
const resolveDesc = (qualified) => {
|
|
8005
8005
|
if (!qualified.startsWith("mcp__")) return void 0;
|
|
8006
8006
|
const rest = qualified.slice(5);
|
|
8007
|
-
const
|
|
8008
|
-
if (
|
|
8009
|
-
const plugin = rest.slice(0,
|
|
8010
|
-
const tool = rest.slice(
|
|
8007
|
+
const sep7 = rest.indexOf("__");
|
|
8008
|
+
if (sep7 < 0) return void 0;
|
|
8009
|
+
const plugin = rest.slice(0, sep7);
|
|
8010
|
+
const tool = rest.slice(sep7 + 2);
|
|
8011
8011
|
let map = descByPlugin.get(plugin);
|
|
8012
8012
|
if (!map) {
|
|
8013
8013
|
const dir = pluginDirs.get(plugin);
|
|
@@ -8396,6 +8396,7 @@ import { realpathSync as realpathSync3 } from "fs";
|
|
|
8396
8396
|
import { resolve as resolve12, normalize, sep as sep2, relative } from "path";
|
|
8397
8397
|
var PLATFORM_ROOT5 = process.env.MAXY_PLATFORM_ROOT ?? resolve12(process.cwd(), "../platform");
|
|
8398
8398
|
var DATA_ROOT = resolve12(PLATFORM_ROOT5, "..", "data");
|
|
8399
|
+
var CLAUDE_UPLOADS_ROOT = process.env.CLAUDE_CONFIG_DIR ? resolve12(process.env.CLAUDE_CONFIG_DIR, "uploads") : null;
|
|
8399
8400
|
var ACCOUNT_PARTITION_DIRS = /* @__PURE__ */ new Set(["uploads", "accounts"]);
|
|
8400
8401
|
var ACCOUNT_UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
8401
8402
|
function crossesForeignAccountPartition(relPath2, accountId) {
|
|
@@ -8406,17 +8407,17 @@ function crossesForeignAccountPartition(relPath2, accountId) {
|
|
|
8406
8407
|
if (!ACCOUNT_UUID_RE.test(owner)) return false;
|
|
8407
8408
|
return owner !== accountId;
|
|
8408
8409
|
}
|
|
8409
|
-
function
|
|
8410
|
+
function resolveUnderRoot(raw, rootAbs, rootLabel) {
|
|
8410
8411
|
const cleaned = normalize("/" + (raw ?? "").replace(/\\/g, "/")).replace(/^\/+/, "");
|
|
8411
|
-
const absolute = resolve12(
|
|
8412
|
-
let
|
|
8412
|
+
const absolute = resolve12(rootAbs, cleaned);
|
|
8413
|
+
let rootReal;
|
|
8413
8414
|
try {
|
|
8414
|
-
|
|
8415
|
+
rootReal = realpathSync3(rootAbs);
|
|
8415
8416
|
} catch (err) {
|
|
8416
8417
|
return {
|
|
8417
8418
|
ok: false,
|
|
8418
8419
|
status: 500,
|
|
8419
|
-
error:
|
|
8420
|
+
error: `${rootLabel} not accessible: ${err instanceof Error ? err.message : String(err)}`
|
|
8420
8421
|
};
|
|
8421
8422
|
}
|
|
8422
8423
|
let resolvedReal;
|
|
@@ -8425,8 +8426,8 @@ function resolveDataPath(raw) {
|
|
|
8425
8426
|
} catch (err) {
|
|
8426
8427
|
const code = err.code;
|
|
8427
8428
|
if (code === "ENOENT") {
|
|
8428
|
-
if (absolute !==
|
|
8429
|
-
return { ok: false, status: 403, error:
|
|
8429
|
+
if (absolute !== rootReal && !absolute.startsWith(rootReal + sep2)) {
|
|
8430
|
+
return { ok: false, status: 403, error: `Path escapes ${rootLabel}`, resolved: absolute };
|
|
8430
8431
|
}
|
|
8431
8432
|
return { ok: false, status: 404, error: "Not found" };
|
|
8432
8433
|
}
|
|
@@ -8436,11 +8437,20 @@ function resolveDataPath(raw) {
|
|
|
8436
8437
|
error: err instanceof Error ? err.message : String(err)
|
|
8437
8438
|
};
|
|
8438
8439
|
}
|
|
8439
|
-
if (resolvedReal !==
|
|
8440
|
-
return { ok: false, status: 403, error:
|
|
8440
|
+
if (resolvedReal !== rootReal && !resolvedReal.startsWith(rootReal + sep2)) {
|
|
8441
|
+
return { ok: false, status: 403, error: `Path escapes ${rootLabel}`, resolved: resolvedReal };
|
|
8442
|
+
}
|
|
8443
|
+
const relPath2 = relative(rootReal, resolvedReal) || ".";
|
|
8444
|
+
return { ok: true, absolute: resolvedReal, dataRootReal: rootReal, relative: relPath2 };
|
|
8445
|
+
}
|
|
8446
|
+
function resolveDataPath(raw) {
|
|
8447
|
+
return resolveUnderRoot(raw, DATA_ROOT, "DATA_ROOT");
|
|
8448
|
+
}
|
|
8449
|
+
function resolveClaudeUploadsPath(raw) {
|
|
8450
|
+
if (!CLAUDE_UPLOADS_ROOT) {
|
|
8451
|
+
return { ok: false, status: 404, error: "Not found" };
|
|
8441
8452
|
}
|
|
8442
|
-
|
|
8443
|
-
return { ok: true, absolute: resolvedReal, dataRootReal, relative: relPath2 };
|
|
8453
|
+
return resolveUnderRoot(raw, CLAUDE_UPLOADS_ROOT, "CLAUDE_UPLOADS_ROOT");
|
|
8444
8454
|
}
|
|
8445
8455
|
|
|
8446
8456
|
// ../lib/graph-trash/src/index.ts
|
|
@@ -9109,6 +9119,23 @@ function buildDisplayPath(relPath2, accountNames) {
|
|
|
9109
9119
|
return dn ? { name: seg, displayName: dn } : { name: seg };
|
|
9110
9120
|
});
|
|
9111
9121
|
}
|
|
9122
|
+
async function knowledgeDocOwnedByAccount(accountId, attachmentId) {
|
|
9123
|
+
if (!attachmentId) return false;
|
|
9124
|
+
const session = getSession();
|
|
9125
|
+
try {
|
|
9126
|
+
const result = await session.run(
|
|
9127
|
+
`MATCH (d:KnowledgeDocument { accountId: $accountId, attachmentId: $attachmentId })
|
|
9128
|
+
RETURN count(d) > 0 AS owned`,
|
|
9129
|
+
{ accountId, attachmentId }
|
|
9130
|
+
);
|
|
9131
|
+
return result.records[0]?.get("owned") === true;
|
|
9132
|
+
} catch (err) {
|
|
9133
|
+
console.error(`[data] kd-ownership-check-failed attachmentId="${attachmentId.slice(0, 8)}\u2026" err="${err instanceof Error ? err.message : String(err)}"`);
|
|
9134
|
+
return false;
|
|
9135
|
+
} finally {
|
|
9136
|
+
await session.close();
|
|
9137
|
+
}
|
|
9138
|
+
}
|
|
9112
9139
|
var app15 = new Hono();
|
|
9113
9140
|
app15.get("/", requireAdminSession, async (c) => {
|
|
9114
9141
|
const cacheKey = c.var.cacheKey;
|
|
@@ -9180,15 +9207,26 @@ app15.get("/download", requireAdminSession, async (c) => {
|
|
|
9180
9207
|
}
|
|
9181
9208
|
const rawPath = c.req.query("path") ?? "";
|
|
9182
9209
|
if (!rawPath) return c.json({ error: "path required" }, 400);
|
|
9183
|
-
const
|
|
9210
|
+
const root = c.req.query("root") ?? "data";
|
|
9211
|
+
if (root !== "data" && root !== "claude-uploads") {
|
|
9212
|
+
return c.json({ error: "invalid root" }, 400);
|
|
9213
|
+
}
|
|
9214
|
+
const resolution = root === "claude-uploads" ? resolveClaudeUploadsPath(rawPath) : resolveDataPath(rawPath);
|
|
9184
9215
|
if (!resolution.ok) {
|
|
9185
9216
|
if (resolution.status === 403) {
|
|
9186
|
-
console.error(`[data] path-traversal-blocked requested="${rawPath}" resolved="${resolution.resolved ?? "n/a"}"`);
|
|
9217
|
+
console.error(`[data] path-traversal-blocked root="${root}" requested="${rawPath}" resolved="${resolution.resolved ?? "n/a"}"`);
|
|
9187
9218
|
}
|
|
9188
9219
|
return c.json({ error: resolution.error }, resolution.status);
|
|
9189
9220
|
}
|
|
9190
9221
|
const { absolute, relative: relPath2 } = resolution;
|
|
9191
|
-
if (
|
|
9222
|
+
if (root === "claude-uploads") {
|
|
9223
|
+
const attachmentId = relPath2.split("/").filter(Boolean)[0] ?? "";
|
|
9224
|
+
const owned = await knowledgeDocOwnedByAccount(accountId, attachmentId);
|
|
9225
|
+
if (!owned) {
|
|
9226
|
+
console.error(`[data] account-scope-blocked root="claude-uploads" attachmentId="${attachmentId.slice(0, 8)}\u2026" account=${accountId.slice(0, 8)}\u2026`);
|
|
9227
|
+
return c.json({ error: "Not found" }, 404);
|
|
9228
|
+
}
|
|
9229
|
+
} else if (crossesForeignAccountPartition(relPath2, accountId)) {
|
|
9192
9230
|
console.error(`[data] account-scope-blocked endpoint="/api/admin/files/download" path="${relPath2}" account=${accountId.slice(0, 8)}\u2026`);
|
|
9193
9231
|
return c.json({ error: "Not found" }, 404);
|
|
9194
9232
|
}
|
|
@@ -9201,7 +9239,7 @@ app15.get("/download", requireAdminSession, async (c) => {
|
|
|
9201
9239
|
const mimeType = detectMimeType(absolute);
|
|
9202
9240
|
const nodeStream = createReadStream2(absolute);
|
|
9203
9241
|
const webStream = Readable2.toWeb(nodeStream);
|
|
9204
|
-
console.error(`[data] file-download path="${relPath2}" size=${info.size}`);
|
|
9242
|
+
console.error(`[data] file-download root="${root}" path="${relPath2}" size=${info.size}`);
|
|
9205
9243
|
const safeQuotedName = filename.replace(/[\r\n"\\]/g, "_");
|
|
9206
9244
|
const encodedName = encodeURIComponent(filename);
|
|
9207
9245
|
return new Response(webStream, {
|
|
@@ -11361,32 +11399,124 @@ var graph_default_view_default = app21;
|
|
|
11361
11399
|
// server/routes/admin/sidebar-artefacts.ts
|
|
11362
11400
|
import neo4j2 from "neo4j-driver";
|
|
11363
11401
|
import { readFile as readFile5, readdir as readdir4, stat as stat5 } from "fs/promises";
|
|
11364
|
-
import { resolve as
|
|
11402
|
+
import { resolve as resolve16, relative as relative4, isAbsolute, sep as sep6 } from "path";
|
|
11365
11403
|
import { existsSync as existsSync16 } from "fs";
|
|
11366
|
-
|
|
11367
|
-
|
|
11368
|
-
|
|
11369
|
-
|
|
11370
|
-
|
|
11371
|
-
return relative3(DATA_ROOT, absPath);
|
|
11372
|
-
}
|
|
11373
|
-
async function resolveDocAbsPath(accountId, attachmentId) {
|
|
11374
|
-
if (!attachmentId) return null;
|
|
11375
|
-
const accountDir = resolve15(ATTACHMENTS_ROOT, accountId);
|
|
11376
|
-
const dir = resolve15(accountDir, attachmentId);
|
|
11404
|
+
|
|
11405
|
+
// app/lib/doc-source-resolve.ts
|
|
11406
|
+
import { readdirSync as readdirSync8, realpathSync as realpathSync5, statSync as statSync7 } from "fs";
|
|
11407
|
+
import { relative as relative3, resolve as resolve15, sep as sep5 } from "path";
|
|
11408
|
+
function containedFile(absPath, root) {
|
|
11377
11409
|
try {
|
|
11378
|
-
|
|
11410
|
+
const real = realpathSync5(absPath);
|
|
11411
|
+
const rootReal = realpathSync5(root);
|
|
11412
|
+
if (real !== rootReal && !real.startsWith(rootReal + sep5)) return null;
|
|
11413
|
+
return statSync7(real).isFile() ? real : null;
|
|
11379
11414
|
} catch {
|
|
11380
11415
|
return null;
|
|
11381
11416
|
}
|
|
11417
|
+
}
|
|
11418
|
+
function dataFileInDir(dir) {
|
|
11419
|
+
let entries;
|
|
11382
11420
|
try {
|
|
11383
|
-
|
|
11384
|
-
const dataFile = entries.find((f) => !f.endsWith(".meta.json"));
|
|
11385
|
-
if (!dataFile) return null;
|
|
11386
|
-
return resolve15(dir, dataFile);
|
|
11421
|
+
entries = readdirSync8(dir, { withFileTypes: true }).flatMap((e) => e.isFile() ? [e.name] : []);
|
|
11387
11422
|
} catch {
|
|
11388
11423
|
return null;
|
|
11389
11424
|
}
|
|
11425
|
+
const file = entries.find((e) => !e.endsWith(".meta.json"));
|
|
11426
|
+
return file ? resolve15(dir, file) : null;
|
|
11427
|
+
}
|
|
11428
|
+
function mapToDownloadRoot(absPath, roots) {
|
|
11429
|
+
const underData = containedFile(absPath, roots.dataRoot);
|
|
11430
|
+
if (underData) {
|
|
11431
|
+
return { root: "data", relPath: relative3(realpathSync5(roots.dataRoot), underData), absReal: underData };
|
|
11432
|
+
}
|
|
11433
|
+
if (roots.claudeUploadsRoot) {
|
|
11434
|
+
const underClaude = containedFile(absPath, roots.claudeUploadsRoot);
|
|
11435
|
+
if (underClaude) {
|
|
11436
|
+
return {
|
|
11437
|
+
root: "claude-uploads",
|
|
11438
|
+
relPath: relative3(realpathSync5(roots.claudeUploadsRoot), underClaude),
|
|
11439
|
+
absReal: underClaude
|
|
11440
|
+
};
|
|
11441
|
+
}
|
|
11442
|
+
}
|
|
11443
|
+
return null;
|
|
11444
|
+
}
|
|
11445
|
+
function resolveDocDownload(input, roots) {
|
|
11446
|
+
const { accountId, attachmentId, name, sourcePath, sourceType } = input;
|
|
11447
|
+
if (sourcePath) {
|
|
11448
|
+
const mapped = mapToDownloadRoot(sourcePath, roots);
|
|
11449
|
+
if (mapped) {
|
|
11450
|
+
return { ok: true, absPath: mapped.absReal, root: mapped.root, relPath: mapped.relPath, via: "stored" };
|
|
11451
|
+
}
|
|
11452
|
+
}
|
|
11453
|
+
if (attachmentId) {
|
|
11454
|
+
const uploadsDir = resolve15(roots.attachmentsRoot, accountId, attachmentId);
|
|
11455
|
+
const file = dataFileInDir(uploadsDir);
|
|
11456
|
+
if (file) {
|
|
11457
|
+
const real = containedFile(file, roots.dataRoot);
|
|
11458
|
+
if (real) {
|
|
11459
|
+
return { ok: true, absPath: real, root: "data", relPath: relative3(realpathSync5(roots.dataRoot), real), via: "uploads" };
|
|
11460
|
+
}
|
|
11461
|
+
}
|
|
11462
|
+
}
|
|
11463
|
+
if (attachmentId && roots.claudeUploadsRoot) {
|
|
11464
|
+
const claudeDir = resolve15(roots.claudeUploadsRoot, attachmentId);
|
|
11465
|
+
const file = dataFileInDir(claudeDir);
|
|
11466
|
+
if (file) {
|
|
11467
|
+
const real = containedFile(file, roots.claudeUploadsRoot);
|
|
11468
|
+
if (real) {
|
|
11469
|
+
return {
|
|
11470
|
+
ok: true,
|
|
11471
|
+
absPath: real,
|
|
11472
|
+
root: "claude-uploads",
|
|
11473
|
+
relPath: relative3(realpathSync5(roots.claudeUploadsRoot), real),
|
|
11474
|
+
via: "claude-uploads"
|
|
11475
|
+
};
|
|
11476
|
+
}
|
|
11477
|
+
}
|
|
11478
|
+
}
|
|
11479
|
+
if (name) {
|
|
11480
|
+
const outputFile = resolve15(roots.accountsDir, accountId, "output", name);
|
|
11481
|
+
const real = containedFile(outputFile, roots.dataRoot);
|
|
11482
|
+
if (real) {
|
|
11483
|
+
return { ok: true, absPath: real, root: "data", relPath: relative3(realpathSync5(roots.dataRoot), real), via: "output" };
|
|
11484
|
+
}
|
|
11485
|
+
}
|
|
11486
|
+
return { ok: false, reason: sourceType === "web" ? "no-persisted-file" : "unresolved" };
|
|
11487
|
+
}
|
|
11488
|
+
|
|
11489
|
+
// server/routes/admin/sidebar-artefacts.ts
|
|
11490
|
+
var LIMIT = 50;
|
|
11491
|
+
var TEXT_MIME_PREFIXES = ["text/", "application/json", "application/markdown"];
|
|
11492
|
+
var ADMIN_AGENT_FILES = ["IDENTITY.md", "SOUL.md", "KNOWLEDGE.md"];
|
|
11493
|
+
function toDataRootRelPath(absPath) {
|
|
11494
|
+
if (absPath !== DATA_ROOT && !absPath.startsWith(DATA_ROOT + sep6)) return null;
|
|
11495
|
+
return relative4(DATA_ROOT, absPath);
|
|
11496
|
+
}
|
|
11497
|
+
function resolveDocDownloadRow(meta) {
|
|
11498
|
+
const result = resolveDocDownload(
|
|
11499
|
+
{
|
|
11500
|
+
accountId: meta.accountId,
|
|
11501
|
+
attachmentId: meta.attachmentId,
|
|
11502
|
+
name: meta.name,
|
|
11503
|
+
sourcePath: meta.sourcePath,
|
|
11504
|
+
sourceType: meta.sourceType
|
|
11505
|
+
},
|
|
11506
|
+
{
|
|
11507
|
+
dataRoot: DATA_ROOT,
|
|
11508
|
+
attachmentsRoot: ATTACHMENTS_ROOT,
|
|
11509
|
+
accountsDir: ACCOUNTS_DIR,
|
|
11510
|
+
claudeUploadsRoot: CLAUDE_UPLOADS_ROOT
|
|
11511
|
+
}
|
|
11512
|
+
);
|
|
11513
|
+
const aid = meta.attachmentId ? meta.attachmentId.slice(0, 8) : "none";
|
|
11514
|
+
if (result.ok) {
|
|
11515
|
+
console.log(`[admin/sidebar-artefacts] download-resolved id=${aid} via=${result.via} root=${result.root}`);
|
|
11516
|
+
return { downloadPath: result.relPath, downloadRoot: result.root };
|
|
11517
|
+
}
|
|
11518
|
+
console.log(`[admin/sidebar-artefacts] not-downloadable id=${aid} reason=${result.reason}`);
|
|
11519
|
+
return { downloadPath: null, downloadRoot: null };
|
|
11390
11520
|
}
|
|
11391
11521
|
var app22 = new Hono();
|
|
11392
11522
|
app22.get("/", requireAdminSession, async (c) => {
|
|
@@ -11400,7 +11530,7 @@ app22.get("/", requireAdminSession, async (c) => {
|
|
|
11400
11530
|
if (docs === null) {
|
|
11401
11531
|
return c.json({ error: "Failed to load artefacts" }, 500);
|
|
11402
11532
|
}
|
|
11403
|
-
const accountDir =
|
|
11533
|
+
const accountDir = resolve16(ACCOUNTS_DIR, accountId);
|
|
11404
11534
|
const agents = await fetchAgentTemplateRows(accountDir);
|
|
11405
11535
|
const artefacts = [...docs, ...agents].sort(
|
|
11406
11536
|
(a, b) => (b.updatedAt ?? "").localeCompare(a.updatedAt ?? "")
|
|
@@ -11419,7 +11549,8 @@ async function fetchKnowledgeDocs(accountId) {
|
|
|
11419
11549
|
`MATCH (d:KnowledgeDocument { accountId: $accountId })
|
|
11420
11550
|
WHERE d.deletedAt IS NULL AND NOT d:Trashed
|
|
11421
11551
|
RETURN d.attachmentId AS id, d.name AS name, d.updatedAt AS updatedAt,
|
|
11422
|
-
d.attachmentId AS attachmentId, d.encodingFormat AS mimeType
|
|
11552
|
+
d.attachmentId AS attachmentId, d.encodingFormat AS mimeType,
|
|
11553
|
+
d.sourcePath AS sourcePath, d.sourceType AS sourceType
|
|
11423
11554
|
ORDER BY d.updatedAt DESC
|
|
11424
11555
|
LIMIT $limit`,
|
|
11425
11556
|
{ accountId, limit: neo4j2.int(LIMIT) }
|
|
@@ -11429,7 +11560,9 @@ async function fetchKnowledgeDocs(accountId) {
|
|
|
11429
11560
|
name: r.get("name") ?? "",
|
|
11430
11561
|
updatedAt: r.get("updatedAt") ?? "",
|
|
11431
11562
|
attachmentId: r.get("attachmentId"),
|
|
11432
|
-
mimeType: r.get("mimeType") ?? ""
|
|
11563
|
+
mimeType: r.get("mimeType") ?? "",
|
|
11564
|
+
sourcePath: r.get("sourcePath") ?? null,
|
|
11565
|
+
sourceType: r.get("sourceType") ?? null
|
|
11433
11566
|
}));
|
|
11434
11567
|
} catch (err) {
|
|
11435
11568
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -11441,7 +11574,13 @@ async function fetchKnowledgeDocs(accountId) {
|
|
|
11441
11574
|
}
|
|
11442
11575
|
return Promise.all(metas.map(async (m) => {
|
|
11443
11576
|
const { content, skipReason } = await readArtefactContent(accountId, m.attachmentId, m.mimeType, m.name);
|
|
11444
|
-
const
|
|
11577
|
+
const { downloadPath, downloadRoot } = resolveDocDownloadRow({
|
|
11578
|
+
accountId,
|
|
11579
|
+
attachmentId: m.attachmentId,
|
|
11580
|
+
name: m.name,
|
|
11581
|
+
sourcePath: m.sourcePath,
|
|
11582
|
+
sourceType: m.sourceType
|
|
11583
|
+
});
|
|
11445
11584
|
return {
|
|
11446
11585
|
id: m.id,
|
|
11447
11586
|
name: m.name,
|
|
@@ -11451,7 +11590,8 @@ async function fetchKnowledgeDocs(accountId) {
|
|
|
11451
11590
|
editable: skipReason === null,
|
|
11452
11591
|
mimeType: m.mimeType,
|
|
11453
11592
|
skipReason,
|
|
11454
|
-
downloadPath
|
|
11593
|
+
downloadPath,
|
|
11594
|
+
downloadRoot
|
|
11455
11595
|
};
|
|
11456
11596
|
}));
|
|
11457
11597
|
}
|
|
@@ -11465,8 +11605,8 @@ async function readArtefactContent(accountId, attachmentId, mimeType, displayNam
|
|
|
11465
11605
|
logSkip(displayName, "non-text-mime", mimeType);
|
|
11466
11606
|
return { content: "", skipReason: "non-text-mime" };
|
|
11467
11607
|
}
|
|
11468
|
-
const accountDir =
|
|
11469
|
-
const dir =
|
|
11608
|
+
const accountDir = resolve16(ATTACHMENTS_ROOT, accountId);
|
|
11609
|
+
const dir = resolve16(accountDir, attachmentId);
|
|
11470
11610
|
try {
|
|
11471
11611
|
validateFilePathInAccount(dir, accountDir);
|
|
11472
11612
|
} catch {
|
|
@@ -11480,7 +11620,7 @@ async function readArtefactContent(accountId, attachmentId, mimeType, displayNam
|
|
|
11480
11620
|
logSkip(displayName, "missing-on-disk", mimeType);
|
|
11481
11621
|
return { content: "", skipReason: "missing-on-disk" };
|
|
11482
11622
|
}
|
|
11483
|
-
return { content: await readFile5(
|
|
11623
|
+
return { content: await readFile5(resolve16(dir, dataFile), "utf-8"), skipReason: null };
|
|
11484
11624
|
} catch (err) {
|
|
11485
11625
|
const message = err instanceof Error ? err.message : String(err);
|
|
11486
11626
|
console.error(`[admin/sidebar-artefacts] read-failed attachmentId=${attachmentId.slice(0, 8)} error="${message}"`);
|
|
@@ -11496,8 +11636,8 @@ function logSkip(name, reason, mimeType) {
|
|
|
11496
11636
|
async function fetchAgentTemplateRows(accountDir) {
|
|
11497
11637
|
const rows = [];
|
|
11498
11638
|
for (const filename of ADMIN_AGENT_FILES) {
|
|
11499
|
-
const overridePath =
|
|
11500
|
-
const bundledPath =
|
|
11639
|
+
const overridePath = resolve16(accountDir, "agents", "admin", filename);
|
|
11640
|
+
const bundledPath = resolve16(PLATFORM_ROOT, "templates", "agents", "admin", filename);
|
|
11501
11641
|
const labelStem = filename.replace(/\.md$/, "");
|
|
11502
11642
|
const row = await readAgentTemplateRow({
|
|
11503
11643
|
id: `agent-template:admin:${filename}`,
|
|
@@ -11511,12 +11651,12 @@ async function fetchAgentTemplateRows(accountDir) {
|
|
|
11511
11651
|
});
|
|
11512
11652
|
if (row) rows.push(row);
|
|
11513
11653
|
}
|
|
11514
|
-
const overrideDir =
|
|
11515
|
-
const bundledDir =
|
|
11654
|
+
const overrideDir = resolve16(accountDir, "specialists", "agents");
|
|
11655
|
+
const bundledDir = resolve16(PLATFORM_ROOT, "templates", "specialists", "agents");
|
|
11516
11656
|
const specialistNames = await unionSpecialistFilenames(overrideDir, bundledDir);
|
|
11517
11657
|
for (const filename of specialistNames) {
|
|
11518
|
-
const overridePath =
|
|
11519
|
-
const bundledPath =
|
|
11658
|
+
const overridePath = resolve16(overrideDir, filename);
|
|
11659
|
+
const bundledPath = resolve16(bundledDir, filename);
|
|
11520
11660
|
const row = await readAgentTemplateRow({
|
|
11521
11661
|
id: `agent-template:specialist:${filename}`,
|
|
11522
11662
|
displayName: filename.replace(/\.md$/, ""),
|
|
@@ -11575,6 +11715,7 @@ async function readAgentTemplateRow(inp) {
|
|
|
11575
11715
|
readFile5(chosenPath, "utf-8"),
|
|
11576
11716
|
stat5(chosenPath)
|
|
11577
11717
|
]);
|
|
11718
|
+
const dlPath = toDataRootRelPath(chosenPath);
|
|
11578
11719
|
return {
|
|
11579
11720
|
id: inp.id,
|
|
11580
11721
|
name: inp.displayName,
|
|
@@ -11587,7 +11728,10 @@ async function readAgentTemplateRow(inp) {
|
|
|
11587
11728
|
// Override paths live under <accountDir> (inside DATA_ROOT) and are
|
|
11588
11729
|
// downloadable; bundled-fallback templates live under PLATFORM_ROOT
|
|
11589
11730
|
// (outside DATA_ROOT) → null, so the client shows "not downloadable".
|
|
11590
|
-
|
|
11731
|
+
// Agent templates only ever resolve under DATA_ROOT, so the download
|
|
11732
|
+
// root is `data` whenever the path is non-null.
|
|
11733
|
+
downloadPath: dlPath,
|
|
11734
|
+
downloadRoot: dlPath ? "data" : null
|
|
11591
11735
|
};
|
|
11592
11736
|
} catch (err) {
|
|
11593
11737
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -11598,14 +11742,14 @@ async function readAgentTemplateRow(inp) {
|
|
|
11598
11742
|
}
|
|
11599
11743
|
}
|
|
11600
11744
|
function isWithin(target, root) {
|
|
11601
|
-
const rel =
|
|
11745
|
+
const rel = relative4(root, target);
|
|
11602
11746
|
return !rel.startsWith("..") && !isAbsolute(rel);
|
|
11603
11747
|
}
|
|
11604
11748
|
var sidebar_artefacts_default = app22;
|
|
11605
11749
|
|
|
11606
11750
|
// server/routes/admin/sidebar-artefact-save.ts
|
|
11607
11751
|
import { mkdir as mkdir4, readdir as readdir5, stat as stat6, writeFile as writeFile5 } from "fs/promises";
|
|
11608
|
-
import { resolve as
|
|
11752
|
+
import { resolve as resolve17 } from "path";
|
|
11609
11753
|
import { existsSync as existsSync17 } from "fs";
|
|
11610
11754
|
var ADMIN_AGENT_FILES2 = /* @__PURE__ */ new Set(["IDENTITY.md", "SOUL.md", "KNOWLEDGE.md"]);
|
|
11611
11755
|
var UUID_RE4 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
|
|
@@ -11618,7 +11762,7 @@ app23.post("/", requireAdminSession, async (c) => {
|
|
|
11618
11762
|
if (!body || typeof body.id !== "string" || typeof body.content !== "string") {
|
|
11619
11763
|
return c.json({ error: "id and content required" }, 400);
|
|
11620
11764
|
}
|
|
11621
|
-
const accountDir =
|
|
11765
|
+
const accountDir = resolve17(ACCOUNTS_DIR, accountId);
|
|
11622
11766
|
const resolved = await resolveSavePath(body.id, accountId, accountDir);
|
|
11623
11767
|
if (resolved.kind === "heal-race") {
|
|
11624
11768
|
c.header("Retry-After", "2");
|
|
@@ -11663,17 +11807,17 @@ async function resolveSavePath(id, accountId, accountDir) {
|
|
|
11663
11807
|
if (role !== "admin" || !ADMIN_AGENT_FILES2.has(filename)) {
|
|
11664
11808
|
return { kind: "reject", status: 400, reason: "invalid-id" };
|
|
11665
11809
|
}
|
|
11666
|
-
const parent =
|
|
11810
|
+
const parent = resolve17(accountDir, "agents", "admin");
|
|
11667
11811
|
await mkdir4(parent, { recursive: true });
|
|
11668
11812
|
try {
|
|
11669
11813
|
validateFilePathInAccount(parent, accountDir);
|
|
11670
11814
|
} catch {
|
|
11671
11815
|
return { kind: "reject", status: 400, reason: "containment-rejected" };
|
|
11672
11816
|
}
|
|
11673
|
-
return { kind: "admin-template", path:
|
|
11817
|
+
return { kind: "admin-template", path: resolve17(parent, filename) };
|
|
11674
11818
|
}
|
|
11675
11819
|
if (UUID_RE4.test(id)) {
|
|
11676
|
-
const dir =
|
|
11820
|
+
const dir = resolve17(ATTACHMENTS_ROOT, accountId, id);
|
|
11677
11821
|
if (!existsSync17(dir)) {
|
|
11678
11822
|
const attShort = id.slice(0, 8);
|
|
11679
11823
|
if (isHealPending(accountId, id)) {
|
|
@@ -11706,7 +11850,7 @@ async function resolveSavePath(id, accountId, accountDir) {
|
|
|
11706
11850
|
}
|
|
11707
11851
|
}
|
|
11708
11852
|
try {
|
|
11709
|
-
validateFilePathInAccount(dir,
|
|
11853
|
+
validateFilePathInAccount(dir, resolve17(ATTACHMENTS_ROOT, accountId));
|
|
11710
11854
|
} catch {
|
|
11711
11855
|
return { kind: "reject", status: 400, reason: "containment-rejected" };
|
|
11712
11856
|
}
|
|
@@ -11715,7 +11859,7 @@ async function resolveSavePath(id, accountId, accountDir) {
|
|
|
11715
11859
|
if (!dataFile) {
|
|
11716
11860
|
return { kind: "reject", status: 400, reason: "not-found" };
|
|
11717
11861
|
}
|
|
11718
|
-
return { kind: "knowledge-doc", path:
|
|
11862
|
+
return { kind: "knowledge-doc", path: resolve17(dir, dataFile) };
|
|
11719
11863
|
}
|
|
11720
11864
|
return { kind: "reject", status: 400, reason: "invalid-id" };
|
|
11721
11865
|
}
|
|
@@ -11727,7 +11871,7 @@ var sidebar_artefact_save_default = app23;
|
|
|
11727
11871
|
// server/routes/admin/sidebar-artefact-content.ts
|
|
11728
11872
|
import { readFile as readFile6, readdir as readdir6 } from "fs/promises";
|
|
11729
11873
|
import { existsSync as existsSync18 } from "fs";
|
|
11730
|
-
import { resolve as
|
|
11874
|
+
import { resolve as resolve18 } from "path";
|
|
11731
11875
|
var UUID_RE5 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
|
|
11732
11876
|
var app24 = new Hono();
|
|
11733
11877
|
app24.get("/", requireAdminSession, async (c) => {
|
|
@@ -11739,14 +11883,14 @@ app24.get("/", requireAdminSession, async (c) => {
|
|
|
11739
11883
|
console.error(`[admin/sidebar-artefact-content] not-found id=${id.slice(0, 8)}`);
|
|
11740
11884
|
return new Response("Not found", { status: 404 });
|
|
11741
11885
|
}
|
|
11742
|
-
const dir =
|
|
11886
|
+
const dir = resolve18(ATTACHMENTS_ROOT, accountId, id);
|
|
11743
11887
|
if (!existsSync18(dir)) {
|
|
11744
11888
|
console.error(`[admin/sidebar-artefact-content] not-found id=${id.slice(0, 8)}`);
|
|
11745
11889
|
return new Response("Not found", { status: 404 });
|
|
11746
11890
|
}
|
|
11747
11891
|
let meta;
|
|
11748
11892
|
try {
|
|
11749
|
-
meta = JSON.parse(await readFile6(
|
|
11893
|
+
meta = JSON.parse(await readFile6(resolve18(dir, `${id}.meta.json`), "utf-8"));
|
|
11750
11894
|
} catch {
|
|
11751
11895
|
console.error(`[admin/sidebar-artefact-content] not-found id=${id.slice(0, 8)}`);
|
|
11752
11896
|
return new Response("Not found", { status: 404 });
|
|
@@ -11758,7 +11902,7 @@ app24.get("/", requireAdminSession, async (c) => {
|
|
|
11758
11902
|
return new Response("Not found", { status: 404 });
|
|
11759
11903
|
}
|
|
11760
11904
|
const start = Date.now();
|
|
11761
|
-
const buffer = await readFile6(
|
|
11905
|
+
const buffer = await readFile6(resolve18(dir, dataFile));
|
|
11762
11906
|
const ms = Date.now() - start;
|
|
11763
11907
|
console.log(
|
|
11764
11908
|
`[admin/sidebar-artefact-content] account=${accountId} id=${id.slice(0, 8)} mime=${meta.mimeType} bytes=${buffer.length} ms=${ms}`
|
|
@@ -11896,8 +12040,8 @@ var system_stats_default = app25;
|
|
|
11896
12040
|
|
|
11897
12041
|
// server/routes/admin/health.ts
|
|
11898
12042
|
import { existsSync as existsSync19, readFileSync as readFileSync14 } from "fs";
|
|
11899
|
-
import { resolve as
|
|
11900
|
-
var PLATFORM_ROOT6 = process.env.MAXY_PLATFORM_ROOT ??
|
|
12043
|
+
import { resolve as resolve19, join as join13 } from "path";
|
|
12044
|
+
var PLATFORM_ROOT6 = process.env.MAXY_PLATFORM_ROOT ?? resolve19(process.cwd(), "..");
|
|
11901
12045
|
var brandHostname = "maxy";
|
|
11902
12046
|
var brandJsonPath = join13(PLATFORM_ROOT6, "config", "brand.json");
|
|
11903
12047
|
if (existsSync19(brandJsonPath)) {
|
|
@@ -11907,7 +12051,7 @@ if (existsSync19(brandJsonPath)) {
|
|
|
11907
12051
|
} catch {
|
|
11908
12052
|
}
|
|
11909
12053
|
}
|
|
11910
|
-
var VERSION_FILE =
|
|
12054
|
+
var VERSION_FILE = resolve19(PLATFORM_ROOT6, `config/.${brandHostname}-version`);
|
|
11911
12055
|
var PROCESS_STARTED_AT = (/* @__PURE__ */ new Date()).toISOString();
|
|
11912
12056
|
var PROBE_TIMEOUT_MS = 1e3;
|
|
11913
12057
|
function readVersion() {
|
|
@@ -12422,13 +12566,13 @@ var admin_default = app32;
|
|
|
12422
12566
|
// app/lib/access-gate.ts
|
|
12423
12567
|
import neo4j5 from "neo4j-driver";
|
|
12424
12568
|
import { readFileSync as readFileSync15 } from "fs";
|
|
12425
|
-
import { resolve as
|
|
12569
|
+
import { resolve as resolve20 } from "path";
|
|
12426
12570
|
import { randomUUID as randomUUID8 } from "crypto";
|
|
12427
|
-
var PLATFORM_ROOT7 = process.env.MAXY_PLATFORM_ROOT ??
|
|
12571
|
+
var PLATFORM_ROOT7 = process.env.MAXY_PLATFORM_ROOT ?? resolve20(process.cwd(), "..");
|
|
12428
12572
|
var driver = null;
|
|
12429
12573
|
function readPassword() {
|
|
12430
12574
|
if (process.env.NEO4J_PASSWORD) return process.env.NEO4J_PASSWORD;
|
|
12431
|
-
const passwordFile =
|
|
12575
|
+
const passwordFile = resolve20(PLATFORM_ROOT7, "config/.neo4j-password");
|
|
12432
12576
|
try {
|
|
12433
12577
|
return readFileSync15(passwordFile, "utf-8").trim();
|
|
12434
12578
|
} catch {
|
|
@@ -12718,9 +12862,9 @@ var verify_token_default = app33;
|
|
|
12718
12862
|
|
|
12719
12863
|
// app/lib/access-email.ts
|
|
12720
12864
|
import { spawn as spawn2 } from "child_process";
|
|
12721
|
-
import { resolve as
|
|
12722
|
-
var PLATFORM_ROOT8 = process.env.MAXY_PLATFORM_ROOT ??
|
|
12723
|
-
var SEND_SCRIPT =
|
|
12865
|
+
import { resolve as resolve21 } from "path";
|
|
12866
|
+
var PLATFORM_ROOT8 = process.env.MAXY_PLATFORM_ROOT ?? resolve21(process.cwd(), "..");
|
|
12867
|
+
var SEND_SCRIPT = resolve21(
|
|
12724
12868
|
PLATFORM_ROOT8,
|
|
12725
12869
|
"plugins",
|
|
12726
12870
|
"email",
|
|
@@ -12862,8 +13006,8 @@ app35.route("/request-magic-link", request_magic_link_default);
|
|
|
12862
13006
|
var access_default = app35;
|
|
12863
13007
|
|
|
12864
13008
|
// server/routes/sites.ts
|
|
12865
|
-
import { existsSync as existsSync20, readFileSync as readFileSync16, realpathSync as
|
|
12866
|
-
import { resolve as
|
|
13009
|
+
import { existsSync as existsSync20, readFileSync as readFileSync16, realpathSync as realpathSync6, statSync as statSync8 } from "fs";
|
|
13010
|
+
import { resolve as resolve22 } from "path";
|
|
12867
13011
|
var SAFE_SEG_RE = /^[a-z0-9_][a-z0-9_.-]{0,99}$/i;
|
|
12868
13012
|
var MIME = {
|
|
12869
13013
|
".html": "text/html; charset=utf-8",
|
|
@@ -12920,15 +13064,15 @@ app36.get("/:rel{.*}", (c) => {
|
|
|
12920
13064
|
}
|
|
12921
13065
|
segments.push(seg);
|
|
12922
13066
|
}
|
|
12923
|
-
const rootDir =
|
|
12924
|
-
let filePath = segments.length === 0 ? rootDir :
|
|
13067
|
+
const rootDir = resolve22(account.accountDir, "sites");
|
|
13068
|
+
let filePath = segments.length === 0 ? rootDir : resolve22(rootDir, ...segments);
|
|
12925
13069
|
if (filePath !== rootDir && !filePath.startsWith(rootDir + "/")) {
|
|
12926
13070
|
console.error(`[sites] path-traversal-rejected path=${reqPath} reason=escape status=403`);
|
|
12927
13071
|
return c.text("Forbidden", 403);
|
|
12928
13072
|
}
|
|
12929
13073
|
let stat7;
|
|
12930
13074
|
try {
|
|
12931
|
-
stat7 = existsSync20(filePath) ?
|
|
13075
|
+
stat7 = existsSync20(filePath) ? statSync8(filePath) : null;
|
|
12932
13076
|
} catch {
|
|
12933
13077
|
stat7 = null;
|
|
12934
13078
|
}
|
|
@@ -12941,7 +13085,7 @@ app36.get("/:rel{.*}", (c) => {
|
|
|
12941
13085
|
return c.redirect(target, 301);
|
|
12942
13086
|
}
|
|
12943
13087
|
if (stat7?.isDirectory()) {
|
|
12944
|
-
filePath =
|
|
13088
|
+
filePath = resolve22(filePath, "index.html");
|
|
12945
13089
|
}
|
|
12946
13090
|
if (!filePath.startsWith(rootDir + "/")) {
|
|
12947
13091
|
console.error(`[sites] path-traversal-rejected path=${reqPath} reason=escape status=403`);
|
|
@@ -12954,8 +13098,8 @@ app36.get("/:rel{.*}", (c) => {
|
|
|
12954
13098
|
let realPath;
|
|
12955
13099
|
let realRoot;
|
|
12956
13100
|
try {
|
|
12957
|
-
realPath =
|
|
12958
|
-
realRoot =
|
|
13101
|
+
realPath = realpathSync6(filePath);
|
|
13102
|
+
realRoot = realpathSync6(rootDir);
|
|
12959
13103
|
} catch {
|
|
12960
13104
|
console.error(`[sites] not-found path=${reqPath} status=404`);
|
|
12961
13105
|
return c.text("Not found", 404);
|
|
@@ -13224,8 +13368,8 @@ function appendConsentParams(pageUrl, token) {
|
|
|
13224
13368
|
const hashIdx = pageUrl.indexOf("#");
|
|
13225
13369
|
const hash = hashIdx >= 0 ? pageUrl.slice(hashIdx) : "";
|
|
13226
13370
|
const base = hashIdx >= 0 ? pageUrl.slice(0, hashIdx) : pageUrl;
|
|
13227
|
-
const
|
|
13228
|
-
return base +
|
|
13371
|
+
const sep7 = base.indexOf("?") >= 0 ? "&" : "?";
|
|
13372
|
+
return base + sep7 + `consent=needed&v=${encodeURIComponent(token)}` + hash;
|
|
13229
13373
|
}
|
|
13230
13374
|
}
|
|
13231
13375
|
var SAFE_SLUG_RE = /^[a-z0-9](?:[a-z0-9-]{0,118}[a-z0-9])?$/;
|
|
@@ -13719,8 +13863,8 @@ function broadcastAdminShutdown(reason) {
|
|
|
13719
13863
|
|
|
13720
13864
|
// ../lib/entitlement/src/index.ts
|
|
13721
13865
|
import { createPublicKey, createHash as createHash4, verify as cryptoVerify } from "crypto";
|
|
13722
|
-
import { existsSync as existsSync22, readFileSync as readFileSync19, statSync as
|
|
13723
|
-
import { resolve as
|
|
13866
|
+
import { existsSync as existsSync22, readFileSync as readFileSync19, statSync as statSync9 } from "fs";
|
|
13867
|
+
import { resolve as resolve23 } from "path";
|
|
13724
13868
|
|
|
13725
13869
|
// ../lib/entitlement/src/canonicalize.ts
|
|
13726
13870
|
function canonicalize(value) {
|
|
@@ -13755,7 +13899,7 @@ var PUBKEY_SHA256 = "8eee6bcb33545fd13b16d3199a5735ca5db5062834c7b49dfe4f23801d9
|
|
|
13755
13899
|
var GRACE_DAYS = 7;
|
|
13756
13900
|
var GRACE_MS = GRACE_DAYS * 24 * 60 * 60 * 1e3;
|
|
13757
13901
|
function pubkeyPath(brand) {
|
|
13758
|
-
return
|
|
13902
|
+
return resolve23(brand.platformRoot, "lib", "entitlement", "rubytech-pubkey.pem");
|
|
13759
13903
|
}
|
|
13760
13904
|
var memo = null;
|
|
13761
13905
|
function memoKey(mtimeMs, account) {
|
|
@@ -13767,11 +13911,11 @@ function resolveEntitlement(brand, account) {
|
|
|
13767
13911
|
if (brand.commercialMode !== true) {
|
|
13768
13912
|
return logResolved(implicitTrust(account), null);
|
|
13769
13913
|
}
|
|
13770
|
-
const entitlementPath =
|
|
13914
|
+
const entitlementPath = resolve23(brand.configDir, "entitlement.json");
|
|
13771
13915
|
if (!existsSync22(entitlementPath)) {
|
|
13772
13916
|
return logResolved(anonymousFallback("missing"), { reason: "missing" });
|
|
13773
13917
|
}
|
|
13774
|
-
const stat7 =
|
|
13918
|
+
const stat7 = statSync9(entitlementPath);
|
|
13775
13919
|
const key = memoKey(stat7.mtimeMs, account);
|
|
13776
13920
|
if (memo && memo.key === key) {
|
|
13777
13921
|
return memo.result;
|
|
@@ -14352,8 +14496,8 @@ app40.get("/agent-assets/:slug/:filename", (c) => {
|
|
|
14352
14496
|
console.error(`[agent-assets] no-account slug=${slug} file=${filename}`);
|
|
14353
14497
|
return c.text("Not found", 404);
|
|
14354
14498
|
}
|
|
14355
|
-
const filePath =
|
|
14356
|
-
const expectedDir =
|
|
14499
|
+
const filePath = resolve24(account.accountDir, "agents", slug, "assets", filename);
|
|
14500
|
+
const expectedDir = resolve24(account.accountDir, "agents", slug, "assets");
|
|
14357
14501
|
if (!filePath.startsWith(expectedDir + "/")) {
|
|
14358
14502
|
console.error(`[agent-assets] path-traversal-rejected slug=${slug} file=${filename}`);
|
|
14359
14503
|
return c.text("Forbidden", 403);
|
|
@@ -14382,8 +14526,8 @@ app40.get("/generated/:filename", (c) => {
|
|
|
14382
14526
|
console.error(`[generated] serve file=${filename} status=404`);
|
|
14383
14527
|
return c.text("Not found", 404);
|
|
14384
14528
|
}
|
|
14385
|
-
const filePath =
|
|
14386
|
-
const expectedDir =
|
|
14529
|
+
const filePath = resolve24(account.accountDir, "generated", filename);
|
|
14530
|
+
const expectedDir = resolve24(account.accountDir, "generated");
|
|
14387
14531
|
if (!filePath.startsWith(expectedDir + "/")) {
|
|
14388
14532
|
console.error(`[generated] serve file=${filename} status=403`);
|
|
14389
14533
|
return c.text("Forbidden", 403);
|
|
@@ -14470,7 +14614,7 @@ var clientErrorReporterScript = `<script>
|
|
|
14470
14614
|
function cachedHtml(file) {
|
|
14471
14615
|
let html = htmlCache.get(file);
|
|
14472
14616
|
if (!html) {
|
|
14473
|
-
html = readFileSync20(
|
|
14617
|
+
html = readFileSync20(resolve24(process.cwd(), "public", file), "utf-8");
|
|
14474
14618
|
const productNameEsc = escapeHtml(BRAND.productName);
|
|
14475
14619
|
html = html.replace(/<title>([^<]*)<\/title>/, (_match, inner) => `<title>${escapeHtml(inner).replace(/Maxy/g, productNameEsc)}</title>`);
|
|
14476
14620
|
html = html.replace('href="/favicon.ico"', `href="${escapeHtml(brandFaviconPath)}"`);
|
|
@@ -14578,7 +14722,7 @@ app40.use("/vnc-popout.html", logViewerFetch);
|
|
|
14578
14722
|
app40.get("/vnc-popout.html", (c) => {
|
|
14579
14723
|
let html = htmlCache.get("vnc-popout.html");
|
|
14580
14724
|
if (!html) {
|
|
14581
|
-
html = readFileSync20(
|
|
14725
|
+
html = readFileSync20(resolve24(process.cwd(), "public", "vnc-popout.html"), "utf-8");
|
|
14582
14726
|
const name = escapeHtml(BRAND.productName);
|
|
14583
14727
|
html = html.replace("<title>Browser \u2014 Maxy</title>", `<title>${name}</title>`);
|
|
14584
14728
|
html = html.replace("</head>", ` ${brandScript}
|
|
@@ -14796,7 +14940,7 @@ if (bootAccountConfig?.whatsapp) {
|
|
|
14796
14940
|
}
|
|
14797
14941
|
init({
|
|
14798
14942
|
configDir: configDirForWhatsApp,
|
|
14799
|
-
platformRoot:
|
|
14943
|
+
platformRoot: resolve24(process.env.MAXY_PLATFORM_ROOT ?? join15(__dirname, "..")),
|
|
14800
14944
|
accountConfig: bootAccountConfig,
|
|
14801
14945
|
onMessage: async (msg) => {
|
|
14802
14946
|
if (process.env.WHATSAPP_PTY_BRIDGE_ENABLED === "true" && msg.text && !msg.isOwnerMirror) {
|