@rubytech/create-maxy-code 0.1.189 → 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/neo4j/schema.cypher +16 -1
- package/payload/platform/plugins/admin/.claude-plugin/plugin.json +1 -1
- package/payload/platform/plugins/admin/PLUGIN.md +3 -4
- package/payload/platform/plugins/admin/mcp/dist/__tests__/admin-authoring-skill-gate.test.d.ts +2 -0
- package/payload/platform/plugins/admin/mcp/dist/__tests__/admin-authoring-skill-gate.test.d.ts.map +1 -0
- package/payload/platform/plugins/admin/mcp/dist/__tests__/admin-authoring-skill-gate.test.js +79 -0
- package/payload/platform/plugins/admin/mcp/dist/__tests__/admin-authoring-skill-gate.test.js.map +1 -0
- package/payload/platform/plugins/admin/mcp/dist/index.js +21 -95
- package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/admin/mcp/dist/skill-resolution.d.ts +9 -0
- package/payload/platform/plugins/admin/mcp/dist/skill-resolution.d.ts.map +1 -1
- package/payload/platform/plugins/admin/mcp/dist/skill-resolution.js +47 -0
- package/payload/platform/plugins/admin/mcp/dist/skill-resolution.js.map +1 -1
- package/payload/platform/plugins/admin/skills/file-presentation/SKILL.md +6 -12
- package/payload/platform/plugins/admin/skills/professional-document/SKILL.md +1 -1
- package/payload/platform/plugins/docs/references/admin-ui.md +17 -2
- 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/plugins/scheduling/PLUGIN.md +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/index.js +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/index.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js +0 -1
- package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js.map +1 -1
- 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 +113 -2
- package/payload/platform/services/claude-session-manager/dist/rc-daemon.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/system-prompt.js +1 -1
- package/payload/platform/services/claude-session-manager/dist/system-prompt.js.map +1 -1
- package/payload/platform/templates/specialists/agents/content-producer.md +2 -2
- package/payload/platform/templates/specialists/agents/personal-assistant.md +1 -1
- package/payload/server/public/assets/{admin-Cst6NkUE.js → admin-DFxXLXfg.js} +53 -53
- package/payload/server/public/assets/{data-COwlCdrl.js → data-DtYcqL1G.js} +1 -1
- package/payload/server/public/assets/{graph-CkUZEqdW.js → graph-C-XPyGsO.js} +1 -1
- package/payload/server/public/assets/graph-labels-CZin3myX.js +1 -0
- package/payload/server/public/assets/page-CKlvbjKn.js +1 -0
- package/payload/server/public/assets/{page-DC2hsMPj.js → page-DSj21hpG.js} +3 -3
- 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 +750 -312
- package/payload/server/public/assets/graph-labels-Dt1bmq6W.js +0 -1
- package/payload/server/public/assets/page-CGXxYYRR.js +0 -1
package/payload/server/server.js
CHANGED
|
@@ -756,8 +756,8 @@ var serveStatic = (options = { root: "" }) => {
|
|
|
756
756
|
);
|
|
757
757
|
let stats = getStats(path2);
|
|
758
758
|
if (stats && stats.isDirectory()) {
|
|
759
|
-
const
|
|
760
|
-
path2 = join(path2,
|
|
759
|
+
const indexFile2 = options.index ?? "index.html";
|
|
760
|
+
path2 = join(path2, indexFile2);
|
|
761
761
|
stats = getStats(path2);
|
|
762
762
|
}
|
|
763
763
|
if (!stats) {
|
|
@@ -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
|
}
|
|
@@ -3865,9 +3865,9 @@ var health_default = app;
|
|
|
3865
3865
|
|
|
3866
3866
|
// app/lib/attachments.ts
|
|
3867
3867
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
3868
|
-
import { mkdir as mkdir2,
|
|
3868
|
+
import { mkdir as mkdir2, writeFile as writeFile2 } from "fs/promises";
|
|
3869
3869
|
import { realpathSync } from "fs";
|
|
3870
|
-
import { resolve as resolve3, extname
|
|
3870
|
+
import { resolve as resolve3, extname } from "path";
|
|
3871
3871
|
var PLATFORM_ROOT3 = process.env.MAXY_PLATFORM_ROOT ?? resolve3(process.cwd(), "../platform");
|
|
3872
3872
|
var ATTACHMENTS_ROOT = resolve3(PLATFORM_ROOT3, "..", "data/uploads");
|
|
3873
3873
|
var SUPPORTED_MIME_TYPES = /* @__PURE__ */ new Set([
|
|
@@ -3957,18 +3957,6 @@ function validateFilePathInAccount(filePath, accountDir) {
|
|
|
3957
3957
|
}
|
|
3958
3958
|
return resolved;
|
|
3959
3959
|
}
|
|
3960
|
-
async function storeGeneratedFile(accountId, filePath) {
|
|
3961
|
-
const fileStat = await stat2(filePath);
|
|
3962
|
-
if (fileStat.size > MAX_FILE_SIZE_BYTES) {
|
|
3963
|
-
throw new Error(
|
|
3964
|
-
`File exceeds the 50 MB limit (${(fileStat.size / 1024 / 1024).toFixed(1)} MB).`
|
|
3965
|
-
);
|
|
3966
|
-
}
|
|
3967
|
-
const filename = basename(filePath);
|
|
3968
|
-
const mimeType = detectMimeType(filePath);
|
|
3969
|
-
const buffer = await readFile(filePath);
|
|
3970
|
-
return writeAttachment(accountId, filename, mimeType, fileStat.size, buffer);
|
|
3971
|
-
}
|
|
3972
3960
|
async function storeComponentArtefact(accountId, attachmentId, mimeType, content, filename) {
|
|
3973
3961
|
const buffer = Buffer.from(content, "utf-8");
|
|
3974
3962
|
if (buffer.byteLength > MAX_FILE_SIZE_BYTES) {
|
|
@@ -4548,12 +4536,12 @@ async function dispatchOnce(input) {
|
|
|
4548
4536
|
});
|
|
4549
4537
|
if (!entry) return { error: "spawn-failed" };
|
|
4550
4538
|
entry.lastInboundAt = Date.now();
|
|
4551
|
-
let
|
|
4539
|
+
let resolve25;
|
|
4552
4540
|
const turnPromise = new Promise((r) => {
|
|
4553
|
-
|
|
4541
|
+
resolve25 = r;
|
|
4554
4542
|
});
|
|
4555
4543
|
const listener = (text) => {
|
|
4556
|
-
|
|
4544
|
+
resolve25(text);
|
|
4557
4545
|
};
|
|
4558
4546
|
entry.subscribers.add(listener);
|
|
4559
4547
|
const writeOk = await writeInput(entry, input.text);
|
|
@@ -4887,8 +4875,8 @@ ${lines}]`;
|
|
|
4887
4875
|
var chat_default = app2;
|
|
4888
4876
|
|
|
4889
4877
|
// server/routes/whatsapp.ts
|
|
4890
|
-
import { join as join5, resolve as resolve6, basename
|
|
4891
|
-
import { readFile
|
|
4878
|
+
import { join as join5, resolve as resolve6, basename } from "path";
|
|
4879
|
+
import { readFile, stat as stat2 } from "fs/promises";
|
|
4892
4880
|
import { realpathSync as realpathSync2, readdirSync as readdirSync2, readFileSync as readFileSync6, existsSync as existsSync4 } from "fs";
|
|
4893
4881
|
|
|
4894
4882
|
// app/lib/whatsapp/login.ts
|
|
@@ -4995,8 +4983,8 @@ async function startLogin(opts) {
|
|
|
4995
4983
|
resetActiveLogin(accountId);
|
|
4996
4984
|
let resolveQr = null;
|
|
4997
4985
|
let rejectQr = null;
|
|
4998
|
-
const qrPromise = new Promise((
|
|
4999
|
-
resolveQr =
|
|
4986
|
+
const qrPromise = new Promise((resolve25, reject) => {
|
|
4987
|
+
resolveQr = resolve25;
|
|
5000
4988
|
rejectQr = reject;
|
|
5001
4989
|
});
|
|
5002
4990
|
const qrTimer = setTimeout(
|
|
@@ -5820,12 +5808,12 @@ app3.post("/send-document", async (c) => {
|
|
|
5820
5808
|
console.error(`${TAG17} send-document path error: ${String(err)}`);
|
|
5821
5809
|
return c.json({ error: String(err) }, 500);
|
|
5822
5810
|
}
|
|
5823
|
-
const fileStat = await
|
|
5811
|
+
const fileStat = await stat2(resolvedPath);
|
|
5824
5812
|
if (fileStat.size > MAX_FILE_SIZE_BYTES) {
|
|
5825
5813
|
return c.json({ error: `File exceeds 50 MB limit (${(fileStat.size / 1024 / 1024).toFixed(1)} MB)` }, 400);
|
|
5826
5814
|
}
|
|
5827
|
-
const buffer = Buffer.from(await
|
|
5828
|
-
const filename =
|
|
5815
|
+
const buffer = Buffer.from(await readFile(resolvedPath));
|
|
5816
|
+
const filename = basename(resolvedPath);
|
|
5829
5817
|
const mimetype = detectMimeType(resolvedPath);
|
|
5830
5818
|
const sock = getSocket(accountId);
|
|
5831
5819
|
if (!sock) {
|
|
@@ -6781,7 +6769,7 @@ var session_default = app6;
|
|
|
6781
6769
|
|
|
6782
6770
|
// server/routes/admin/logs.ts
|
|
6783
6771
|
import { existsSync as existsSync10, readdirSync as readdirSync5, readFileSync as readFileSync10, statSync as statSync5 } from "fs";
|
|
6784
|
-
import { resolve as resolve7, basename as
|
|
6772
|
+
import { resolve as resolve7, basename as basename2 } from "path";
|
|
6785
6773
|
|
|
6786
6774
|
// app/lib/logs-read-resolve.ts
|
|
6787
6775
|
import { existsSync as existsSync9 } from "fs";
|
|
@@ -6813,7 +6801,7 @@ app7.get("/", async (c) => {
|
|
|
6813
6801
|
if (accountLogDir) logDirs.push(accountLogDir);
|
|
6814
6802
|
logDirs.push(LOG_DIR);
|
|
6815
6803
|
if (fileParam) {
|
|
6816
|
-
const safe =
|
|
6804
|
+
const safe = basename2(fileParam);
|
|
6817
6805
|
const searched = [];
|
|
6818
6806
|
for (const dir of logDirs) {
|
|
6819
6807
|
const filePath = resolve7(dir, safe);
|
|
@@ -6883,7 +6871,7 @@ app7.get("/", async (c) => {
|
|
|
6883
6871
|
if (hit) {
|
|
6884
6872
|
console.info(`[admin/logs] resolved cacheKey=${cacheKeySlice} sessionId=${sessionIdSlice} via=${primaryId === cacheKey ? "cacheKey" : primaryId === sessionKeyFromConv ? "reverse-lookup" : "sessionId-fallback"}`);
|
|
6885
6873
|
try {
|
|
6886
|
-
const filename =
|
|
6874
|
+
const filename = basename2(hit.path);
|
|
6887
6875
|
const buffer = readFileSync10(hit.path);
|
|
6888
6876
|
const onDiskBytes = statSync5(hit.path).size;
|
|
6889
6877
|
const headers = {
|
|
@@ -6969,7 +6957,7 @@ app8.get("/", (c) => {
|
|
|
6969
6957
|
var claude_info_default = app8;
|
|
6970
6958
|
|
|
6971
6959
|
// server/routes/admin/attachment.ts
|
|
6972
|
-
import { readFile as
|
|
6960
|
+
import { readFile as readFile2, readdir } from "fs/promises";
|
|
6973
6961
|
import { existsSync as existsSync11 } from "fs";
|
|
6974
6962
|
import { resolve as resolve8 } from "path";
|
|
6975
6963
|
var app9 = new Hono();
|
|
@@ -6993,7 +6981,7 @@ app9.get("/:attachmentId", requireAdminSession, async (c) => {
|
|
|
6993
6981
|
}
|
|
6994
6982
|
let meta;
|
|
6995
6983
|
try {
|
|
6996
|
-
meta = JSON.parse(await
|
|
6984
|
+
meta = JSON.parse(await readFile2(metaPath, "utf-8"));
|
|
6997
6985
|
} catch {
|
|
6998
6986
|
return new Response("Not found", { status: 404 });
|
|
6999
6987
|
}
|
|
@@ -7003,7 +6991,7 @@ app9.get("/:attachmentId", requireAdminSession, async (c) => {
|
|
|
7003
6991
|
return new Response("Not found", { status: 404 });
|
|
7004
6992
|
}
|
|
7005
6993
|
const filePath = resolve8(dir, dataFile);
|
|
7006
|
-
const buffer = await
|
|
6994
|
+
const buffer = await readFile2(filePath);
|
|
7007
6995
|
return new Response(new Uint8Array(buffer), {
|
|
7008
6996
|
headers: {
|
|
7009
6997
|
"Content-Type": meta.mimeType,
|
|
@@ -8016,10 +8004,10 @@ function computeSpecialistDomains(accountId) {
|
|
|
8016
8004
|
const resolveDesc = (qualified) => {
|
|
8017
8005
|
if (!qualified.startsWith("mcp__")) return void 0;
|
|
8018
8006
|
const rest = qualified.slice(5);
|
|
8019
|
-
const
|
|
8020
|
-
if (
|
|
8021
|
-
const plugin = rest.slice(0,
|
|
8022
|
-
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);
|
|
8023
8011
|
let map = descByPlugin.get(plugin);
|
|
8024
8012
|
if (!map) {
|
|
8025
8013
|
const dir = pluginDirs.get(plugin);
|
|
@@ -8398,9 +8386,9 @@ var events_default = app14;
|
|
|
8398
8386
|
|
|
8399
8387
|
// server/routes/admin/files.ts
|
|
8400
8388
|
import { createReadStream as createReadStream2 } from "fs";
|
|
8401
|
-
import { readdir as
|
|
8389
|
+
import { readdir as readdir3, readFile as readFile4, stat as stat4, mkdir as mkdir3, writeFile as writeFile4, unlink as unlink2 } from "fs/promises";
|
|
8402
8390
|
import { realpathSync as realpathSync4 } from "fs";
|
|
8403
|
-
import { basename as basename4, dirname as dirname3, join as
|
|
8391
|
+
import { basename as basename4, dirname as dirname3, join as join12, resolve as resolve14, sep as sep4 } from "path";
|
|
8404
8392
|
import { Readable as Readable2 } from "stream";
|
|
8405
8393
|
|
|
8406
8394
|
// app/lib/data-path.ts
|
|
@@ -8408,17 +8396,28 @@ import { realpathSync as realpathSync3 } from "fs";
|
|
|
8408
8396
|
import { resolve as resolve12, normalize, sep as sep2, relative } from "path";
|
|
8409
8397
|
var PLATFORM_ROOT5 = process.env.MAXY_PLATFORM_ROOT ?? resolve12(process.cwd(), "../platform");
|
|
8410
8398
|
var DATA_ROOT = resolve12(PLATFORM_ROOT5, "..", "data");
|
|
8411
|
-
|
|
8399
|
+
var CLAUDE_UPLOADS_ROOT = process.env.CLAUDE_CONFIG_DIR ? resolve12(process.env.CLAUDE_CONFIG_DIR, "uploads") : null;
|
|
8400
|
+
var ACCOUNT_PARTITION_DIRS = /* @__PURE__ */ new Set(["uploads", "accounts"]);
|
|
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;
|
|
8402
|
+
function crossesForeignAccountPartition(relPath2, accountId) {
|
|
8403
|
+
const segs = relPath2.split("/").filter(Boolean);
|
|
8404
|
+
if (segs.length < 2) return false;
|
|
8405
|
+
if (!ACCOUNT_PARTITION_DIRS.has(segs[0])) return false;
|
|
8406
|
+
const owner = segs[1];
|
|
8407
|
+
if (!ACCOUNT_UUID_RE.test(owner)) return false;
|
|
8408
|
+
return owner !== accountId;
|
|
8409
|
+
}
|
|
8410
|
+
function resolveUnderRoot(raw, rootAbs, rootLabel) {
|
|
8412
8411
|
const cleaned = normalize("/" + (raw ?? "").replace(/\\/g, "/")).replace(/^\/+/, "");
|
|
8413
|
-
const absolute = resolve12(
|
|
8414
|
-
let
|
|
8412
|
+
const absolute = resolve12(rootAbs, cleaned);
|
|
8413
|
+
let rootReal;
|
|
8415
8414
|
try {
|
|
8416
|
-
|
|
8415
|
+
rootReal = realpathSync3(rootAbs);
|
|
8417
8416
|
} catch (err) {
|
|
8418
8417
|
return {
|
|
8419
8418
|
ok: false,
|
|
8420
8419
|
status: 500,
|
|
8421
|
-
error:
|
|
8420
|
+
error: `${rootLabel} not accessible: ${err instanceof Error ? err.message : String(err)}`
|
|
8422
8421
|
};
|
|
8423
8422
|
}
|
|
8424
8423
|
let resolvedReal;
|
|
@@ -8427,8 +8426,8 @@ function resolveDataPath(raw) {
|
|
|
8427
8426
|
} catch (err) {
|
|
8428
8427
|
const code = err.code;
|
|
8429
8428
|
if (code === "ENOENT") {
|
|
8430
|
-
if (absolute !==
|
|
8431
|
-
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 };
|
|
8432
8431
|
}
|
|
8433
8432
|
return { ok: false, status: 404, error: "Not found" };
|
|
8434
8433
|
}
|
|
@@ -8438,11 +8437,20 @@ function resolveDataPath(raw) {
|
|
|
8438
8437
|
error: err instanceof Error ? err.message : String(err)
|
|
8439
8438
|
};
|
|
8440
8439
|
}
|
|
8441
|
-
if (resolvedReal !==
|
|
8442
|
-
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" };
|
|
8443
8452
|
}
|
|
8444
|
-
|
|
8445
|
-
return { ok: true, absolute: resolvedReal, dataRootReal, relative: relPath2 };
|
|
8453
|
+
return resolveUnderRoot(raw, CLAUDE_UPLOADS_ROOT, "CLAUDE_UPLOADS_ROOT");
|
|
8446
8454
|
}
|
|
8447
8455
|
|
|
8448
8456
|
// ../lib/graph-trash/src/index.ts
|
|
@@ -8758,11 +8766,291 @@ async function cascadeDeleteDocument(params) {
|
|
|
8758
8766
|
}
|
|
8759
8767
|
}
|
|
8760
8768
|
|
|
8769
|
+
// app/lib/file-index.ts
|
|
8770
|
+
import * as fsp from "fs/promises";
|
|
8771
|
+
import { resolve as resolve13, relative as relative2, join as join11, basename as basename3, extname as extname2, sep as sep3 } from "path";
|
|
8772
|
+
import { execFile as execFile2 } from "child_process";
|
|
8773
|
+
import { promisify as promisify2 } from "util";
|
|
8774
|
+
var execFileAsync2 = promisify2(execFile2);
|
|
8775
|
+
var CONTENT_CAP = 1e5;
|
|
8776
|
+
var RECONCILE_DEBOUNCE_MS = 1e4;
|
|
8777
|
+
var lastReconcileAt = /* @__PURE__ */ new Map();
|
|
8778
|
+
var TEXT_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
8779
|
+
".txt",
|
|
8780
|
+
".md",
|
|
8781
|
+
".markdown",
|
|
8782
|
+
".csv",
|
|
8783
|
+
".tsv",
|
|
8784
|
+
".json",
|
|
8785
|
+
".jsonl",
|
|
8786
|
+
".log",
|
|
8787
|
+
".yaml",
|
|
8788
|
+
".yml",
|
|
8789
|
+
".xml",
|
|
8790
|
+
".toml",
|
|
8791
|
+
".ini",
|
|
8792
|
+
".env",
|
|
8793
|
+
".sql",
|
|
8794
|
+
".sh",
|
|
8795
|
+
".py",
|
|
8796
|
+
".js",
|
|
8797
|
+
".ts",
|
|
8798
|
+
".tsx",
|
|
8799
|
+
".jsx",
|
|
8800
|
+
".css",
|
|
8801
|
+
".svg",
|
|
8802
|
+
".rtf"
|
|
8803
|
+
]);
|
|
8804
|
+
var HTML_EXTENSIONS = /* @__PURE__ */ new Set([".html", ".htm"]);
|
|
8805
|
+
var MIME_BY_EXT2 = {
|
|
8806
|
+
".pdf": "application/pdf",
|
|
8807
|
+
".txt": "text/plain",
|
|
8808
|
+
".md": "text/markdown",
|
|
8809
|
+
".markdown": "text/markdown",
|
|
8810
|
+
".csv": "text/csv",
|
|
8811
|
+
".json": "application/json",
|
|
8812
|
+
".html": "text/html",
|
|
8813
|
+
".htm": "text/html",
|
|
8814
|
+
".png": "image/png",
|
|
8815
|
+
".jpg": "image/jpeg",
|
|
8816
|
+
".jpeg": "image/jpeg",
|
|
8817
|
+
".gif": "image/gif",
|
|
8818
|
+
".webp": "image/webp"
|
|
8819
|
+
};
|
|
8820
|
+
function mimeForExt(ext) {
|
|
8821
|
+
return MIME_BY_EXT2[ext.toLowerCase()] ?? "application/octet-stream";
|
|
8822
|
+
}
|
|
8823
|
+
async function extractPdf(absolute) {
|
|
8824
|
+
const { stdout } = await execFileAsync2("pdftotext", [absolute, "-"], {
|
|
8825
|
+
maxBuffer: 10 * 1024 * 1024
|
|
8826
|
+
});
|
|
8827
|
+
return stdout.trim();
|
|
8828
|
+
}
|
|
8829
|
+
function stripHtml(html) {
|
|
8830
|
+
return html.replace(/<script[\s\S]*?<\/script>/gi, " ").replace(/<style[\s\S]*?<\/style>/gi, " ").replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
|
|
8831
|
+
}
|
|
8832
|
+
async function extractFileContent(absolute) {
|
|
8833
|
+
const ext = extname2(absolute).toLowerCase();
|
|
8834
|
+
try {
|
|
8835
|
+
if (ext === ".pdf") return (await extractPdf(absolute)).slice(0, CONTENT_CAP);
|
|
8836
|
+
if (HTML_EXTENSIONS.has(ext)) {
|
|
8837
|
+
const raw = await fsp.readFile(absolute, "utf-8");
|
|
8838
|
+
return stripHtml(raw).slice(0, CONTENT_CAP);
|
|
8839
|
+
}
|
|
8840
|
+
if (TEXT_EXTENSIONS.has(ext)) {
|
|
8841
|
+
const raw = await fsp.readFile(absolute, "utf-8");
|
|
8842
|
+
return raw.slice(0, CONTENT_CAP);
|
|
8843
|
+
}
|
|
8844
|
+
return "";
|
|
8845
|
+
} catch (err) {
|
|
8846
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
8847
|
+
console.error(`[file-index] extract-failed path="${relative2(DATA_ROOT, absolute)}" err="${msg}"`);
|
|
8848
|
+
return "";
|
|
8849
|
+
}
|
|
8850
|
+
}
|
|
8851
|
+
async function readDisplayName(absolute) {
|
|
8852
|
+
const dir = absolute.slice(0, absolute.length - basename3(absolute).length);
|
|
8853
|
+
const base = basename3(absolute);
|
|
8854
|
+
const dot = base.lastIndexOf(".");
|
|
8855
|
+
const stem = dot === -1 ? base : base.slice(0, dot);
|
|
8856
|
+
if (base === `${stem}.meta.json`) return null;
|
|
8857
|
+
try {
|
|
8858
|
+
const raw = await fsp.readFile(join11(dir, `${stem}.meta.json`), "utf-8");
|
|
8859
|
+
const meta = JSON.parse(raw);
|
|
8860
|
+
const dn = meta.displayName ?? meta.filename;
|
|
8861
|
+
return typeof dn === "string" && dn.length > 0 ? dn : null;
|
|
8862
|
+
} catch {
|
|
8863
|
+
return null;
|
|
8864
|
+
}
|
|
8865
|
+
}
|
|
8866
|
+
async function walkSubtree(root, dataRoot, out) {
|
|
8867
|
+
let entries;
|
|
8868
|
+
try {
|
|
8869
|
+
entries = await fsp.readdir(root, { withFileTypes: true });
|
|
8870
|
+
} catch (err) {
|
|
8871
|
+
if (err.code === "ENOENT") return { clean: true };
|
|
8872
|
+
console.error(`[file-index] walk-error dir="${relative2(dataRoot, root)}" err="${err.message}"`);
|
|
8873
|
+
return { clean: false };
|
|
8874
|
+
}
|
|
8875
|
+
let clean = true;
|
|
8876
|
+
for (const entry of entries) {
|
|
8877
|
+
if (entry.isSymbolicLink()) continue;
|
|
8878
|
+
const abs = join11(root, entry.name);
|
|
8879
|
+
if (entry.isDirectory()) {
|
|
8880
|
+
const sub = await walkSubtree(abs, dataRoot, out);
|
|
8881
|
+
if (!sub.clean) clean = false;
|
|
8882
|
+
} else if (entry.isFile()) {
|
|
8883
|
+
try {
|
|
8884
|
+
const st = await fsp.stat(abs);
|
|
8885
|
+
out.push({
|
|
8886
|
+
absolute: abs,
|
|
8887
|
+
relativePath: relative2(dataRoot, abs).split(sep3).join("/"),
|
|
8888
|
+
sizeBytes: st.size,
|
|
8889
|
+
modifiedAt: st.mtime.toISOString()
|
|
8890
|
+
});
|
|
8891
|
+
} catch (err) {
|
|
8892
|
+
console.error(`[file-index] stat-error path="${relative2(dataRoot, abs)}" err="${err.message}"`);
|
|
8893
|
+
clean = false;
|
|
8894
|
+
}
|
|
8895
|
+
}
|
|
8896
|
+
}
|
|
8897
|
+
return { clean };
|
|
8898
|
+
}
|
|
8899
|
+
async function upsertFileArtifact(session, f) {
|
|
8900
|
+
await session.run(
|
|
8901
|
+
`MERGE (f:FileArtifact {accountId: $accountId, relativePath: $relativePath})
|
|
8902
|
+
SET f.name = $name,
|
|
8903
|
+
f.displayName = $displayName,
|
|
8904
|
+
f.mimeType = $mimeType,
|
|
8905
|
+
f.sizeBytes = $sizeBytes,
|
|
8906
|
+
f.modifiedAt = $modifiedAt,
|
|
8907
|
+
f.content = $content,
|
|
8908
|
+
f.embedding = $embedding`,
|
|
8909
|
+
f
|
|
8910
|
+
);
|
|
8911
|
+
}
|
|
8912
|
+
async function buildArtifact(accountId, wf, embed2) {
|
|
8913
|
+
const name = basename3(wf.absolute);
|
|
8914
|
+
const content = await extractFileContent(wf.absolute);
|
|
8915
|
+
const displayName = await readDisplayName(wf.absolute);
|
|
8916
|
+
const embedInput = `${name}
|
|
8917
|
+
${content}`.slice(0, CONTENT_CAP);
|
|
8918
|
+
let embedding = null;
|
|
8919
|
+
const embedStart = Date.now();
|
|
8920
|
+
try {
|
|
8921
|
+
embedding = await embed2(embedInput);
|
|
8922
|
+
} catch (err) {
|
|
8923
|
+
console.error(`[file-index] embed-failed path="${wf.relativePath}" err="${err.message}"`);
|
|
8924
|
+
}
|
|
8925
|
+
console.error(
|
|
8926
|
+
`[file-index] index-file account=${accountId} path="${wf.relativePath}" chars=${content.length} embed-ms=${Date.now() - embedStart}`
|
|
8927
|
+
);
|
|
8928
|
+
return {
|
|
8929
|
+
accountId,
|
|
8930
|
+
relativePath: wf.relativePath,
|
|
8931
|
+
name,
|
|
8932
|
+
displayName,
|
|
8933
|
+
mimeType: mimeForExt(extname2(wf.absolute)),
|
|
8934
|
+
sizeBytes: wf.sizeBytes,
|
|
8935
|
+
modifiedAt: wf.modifiedAt,
|
|
8936
|
+
content,
|
|
8937
|
+
embedding
|
|
8938
|
+
};
|
|
8939
|
+
}
|
|
8940
|
+
async function withSession(opts, fn) {
|
|
8941
|
+
if (opts?.session) return fn(opts.session);
|
|
8942
|
+
const session = getSession();
|
|
8943
|
+
try {
|
|
8944
|
+
return await fn(session);
|
|
8945
|
+
} finally {
|
|
8946
|
+
await session.close();
|
|
8947
|
+
}
|
|
8948
|
+
}
|
|
8949
|
+
async function reconcileFileIndex(accountId, opts) {
|
|
8950
|
+
const dataRoot = opts?.dataRoot ?? DATA_ROOT;
|
|
8951
|
+
const embed2 = opts?.embed ?? embed;
|
|
8952
|
+
const t0 = Date.now();
|
|
8953
|
+
return withSession(opts, async (session) => {
|
|
8954
|
+
const walked = [];
|
|
8955
|
+
const subtreeRoots = [
|
|
8956
|
+
resolve13(dataRoot, "uploads", accountId),
|
|
8957
|
+
resolve13(dataRoot, "accounts", accountId)
|
|
8958
|
+
];
|
|
8959
|
+
let clean = true;
|
|
8960
|
+
for (const root of subtreeRoots) {
|
|
8961
|
+
const r = await walkSubtree(root, dataRoot, walked);
|
|
8962
|
+
if (!r.clean) clean = false;
|
|
8963
|
+
}
|
|
8964
|
+
const existing = /* @__PURE__ */ new Map();
|
|
8965
|
+
const existingRes = await session.run(
|
|
8966
|
+
`MATCH (f:FileArtifact {accountId: $accountId})
|
|
8967
|
+
RETURN f.relativePath AS relativePath, f.sizeBytes AS sizeBytes,
|
|
8968
|
+
f.modifiedAt AS modifiedAt, f.embedding IS NOT NULL AS hasEmbedding`,
|
|
8969
|
+
{ accountId }
|
|
8970
|
+
);
|
|
8971
|
+
for (const rec of existingRes.records) {
|
|
8972
|
+
existing.set(rec.get("relativePath"), {
|
|
8973
|
+
sizeBytes: typeof rec.get("sizeBytes")?.toNumber === "function" ? rec.get("sizeBytes").toNumber() : Number(rec.get("sizeBytes")),
|
|
8974
|
+
modifiedAt: rec.get("modifiedAt"),
|
|
8975
|
+
hasEmbedding: rec.get("hasEmbedding") === true
|
|
8976
|
+
});
|
|
8977
|
+
}
|
|
8978
|
+
let upserted = 0;
|
|
8979
|
+
let skipped = 0;
|
|
8980
|
+
for (const wf of walked) {
|
|
8981
|
+
const prior = existing.get(wf.relativePath);
|
|
8982
|
+
const unchanged = prior && prior.sizeBytes === wf.sizeBytes && prior.modifiedAt === wf.modifiedAt && prior.hasEmbedding;
|
|
8983
|
+
if (unchanged) {
|
|
8984
|
+
skipped++;
|
|
8985
|
+
continue;
|
|
8986
|
+
}
|
|
8987
|
+
await upsertFileArtifact(session, await buildArtifact(accountId, wf, embed2));
|
|
8988
|
+
upserted++;
|
|
8989
|
+
}
|
|
8990
|
+
let pruned = 0;
|
|
8991
|
+
if (clean) {
|
|
8992
|
+
const foundPaths = walked.map((w) => w.relativePath);
|
|
8993
|
+
const pruneRes = await session.run(
|
|
8994
|
+
`MATCH (f:FileArtifact {accountId: $accountId})
|
|
8995
|
+
WHERE NOT f.relativePath IN $foundPaths
|
|
8996
|
+
DETACH DELETE f
|
|
8997
|
+
RETURN count(f) AS pruned`,
|
|
8998
|
+
{ accountId, foundPaths }
|
|
8999
|
+
);
|
|
9000
|
+
const c = pruneRes.records[0]?.get("pruned");
|
|
9001
|
+
pruned = typeof c?.toNumber === "function" ? c.toNumber() : Number(c ?? 0);
|
|
9002
|
+
}
|
|
9003
|
+
console.error(
|
|
9004
|
+
`[file-index] reconcile account=${accountId} walked=${walked.length} upserted=${upserted} skipped=${skipped} pruned=${pruned} clean=${clean} ms=${Date.now() - t0}`
|
|
9005
|
+
);
|
|
9006
|
+
return { walked: walked.length, upserted, skipped, pruned };
|
|
9007
|
+
});
|
|
9008
|
+
}
|
|
9009
|
+
async function reconcileFileIndexDebounced(accountId, opts) {
|
|
9010
|
+
const now = Date.now();
|
|
9011
|
+
const last = lastReconcileAt.get(accountId) ?? 0;
|
|
9012
|
+
if (now - last < RECONCILE_DEBOUNCE_MS) return;
|
|
9013
|
+
lastReconcileAt.set(accountId, now);
|
|
9014
|
+
try {
|
|
9015
|
+
await reconcileFileIndex(accountId, opts);
|
|
9016
|
+
} catch (err) {
|
|
9017
|
+
console.error(`[file-index] reconcile-failed account=${accountId} err="${err.message}"`);
|
|
9018
|
+
}
|
|
9019
|
+
}
|
|
9020
|
+
async function indexFile(accountId, relativePath, opts) {
|
|
9021
|
+
const dataRoot = opts?.dataRoot ?? DATA_ROOT;
|
|
9022
|
+
const embed2 = opts?.embed ?? embed;
|
|
9023
|
+
const absolute = resolve13(dataRoot, relativePath);
|
|
9024
|
+
await withSession(opts, async (session) => {
|
|
9025
|
+
const st = await fsp.stat(absolute);
|
|
9026
|
+
const wf = {
|
|
9027
|
+
absolute,
|
|
9028
|
+
relativePath,
|
|
9029
|
+
sizeBytes: st.size,
|
|
9030
|
+
modifiedAt: st.mtime.toISOString()
|
|
9031
|
+
};
|
|
9032
|
+
await upsertFileArtifact(session, await buildArtifact(accountId, wf, embed2));
|
|
9033
|
+
});
|
|
9034
|
+
}
|
|
9035
|
+
async function dropFileIndex(accountId, relativePath, opts) {
|
|
9036
|
+
await withSession(opts, async (session) => {
|
|
9037
|
+
const res = await session.run(
|
|
9038
|
+
`MATCH (f:FileArtifact {accountId: $accountId, relativePath: $relativePath})
|
|
9039
|
+
DETACH DELETE f
|
|
9040
|
+
RETURN count(f) AS dropped`,
|
|
9041
|
+
{ accountId, relativePath }
|
|
9042
|
+
);
|
|
9043
|
+
const c = res.records[0]?.get("dropped");
|
|
9044
|
+
const dropped = typeof c?.toNumber === "function" ? c.toNumber() : Number(c ?? 0);
|
|
9045
|
+
console.error(`[file-index] drop account=${accountId} path="${relativePath}" dropped=${dropped}`);
|
|
9046
|
+
});
|
|
9047
|
+
}
|
|
9048
|
+
|
|
8761
9049
|
// server/routes/admin/files.ts
|
|
8762
9050
|
var UUID_RE3 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
8763
9051
|
async function readMeta(absDir, baseName) {
|
|
8764
9052
|
try {
|
|
8765
|
-
const raw = await readFile4(
|
|
9053
|
+
const raw = await readFile4(join12(absDir, `${baseName}.meta.json`), "utf8");
|
|
8766
9054
|
const parsed = JSON.parse(raw);
|
|
8767
9055
|
if (typeof parsed?.filename === "string") {
|
|
8768
9056
|
return { filename: parsed.filename, mimeType: typeof parsed.mimeType === "string" ? parsed.mimeType : void 0 };
|
|
@@ -8773,16 +9061,16 @@ async function readMeta(absDir, baseName) {
|
|
|
8773
9061
|
}
|
|
8774
9062
|
async function readAccountNames() {
|
|
8775
9063
|
const map = /* @__PURE__ */ new Map();
|
|
8776
|
-
const accountsDir =
|
|
9064
|
+
const accountsDir = resolve14(DATA_ROOT, "accounts");
|
|
8777
9065
|
let names;
|
|
8778
9066
|
try {
|
|
8779
|
-
names = await
|
|
9067
|
+
names = await readdir3(accountsDir);
|
|
8780
9068
|
} catch {
|
|
8781
9069
|
return map;
|
|
8782
9070
|
}
|
|
8783
9071
|
for (const name of names) {
|
|
8784
9072
|
if (!UUID_RE3.test(name)) continue;
|
|
8785
|
-
const configPath2 =
|
|
9073
|
+
const configPath2 = resolve14(accountsDir, name, "account.json");
|
|
8786
9074
|
try {
|
|
8787
9075
|
const raw = await readFile4(configPath2, "utf8");
|
|
8788
9076
|
const parsed = JSON.parse(raw);
|
|
@@ -8800,7 +9088,7 @@ async function readAccountNames() {
|
|
|
8800
9088
|
}
|
|
8801
9089
|
async function enrich(absolute, entry, accountNames) {
|
|
8802
9090
|
if (entry.kind === "directory" && UUID_RE3.test(entry.name)) {
|
|
8803
|
-
const meta = await readMeta(
|
|
9091
|
+
const meta = await readMeta(join12(absolute, entry.name), entry.name);
|
|
8804
9092
|
if (meta?.filename) {
|
|
8805
9093
|
entry.displayName = meta.filename;
|
|
8806
9094
|
entry.mimeType = meta.mimeType;
|
|
@@ -8831,6 +9119,23 @@ function buildDisplayPath(relPath2, accountNames) {
|
|
|
8831
9119
|
return dn ? { name: seg, displayName: dn } : { name: seg };
|
|
8832
9120
|
});
|
|
8833
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
|
+
}
|
|
8834
9139
|
var app15 = new Hono();
|
|
8835
9140
|
app15.get("/", requireAdminSession, async (c) => {
|
|
8836
9141
|
const cacheKey = c.var.cacheKey;
|
|
@@ -8852,14 +9157,14 @@ app15.get("/", requireAdminSession, async (c) => {
|
|
|
8852
9157
|
if (!info.isDirectory()) {
|
|
8853
9158
|
return c.json({ error: "Path is not a directory \u2014 use /api/admin/files/download for files" }, 400);
|
|
8854
9159
|
}
|
|
8855
|
-
const names = await
|
|
9160
|
+
const names = await readdir3(absolute);
|
|
8856
9161
|
const entries = [];
|
|
8857
9162
|
for (const name of names) {
|
|
8858
9163
|
if (UUID_RE3.test(name.replace(/\.meta\.json$/, "")) && name.endsWith(".meta.json")) {
|
|
8859
9164
|
continue;
|
|
8860
9165
|
}
|
|
8861
9166
|
try {
|
|
8862
|
-
const entryPath =
|
|
9167
|
+
const entryPath = join12(absolute, name);
|
|
8863
9168
|
const s = await stat4(entryPath);
|
|
8864
9169
|
entries.push({
|
|
8865
9170
|
name,
|
|
@@ -8895,20 +9200,36 @@ app15.get("/", requireAdminSession, async (c) => {
|
|
|
8895
9200
|
});
|
|
8896
9201
|
app15.get("/download", requireAdminSession, async (c) => {
|
|
8897
9202
|
const cacheKey = c.var.cacheKey;
|
|
8898
|
-
|
|
9203
|
+
const accountId = getAccountIdForSession(cacheKey);
|
|
9204
|
+
if (!accountId) {
|
|
8899
9205
|
console.error(`[data] auth-rejected endpoint="/api/admin/files/download" reason="no account for session"`);
|
|
8900
9206
|
return c.json({ error: "Account not found for session" }, 401);
|
|
8901
9207
|
}
|
|
8902
9208
|
const rawPath = c.req.query("path") ?? "";
|
|
8903
9209
|
if (!rawPath) return c.json({ error: "path required" }, 400);
|
|
8904
|
-
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);
|
|
8905
9215
|
if (!resolution.ok) {
|
|
8906
9216
|
if (resolution.status === 403) {
|
|
8907
|
-
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"}"`);
|
|
8908
9218
|
}
|
|
8909
9219
|
return c.json({ error: resolution.error }, resolution.status);
|
|
8910
9220
|
}
|
|
8911
9221
|
const { absolute, relative: relPath2 } = resolution;
|
|
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)) {
|
|
9230
|
+
console.error(`[data] account-scope-blocked endpoint="/api/admin/files/download" path="${relPath2}" account=${accountId.slice(0, 8)}\u2026`);
|
|
9231
|
+
return c.json({ error: "Not found" }, 404);
|
|
9232
|
+
}
|
|
8912
9233
|
try {
|
|
8913
9234
|
const info = await stat4(absolute);
|
|
8914
9235
|
if (!info.isFile()) {
|
|
@@ -8918,7 +9239,7 @@ app15.get("/download", requireAdminSession, async (c) => {
|
|
|
8918
9239
|
const mimeType = detectMimeType(absolute);
|
|
8919
9240
|
const nodeStream = createReadStream2(absolute);
|
|
8920
9241
|
const webStream = Readable2.toWeb(nodeStream);
|
|
8921
|
-
console.error(`[data] file-download path="${relPath2}" size=${info.size}`);
|
|
9242
|
+
console.error(`[data] file-download root="${root}" path="${relPath2}" size=${info.size}`);
|
|
8922
9243
|
const safeQuotedName = filename.replace(/[\r\n"\\]/g, "_");
|
|
8923
9244
|
const encodedName = encodeURIComponent(filename);
|
|
8924
9245
|
return new Response(webStream, {
|
|
@@ -8973,13 +9294,13 @@ app15.post("/upload", requireAdminSession, async (c) => {
|
|
|
8973
9294
|
}
|
|
8974
9295
|
const safeName = basename4(file.name).replace(/[\0/\\]/g, "_");
|
|
8975
9296
|
const finalName = `${Date.now()}-${safeName}`;
|
|
8976
|
-
const destDir =
|
|
8977
|
-
const destPath =
|
|
9297
|
+
const destDir = resolve14(DATA_ROOT, "uploads", accountId);
|
|
9298
|
+
const destPath = resolve14(destDir, finalName);
|
|
8978
9299
|
try {
|
|
8979
9300
|
await mkdir3(destDir, { recursive: true });
|
|
8980
9301
|
const dataRootReal = realpathSync4(DATA_ROOT);
|
|
8981
9302
|
const destDirReal = realpathSync4(destDir);
|
|
8982
|
-
if (destDirReal !== dataRootReal && !destDirReal.startsWith(dataRootReal +
|
|
9303
|
+
if (destDirReal !== dataRootReal && !destDirReal.startsWith(dataRootReal + sep4)) {
|
|
8983
9304
|
console.error(`[data] path-traversal-blocked requested="uploads/${accountId}" resolved="${destDirReal}"`);
|
|
8984
9305
|
return c.json({ error: "Upload destination escapes DATA_ROOT" }, 403);
|
|
8985
9306
|
}
|
|
@@ -8992,6 +9313,9 @@ app15.post("/upload", requireAdminSession, async (c) => {
|
|
|
8992
9313
|
}
|
|
8993
9314
|
const relativeDest = `uploads/${accountId}/${finalName}`;
|
|
8994
9315
|
console.error(`[data] file-upload filename="${safeName}" size=${file.size} dest="${relativeDest}"`);
|
|
9316
|
+
void indexFile(accountId, relativeDest).catch((err) => {
|
|
9317
|
+
console.error(`[data] file-upload index-failed dest="${relativeDest}" err="${err instanceof Error ? err.message : String(err)}"`);
|
|
9318
|
+
});
|
|
8995
9319
|
return c.json({
|
|
8996
9320
|
path: relativeDest,
|
|
8997
9321
|
filename: finalName,
|
|
@@ -9032,7 +9356,7 @@ app15.delete("/", requireAdminSession, async (c) => {
|
|
|
9032
9356
|
}
|
|
9033
9357
|
const dot = base.lastIndexOf(".");
|
|
9034
9358
|
const stem = dot === -1 ? base : base.slice(0, dot);
|
|
9035
|
-
const sidecarPath = UUID_RE3.test(stem) && base !== `${stem}.meta.json` ?
|
|
9359
|
+
const sidecarPath = UUID_RE3.test(stem) && base !== `${stem}.meta.json` ? join12(dirname3(absolute), `${stem}.meta.json`) : null;
|
|
9036
9360
|
await unlink2(absolute);
|
|
9037
9361
|
if (sidecarPath) {
|
|
9038
9362
|
try {
|
|
@@ -9041,6 +9365,11 @@ app15.delete("/", requireAdminSession, async (c) => {
|
|
|
9041
9365
|
}
|
|
9042
9366
|
}
|
|
9043
9367
|
console.error(`[data] file-delete path="${relPath2}" bytes=${info.size}`);
|
|
9368
|
+
try {
|
|
9369
|
+
await dropFileIndex(accountId, relPath2);
|
|
9370
|
+
} catch (err) {
|
|
9371
|
+
console.error(`[data] file-delete index-drop-failed path="${relPath2}" err="${err instanceof Error ? err.message : String(err)}"`);
|
|
9372
|
+
}
|
|
9044
9373
|
const parsed = parseAttachmentPath(relPath2);
|
|
9045
9374
|
if (parsed) {
|
|
9046
9375
|
try {
|
|
@@ -9860,6 +10189,9 @@ app16.get("/", requireAdminSession, async (c) => {
|
|
|
9860
10189
|
}
|
|
9861
10190
|
const wantsBodyFulltext = !wildcard && labels.some((l) => CONVERSATION_PARENT_LABELS.has(l));
|
|
9862
10191
|
const forwardedLabels = wildcard ? void 0 : wantsBodyFulltext ? Array.from(/* @__PURE__ */ new Set([...labels, ...MESSAGE_FAMILY_LABELS])) : labels;
|
|
10192
|
+
if (labels.includes("FileArtifact")) {
|
|
10193
|
+
await reconcileFileIndexDebounced(accountId);
|
|
10194
|
+
}
|
|
9863
10195
|
const started = Date.now();
|
|
9864
10196
|
const session = getSession();
|
|
9865
10197
|
try {
|
|
@@ -11064,43 +11396,130 @@ var WRITE_CYPHER = `
|
|
|
11064
11396
|
`;
|
|
11065
11397
|
var graph_default_view_default = app21;
|
|
11066
11398
|
|
|
11067
|
-
// server/routes/admin/
|
|
11068
|
-
|
|
11069
|
-
|
|
11399
|
+
// server/routes/admin/sidebar-artefacts.ts
|
|
11400
|
+
import neo4j2 from "neo4j-driver";
|
|
11401
|
+
import { readFile as readFile5, readdir as readdir4, stat as stat5 } from "fs/promises";
|
|
11402
|
+
import { resolve as resolve16, relative as relative4, isAbsolute, sep as sep6 } from "path";
|
|
11403
|
+
import { existsSync as existsSync16 } from "fs";
|
|
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) {
|
|
11070
11409
|
try {
|
|
11071
|
-
const
|
|
11072
|
-
const
|
|
11073
|
-
if (!
|
|
11074
|
-
|
|
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;
|
|
11414
|
+
} catch {
|
|
11415
|
+
return null;
|
|
11416
|
+
}
|
|
11417
|
+
}
|
|
11418
|
+
function dataFileInDir(dir) {
|
|
11419
|
+
let entries;
|
|
11420
|
+
try {
|
|
11421
|
+
entries = readdirSync8(dir, { withFileTypes: true }).flatMap((e) => e.isFile() ? [e.name] : []);
|
|
11422
|
+
} catch {
|
|
11423
|
+
return null;
|
|
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
|
+
};
|
|
11075
11441
|
}
|
|
11076
|
-
const attachment = await storeGeneratedFile(accountId, filePath);
|
|
11077
|
-
console.error(
|
|
11078
|
-
`[admin:file-attach] stored path=${filePath} id=${attachment.attachmentId} filename=${JSON.stringify(attachment.filename)} size=${attachment.sizeBytes} mime=${attachment.mimeType}`
|
|
11079
|
-
);
|
|
11080
|
-
return c.json({
|
|
11081
|
-
attachmentId: attachment.attachmentId,
|
|
11082
|
-
filename: attachment.filename,
|
|
11083
|
-
sizeBytes: attachment.sizeBytes,
|
|
11084
|
-
mimeType: attachment.mimeType
|
|
11085
|
-
});
|
|
11086
|
-
} catch (err) {
|
|
11087
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
11088
|
-
console.error(`[admin:file-attach] error: ${message}`);
|
|
11089
|
-
return c.json({ error: message }, 500);
|
|
11090
11442
|
}
|
|
11091
|
-
|
|
11092
|
-
|
|
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
|
+
}
|
|
11093
11488
|
|
|
11094
11489
|
// server/routes/admin/sidebar-artefacts.ts
|
|
11095
|
-
import neo4j2 from "neo4j-driver";
|
|
11096
|
-
import { readFile as readFile5, readdir as readdir3, stat as stat5 } from "fs/promises";
|
|
11097
|
-
import { resolve as resolve14, relative as relative2, isAbsolute } from "path";
|
|
11098
|
-
import { existsSync as existsSync16 } from "fs";
|
|
11099
11490
|
var LIMIT = 50;
|
|
11100
11491
|
var TEXT_MIME_PREFIXES = ["text/", "application/json", "application/markdown"];
|
|
11101
11492
|
var ADMIN_AGENT_FILES = ["IDENTITY.md", "SOUL.md", "KNOWLEDGE.md"];
|
|
11102
|
-
|
|
11103
|
-
|
|
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 };
|
|
11520
|
+
}
|
|
11521
|
+
var app22 = new Hono();
|
|
11522
|
+
app22.get("/", requireAdminSession, async (c) => {
|
|
11104
11523
|
const cacheKey = c.var.cacheKey;
|
|
11105
11524
|
const accountId = getAccountIdForSession(cacheKey);
|
|
11106
11525
|
if (!accountId) {
|
|
@@ -11111,7 +11530,7 @@ app23.get("/", requireAdminSession, async (c) => {
|
|
|
11111
11530
|
if (docs === null) {
|
|
11112
11531
|
return c.json({ error: "Failed to load artefacts" }, 500);
|
|
11113
11532
|
}
|
|
11114
|
-
const accountDir =
|
|
11533
|
+
const accountDir = resolve16(ACCOUNTS_DIR, accountId);
|
|
11115
11534
|
const agents = await fetchAgentTemplateRows(accountDir);
|
|
11116
11535
|
const artefacts = [...docs, ...agents].sort(
|
|
11117
11536
|
(a, b) => (b.updatedAt ?? "").localeCompare(a.updatedAt ?? "")
|
|
@@ -11130,7 +11549,8 @@ async function fetchKnowledgeDocs(accountId) {
|
|
|
11130
11549
|
`MATCH (d:KnowledgeDocument { accountId: $accountId })
|
|
11131
11550
|
WHERE d.deletedAt IS NULL AND NOT d:Trashed
|
|
11132
11551
|
RETURN d.attachmentId AS id, d.name AS name, d.updatedAt AS updatedAt,
|
|
11133
|
-
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
|
|
11134
11554
|
ORDER BY d.updatedAt DESC
|
|
11135
11555
|
LIMIT $limit`,
|
|
11136
11556
|
{ accountId, limit: neo4j2.int(LIMIT) }
|
|
@@ -11140,7 +11560,9 @@ async function fetchKnowledgeDocs(accountId) {
|
|
|
11140
11560
|
name: r.get("name") ?? "",
|
|
11141
11561
|
updatedAt: r.get("updatedAt") ?? "",
|
|
11142
11562
|
attachmentId: r.get("attachmentId"),
|
|
11143
|
-
mimeType: r.get("mimeType") ?? ""
|
|
11563
|
+
mimeType: r.get("mimeType") ?? "",
|
|
11564
|
+
sourcePath: r.get("sourcePath") ?? null,
|
|
11565
|
+
sourceType: r.get("sourceType") ?? null
|
|
11144
11566
|
}));
|
|
11145
11567
|
} catch (err) {
|
|
11146
11568
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -11152,6 +11574,13 @@ async function fetchKnowledgeDocs(accountId) {
|
|
|
11152
11574
|
}
|
|
11153
11575
|
return Promise.all(metas.map(async (m) => {
|
|
11154
11576
|
const { content, skipReason } = await readArtefactContent(accountId, m.attachmentId, m.mimeType, m.name);
|
|
11577
|
+
const { downloadPath, downloadRoot } = resolveDocDownloadRow({
|
|
11578
|
+
accountId,
|
|
11579
|
+
attachmentId: m.attachmentId,
|
|
11580
|
+
name: m.name,
|
|
11581
|
+
sourcePath: m.sourcePath,
|
|
11582
|
+
sourceType: m.sourceType
|
|
11583
|
+
});
|
|
11155
11584
|
return {
|
|
11156
11585
|
id: m.id,
|
|
11157
11586
|
name: m.name,
|
|
@@ -11160,7 +11589,9 @@ async function fetchKnowledgeDocs(accountId) {
|
|
|
11160
11589
|
content,
|
|
11161
11590
|
editable: skipReason === null,
|
|
11162
11591
|
mimeType: m.mimeType,
|
|
11163
|
-
skipReason
|
|
11592
|
+
skipReason,
|
|
11593
|
+
downloadPath,
|
|
11594
|
+
downloadRoot
|
|
11164
11595
|
};
|
|
11165
11596
|
}));
|
|
11166
11597
|
}
|
|
@@ -11174,8 +11605,8 @@ async function readArtefactContent(accountId, attachmentId, mimeType, displayNam
|
|
|
11174
11605
|
logSkip(displayName, "non-text-mime", mimeType);
|
|
11175
11606
|
return { content: "", skipReason: "non-text-mime" };
|
|
11176
11607
|
}
|
|
11177
|
-
const accountDir =
|
|
11178
|
-
const dir =
|
|
11608
|
+
const accountDir = resolve16(ATTACHMENTS_ROOT, accountId);
|
|
11609
|
+
const dir = resolve16(accountDir, attachmentId);
|
|
11179
11610
|
try {
|
|
11180
11611
|
validateFilePathInAccount(dir, accountDir);
|
|
11181
11612
|
} catch {
|
|
@@ -11183,13 +11614,13 @@ async function readArtefactContent(accountId, attachmentId, mimeType, displayNam
|
|
|
11183
11614
|
return { content: "", skipReason: "containment-rejected" };
|
|
11184
11615
|
}
|
|
11185
11616
|
try {
|
|
11186
|
-
const entries = await
|
|
11617
|
+
const entries = await readdir4(dir);
|
|
11187
11618
|
const dataFile = entries.find((f) => !f.endsWith(".meta.json"));
|
|
11188
11619
|
if (!dataFile) {
|
|
11189
11620
|
logSkip(displayName, "missing-on-disk", mimeType);
|
|
11190
11621
|
return { content: "", skipReason: "missing-on-disk" };
|
|
11191
11622
|
}
|
|
11192
|
-
return { content: await readFile5(
|
|
11623
|
+
return { content: await readFile5(resolve16(dir, dataFile), "utf-8"), skipReason: null };
|
|
11193
11624
|
} catch (err) {
|
|
11194
11625
|
const message = err instanceof Error ? err.message : String(err);
|
|
11195
11626
|
console.error(`[admin/sidebar-artefacts] read-failed attachmentId=${attachmentId.slice(0, 8)} error="${message}"`);
|
|
@@ -11205,8 +11636,8 @@ function logSkip(name, reason, mimeType) {
|
|
|
11205
11636
|
async function fetchAgentTemplateRows(accountDir) {
|
|
11206
11637
|
const rows = [];
|
|
11207
11638
|
for (const filename of ADMIN_AGENT_FILES) {
|
|
11208
|
-
const overridePath =
|
|
11209
|
-
const bundledPath =
|
|
11639
|
+
const overridePath = resolve16(accountDir, "agents", "admin", filename);
|
|
11640
|
+
const bundledPath = resolve16(PLATFORM_ROOT, "templates", "agents", "admin", filename);
|
|
11210
11641
|
const labelStem = filename.replace(/\.md$/, "");
|
|
11211
11642
|
const row = await readAgentTemplateRow({
|
|
11212
11643
|
id: `agent-template:admin:${filename}`,
|
|
@@ -11220,12 +11651,12 @@ async function fetchAgentTemplateRows(accountDir) {
|
|
|
11220
11651
|
});
|
|
11221
11652
|
if (row) rows.push(row);
|
|
11222
11653
|
}
|
|
11223
|
-
const overrideDir =
|
|
11224
|
-
const bundledDir =
|
|
11654
|
+
const overrideDir = resolve16(accountDir, "specialists", "agents");
|
|
11655
|
+
const bundledDir = resolve16(PLATFORM_ROOT, "templates", "specialists", "agents");
|
|
11225
11656
|
const specialistNames = await unionSpecialistFilenames(overrideDir, bundledDir);
|
|
11226
11657
|
for (const filename of specialistNames) {
|
|
11227
|
-
const overridePath =
|
|
11228
|
-
const bundledPath =
|
|
11658
|
+
const overridePath = resolve16(overrideDir, filename);
|
|
11659
|
+
const bundledPath = resolve16(bundledDir, filename);
|
|
11229
11660
|
const row = await readAgentTemplateRow({
|
|
11230
11661
|
id: `agent-template:specialist:${filename}`,
|
|
11231
11662
|
displayName: filename.replace(/\.md$/, ""),
|
|
@@ -11245,7 +11676,7 @@ async function unionSpecialistFilenames(overrideDir, bundledDir) {
|
|
|
11245
11676
|
for (const dir of [overrideDir, bundledDir]) {
|
|
11246
11677
|
if (!existsSync16(dir)) continue;
|
|
11247
11678
|
try {
|
|
11248
|
-
const entries = await
|
|
11679
|
+
const entries = await readdir4(dir);
|
|
11249
11680
|
for (const entry of entries) {
|
|
11250
11681
|
if (entry.endsWith(".md")) names.add(entry);
|
|
11251
11682
|
}
|
|
@@ -11284,6 +11715,7 @@ async function readAgentTemplateRow(inp) {
|
|
|
11284
11715
|
readFile5(chosenPath, "utf-8"),
|
|
11285
11716
|
stat5(chosenPath)
|
|
11286
11717
|
]);
|
|
11718
|
+
const dlPath = toDataRootRelPath(chosenPath);
|
|
11287
11719
|
return {
|
|
11288
11720
|
id: inp.id,
|
|
11289
11721
|
name: inp.displayName,
|
|
@@ -11292,7 +11724,14 @@ async function readAgentTemplateRow(inp) {
|
|
|
11292
11724
|
content,
|
|
11293
11725
|
editable: inp.editable,
|
|
11294
11726
|
mimeType: "text/markdown",
|
|
11295
|
-
skipReason: null
|
|
11727
|
+
skipReason: null,
|
|
11728
|
+
// Override paths live under <accountDir> (inside DATA_ROOT) and are
|
|
11729
|
+
// downloadable; bundled-fallback templates live under PLATFORM_ROOT
|
|
11730
|
+
// (outside DATA_ROOT) → null, so the client shows "not downloadable".
|
|
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
|
|
11296
11735
|
};
|
|
11297
11736
|
} catch (err) {
|
|
11298
11737
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -11303,19 +11742,19 @@ async function readAgentTemplateRow(inp) {
|
|
|
11303
11742
|
}
|
|
11304
11743
|
}
|
|
11305
11744
|
function isWithin(target, root) {
|
|
11306
|
-
const rel =
|
|
11745
|
+
const rel = relative4(root, target);
|
|
11307
11746
|
return !rel.startsWith("..") && !isAbsolute(rel);
|
|
11308
11747
|
}
|
|
11309
|
-
var sidebar_artefacts_default =
|
|
11748
|
+
var sidebar_artefacts_default = app22;
|
|
11310
11749
|
|
|
11311
11750
|
// server/routes/admin/sidebar-artefact-save.ts
|
|
11312
|
-
import { mkdir as mkdir4, readdir as
|
|
11313
|
-
import { resolve as
|
|
11751
|
+
import { mkdir as mkdir4, readdir as readdir5, stat as stat6, writeFile as writeFile5 } from "fs/promises";
|
|
11752
|
+
import { resolve as resolve17 } from "path";
|
|
11314
11753
|
import { existsSync as existsSync17 } from "fs";
|
|
11315
11754
|
var ADMIN_AGENT_FILES2 = /* @__PURE__ */ new Set(["IDENTITY.md", "SOUL.md", "KNOWLEDGE.md"]);
|
|
11316
11755
|
var UUID_RE4 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
|
|
11317
|
-
var
|
|
11318
|
-
|
|
11756
|
+
var app23 = new Hono();
|
|
11757
|
+
app23.post("/", requireAdminSession, async (c) => {
|
|
11319
11758
|
const cacheKey = c.var.cacheKey;
|
|
11320
11759
|
const accountId = getAccountIdForSession(cacheKey);
|
|
11321
11760
|
if (!accountId) return c.json({ error: "Account not found for session" }, 401);
|
|
@@ -11323,7 +11762,7 @@ app24.post("/", requireAdminSession, async (c) => {
|
|
|
11323
11762
|
if (!body || typeof body.id !== "string" || typeof body.content !== "string") {
|
|
11324
11763
|
return c.json({ error: "id and content required" }, 400);
|
|
11325
11764
|
}
|
|
11326
|
-
const accountDir =
|
|
11765
|
+
const accountDir = resolve17(ACCOUNTS_DIR, accountId);
|
|
11327
11766
|
const resolved = await resolveSavePath(body.id, accountId, accountDir);
|
|
11328
11767
|
if (resolved.kind === "heal-race") {
|
|
11329
11768
|
c.header("Retry-After", "2");
|
|
@@ -11368,17 +11807,17 @@ async function resolveSavePath(id, accountId, accountDir) {
|
|
|
11368
11807
|
if (role !== "admin" || !ADMIN_AGENT_FILES2.has(filename)) {
|
|
11369
11808
|
return { kind: "reject", status: 400, reason: "invalid-id" };
|
|
11370
11809
|
}
|
|
11371
|
-
const parent =
|
|
11810
|
+
const parent = resolve17(accountDir, "agents", "admin");
|
|
11372
11811
|
await mkdir4(parent, { recursive: true });
|
|
11373
11812
|
try {
|
|
11374
11813
|
validateFilePathInAccount(parent, accountDir);
|
|
11375
11814
|
} catch {
|
|
11376
11815
|
return { kind: "reject", status: 400, reason: "containment-rejected" };
|
|
11377
11816
|
}
|
|
11378
|
-
return { kind: "admin-template", path:
|
|
11817
|
+
return { kind: "admin-template", path: resolve17(parent, filename) };
|
|
11379
11818
|
}
|
|
11380
11819
|
if (UUID_RE4.test(id)) {
|
|
11381
|
-
const dir =
|
|
11820
|
+
const dir = resolve17(ATTACHMENTS_ROOT, accountId, id);
|
|
11382
11821
|
if (!existsSync17(dir)) {
|
|
11383
11822
|
const attShort = id.slice(0, 8);
|
|
11384
11823
|
if (isHealPending(accountId, id)) {
|
|
@@ -11411,31 +11850,31 @@ async function resolveSavePath(id, accountId, accountDir) {
|
|
|
11411
11850
|
}
|
|
11412
11851
|
}
|
|
11413
11852
|
try {
|
|
11414
|
-
validateFilePathInAccount(dir,
|
|
11853
|
+
validateFilePathInAccount(dir, resolve17(ATTACHMENTS_ROOT, accountId));
|
|
11415
11854
|
} catch {
|
|
11416
11855
|
return { kind: "reject", status: 400, reason: "containment-rejected" };
|
|
11417
11856
|
}
|
|
11418
|
-
const entries = await
|
|
11857
|
+
const entries = await readdir5(dir);
|
|
11419
11858
|
const dataFile = entries.find((f) => !f.endsWith(".meta.json"));
|
|
11420
11859
|
if (!dataFile) {
|
|
11421
11860
|
return { kind: "reject", status: 400, reason: "not-found" };
|
|
11422
11861
|
}
|
|
11423
|
-
return { kind: "knowledge-doc", path:
|
|
11862
|
+
return { kind: "knowledge-doc", path: resolve17(dir, dataFile) };
|
|
11424
11863
|
}
|
|
11425
11864
|
return { kind: "reject", status: 400, reason: "invalid-id" };
|
|
11426
11865
|
}
|
|
11427
11866
|
function relPath(absPath, root) {
|
|
11428
11867
|
return absPath.startsWith(root) ? absPath.slice(root.length + 1) : absPath;
|
|
11429
11868
|
}
|
|
11430
|
-
var sidebar_artefact_save_default =
|
|
11869
|
+
var sidebar_artefact_save_default = app23;
|
|
11431
11870
|
|
|
11432
11871
|
// server/routes/admin/sidebar-artefact-content.ts
|
|
11433
|
-
import { readFile as readFile6, readdir as
|
|
11872
|
+
import { readFile as readFile6, readdir as readdir6 } from "fs/promises";
|
|
11434
11873
|
import { existsSync as existsSync18 } from "fs";
|
|
11435
|
-
import { resolve as
|
|
11874
|
+
import { resolve as resolve18 } from "path";
|
|
11436
11875
|
var UUID_RE5 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
|
|
11437
|
-
var
|
|
11438
|
-
|
|
11876
|
+
var app24 = new Hono();
|
|
11877
|
+
app24.get("/", requireAdminSession, async (c) => {
|
|
11439
11878
|
const cacheKey = c.var.cacheKey;
|
|
11440
11879
|
const accountId = getAccountIdForSession(cacheKey);
|
|
11441
11880
|
if (!accountId) return new Response("Unauthorized", { status: 401 });
|
|
@@ -11444,26 +11883,26 @@ app25.get("/", requireAdminSession, async (c) => {
|
|
|
11444
11883
|
console.error(`[admin/sidebar-artefact-content] not-found id=${id.slice(0, 8)}`);
|
|
11445
11884
|
return new Response("Not found", { status: 404 });
|
|
11446
11885
|
}
|
|
11447
|
-
const dir =
|
|
11886
|
+
const dir = resolve18(ATTACHMENTS_ROOT, accountId, id);
|
|
11448
11887
|
if (!existsSync18(dir)) {
|
|
11449
11888
|
console.error(`[admin/sidebar-artefact-content] not-found id=${id.slice(0, 8)}`);
|
|
11450
11889
|
return new Response("Not found", { status: 404 });
|
|
11451
11890
|
}
|
|
11452
11891
|
let meta;
|
|
11453
11892
|
try {
|
|
11454
|
-
meta = JSON.parse(await readFile6(
|
|
11893
|
+
meta = JSON.parse(await readFile6(resolve18(dir, `${id}.meta.json`), "utf-8"));
|
|
11455
11894
|
} catch {
|
|
11456
11895
|
console.error(`[admin/sidebar-artefact-content] not-found id=${id.slice(0, 8)}`);
|
|
11457
11896
|
return new Response("Not found", { status: 404 });
|
|
11458
11897
|
}
|
|
11459
|
-
const entries = await
|
|
11898
|
+
const entries = await readdir6(dir);
|
|
11460
11899
|
const dataFile = entries.find((f) => !f.endsWith(".meta.json"));
|
|
11461
11900
|
if (!dataFile) {
|
|
11462
11901
|
console.error(`[admin/sidebar-artefact-content] not-found id=${id.slice(0, 8)}`);
|
|
11463
11902
|
return new Response("Not found", { status: 404 });
|
|
11464
11903
|
}
|
|
11465
11904
|
const start = Date.now();
|
|
11466
|
-
const buffer = await readFile6(
|
|
11905
|
+
const buffer = await readFile6(resolve18(dir, dataFile));
|
|
11467
11906
|
const ms = Date.now() - start;
|
|
11468
11907
|
console.log(
|
|
11469
11908
|
`[admin/sidebar-artefact-content] account=${accountId} id=${id.slice(0, 8)} mime=${meta.mimeType} bytes=${buffer.length} ms=${ms}`
|
|
@@ -11476,7 +11915,7 @@ app25.get("/", requireAdminSession, async (c) => {
|
|
|
11476
11915
|
}
|
|
11477
11916
|
});
|
|
11478
11917
|
});
|
|
11479
|
-
var sidebar_artefact_content_default =
|
|
11918
|
+
var sidebar_artefact_content_default = app24;
|
|
11480
11919
|
|
|
11481
11920
|
// server/routes/admin/system-stats.ts
|
|
11482
11921
|
import { readFile as readFile7 } from "fs/promises";
|
|
@@ -11573,8 +12012,8 @@ async function sample() {
|
|
|
11573
12012
|
if (process.platform === "linux") return sampleLinux();
|
|
11574
12013
|
return sampleOsFallback();
|
|
11575
12014
|
}
|
|
11576
|
-
var
|
|
11577
|
-
|
|
12015
|
+
var app25 = new Hono();
|
|
12016
|
+
app25.get("/", async (c) => {
|
|
11578
12017
|
const started = Date.now();
|
|
11579
12018
|
if (!inflight) {
|
|
11580
12019
|
inflight = sample().finally(() => {
|
|
@@ -11597,14 +12036,14 @@ app26.get("/", async (c) => {
|
|
|
11597
12036
|
return c.json({ degraded: true, reason, sampledAtMs: Date.now() });
|
|
11598
12037
|
}
|
|
11599
12038
|
});
|
|
11600
|
-
var system_stats_default =
|
|
12039
|
+
var system_stats_default = app25;
|
|
11601
12040
|
|
|
11602
12041
|
// server/routes/admin/health.ts
|
|
11603
12042
|
import { existsSync as existsSync19, readFileSync as readFileSync14 } from "fs";
|
|
11604
|
-
import { resolve as
|
|
11605
|
-
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(), "..");
|
|
11606
12045
|
var brandHostname = "maxy";
|
|
11607
|
-
var brandJsonPath =
|
|
12046
|
+
var brandJsonPath = join13(PLATFORM_ROOT6, "config", "brand.json");
|
|
11608
12047
|
if (existsSync19(brandJsonPath)) {
|
|
11609
12048
|
try {
|
|
11610
12049
|
const brand = JSON.parse(readFileSync14(brandJsonPath, "utf-8"));
|
|
@@ -11612,7 +12051,7 @@ if (existsSync19(brandJsonPath)) {
|
|
|
11612
12051
|
} catch {
|
|
11613
12052
|
}
|
|
11614
12053
|
}
|
|
11615
|
-
var VERSION_FILE =
|
|
12054
|
+
var VERSION_FILE = resolve19(PLATFORM_ROOT6, `config/.${brandHostname}-version`);
|
|
11616
12055
|
var PROCESS_STARTED_AT = (/* @__PURE__ */ new Date()).toISOString();
|
|
11617
12056
|
var PROBE_TIMEOUT_MS = 1e3;
|
|
11618
12057
|
function readVersion() {
|
|
@@ -11642,8 +12081,8 @@ async function probeConversationDb() {
|
|
|
11642
12081
|
});
|
|
11643
12082
|
}
|
|
11644
12083
|
}
|
|
11645
|
-
var
|
|
11646
|
-
|
|
12084
|
+
var app26 = new Hono();
|
|
12085
|
+
app26.get("/", async (c) => {
|
|
11647
12086
|
const version = readVersion();
|
|
11648
12087
|
const probe = await probeConversationDb();
|
|
11649
12088
|
const uptimeMs = Date.now() - new Date(PROCESS_STARTED_AT).getTime();
|
|
@@ -11665,7 +12104,7 @@ app27.get("/", async (c) => {
|
|
|
11665
12104
|
uptimeMs
|
|
11666
12105
|
});
|
|
11667
12106
|
});
|
|
11668
|
-
var health_default2 =
|
|
12107
|
+
var health_default2 = app26;
|
|
11669
12108
|
|
|
11670
12109
|
// server/routes/admin/linkedin-ingest.ts
|
|
11671
12110
|
var TAG20 = "[linkedin-ingest-route]";
|
|
@@ -11761,8 +12200,8 @@ function buildInitialMessage(env) {
|
|
|
11761
12200
|
}
|
|
11762
12201
|
return header;
|
|
11763
12202
|
}
|
|
11764
|
-
var
|
|
11765
|
-
|
|
12203
|
+
var app27 = new Hono();
|
|
12204
|
+
app27.post("/", requireAdminSession, async (c) => {
|
|
11766
12205
|
let body;
|
|
11767
12206
|
try {
|
|
11768
12207
|
body = await c.req.json();
|
|
@@ -11816,7 +12255,7 @@ app28.post("/", requireAdminSession, async (c) => {
|
|
|
11816
12255
|
);
|
|
11817
12256
|
return c.json({ ok: true, dispatchId: envelope.dispatchId, taskId: claudeSessionId }, 202);
|
|
11818
12257
|
});
|
|
11819
|
-
var linkedin_ingest_default =
|
|
12258
|
+
var linkedin_ingest_default = app27;
|
|
11820
12259
|
|
|
11821
12260
|
// server/routes/admin/post-turn-context.ts
|
|
11822
12261
|
import neo4j3 from "neo4j-driver";
|
|
@@ -11858,8 +12297,8 @@ function pruneProperties(raw) {
|
|
|
11858
12297
|
}
|
|
11859
12298
|
return out;
|
|
11860
12299
|
}
|
|
11861
|
-
var
|
|
11862
|
-
|
|
12300
|
+
var app28 = new Hono();
|
|
12301
|
+
app28.get("/", async (c) => {
|
|
11863
12302
|
const remoteAddr = c.env?.incoming?.socket?.remoteAddress ?? "";
|
|
11864
12303
|
if (!isLoopbackAddr2(remoteAddr)) {
|
|
11865
12304
|
console.error(`${TAG21} reject reason=non-loopback remoteAddr=${remoteAddr}`);
|
|
@@ -11919,7 +12358,7 @@ app29.get("/", async (c) => {
|
|
|
11919
12358
|
await session.close();
|
|
11920
12359
|
}
|
|
11921
12360
|
});
|
|
11922
|
-
var post_turn_context_default =
|
|
12361
|
+
var post_turn_context_default = app28;
|
|
11923
12362
|
|
|
11924
12363
|
// server/routes/admin/public-session-context.ts
|
|
11925
12364
|
import neo4j4 from "neo4j-driver";
|
|
@@ -11961,8 +12400,8 @@ function pruneProperties2(raw) {
|
|
|
11961
12400
|
}
|
|
11962
12401
|
return out;
|
|
11963
12402
|
}
|
|
11964
|
-
var
|
|
11965
|
-
|
|
12403
|
+
var app29 = new Hono();
|
|
12404
|
+
app29.get("/", async (c) => {
|
|
11966
12405
|
const remoteAddr = c.env?.incoming?.socket?.remoteAddress ?? "";
|
|
11967
12406
|
if (!isLoopbackAddr3(remoteAddr)) {
|
|
11968
12407
|
console.error(`${TAG22} reject reason=non-loopback remoteAddr=${remoteAddr}`);
|
|
@@ -12021,15 +12460,15 @@ app30.get("/", async (c) => {
|
|
|
12021
12460
|
await session.close();
|
|
12022
12461
|
}
|
|
12023
12462
|
});
|
|
12024
|
-
var public_session_context_default =
|
|
12463
|
+
var public_session_context_default = app29;
|
|
12025
12464
|
|
|
12026
12465
|
// server/routes/admin/public-session-exit.ts
|
|
12027
12466
|
var TAG23 = "[public-session-exit-route]";
|
|
12028
12467
|
function isLoopbackAddr4(addr) {
|
|
12029
12468
|
return addr === "127.0.0.1" || addr === "::1" || addr === "::ffff:127.0.0.1";
|
|
12030
12469
|
}
|
|
12031
|
-
var
|
|
12032
|
-
|
|
12470
|
+
var app30 = new Hono();
|
|
12471
|
+
app30.post("/", async (c) => {
|
|
12033
12472
|
const remoteAddr = c.env?.incoming?.socket?.remoteAddress ?? "";
|
|
12034
12473
|
if (!isLoopbackAddr4(remoteAddr)) {
|
|
12035
12474
|
console.error(`${TAG23} reject reason=non-loopback remoteAddr=${remoteAddr}`);
|
|
@@ -12056,15 +12495,15 @@ app31.post("/", async (c) => {
|
|
|
12056
12495
|
handlePublicSessionExit(sessionId);
|
|
12057
12496
|
return c.json({ ok: true });
|
|
12058
12497
|
});
|
|
12059
|
-
var public_session_exit_default =
|
|
12498
|
+
var public_session_exit_default = app30;
|
|
12060
12499
|
|
|
12061
12500
|
// server/routes/admin/access-session-evict.ts
|
|
12062
12501
|
var TAG24 = "[access-session-evict]";
|
|
12063
12502
|
function isLoopbackAddr5(addr) {
|
|
12064
12503
|
return addr === "127.0.0.1" || addr === "::1" || addr === "::ffff:127.0.0.1";
|
|
12065
12504
|
}
|
|
12066
|
-
var
|
|
12067
|
-
|
|
12505
|
+
var app31 = new Hono();
|
|
12506
|
+
app31.post("/", async (c) => {
|
|
12068
12507
|
const remoteAddr = c.env?.incoming?.socket?.remoteAddress ?? "";
|
|
12069
12508
|
if (!isLoopbackAddr5(remoteAddr)) {
|
|
12070
12509
|
console.error(`${TAG24} reject reason=non-loopback remoteAddr=${remoteAddr}`);
|
|
@@ -12092,49 +12531,48 @@ app32.post("/", async (c) => {
|
|
|
12092
12531
|
console.log(`${TAG24} grantId=${grantId} dropped=${dropped}`);
|
|
12093
12532
|
return c.json({ ok: true, dropped });
|
|
12094
12533
|
});
|
|
12095
|
-
var access_session_evict_default =
|
|
12534
|
+
var access_session_evict_default = app31;
|
|
12096
12535
|
|
|
12097
12536
|
// server/routes/admin/index.ts
|
|
12098
|
-
var
|
|
12099
|
-
|
|
12100
|
-
|
|
12101
|
-
|
|
12102
|
-
|
|
12103
|
-
|
|
12104
|
-
|
|
12105
|
-
|
|
12106
|
-
|
|
12107
|
-
|
|
12108
|
-
|
|
12109
|
-
|
|
12110
|
-
|
|
12111
|
-
|
|
12112
|
-
|
|
12113
|
-
|
|
12114
|
-
|
|
12115
|
-
|
|
12116
|
-
|
|
12117
|
-
|
|
12118
|
-
|
|
12119
|
-
|
|
12120
|
-
|
|
12121
|
-
|
|
12122
|
-
|
|
12123
|
-
|
|
12124
|
-
|
|
12125
|
-
|
|
12126
|
-
var admin_default = app33;
|
|
12537
|
+
var app32 = new Hono();
|
|
12538
|
+
app32.route("/session", session_default);
|
|
12539
|
+
app32.route("/logs", logs_default);
|
|
12540
|
+
app32.route("/claude-info", claude_info_default);
|
|
12541
|
+
app32.route("/attachment", attachment_default);
|
|
12542
|
+
app32.route("/agents", agents_default);
|
|
12543
|
+
app32.route("/sessions", sessions_default);
|
|
12544
|
+
app32.route("/claude-sessions", claude_sessions_default);
|
|
12545
|
+
app32.route("/log-ingest", log_ingest_default);
|
|
12546
|
+
app32.route("/events", events_default);
|
|
12547
|
+
app32.route("/files", files_default);
|
|
12548
|
+
app32.route("/graph-search", graph_search_default);
|
|
12549
|
+
app32.route("/graph-subgraph", graph_subgraph_default);
|
|
12550
|
+
app32.route("/graph-delete", graph_delete_default);
|
|
12551
|
+
app32.route("/graph-restore", graph_restore_default);
|
|
12552
|
+
app32.route("/graph-labels-in-graph", graph_labels_in_graph_default);
|
|
12553
|
+
app32.route("/graph-default-view", graph_default_view_default);
|
|
12554
|
+
app32.route("/sidebar-artefacts", sidebar_artefacts_default);
|
|
12555
|
+
app32.route("/sidebar-artefact-save", sidebar_artefact_save_default);
|
|
12556
|
+
app32.route("/sidebar-artefact-content", sidebar_artefact_content_default);
|
|
12557
|
+
app32.route("/system-stats", system_stats_default);
|
|
12558
|
+
app32.route("/health-brand", health_default2);
|
|
12559
|
+
app32.route("/linkedin-ingest", linkedin_ingest_default);
|
|
12560
|
+
app32.route("/post-turn-context", post_turn_context_default);
|
|
12561
|
+
app32.route("/public-session-context", public_session_context_default);
|
|
12562
|
+
app32.route("/public-session-exit", public_session_exit_default);
|
|
12563
|
+
app32.route("/access-session-evict", access_session_evict_default);
|
|
12564
|
+
var admin_default = app32;
|
|
12127
12565
|
|
|
12128
12566
|
// app/lib/access-gate.ts
|
|
12129
12567
|
import neo4j5 from "neo4j-driver";
|
|
12130
12568
|
import { readFileSync as readFileSync15 } from "fs";
|
|
12131
|
-
import { resolve as
|
|
12569
|
+
import { resolve as resolve20 } from "path";
|
|
12132
12570
|
import { randomUUID as randomUUID8 } from "crypto";
|
|
12133
|
-
var PLATFORM_ROOT7 = process.env.MAXY_PLATFORM_ROOT ??
|
|
12571
|
+
var PLATFORM_ROOT7 = process.env.MAXY_PLATFORM_ROOT ?? resolve20(process.cwd(), "..");
|
|
12134
12572
|
var driver = null;
|
|
12135
12573
|
function readPassword() {
|
|
12136
12574
|
if (process.env.NEO4J_PASSWORD) return process.env.NEO4J_PASSWORD;
|
|
12137
|
-
const passwordFile =
|
|
12575
|
+
const passwordFile = resolve20(PLATFORM_ROOT7, "config/.neo4j-password");
|
|
12138
12576
|
try {
|
|
12139
12577
|
return readFileSync15(passwordFile, "utf-8").trim();
|
|
12140
12578
|
} catch {
|
|
@@ -12337,8 +12775,8 @@ async function generateNewMagicToken(grantId) {
|
|
|
12337
12775
|
var TAG25 = "[access-verify]";
|
|
12338
12776
|
var MINT_TAG = "[access-session-mint]";
|
|
12339
12777
|
var COOKIE_NAME = "__access_session";
|
|
12340
|
-
var
|
|
12341
|
-
|
|
12778
|
+
var app33 = new Hono();
|
|
12779
|
+
app33.post("/", async (c) => {
|
|
12342
12780
|
const ip = c.var.clientIp || "unknown";
|
|
12343
12781
|
let body;
|
|
12344
12782
|
try {
|
|
@@ -12420,13 +12858,13 @@ app34.post("/", async (c) => {
|
|
|
12420
12858
|
displayName: grant.displayName
|
|
12421
12859
|
});
|
|
12422
12860
|
});
|
|
12423
|
-
var verify_token_default =
|
|
12861
|
+
var verify_token_default = app33;
|
|
12424
12862
|
|
|
12425
12863
|
// app/lib/access-email.ts
|
|
12426
12864
|
import { spawn as spawn2 } from "child_process";
|
|
12427
|
-
import { resolve as
|
|
12428
|
-
var PLATFORM_ROOT8 = process.env.MAXY_PLATFORM_ROOT ??
|
|
12429
|
-
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(
|
|
12430
12868
|
PLATFORM_ROOT8,
|
|
12431
12869
|
"plugins",
|
|
12432
12870
|
"email",
|
|
@@ -12483,9 +12921,9 @@ async function sendMagicLinkEmail(payload) {
|
|
|
12483
12921
|
|
|
12484
12922
|
// server/routes/access/request-magic-link.ts
|
|
12485
12923
|
var TAG26 = "[access-request-link]";
|
|
12486
|
-
var
|
|
12924
|
+
var app34 = new Hono();
|
|
12487
12925
|
var VISITOR_MESSAGE = "If that email is on the invite list, a fresh link is on the way.";
|
|
12488
|
-
|
|
12926
|
+
app34.post("/", async (c) => {
|
|
12489
12927
|
let body;
|
|
12490
12928
|
try {
|
|
12491
12929
|
body = await c.req.json();
|
|
@@ -12559,17 +12997,17 @@ app35.post("/", async (c) => {
|
|
|
12559
12997
|
);
|
|
12560
12998
|
return c.json({ message: VISITOR_MESSAGE }, 200);
|
|
12561
12999
|
});
|
|
12562
|
-
var request_magic_link_default =
|
|
13000
|
+
var request_magic_link_default = app34;
|
|
12563
13001
|
|
|
12564
13002
|
// server/routes/access/index.ts
|
|
12565
|
-
var
|
|
12566
|
-
|
|
12567
|
-
|
|
12568
|
-
var access_default =
|
|
13003
|
+
var app35 = new Hono();
|
|
13004
|
+
app35.route("/verify-token", verify_token_default);
|
|
13005
|
+
app35.route("/request-magic-link", request_magic_link_default);
|
|
13006
|
+
var access_default = app35;
|
|
12569
13007
|
|
|
12570
13008
|
// server/routes/sites.ts
|
|
12571
|
-
import { existsSync as existsSync20, readFileSync as readFileSync16, realpathSync as
|
|
12572
|
-
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";
|
|
12573
13011
|
var SAFE_SEG_RE = /^[a-z0-9_][a-z0-9_.-]{0,99}$/i;
|
|
12574
13012
|
var MIME = {
|
|
12575
13013
|
".html": "text/html; charset=utf-8",
|
|
@@ -12600,8 +13038,8 @@ function getExt(p) {
|
|
|
12600
13038
|
if (idx < p.lastIndexOf("/")) return "";
|
|
12601
13039
|
return p.slice(idx).toLowerCase();
|
|
12602
13040
|
}
|
|
12603
|
-
var
|
|
12604
|
-
|
|
13041
|
+
var app36 = new Hono();
|
|
13042
|
+
app36.get("/:rel{.*}", (c) => {
|
|
12605
13043
|
const reqPath = c.req.path;
|
|
12606
13044
|
const rawRel = c.req.param("rel") ?? "";
|
|
12607
13045
|
const trimmed = rawRel.replace(/^\/+/, "").replace(/\/+$/, "");
|
|
@@ -12626,15 +13064,15 @@ app37.get("/:rel{.*}", (c) => {
|
|
|
12626
13064
|
}
|
|
12627
13065
|
segments.push(seg);
|
|
12628
13066
|
}
|
|
12629
|
-
const rootDir =
|
|
12630
|
-
let filePath = segments.length === 0 ? rootDir :
|
|
13067
|
+
const rootDir = resolve22(account.accountDir, "sites");
|
|
13068
|
+
let filePath = segments.length === 0 ? rootDir : resolve22(rootDir, ...segments);
|
|
12631
13069
|
if (filePath !== rootDir && !filePath.startsWith(rootDir + "/")) {
|
|
12632
13070
|
console.error(`[sites] path-traversal-rejected path=${reqPath} reason=escape status=403`);
|
|
12633
13071
|
return c.text("Forbidden", 403);
|
|
12634
13072
|
}
|
|
12635
13073
|
let stat7;
|
|
12636
13074
|
try {
|
|
12637
|
-
stat7 = existsSync20(filePath) ?
|
|
13075
|
+
stat7 = existsSync20(filePath) ? statSync8(filePath) : null;
|
|
12638
13076
|
} catch {
|
|
12639
13077
|
stat7 = null;
|
|
12640
13078
|
}
|
|
@@ -12647,7 +13085,7 @@ app37.get("/:rel{.*}", (c) => {
|
|
|
12647
13085
|
return c.redirect(target, 301);
|
|
12648
13086
|
}
|
|
12649
13087
|
if (stat7?.isDirectory()) {
|
|
12650
|
-
filePath =
|
|
13088
|
+
filePath = resolve22(filePath, "index.html");
|
|
12651
13089
|
}
|
|
12652
13090
|
if (!filePath.startsWith(rootDir + "/")) {
|
|
12653
13091
|
console.error(`[sites] path-traversal-rejected path=${reqPath} reason=escape status=403`);
|
|
@@ -12660,8 +13098,8 @@ app37.get("/:rel{.*}", (c) => {
|
|
|
12660
13098
|
let realPath;
|
|
12661
13099
|
let realRoot;
|
|
12662
13100
|
try {
|
|
12663
|
-
realPath =
|
|
12664
|
-
realRoot =
|
|
13101
|
+
realPath = realpathSync6(filePath);
|
|
13102
|
+
realRoot = realpathSync6(rootDir);
|
|
12665
13103
|
} catch {
|
|
12666
13104
|
console.error(`[sites] not-found path=${reqPath} status=404`);
|
|
12667
13105
|
return c.text("Not found", 404);
|
|
@@ -12704,7 +13142,7 @@ app37.get("/:rel{.*}", (c) => {
|
|
|
12704
13142
|
"X-Content-Type-Options": "nosniff"
|
|
12705
13143
|
});
|
|
12706
13144
|
});
|
|
12707
|
-
var sites_default =
|
|
13145
|
+
var sites_default = app36;
|
|
12708
13146
|
|
|
12709
13147
|
// app/lib/visitor-token.ts
|
|
12710
13148
|
import { createHmac, randomBytes, timingSafeEqual } from "crypto";
|
|
@@ -12785,7 +13223,7 @@ var VISITOR_COOKIE_MAX_AGE_SECONDS = Math.floor(DEFAULT_TTL_MS / 1e3);
|
|
|
12785
13223
|
|
|
12786
13224
|
// app/lib/brand-config.ts
|
|
12787
13225
|
import { existsSync as existsSync21, readFileSync as readFileSync18 } from "fs";
|
|
12788
|
-
import { join as
|
|
13226
|
+
import { join as join14 } from "path";
|
|
12789
13227
|
var cached2 = null;
|
|
12790
13228
|
var cachedAttempted = false;
|
|
12791
13229
|
function readBrandConfig() {
|
|
@@ -12793,7 +13231,7 @@ function readBrandConfig() {
|
|
|
12793
13231
|
cachedAttempted = true;
|
|
12794
13232
|
const platformRoot = process.env.MAXY_PLATFORM_ROOT;
|
|
12795
13233
|
if (!platformRoot) return null;
|
|
12796
|
-
const brandPath =
|
|
13234
|
+
const brandPath = join14(platformRoot, "config", "brand.json");
|
|
12797
13235
|
if (!existsSync21(brandPath)) return null;
|
|
12798
13236
|
try {
|
|
12799
13237
|
cached2 = JSON.parse(readFileSync18(brandPath, "utf-8"));
|
|
@@ -12804,7 +13242,7 @@ function readBrandConfig() {
|
|
|
12804
13242
|
}
|
|
12805
13243
|
|
|
12806
13244
|
// server/routes/visitor-consent.ts
|
|
12807
|
-
var
|
|
13245
|
+
var app37 = new Hono();
|
|
12808
13246
|
var CONSENT_COOKIE_NAME = "mxy_consent";
|
|
12809
13247
|
var CONSENT_COOKIE_MAX_AGE_SECONDS = 60 * 60 * 24 * 365;
|
|
12810
13248
|
var DEFAULT_CONSENT_COPY = {
|
|
@@ -12849,17 +13287,17 @@ function siteSlugFromReferer(referer) {
|
|
|
12849
13287
|
return "";
|
|
12850
13288
|
}
|
|
12851
13289
|
}
|
|
12852
|
-
|
|
13290
|
+
app37.options("/consent", (c) => {
|
|
12853
13291
|
const origin = getOrigin(c);
|
|
12854
13292
|
setCorsHeaders(c, origin);
|
|
12855
13293
|
return c.body(null, 204);
|
|
12856
13294
|
});
|
|
12857
|
-
|
|
13295
|
+
app37.options("/brand-config", (c) => {
|
|
12858
13296
|
const origin = getOrigin(c);
|
|
12859
13297
|
setCorsHeaders(c, origin);
|
|
12860
13298
|
return c.body(null, 204);
|
|
12861
13299
|
});
|
|
12862
|
-
|
|
13300
|
+
app37.post("/consent", async (c) => {
|
|
12863
13301
|
const origin = getOrigin(c);
|
|
12864
13302
|
setCorsHeaders(c, origin);
|
|
12865
13303
|
let raw;
|
|
@@ -12899,7 +13337,7 @@ app38.post("/consent", async (c) => {
|
|
|
12899
13337
|
console.log(`[consent] ${parsed.decision} site=${site} brand=${brandName} tokenBound=${tokenBound}`);
|
|
12900
13338
|
return c.body(null, 204);
|
|
12901
13339
|
});
|
|
12902
|
-
|
|
13340
|
+
app37.get("/brand-config", (c) => {
|
|
12903
13341
|
const origin = getOrigin(c);
|
|
12904
13342
|
setCorsHeaders(c, origin);
|
|
12905
13343
|
const brand = readBrandConfig();
|
|
@@ -12909,7 +13347,7 @@ app38.get("/brand-config", (c) => {
|
|
|
12909
13347
|
c.header("Cache-Control", "public, max-age=300");
|
|
12910
13348
|
return c.json({ consent: { copy, palette } });
|
|
12911
13349
|
});
|
|
12912
|
-
var visitor_consent_default =
|
|
13350
|
+
var visitor_consent_default = app37;
|
|
12913
13351
|
|
|
12914
13352
|
// server/routes/listings.ts
|
|
12915
13353
|
function getCookie(headerValue, name) {
|
|
@@ -12930,14 +13368,14 @@ function appendConsentParams(pageUrl, token) {
|
|
|
12930
13368
|
const hashIdx = pageUrl.indexOf("#");
|
|
12931
13369
|
const hash = hashIdx >= 0 ? pageUrl.slice(hashIdx) : "";
|
|
12932
13370
|
const base = hashIdx >= 0 ? pageUrl.slice(0, hashIdx) : pageUrl;
|
|
12933
|
-
const
|
|
12934
|
-
return base +
|
|
13371
|
+
const sep7 = base.indexOf("?") >= 0 ? "&" : "?";
|
|
13372
|
+
return base + sep7 + `consent=needed&v=${encodeURIComponent(token)}` + hash;
|
|
12935
13373
|
}
|
|
12936
13374
|
}
|
|
12937
13375
|
var SAFE_SLUG_RE = /^[a-z0-9](?:[a-z0-9-]{0,118}[a-z0-9])?$/;
|
|
12938
13376
|
var CHAT_SESSION_KEY_RE = /^[A-Za-z0-9][A-Za-z0-9_-]{7,127}$/;
|
|
12939
|
-
var
|
|
12940
|
-
|
|
13377
|
+
var app38 = new Hono();
|
|
13378
|
+
app38.get("/:slug/click", async (c) => {
|
|
12941
13379
|
const slug = c.req.param("slug") ?? "";
|
|
12942
13380
|
const rawSession = c.req.query("session") ?? "";
|
|
12943
13381
|
const sessionKey = CHAT_SESSION_KEY_RE.test(rawSession) ? rawSession : "invalid";
|
|
@@ -13001,10 +13439,10 @@ app39.get("/:slug/click", async (c) => {
|
|
|
13001
13439
|
console.log(`[property-card-click] sessionKey=${sessionKey} listingSlug=${slug} consent=${consentCookie ?? "absent"} ts=${(/* @__PURE__ */ new Date()).toISOString()}`);
|
|
13002
13440
|
return c.redirect(redirectUrl, 302);
|
|
13003
13441
|
});
|
|
13004
|
-
var listings_default =
|
|
13442
|
+
var listings_default = app38;
|
|
13005
13443
|
|
|
13006
13444
|
// server/routes/visitor-event.ts
|
|
13007
|
-
var
|
|
13445
|
+
var app39 = new Hono();
|
|
13008
13446
|
var BOT_UA_RE = /\b(bot|crawl|spider|slurp|headlesschrome|phantomjs|googlebot|bingbot|yandex|baiduspider|ahrefsbot|semrushbot|mj12bot|dotbot|petalbot)\b/i;
|
|
13009
13447
|
var buckets = /* @__PURE__ */ new Map();
|
|
13010
13448
|
var RATE_LIMIT = 60;
|
|
@@ -13071,12 +13509,12 @@ function originAllowed(origin, allowlist) {
|
|
|
13071
13509
|
return false;
|
|
13072
13510
|
}
|
|
13073
13511
|
}
|
|
13074
|
-
|
|
13512
|
+
app39.options("/event", (c) => {
|
|
13075
13513
|
const origin = getOrigin2(c);
|
|
13076
13514
|
setCorsHeaders2(c, origin);
|
|
13077
13515
|
return c.body(null, 204);
|
|
13078
13516
|
});
|
|
13079
|
-
|
|
13517
|
+
app39.post("/event", async (c) => {
|
|
13080
13518
|
const origin = getOrigin2(c);
|
|
13081
13519
|
setCorsHeaders2(c, origin);
|
|
13082
13520
|
const ua = c.req.header("user-agent") ?? "";
|
|
@@ -13281,7 +13719,7 @@ async function writeEvent(opts) {
|
|
|
13281
13719
|
);
|
|
13282
13720
|
}
|
|
13283
13721
|
}
|
|
13284
|
-
var visitor_event_default =
|
|
13722
|
+
var visitor_event_default = app39;
|
|
13285
13723
|
|
|
13286
13724
|
// app/lib/graph-health.ts
|
|
13287
13725
|
var HOUR_MS = 60 * 60 * 1e3;
|
|
@@ -13425,8 +13863,8 @@ function broadcastAdminShutdown(reason) {
|
|
|
13425
13863
|
|
|
13426
13864
|
// ../lib/entitlement/src/index.ts
|
|
13427
13865
|
import { createPublicKey, createHash as createHash4, verify as cryptoVerify } from "crypto";
|
|
13428
|
-
import { existsSync as existsSync22, readFileSync as readFileSync19, statSync as
|
|
13429
|
-
import { resolve as
|
|
13866
|
+
import { existsSync as existsSync22, readFileSync as readFileSync19, statSync as statSync9 } from "fs";
|
|
13867
|
+
import { resolve as resolve23 } from "path";
|
|
13430
13868
|
|
|
13431
13869
|
// ../lib/entitlement/src/canonicalize.ts
|
|
13432
13870
|
function canonicalize(value) {
|
|
@@ -13461,7 +13899,7 @@ var PUBKEY_SHA256 = "8eee6bcb33545fd13b16d3199a5735ca5db5062834c7b49dfe4f23801d9
|
|
|
13461
13899
|
var GRACE_DAYS = 7;
|
|
13462
13900
|
var GRACE_MS = GRACE_DAYS * 24 * 60 * 60 * 1e3;
|
|
13463
13901
|
function pubkeyPath(brand) {
|
|
13464
|
-
return
|
|
13902
|
+
return resolve23(brand.platformRoot, "lib", "entitlement", "rubytech-pubkey.pem");
|
|
13465
13903
|
}
|
|
13466
13904
|
var memo = null;
|
|
13467
13905
|
function memoKey(mtimeMs, account) {
|
|
@@ -13473,11 +13911,11 @@ function resolveEntitlement(brand, account) {
|
|
|
13473
13911
|
if (brand.commercialMode !== true) {
|
|
13474
13912
|
return logResolved(implicitTrust(account), null);
|
|
13475
13913
|
}
|
|
13476
|
-
const entitlementPath =
|
|
13914
|
+
const entitlementPath = resolve23(brand.configDir, "entitlement.json");
|
|
13477
13915
|
if (!existsSync22(entitlementPath)) {
|
|
13478
13916
|
return logResolved(anonymousFallback("missing"), { reason: "missing" });
|
|
13479
13917
|
}
|
|
13480
|
-
const stat7 =
|
|
13918
|
+
const stat7 = statSync9(entitlementPath);
|
|
13481
13919
|
const key = memoKey(stat7.mtimeMs, account);
|
|
13482
13920
|
if (memo && memo.key === key) {
|
|
13483
13921
|
return memo.result;
|
|
@@ -13664,7 +14102,7 @@ function clientFrom(c) {
|
|
|
13664
14102
|
);
|
|
13665
14103
|
}
|
|
13666
14104
|
var PLATFORM_ROOT9 = process.env.MAXY_PLATFORM_ROOT || "";
|
|
13667
|
-
var BRAND_JSON_PATH = PLATFORM_ROOT9 ?
|
|
14105
|
+
var BRAND_JSON_PATH = PLATFORM_ROOT9 ? join15(PLATFORM_ROOT9, "config", "brand.json") : "";
|
|
13668
14106
|
var BRAND = { productName: "Maxy", hostname: "maxy", configDir: ".maxy", marketingUrl: "https://getmaxy.com" };
|
|
13669
14107
|
if (BRAND_JSON_PATH && !existsSync23(BRAND_JSON_PATH)) {
|
|
13670
14108
|
console.error(`[brand] WARNING: brand.json not found at ${BRAND_JSON_PATH} \u2014 using Maxy defaults`);
|
|
@@ -13690,7 +14128,7 @@ var brandLoginOpts = {
|
|
|
13690
14128
|
bodyFont: BRAND.defaultFonts?.body,
|
|
13691
14129
|
logoContainsName: !!BRAND.logoContainsName
|
|
13692
14130
|
};
|
|
13693
|
-
var ALIAS_DOMAINS_PATH =
|
|
14131
|
+
var ALIAS_DOMAINS_PATH = join15(homedir2(), BRAND.configDir, "alias-domains.json");
|
|
13694
14132
|
function loadAliasDomains() {
|
|
13695
14133
|
try {
|
|
13696
14134
|
if (!existsSync23(ALIAS_DOMAINS_PATH)) return null;
|
|
@@ -13718,9 +14156,9 @@ watchFile(ALIAS_DOMAINS_PATH, { interval: 2e3 }, () => {
|
|
|
13718
14156
|
function isPublicHost(host) {
|
|
13719
14157
|
return host.startsWith("public.") || aliasDomains.has(host);
|
|
13720
14158
|
}
|
|
13721
|
-
var
|
|
13722
|
-
|
|
13723
|
-
|
|
14159
|
+
var app40 = new Hono();
|
|
14160
|
+
app40.use("*", clientIpMiddleware);
|
|
14161
|
+
app40.use("*", async (c, next) => {
|
|
13724
14162
|
await next();
|
|
13725
14163
|
c.header("X-Content-Type-Options", "nosniff");
|
|
13726
14164
|
c.header("Referrer-Policy", "strict-origin-when-cross-origin");
|
|
@@ -13730,7 +14168,7 @@ app41.use("*", async (c, next) => {
|
|
|
13730
14168
|
);
|
|
13731
14169
|
});
|
|
13732
14170
|
var HTTP_LOG_PATHS = /* @__PURE__ */ new Set(["/vnc-viewer.html", "/vnc-popout.html"]);
|
|
13733
|
-
|
|
14171
|
+
app40.use("*", async (c, next) => {
|
|
13734
14172
|
if (!HTTP_LOG_PATHS.has(c.req.path)) {
|
|
13735
14173
|
await next();
|
|
13736
14174
|
return;
|
|
@@ -13763,7 +14201,7 @@ var PUBLIC_ALLOWED_PREFIXES = [
|
|
|
13763
14201
|
"/sites/"
|
|
13764
14202
|
];
|
|
13765
14203
|
var PUBLIC_ALLOWED_EXACT = ["/favicon.ico"];
|
|
13766
|
-
|
|
14204
|
+
app40.use("*", async (c, next) => {
|
|
13767
14205
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
13768
14206
|
if (!isPublicHost(host)) {
|
|
13769
14207
|
await next();
|
|
@@ -13803,7 +14241,7 @@ function resolveRemoteAuthOpts() {
|
|
|
13803
14241
|
return brandLoginOpts;
|
|
13804
14242
|
}
|
|
13805
14243
|
var MAX_LOGIN_BODY = 8 * 1024;
|
|
13806
|
-
|
|
14244
|
+
app40.post("/__remote-auth/login", async (c) => {
|
|
13807
14245
|
const client = clientFrom(c);
|
|
13808
14246
|
const clientIp = client.ip || "unknown";
|
|
13809
14247
|
if (!requestIsTlsTerminated(c)) {
|
|
@@ -13848,7 +14286,7 @@ app41.post("/__remote-auth/login", async (c) => {
|
|
|
13848
14286
|
}
|
|
13849
14287
|
});
|
|
13850
14288
|
});
|
|
13851
|
-
|
|
14289
|
+
app40.get("/__remote-auth/logout", (c) => {
|
|
13852
14290
|
const client = clientFrom(c);
|
|
13853
14291
|
const clientIp = client.ip || "unknown";
|
|
13854
14292
|
console.error(`[remote-auth] logout ip=${clientIp}`);
|
|
@@ -13861,7 +14299,7 @@ app41.get("/__remote-auth/logout", (c) => {
|
|
|
13861
14299
|
}
|
|
13862
14300
|
});
|
|
13863
14301
|
});
|
|
13864
|
-
|
|
14302
|
+
app40.post("/__remote-auth/change-password", async (c) => {
|
|
13865
14303
|
const client = clientFrom(c);
|
|
13866
14304
|
const clientIp = client.ip || "unknown";
|
|
13867
14305
|
const rateLimited = checkRateLimit(client);
|
|
@@ -13912,13 +14350,13 @@ app41.post("/__remote-auth/change-password", async (c) => {
|
|
|
13912
14350
|
return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(), mode: "change", changeError: "Failed to save password", redirect }), 200);
|
|
13913
14351
|
}
|
|
13914
14352
|
});
|
|
13915
|
-
|
|
14353
|
+
app40.get("/__remote-auth/setup", (c) => {
|
|
13916
14354
|
if (isRemoteAuthConfigured()) {
|
|
13917
14355
|
return c.redirect("/");
|
|
13918
14356
|
}
|
|
13919
14357
|
return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(), mode: "setup" }), 200);
|
|
13920
14358
|
});
|
|
13921
|
-
|
|
14359
|
+
app40.post("/__remote-auth/set-initial-password", async (c) => {
|
|
13922
14360
|
if (isRemoteAuthConfigured()) {
|
|
13923
14361
|
return c.redirect("/");
|
|
13924
14362
|
}
|
|
@@ -13956,10 +14394,10 @@ app41.post("/__remote-auth/set-initial-password", async (c) => {
|
|
|
13956
14394
|
return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(), mode: "setup", setupError: "Failed to save password. Please try again." }), 200);
|
|
13957
14395
|
}
|
|
13958
14396
|
});
|
|
13959
|
-
|
|
14397
|
+
app40.get("/api/remote-auth/status", (c) => {
|
|
13960
14398
|
return c.json({ configured: isRemoteAuthConfigured() });
|
|
13961
14399
|
});
|
|
13962
|
-
|
|
14400
|
+
app40.post("/api/remote-auth/set-password", async (c) => {
|
|
13963
14401
|
let body;
|
|
13964
14402
|
try {
|
|
13965
14403
|
body = await c.req.json();
|
|
@@ -13990,9 +14428,9 @@ app41.post("/api/remote-auth/set-password", async (c) => {
|
|
|
13990
14428
|
return c.json({ error: "Failed to save password" }, 500);
|
|
13991
14429
|
}
|
|
13992
14430
|
});
|
|
13993
|
-
|
|
14431
|
+
app40.route("/api/_client-error", client_error_default);
|
|
13994
14432
|
console.log("[client-error-route] mounted");
|
|
13995
|
-
|
|
14433
|
+
app40.use("*", async (c, next) => {
|
|
13996
14434
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
13997
14435
|
const path2 = c.req.path;
|
|
13998
14436
|
if (path2 === "/favicon.ico" || path2.startsWith("/assets/") || path2.startsWith("/brand/")) {
|
|
@@ -14025,12 +14463,12 @@ app41.use("*", async (c, next) => {
|
|
|
14025
14463
|
console.error(`[remote-auth] login required ip=${clientIp} path=${path2} ${disambig}`);
|
|
14026
14464
|
return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(), redirect: path2 }), 200);
|
|
14027
14465
|
});
|
|
14028
|
-
|
|
14029
|
-
|
|
14030
|
-
|
|
14031
|
-
|
|
14032
|
-
|
|
14033
|
-
|
|
14466
|
+
app40.route("/api/health", health_default);
|
|
14467
|
+
app40.route("/api/chat", chat_default);
|
|
14468
|
+
app40.route("/api/whatsapp", whatsapp_default);
|
|
14469
|
+
app40.route("/api/onboarding", onboarding_default);
|
|
14470
|
+
app40.route("/api/admin", admin_default);
|
|
14471
|
+
app40.route("/api/access", access_default);
|
|
14034
14472
|
var SAFE_SLUG_RE2 = /^[a-z][a-z0-9-]{2,49}$/;
|
|
14035
14473
|
var SAFE_FILENAME_RE = /^[a-z0-9_][a-z0-9_.-]{0,99}$/i;
|
|
14036
14474
|
var IMAGE_MIME = {
|
|
@@ -14042,7 +14480,7 @@ var IMAGE_MIME = {
|
|
|
14042
14480
|
".svg": "image/svg+xml",
|
|
14043
14481
|
".ico": "image/x-icon"
|
|
14044
14482
|
};
|
|
14045
|
-
|
|
14483
|
+
app40.get("/agent-assets/:slug/:filename", (c) => {
|
|
14046
14484
|
const slug = c.req.param("slug");
|
|
14047
14485
|
const filename = c.req.param("filename");
|
|
14048
14486
|
if (!SAFE_SLUG_RE2.test(slug)) {
|
|
@@ -14058,8 +14496,8 @@ app41.get("/agent-assets/:slug/:filename", (c) => {
|
|
|
14058
14496
|
console.error(`[agent-assets] no-account slug=${slug} file=${filename}`);
|
|
14059
14497
|
return c.text("Not found", 404);
|
|
14060
14498
|
}
|
|
14061
|
-
const filePath =
|
|
14062
|
-
const expectedDir =
|
|
14499
|
+
const filePath = resolve24(account.accountDir, "agents", slug, "assets", filename);
|
|
14500
|
+
const expectedDir = resolve24(account.accountDir, "agents", slug, "assets");
|
|
14063
14501
|
if (!filePath.startsWith(expectedDir + "/")) {
|
|
14064
14502
|
console.error(`[agent-assets] path-traversal-rejected slug=${slug} file=${filename}`);
|
|
14065
14503
|
return c.text("Forbidden", 403);
|
|
@@ -14077,7 +14515,7 @@ app41.get("/agent-assets/:slug/:filename", (c) => {
|
|
|
14077
14515
|
"Cache-Control": "public, max-age=3600"
|
|
14078
14516
|
});
|
|
14079
14517
|
});
|
|
14080
|
-
|
|
14518
|
+
app40.get("/generated/:filename", (c) => {
|
|
14081
14519
|
const filename = c.req.param("filename");
|
|
14082
14520
|
if (!SAFE_FILENAME_RE.test(filename) || filename.includes("..")) {
|
|
14083
14521
|
console.error(`[generated] serve file=${filename} status=403`);
|
|
@@ -14088,8 +14526,8 @@ app41.get("/generated/:filename", (c) => {
|
|
|
14088
14526
|
console.error(`[generated] serve file=${filename} status=404`);
|
|
14089
14527
|
return c.text("Not found", 404);
|
|
14090
14528
|
}
|
|
14091
|
-
const filePath =
|
|
14092
|
-
const expectedDir =
|
|
14529
|
+
const filePath = resolve24(account.accountDir, "generated", filename);
|
|
14530
|
+
const expectedDir = resolve24(account.accountDir, "generated");
|
|
14093
14531
|
if (!filePath.startsWith(expectedDir + "/")) {
|
|
14094
14532
|
console.error(`[generated] serve file=${filename} status=403`);
|
|
14095
14533
|
return c.text("Forbidden", 403);
|
|
@@ -14107,10 +14545,10 @@ app41.get("/generated/:filename", (c) => {
|
|
|
14107
14545
|
"Cache-Control": "public, max-age=86400"
|
|
14108
14546
|
});
|
|
14109
14547
|
});
|
|
14110
|
-
|
|
14111
|
-
|
|
14112
|
-
|
|
14113
|
-
|
|
14548
|
+
app40.route("/sites", sites_default);
|
|
14549
|
+
app40.route("/listings", listings_default);
|
|
14550
|
+
app40.route("/v", visitor_event_default);
|
|
14551
|
+
app40.route("/v", visitor_consent_default);
|
|
14114
14552
|
var htmlCache = /* @__PURE__ */ new Map();
|
|
14115
14553
|
var brandLogoPath = "/brand/maxy-monochrome.png";
|
|
14116
14554
|
var brandIconPath = "/brand/maxy-monochrome.png";
|
|
@@ -14133,7 +14571,7 @@ var brandScript = `<script>window.__BRAND__=${JSON.stringify({
|
|
|
14133
14571
|
function readInstalledVersion() {
|
|
14134
14572
|
try {
|
|
14135
14573
|
if (!PLATFORM_ROOT9) return "unknown";
|
|
14136
|
-
const versionFile =
|
|
14574
|
+
const versionFile = join15(PLATFORM_ROOT9, "config", `.${BRAND.hostname}-version`);
|
|
14137
14575
|
if (!existsSync23(versionFile)) return "unknown";
|
|
14138
14576
|
const content = readFileSync20(versionFile, "utf-8").trim();
|
|
14139
14577
|
return content || "unknown";
|
|
@@ -14176,7 +14614,7 @@ var clientErrorReporterScript = `<script>
|
|
|
14176
14614
|
function cachedHtml(file) {
|
|
14177
14615
|
let html = htmlCache.get(file);
|
|
14178
14616
|
if (!html) {
|
|
14179
|
-
html = readFileSync20(
|
|
14617
|
+
html = readFileSync20(resolve24(process.cwd(), "public", file), "utf-8");
|
|
14180
14618
|
const productNameEsc = escapeHtml(BRAND.productName);
|
|
14181
14619
|
html = html.replace(/<title>([^<]*)<\/title>/, (_match, inner) => `<title>${escapeHtml(inner).replace(/Maxy/g, productNameEsc)}</title>`);
|
|
14182
14620
|
html = html.replace('href="/favicon.ico"', `href="${escapeHtml(brandFaviconPath)}"`);
|
|
@@ -14192,14 +14630,14 @@ ${clientErrorReporterScript}
|
|
|
14192
14630
|
}
|
|
14193
14631
|
var brandedHtmlCache = /* @__PURE__ */ new Map();
|
|
14194
14632
|
function loadBrandingCache(agentSlug) {
|
|
14195
|
-
const configDir2 =
|
|
14633
|
+
const configDir2 = join15(homedir2(), BRAND.configDir);
|
|
14196
14634
|
try {
|
|
14197
|
-
const accountJsonPath =
|
|
14635
|
+
const accountJsonPath = join15(configDir2, "account.json");
|
|
14198
14636
|
if (!existsSync23(accountJsonPath)) return null;
|
|
14199
14637
|
const account = JSON.parse(readFileSync20(accountJsonPath, "utf-8"));
|
|
14200
14638
|
const accountId = account.accountId;
|
|
14201
14639
|
if (!accountId) return null;
|
|
14202
|
-
const cachePath =
|
|
14640
|
+
const cachePath = join15(configDir2, "branding-cache", accountId, `${agentSlug}.json`);
|
|
14203
14641
|
if (!existsSync23(cachePath)) return null;
|
|
14204
14642
|
return JSON.parse(readFileSync20(cachePath, "utf-8"));
|
|
14205
14643
|
} catch {
|
|
@@ -14208,8 +14646,8 @@ function loadBrandingCache(agentSlug) {
|
|
|
14208
14646
|
}
|
|
14209
14647
|
function resolveDefaultSlug() {
|
|
14210
14648
|
try {
|
|
14211
|
-
const configDir2 =
|
|
14212
|
-
const accountJsonPath =
|
|
14649
|
+
const configDir2 = join15(homedir2(), BRAND.configDir);
|
|
14650
|
+
const accountJsonPath = join15(configDir2, "account.json");
|
|
14213
14651
|
if (!existsSync23(accountJsonPath)) return null;
|
|
14214
14652
|
const account = JSON.parse(readFileSync20(accountJsonPath, "utf-8"));
|
|
14215
14653
|
return account.defaultAgent || null;
|
|
@@ -14247,7 +14685,7 @@ function brandedPublicHtml(agentSlug) {
|
|
|
14247
14685
|
function escapeHtml(s) {
|
|
14248
14686
|
return s.replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<").replace(/>/g, ">");
|
|
14249
14687
|
}
|
|
14250
|
-
|
|
14688
|
+
app40.get("/", (c) => {
|
|
14251
14689
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
14252
14690
|
if (isPublicHost(host)) {
|
|
14253
14691
|
const defaultSlug = resolveDefaultSlug();
|
|
@@ -14255,12 +14693,12 @@ app41.get("/", (c) => {
|
|
|
14255
14693
|
}
|
|
14256
14694
|
return c.html(cachedHtml("index.html"));
|
|
14257
14695
|
});
|
|
14258
|
-
|
|
14696
|
+
app40.get("/public", (c) => {
|
|
14259
14697
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
14260
14698
|
if (isPublicHost(host)) return c.text("Not found", 404);
|
|
14261
14699
|
return c.html(cachedHtml("public.html"));
|
|
14262
14700
|
});
|
|
14263
|
-
|
|
14701
|
+
app40.get("/chat", (c) => {
|
|
14264
14702
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
14265
14703
|
if (isPublicHost(host)) return c.text("Not found", 404);
|
|
14266
14704
|
return c.html(cachedHtml("public.html"));
|
|
@@ -14279,12 +14717,12 @@ async function logViewerFetch(c, next) {
|
|
|
14279
14717
|
duration_ms: Date.now() - start
|
|
14280
14718
|
});
|
|
14281
14719
|
}
|
|
14282
|
-
|
|
14283
|
-
|
|
14284
|
-
|
|
14720
|
+
app40.use("/vnc-viewer.html", logViewerFetch);
|
|
14721
|
+
app40.use("/vnc-popout.html", logViewerFetch);
|
|
14722
|
+
app40.get("/vnc-popout.html", (c) => {
|
|
14285
14723
|
let html = htmlCache.get("vnc-popout.html");
|
|
14286
14724
|
if (!html) {
|
|
14287
|
-
html = readFileSync20(
|
|
14725
|
+
html = readFileSync20(resolve24(process.cwd(), "public", "vnc-popout.html"), "utf-8");
|
|
14288
14726
|
const name = escapeHtml(BRAND.productName);
|
|
14289
14727
|
html = html.replace("<title>Browser \u2014 Maxy</title>", `<title>${name}</title>`);
|
|
14290
14728
|
html = html.replace("</head>", ` ${brandScript}
|
|
@@ -14294,7 +14732,7 @@ app41.get("/vnc-popout.html", (c) => {
|
|
|
14294
14732
|
}
|
|
14295
14733
|
return c.html(html);
|
|
14296
14734
|
});
|
|
14297
|
-
|
|
14735
|
+
app40.post("/api/vnc/client-event", async (c) => {
|
|
14298
14736
|
let body;
|
|
14299
14737
|
try {
|
|
14300
14738
|
body = await c.req.json();
|
|
@@ -14315,25 +14753,25 @@ app41.post("/api/vnc/client-event", async (c) => {
|
|
|
14315
14753
|
});
|
|
14316
14754
|
return c.json({ ok: true });
|
|
14317
14755
|
});
|
|
14318
|
-
|
|
14756
|
+
app40.get("/g/:slug", (c) => {
|
|
14319
14757
|
return c.html(brandedPublicHtml());
|
|
14320
14758
|
});
|
|
14321
|
-
|
|
14759
|
+
app40.get("/graph", (c) => {
|
|
14322
14760
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
14323
14761
|
if (isPublicHost(host)) return c.text("Not found", 404);
|
|
14324
14762
|
return c.html(cachedHtml("graph.html"));
|
|
14325
14763
|
});
|
|
14326
|
-
|
|
14764
|
+
app40.get("/sessions", (c) => {
|
|
14327
14765
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
14328
14766
|
if (isPublicHost(host)) return c.text("Not found", 404);
|
|
14329
14767
|
return c.html(cachedHtml("sessions.html"));
|
|
14330
14768
|
});
|
|
14331
|
-
|
|
14769
|
+
app40.get("/data", (c) => {
|
|
14332
14770
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
14333
14771
|
if (isPublicHost(host)) return c.text("Not found", 404);
|
|
14334
14772
|
return c.html(cachedHtml("data.html"));
|
|
14335
14773
|
});
|
|
14336
|
-
|
|
14774
|
+
app40.get("/:slug", async (c, next) => {
|
|
14337
14775
|
const slug = c.req.param("slug");
|
|
14338
14776
|
if (AGENT_SLUG_PATTERN.test(`/${slug}`)) {
|
|
14339
14777
|
const branding = loadBrandingCache(slug);
|
|
@@ -14343,15 +14781,15 @@ app41.get("/:slug", async (c, next) => {
|
|
|
14343
14781
|
await next();
|
|
14344
14782
|
});
|
|
14345
14783
|
if (brandFaviconPath !== "/favicon.ico") {
|
|
14346
|
-
|
|
14784
|
+
app40.get("/favicon.ico", (c) => {
|
|
14347
14785
|
c.header("Cache-Control", "public, max-age=300");
|
|
14348
14786
|
return c.redirect(brandFaviconPath, 302);
|
|
14349
14787
|
});
|
|
14350
14788
|
}
|
|
14351
|
-
|
|
14789
|
+
app40.use("/*", serveStatic({ root: "./public" }));
|
|
14352
14790
|
var port = requirePortEnv("MAXY_UI_INTERNAL_PORT", { tag: "ui-server" });
|
|
14353
14791
|
var hostname = process.env.HOSTNAME ?? "127.0.0.1";
|
|
14354
|
-
var httpServer = serve({ fetch:
|
|
14792
|
+
var httpServer = serve({ fetch: app40.fetch, port, hostname });
|
|
14355
14793
|
console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
14356
14794
|
{
|
|
14357
14795
|
const loopHist = monitorEventLoopDelay({ resolution: 50 });
|
|
@@ -14387,7 +14825,7 @@ for (const m of SUBAPP_MANIFEST) {
|
|
|
14387
14825
|
}
|
|
14388
14826
|
try {
|
|
14389
14827
|
const registered = [];
|
|
14390
|
-
for (const r of
|
|
14828
|
+
for (const r of app40.routes ?? []) {
|
|
14391
14829
|
if (typeof r.path !== "string" || r.path.includes(":") || r.path.includes("*")) continue;
|
|
14392
14830
|
if (AGENT_SLUG_PATTERN.test(r.path)) {
|
|
14393
14831
|
registered.push({ method: (r.method ?? "ALL").toUpperCase(), path: r.path });
|
|
@@ -14502,7 +14940,7 @@ if (bootAccountConfig?.whatsapp) {
|
|
|
14502
14940
|
}
|
|
14503
14941
|
init({
|
|
14504
14942
|
configDir: configDirForWhatsApp,
|
|
14505
|
-
platformRoot:
|
|
14943
|
+
platformRoot: resolve24(process.env.MAXY_PLATFORM_ROOT ?? join15(__dirname, "..")),
|
|
14506
14944
|
accountConfig: bootAccountConfig,
|
|
14507
14945
|
onMessage: async (msg) => {
|
|
14508
14946
|
if (process.env.WHATSAPP_PTY_BRIDGE_ENABLED === "true" && msg.text && !msg.isOwnerMirror) {
|