@rubytech/create-maxy-code 0.1.313 → 0.1.314
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/payload/platform/lib/graph-style/dist/index.d.ts.map +1 -1
- package/payload/platform/lib/graph-style/dist/index.js +5 -3
- package/payload/platform/lib/graph-style/dist/index.js.map +1 -1
- package/payload/platform/lib/graph-style/src/index.ts +5 -3
- package/payload/platform/neo4j/schema.cypher +7 -17
- package/payload/platform/plugins/admin/skills/file-presentation/SKILL.md +1 -1
- package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +4 -4
- package/payload/platform/plugins/admin/skills/professional-document/SKILL.md +1 -1
- package/payload/platform/plugins/docs/references/admin-session.md +2 -0
- package/payload/platform/plugins/docs/references/admin-ui.md +2 -2
- package/payload/platform/plugins/docs/references/memory-guide.md +1 -1
- package/payload/platform/plugins/graph-viewer/mcp/dist/index.js +1 -1
- package/payload/platform/plugins/graph-viewer/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/graph-viewer/mcp/dist/tools/graph-render.d.ts.map +1 -1
- package/payload/platform/plugins/graph-viewer/mcp/dist/tools/graph-render.js +8 -7
- package/payload/platform/plugins/graph-viewer/mcp/dist/tools/graph-render.js.map +1 -1
- package/payload/platform/plugins/graph-viewer/skills/render-graph/SKILL.md +8 -5
- package/payload/platform/plugins/memory/mcp/dist/index.js +2 -0
- package/payload/platform/plugins/memory/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/relationship-patterns.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/relationship-patterns.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/relationship-patterns.test.js +146 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/relationship-patterns.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/schema-cypher-drift.test.js +4 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/schema-cypher-drift.test.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/schema-validator.test.js +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/schema-validator.test.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/relationship-patterns.d.ts +66 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/relationship-patterns.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/relationship-patterns.js +102 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/relationship-patterns.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/schema-loader.d.ts +9 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/schema-loader.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/schema-loader.js +6 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/schema-loader.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/profile-update-personfields-open.test.js +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/profile-update-personfields-open.test.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-edge.d.ts +9 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-edge.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-edge.js +25 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-edge.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.js +21 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/vitest.config.ts +2 -0
- package/payload/platform/plugins/memory/references/schema-construction.md +37 -15
- package/payload/platform/scripts/check-no-esm-require.mjs +4 -0
- package/payload/platform/services/claude-session-manager/dist/http-server.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/http-server.js +53 -3
- package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts +4 -0
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.js +6 -0
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/session-sidecar.d.ts +10 -0
- package/payload/platform/services/claude-session-manager/dist/session-sidecar.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/session-sidecar.js +15 -0
- package/payload/platform/services/claude-session-manager/dist/session-sidecar.js.map +1 -1
- package/payload/platform/services/webchat-channel/dist/instructions.d.ts.map +1 -1
- package/payload/platform/services/webchat-channel/dist/instructions.js +8 -1
- package/payload/platform/services/webchat-channel/dist/instructions.js.map +1 -1
- package/payload/platform/services/whatsapp-channel/dist/notification.d.ts.map +1 -1
- package/payload/platform/services/whatsapp-channel/dist/notification.js +8 -1
- package/payload/platform/services/whatsapp-channel/dist/notification.js.map +1 -1
- package/payload/platform/templates/specialists/agents/content-producer.md +1 -1
- package/payload/server/public/assets/AccessGate-B_sL5-0I.js +1 -0
- package/payload/server/public/assets/{AdminLoginScreens-DgwQi3HN.js → AdminLoginScreens-BgicJfUu.js} +1 -1
- package/payload/server/public/assets/AdminShell-C0gZ5JKc.js +1 -0
- package/payload/server/public/assets/{Checkbox-C54JSreC.js → Checkbox-BJdwS4v2.js} +1 -1
- package/payload/server/public/assets/OperatorConversations-DPlZIELy.js +1 -0
- package/payload/server/public/assets/{admin-B25rkI9Q.js → admin-DAFDivnO.js} +1 -1
- package/payload/server/public/assets/audio-attachment-mime-h53tpH_4.js +30 -0
- package/payload/server/public/assets/{brand-TWflEl22.css → brand-bye0l_rp.css} +1 -1
- package/payload/server/public/assets/{browser-C9Z5dLlN.js → browser-CmFjVZYo.js} +1 -1
- package/payload/server/public/assets/chat-CSw2u48Q.js +1 -0
- package/payload/server/public/assets/{data-DCQDh0-Y.js → data-C4v6AdQ-.js} +1 -1
- package/payload/server/public/assets/graph-BJyMQdRx.js +51 -0
- package/payload/server/public/assets/graph-labels-BTpv_U3g.js +1 -0
- package/payload/server/public/assets/operator-Bj88rdnI.js +1 -0
- package/payload/server/public/assets/page-hmlVCBRQ.js +1 -0
- package/payload/server/public/assets/{public-Cs5N4-Az.js → public-IRKxc9hu.js} +3 -3
- package/payload/server/public/assets/{public-next-DoGPYX6T.js → public-next-DqQ4PZLk.js} +1 -1
- package/payload/server/public/assets/{useSelectionMode-Cs-vtuKI.js → useSelectionMode-CTJhWnNp.js} +1 -1
- package/payload/server/public/browser.html +6 -6
- package/payload/server/public/chat.html +8 -8
- package/payload/server/public/data.html +5 -5
- package/payload/server/public/graph.html +8 -8
- package/payload/server/public/index.html +9 -9
- package/payload/server/public/operator.html +10 -10
- package/payload/server/public/public-next.html +9 -9
- package/payload/server/public/public.html +7 -7
- package/payload/server/server.js +967 -513
- package/payload/server/public/assets/AccessGate-Byskkf47.js +0 -1
- package/payload/server/public/assets/AdminShell-DQfL2c_L.js +0 -1
- package/payload/server/public/assets/OperatorConversations-DulQyF8c.js +0 -1
- package/payload/server/public/assets/audio-attachment-mime-BlwrgPQa.js +0 -30
- package/payload/server/public/assets/chat-CGNWDmid.js +0 -1
- package/payload/server/public/assets/graph-labels-BBtL--_M.js +0 -1
- package/payload/server/public/assets/graph-sMzOI71g.js +0 -51
- package/payload/server/public/assets/operator-hI6kJi0I.js +0 -1
- package/payload/server/public/assets/page-DbsqlpuX.js +0 -1
- /package/payload/server/public/assets/{brand-CYjs10m-.js → brand-DqQPSWa7.js} +0 -0
package/payload/server/server.js
CHANGED
|
@@ -429,12 +429,14 @@ var require_dist2 = __commonJS({
|
|
|
429
429
|
// apart from people-terracotta, viewing-amber, and task-rose bands so
|
|
430
430
|
// siteoffice legends read as their own group. Top-level (operator-entry)
|
|
431
431
|
// labels only; child labels (LineItem, Milestone, Valuation,
|
|
432
|
-
// VariationNote,
|
|
432
|
+
// VariationNote, CheckIn) intentionally stay fallback-grey. The
|
|
433
|
+
// field-worker Worker label is an additional label on Person and takes
|
|
434
|
+
// the Person colour. A subcontractor company's received invoices use the
|
|
435
|
+
// supplier model (Organization + InboundInvoice), so they carry no
|
|
436
|
+
// construction-specific colour.
|
|
433
437
|
Job: "#8C3B2E",
|
|
434
438
|
QuoteDocument: "#A65A4A",
|
|
435
439
|
InboundInvoice: "#6B2A20",
|
|
436
|
-
SubContractor: "#BF7E6E",
|
|
437
|
-
SubInvoice: "#52201A",
|
|
438
440
|
WhatsAppGroup: "#D4A096",
|
|
439
441
|
// External business entity — schema:Organization, written by memory-write
|
|
440
442
|
// as the Supplier label. Warm tobacco — distinct hue from internal-business
|
|
@@ -1271,8 +1273,8 @@ var serveStatic = (options = { root: "" }) => {
|
|
|
1271
1273
|
};
|
|
1272
1274
|
|
|
1273
1275
|
// server/index.ts
|
|
1274
|
-
import { readFileSync as
|
|
1275
|
-
import { resolve as
|
|
1276
|
+
import { readFileSync as readFileSync31, existsSync as existsSync31, watchFile } from "fs";
|
|
1277
|
+
import { resolve as resolve33, join as join28, basename as basename10 } from "path";
|
|
1276
1278
|
import { homedir as homedir3 } from "os";
|
|
1277
1279
|
import { monitorEventLoopDelay } from "perf_hooks";
|
|
1278
1280
|
|
|
@@ -1920,7 +1922,7 @@ var credsSaveQueue = Promise.resolve();
|
|
|
1920
1922
|
async function drainCredsSaveQueue(timeoutMs = 5e3) {
|
|
1921
1923
|
console.error(`${TAG2} draining credential save queue\u2026`);
|
|
1922
1924
|
const timer2 = new Promise(
|
|
1923
|
-
(
|
|
1925
|
+
(resolve34) => setTimeout(() => resolve34("timeout"), timeoutMs)
|
|
1924
1926
|
);
|
|
1925
1927
|
const result = await Promise.race([
|
|
1926
1928
|
credsSaveQueue.then(() => "drained"),
|
|
@@ -2048,11 +2050,11 @@ async function createWaSocket(opts) {
|
|
|
2048
2050
|
return sock;
|
|
2049
2051
|
}
|
|
2050
2052
|
async function waitForConnection(sock) {
|
|
2051
|
-
return new Promise((
|
|
2053
|
+
return new Promise((resolve34, reject) => {
|
|
2052
2054
|
const handler = (update) => {
|
|
2053
2055
|
if (update.connection === "open") {
|
|
2054
2056
|
sock.ev.off("connection.update", handler);
|
|
2055
|
-
|
|
2057
|
+
resolve34();
|
|
2056
2058
|
}
|
|
2057
2059
|
if (update.connection === "close") {
|
|
2058
2060
|
sock.ev.off("connection.update", handler);
|
|
@@ -2166,14 +2168,14 @@ ${inspected}`;
|
|
|
2166
2168
|
return inspect2(err, INSPECT_OPTS2);
|
|
2167
2169
|
}
|
|
2168
2170
|
function withTimeout(label, promise, timeoutMs) {
|
|
2169
|
-
return new Promise((
|
|
2171
|
+
return new Promise((resolve34, reject) => {
|
|
2170
2172
|
const timer2 = setTimeout(() => {
|
|
2171
2173
|
reject(new Error(`${label} timed out after ${timeoutMs}ms`));
|
|
2172
2174
|
}, timeoutMs);
|
|
2173
2175
|
promise.then(
|
|
2174
2176
|
(value) => {
|
|
2175
2177
|
clearTimeout(timer2);
|
|
2176
|
-
|
|
2178
|
+
resolve34(value);
|
|
2177
2179
|
},
|
|
2178
2180
|
(err) => {
|
|
2179
2181
|
clearTimeout(timer2);
|
|
@@ -3057,8 +3059,8 @@ async function persistWhatsAppMessage(input) {
|
|
|
3057
3059
|
const { givenName, familyName } = splitName(input.pushName);
|
|
3058
3060
|
const prev = sessionWriteLocks.get(input.cacheKey);
|
|
3059
3061
|
let release;
|
|
3060
|
-
const mine = new Promise((
|
|
3061
|
-
release =
|
|
3062
|
+
const mine = new Promise((resolve34) => {
|
|
3063
|
+
release = resolve34;
|
|
3062
3064
|
});
|
|
3063
3065
|
const chained = (prev ?? Promise.resolve()).then(() => mine);
|
|
3064
3066
|
sessionWriteLocks.set(input.cacheKey, chained);
|
|
@@ -4138,11 +4140,11 @@ async function connectWithReconnect(conn) {
|
|
|
4138
4140
|
console.error(
|
|
4139
4141
|
`${TAG13} reconnecting account=${conn.accountId} in ${delay}ms (attempt ${decision.nextAttempts}/${maxAttempts})`
|
|
4140
4142
|
);
|
|
4141
|
-
await new Promise((
|
|
4142
|
-
const timer2 = setTimeout(
|
|
4143
|
+
await new Promise((resolve34) => {
|
|
4144
|
+
const timer2 = setTimeout(resolve34, delay);
|
|
4143
4145
|
conn.abortController.signal.addEventListener("abort", () => {
|
|
4144
4146
|
clearTimeout(timer2);
|
|
4145
|
-
|
|
4147
|
+
resolve34();
|
|
4146
4148
|
}, { once: true });
|
|
4147
4149
|
});
|
|
4148
4150
|
}
|
|
@@ -4150,16 +4152,16 @@ async function connectWithReconnect(conn) {
|
|
|
4150
4152
|
}
|
|
4151
4153
|
}
|
|
4152
4154
|
function waitForDisconnectEvent(conn) {
|
|
4153
|
-
return new Promise((
|
|
4155
|
+
return new Promise((resolve34) => {
|
|
4154
4156
|
if (!conn.sock) {
|
|
4155
|
-
|
|
4157
|
+
resolve34();
|
|
4156
4158
|
return;
|
|
4157
4159
|
}
|
|
4158
4160
|
const sock = conn.sock;
|
|
4159
4161
|
const handler = (update) => {
|
|
4160
4162
|
if (update.connection === "close") {
|
|
4161
4163
|
sock.ev.off("connection.update", handler);
|
|
4162
|
-
|
|
4164
|
+
resolve34();
|
|
4163
4165
|
}
|
|
4164
4166
|
};
|
|
4165
4167
|
sock.ev.on("connection.update", handler);
|
|
@@ -4419,8 +4421,8 @@ async function handleInboundMessage(conn, msg) {
|
|
|
4419
4421
|
const conversationKey = isGroup ? remoteJid : senderPhone;
|
|
4420
4422
|
const debounceKey = `${conn.accountId}:${conversationKey}:${senderPhone}`;
|
|
4421
4423
|
let resolvePending;
|
|
4422
|
-
const sttPending = new Promise((
|
|
4423
|
-
resolvePending =
|
|
4424
|
+
const sttPending = new Promise((resolve34) => {
|
|
4425
|
+
resolvePending = resolve34;
|
|
4424
4426
|
});
|
|
4425
4427
|
if (conn.debouncer) conn.debouncer.registerPending(debounceKey, sttPending);
|
|
4426
4428
|
try {
|
|
@@ -4814,20 +4816,20 @@ function buildX11Env(chromiumWrapperPath, transport = "vnc") {
|
|
|
4814
4816
|
|
|
4815
4817
|
// server/routes/health.ts
|
|
4816
4818
|
function checkPort(port2, timeoutMs = 500) {
|
|
4817
|
-
return new Promise((
|
|
4819
|
+
return new Promise((resolve34) => {
|
|
4818
4820
|
const socket = createConnection2(port2, "127.0.0.1");
|
|
4819
4821
|
socket.setTimeout(timeoutMs);
|
|
4820
4822
|
socket.once("connect", () => {
|
|
4821
4823
|
socket.destroy();
|
|
4822
|
-
|
|
4824
|
+
resolve34(true);
|
|
4823
4825
|
});
|
|
4824
4826
|
socket.once("error", () => {
|
|
4825
4827
|
socket.destroy();
|
|
4826
|
-
|
|
4828
|
+
resolve34(false);
|
|
4827
4829
|
});
|
|
4828
4830
|
socket.once("timeout", () => {
|
|
4829
4831
|
socket.destroy();
|
|
4830
|
-
|
|
4832
|
+
resolve34(false);
|
|
4831
4833
|
});
|
|
4832
4834
|
});
|
|
4833
4835
|
}
|
|
@@ -5383,6 +5385,13 @@ ${result.result.text}` : result.result.text;
|
|
|
5383
5385
|
sliceToken = session.sliceToken;
|
|
5384
5386
|
personId = session.personId;
|
|
5385
5387
|
}
|
|
5388
|
+
let visitorId;
|
|
5389
|
+
if (accessMode === "open") {
|
|
5390
|
+
const cookieHeader = c.req.header("cookie") ?? "";
|
|
5391
|
+
const m = cookieHeader.match(/(?:^|;\s*)maxy_visitor=([^;]*)/);
|
|
5392
|
+
const v = m ? decodeURIComponent(m[1]).trim() : "";
|
|
5393
|
+
if (/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(v)) visitorId = v;
|
|
5394
|
+
}
|
|
5386
5395
|
const sk = session_key.slice(0, 8);
|
|
5387
5396
|
console.error(
|
|
5388
5397
|
`${WEBCHAT_TAG} inbound from=${sk} bytes=${Buffer.byteLength(fullMessage, "utf8")} attachments=${attachmentCount} accountId=${account.accountId}`
|
|
@@ -5405,6 +5414,7 @@ ${result.result.text}` : result.result.text;
|
|
|
5405
5414
|
agentSlug,
|
|
5406
5415
|
...sliceToken ? { sliceToken } : {},
|
|
5407
5416
|
...personId ? { personId } : {},
|
|
5417
|
+
...visitorId ? { visitorId } : {},
|
|
5408
5418
|
name: publicTitle
|
|
5409
5419
|
}
|
|
5410
5420
|
});
|
|
@@ -5871,8 +5881,8 @@ async function startLogin(opts) {
|
|
|
5871
5881
|
await clearAuth(authDir);
|
|
5872
5882
|
let resolveCode = null;
|
|
5873
5883
|
let rejectCode = null;
|
|
5874
|
-
const codePromise = new Promise((
|
|
5875
|
-
resolveCode =
|
|
5884
|
+
const codePromise = new Promise((resolve34, reject) => {
|
|
5885
|
+
resolveCode = resolve34;
|
|
5876
5886
|
rejectCode = reject;
|
|
5877
5887
|
});
|
|
5878
5888
|
const codeTimer = setTimeout(
|
|
@@ -6008,9 +6018,6 @@ function normalizeJid2(to) {
|
|
|
6008
6018
|
function routeKey(to, filePath) {
|
|
6009
6019
|
return `${normalizeJid2(to)}\0${filePath}`;
|
|
6010
6020
|
}
|
|
6011
|
-
function documentOutboundAt(to) {
|
|
6012
|
-
return lastDocumentOutboundAt.get(normalizeJid2(to));
|
|
6013
|
-
}
|
|
6014
6021
|
function recordDocumentOutbound(to) {
|
|
6015
6022
|
lastDocumentOutboundAt.set(normalizeJid2(to), Date.now());
|
|
6016
6023
|
}
|
|
@@ -6021,14 +6028,14 @@ function routeDocumentOutboundAt(to, filePath) {
|
|
|
6021
6028
|
return lastRouteDocumentOutboundAt.get(routeKey(to, filePath));
|
|
6022
6029
|
}
|
|
6023
6030
|
async function sendWhatsAppDocument(input) {
|
|
6024
|
-
const { to, filePath, caption, accountId, maxyAccountId, platformRoot:
|
|
6031
|
+
const { to, filePath, caption, accountId, maxyAccountId, platformRoot: platformRoot4 } = input;
|
|
6025
6032
|
if (!to || !filePath) {
|
|
6026
6033
|
return { ok: false, status: 400, error: "Missing required fields: to, filePath" };
|
|
6027
6034
|
}
|
|
6028
|
-
if (!maxyAccountId || !
|
|
6035
|
+
if (!maxyAccountId || !platformRoot4) {
|
|
6029
6036
|
return { ok: false, status: 400, error: "Cannot validate file path: missing account or platform context" };
|
|
6030
6037
|
}
|
|
6031
|
-
const accountDir = resolve8(
|
|
6038
|
+
const accountDir = resolve8(platformRoot4, "..", "data/accounts", maxyAccountId);
|
|
6032
6039
|
let resolvedPath;
|
|
6033
6040
|
try {
|
|
6034
6041
|
resolvedPath = realpathSync3(filePath);
|
|
@@ -6634,8 +6641,8 @@ app2.get("/group-info", async (c) => {
|
|
|
6634
6641
|
var whatsapp_default = app2;
|
|
6635
6642
|
|
|
6636
6643
|
// server/routes/whatsapp-reader.ts
|
|
6637
|
-
import { readFileSync as
|
|
6638
|
-
import { basename as basename2, dirname, join as
|
|
6644
|
+
import { readFileSync as readFileSync12, watch, statSync as statSync4, openSync, readSync, closeSync, existsSync as existsSync7, readdirSync as readdirSync7 } from "fs";
|
|
6645
|
+
import { basename as basename2, dirname, isAbsolute, join as join11, relative as relative2, resolve as resolve11, sep as sep3 } from "path";
|
|
6639
6646
|
|
|
6640
6647
|
// server/routes/admin/sidebar-sessions.ts
|
|
6641
6648
|
import { readdirSync as readdirSync4, readFileSync as readFileSync9, statSync as statSync2 } from "fs";
|
|
@@ -6657,12 +6664,30 @@ function selectReaderChannelSessions(rows) {
|
|
|
6657
6664
|
operatorName: null,
|
|
6658
6665
|
whatsappName: null,
|
|
6659
6666
|
lastMessageAt: r.lastMessageAt,
|
|
6660
|
-
modelGated: r.modelGated
|
|
6667
|
+
modelGated: r.modelGated,
|
|
6668
|
+
model: r.model
|
|
6661
6669
|
}));
|
|
6662
6670
|
}
|
|
6663
6671
|
|
|
6664
6672
|
// server/routes/admin/sidebar-sessions.ts
|
|
6665
6673
|
var SESSION_ID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\.jsonl$/i;
|
|
6674
|
+
function lastAssistantModel(body) {
|
|
6675
|
+
const lines = body.split("\n");
|
|
6676
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
6677
|
+
const line = lines[i];
|
|
6678
|
+
if (!line || line[0] !== "{") continue;
|
|
6679
|
+
let rec;
|
|
6680
|
+
try {
|
|
6681
|
+
rec = JSON.parse(line);
|
|
6682
|
+
} catch {
|
|
6683
|
+
continue;
|
|
6684
|
+
}
|
|
6685
|
+
if (rec.type !== "assistant") continue;
|
|
6686
|
+
const model = rec.message?.model;
|
|
6687
|
+
if (typeof model === "string" && model !== "<synthetic>") return model;
|
|
6688
|
+
}
|
|
6689
|
+
return null;
|
|
6690
|
+
}
|
|
6666
6691
|
var CLI_MARKER_PREFIXES = ["<local-command-", "<command-name>", "<command-message>"];
|
|
6667
6692
|
var ADMIN_ROLE = "admin";
|
|
6668
6693
|
var WEBCHAT_CHANNEL = "webchat";
|
|
@@ -6778,7 +6803,7 @@ function loadUserTitles(accountDir) {
|
|
|
6778
6803
|
return out;
|
|
6779
6804
|
}
|
|
6780
6805
|
function readSidecarMeta(metaPath) {
|
|
6781
|
-
const empty = { bridgeIds: [], role: null, channel: null, senderId: null, startedAt: null, personId: null, adminUserId: null };
|
|
6806
|
+
const empty = { bridgeIds: [], role: null, channel: null, senderId: null, startedAt: null, personId: null, adminUserId: null, visitorId: null, agentSlug: null };
|
|
6782
6807
|
let raw;
|
|
6783
6808
|
try {
|
|
6784
6809
|
raw = readFileSync9(metaPath, "utf8");
|
|
@@ -6801,7 +6826,9 @@ function readSidecarMeta(metaPath) {
|
|
|
6801
6826
|
senderId: typeof r.senderId === "string" ? r.senderId : null,
|
|
6802
6827
|
startedAt: typeof r.startedAt === "string" ? r.startedAt : null,
|
|
6803
6828
|
personId: typeof r.personId === "string" ? r.personId : null,
|
|
6804
|
-
adminUserId: typeof r.adminUserId === "string" ? r.adminUserId : null
|
|
6829
|
+
adminUserId: typeof r.adminUserId === "string" ? r.adminUserId : null,
|
|
6830
|
+
visitorId: typeof r.visitorId === "string" ? r.visitorId : null,
|
|
6831
|
+
agentSlug: typeof r.agentSlug === "string" ? r.agentSlug : null
|
|
6805
6832
|
};
|
|
6806
6833
|
}
|
|
6807
6834
|
function startsWithCliMarker(s) {
|
|
@@ -6932,7 +6959,8 @@ app3.get("/", requireAdminSession, async (c) => {
|
|
|
6932
6959
|
bridgeIds,
|
|
6933
6960
|
archived,
|
|
6934
6961
|
channel: resolved.channel,
|
|
6935
|
-
personName: null
|
|
6962
|
+
personName: null,
|
|
6963
|
+
model: lastAssistantModel(body)
|
|
6936
6964
|
});
|
|
6937
6965
|
if (live) liveCount += 1;
|
|
6938
6966
|
if (isSubagent) subagentCount += 1;
|
|
@@ -7086,6 +7114,7 @@ var WA_REPLY_DOCUMENT_TOOL = "mcp__whatsapp-channel__reply-document";
|
|
|
7086
7114
|
var WEBCHAT_REPLY_TOOL = "mcp__webchat-channel__reply";
|
|
7087
7115
|
var CHANNEL_REPLY_TOOL = "mcp__channel__reply";
|
|
7088
7116
|
var CHANNEL_REPLY_DOCUMENT_TOOL = "mcp__channel__reply-document";
|
|
7117
|
+
var SEND_USER_FILE_TOOL = "SendUserFile";
|
|
7089
7118
|
var CLI_MARKER_PREFIXES2 = ["<local-command-", "<command-name>", "<command-message>"];
|
|
7090
7119
|
var CHANNEL_WRAPPER2 = /^<channel\b([^>]*)>\n?([\s\S]*?)\n?<\/channel>$/;
|
|
7091
7120
|
var SOURCE_ATTR2 = /\bsource="([^"]+)"/;
|
|
@@ -7111,17 +7140,29 @@ function unwrapChannel(text) {
|
|
|
7111
7140
|
return source ? { text: inner, source } : { text: inner };
|
|
7112
7141
|
}
|
|
7113
7142
|
var COMPOSED_ADMIN_FRAME = /^## Context\n([\s\S]*)\n\n## Instruction\n[\s\S]*$/;
|
|
7143
|
+
var UPLOAD_ANNOTATION = /(?:\n\n)?\[The operator uploaded \d+ file\(s\): [\s\S]*\]$/;
|
|
7114
7144
|
function rawFromComposedAdminFrame(text) {
|
|
7115
|
-
|
|
7145
|
+
const context = text.match(COMPOSED_ADMIN_FRAME)?.[1];
|
|
7146
|
+
if (context === void 0) return void 0;
|
|
7147
|
+
return context.replace(UPLOAD_ANNOTATION, "");
|
|
7148
|
+
}
|
|
7149
|
+
var PUBLIC_UPLOAD_ANNOTATION = /(?:\n\n)?\[The user uploaded \d+ file\(s\): [\s\S]*\]$/;
|
|
7150
|
+
var UPLOAD_COUNT = /\[The (?:operator|user) uploaded (\d+) file\(s\):/;
|
|
7151
|
+
function uploadCountOf(text) {
|
|
7152
|
+
const n = Number(text.match(UPLOAD_COUNT)?.[1]);
|
|
7153
|
+
return Number.isInteger(n) && n > 0 ? n : void 0;
|
|
7116
7154
|
}
|
|
7117
7155
|
function operatorInboundTurn(u, ts) {
|
|
7118
7156
|
const rawBody = rawFromComposedAdminFrame(u.text);
|
|
7157
|
+
const uploadCount = uploadCountOf(u.text);
|
|
7158
|
+
const text = rawBody === void 0 ? u.text.replace(PUBLIC_UPLOAD_ANNOTATION, "") : u.text;
|
|
7119
7159
|
return {
|
|
7120
7160
|
kind: "operator-inbound",
|
|
7121
|
-
text
|
|
7161
|
+
text,
|
|
7122
7162
|
ts,
|
|
7123
7163
|
...u.source ? { source: u.source } : {},
|
|
7124
|
-
...rawBody !== void 0 ? { rawBody } : {}
|
|
7164
|
+
...rawBody !== void 0 ? { rawBody } : {},
|
|
7165
|
+
...uploadCount !== void 0 ? { uploadCount } : {}
|
|
7125
7166
|
};
|
|
7126
7167
|
}
|
|
7127
7168
|
function asString(content) {
|
|
@@ -7157,6 +7198,11 @@ function assistantTurns(content, ts) {
|
|
|
7157
7198
|
const files = Array.isArray(input.files) ? input.files.filter((f) => typeof f === "string") : [];
|
|
7158
7199
|
const caption = typeof input.caption === "string" ? input.caption : null;
|
|
7159
7200
|
out.push({ kind: "agent-reply-document", files, caption, ts });
|
|
7201
|
+
} else if (b.type === "tool_use" && b.name === SEND_USER_FILE_TOOL) {
|
|
7202
|
+
const input = b.input ?? {};
|
|
7203
|
+
const files = Array.isArray(input.files) ? input.files.filter((f) => typeof f === "string") : [];
|
|
7204
|
+
const caption = typeof input.caption === "string" ? input.caption : null;
|
|
7205
|
+
out.push({ kind: "agent-file", files, caption, ts });
|
|
7160
7206
|
} else if (b.type === "tool_use" && typeof b.name === "string") {
|
|
7161
7207
|
out.push({ kind: "tool-call", name: b.name, input: b.input ?? null, ts });
|
|
7162
7208
|
}
|
|
@@ -7194,9 +7240,8 @@ function renderOrSuppressChannelInbound(u, ts, out, queuedPendingSuppress) {
|
|
|
7194
7240
|
out.push(operatorInboundTurn(u, ts));
|
|
7195
7241
|
}
|
|
7196
7242
|
}
|
|
7197
|
-
function parseTranscript(lines) {
|
|
7243
|
+
function parseTranscript(lines, queuedPendingSuppress = /* @__PURE__ */ new Map()) {
|
|
7198
7244
|
const out = [];
|
|
7199
|
-
const queuedPendingSuppress = /* @__PURE__ */ new Map();
|
|
7200
7245
|
for (const line of lines) {
|
|
7201
7246
|
if (!line || line[0] !== "{") continue;
|
|
7202
7247
|
let row;
|
|
@@ -7256,6 +7301,65 @@ function parseTranscript(lines) {
|
|
|
7256
7301
|
return out;
|
|
7257
7302
|
}
|
|
7258
7303
|
|
|
7304
|
+
// app/lib/whatsapp-reader/enrich-attachments.ts
|
|
7305
|
+
import { readFileSync as readFileSync11, readdirSync as readdirSync6 } from "fs";
|
|
7306
|
+
import { join as join10 } from "path";
|
|
7307
|
+
var ADMIN_UPLOAD_PATH = /\/uploads\/(?!public\/)([0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12})\//gi;
|
|
7308
|
+
function adminAttachmentIds(text) {
|
|
7309
|
+
const ids = [];
|
|
7310
|
+
for (const m of text.matchAll(ADMIN_UPLOAD_PATH)) {
|
|
7311
|
+
const id = m[1].toLowerCase();
|
|
7312
|
+
if (!ids.includes(id)) ids.push(id);
|
|
7313
|
+
}
|
|
7314
|
+
return ids;
|
|
7315
|
+
}
|
|
7316
|
+
function adminAttachmentsFromText(text, uploadsBase) {
|
|
7317
|
+
const out = [];
|
|
7318
|
+
for (const id of adminAttachmentIds(text)) {
|
|
7319
|
+
const meta = readAttachmentMeta(join10(uploadsBase, id));
|
|
7320
|
+
if (meta) out.push(meta);
|
|
7321
|
+
}
|
|
7322
|
+
return out;
|
|
7323
|
+
}
|
|
7324
|
+
function readSidecar(dir, attachmentId) {
|
|
7325
|
+
let parsed;
|
|
7326
|
+
try {
|
|
7327
|
+
parsed = JSON.parse(readFileSync11(join10(dir, `${attachmentId}.meta.json`), "utf8"));
|
|
7328
|
+
} catch {
|
|
7329
|
+
return null;
|
|
7330
|
+
}
|
|
7331
|
+
const filename = parsed.filename;
|
|
7332
|
+
const mimeType = parsed.mimeType;
|
|
7333
|
+
const sizeBytes = parsed.sizeBytes;
|
|
7334
|
+
const storedAt = parsed.storedAt;
|
|
7335
|
+
if (typeof filename !== "string" || typeof mimeType !== "string" || typeof sizeBytes !== "number" || typeof storedAt !== "string") {
|
|
7336
|
+
return null;
|
|
7337
|
+
}
|
|
7338
|
+
return { attachmentId, filename, mimeType, sizeBytes, storedAt };
|
|
7339
|
+
}
|
|
7340
|
+
function readAttachmentMeta(dir) {
|
|
7341
|
+
const attachmentId = dir.split("/").filter(Boolean).pop() ?? "";
|
|
7342
|
+
const sidecar = readSidecar(dir, attachmentId);
|
|
7343
|
+
if (!sidecar) return null;
|
|
7344
|
+
const { storedAt: _storedAt, ...render } = sidecar;
|
|
7345
|
+
return render;
|
|
7346
|
+
}
|
|
7347
|
+
function listSessionAttachmentsInDir(dir) {
|
|
7348
|
+
let entries;
|
|
7349
|
+
try {
|
|
7350
|
+
entries = readdirSync6(dir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
|
|
7351
|
+
} catch {
|
|
7352
|
+
return [];
|
|
7353
|
+
}
|
|
7354
|
+
const sidecars = [];
|
|
7355
|
+
for (const name of entries) {
|
|
7356
|
+
const sidecar = readSidecar(join10(dir, name), name);
|
|
7357
|
+
if (sidecar) sidecars.push(sidecar);
|
|
7358
|
+
}
|
|
7359
|
+
sidecars.sort((a, b) => a.storedAt.localeCompare(b.storedAt));
|
|
7360
|
+
return sidecars.map(({ storedAt: _storedAt, ...render }) => render);
|
|
7361
|
+
}
|
|
7362
|
+
|
|
7259
7363
|
// app/lib/whatsapp-reader/tail.ts
|
|
7260
7364
|
function splitNewLines(buf, baseOffset) {
|
|
7261
7365
|
const text = buf.toString("utf8");
|
|
@@ -7284,17 +7388,17 @@ var app4 = new Hono();
|
|
|
7284
7388
|
app4.get("/conversations", requireAdminSession, async (c) => {
|
|
7285
7389
|
const cfg = claudeConfigDir();
|
|
7286
7390
|
if (!cfg) return c.json({ conversations: [] });
|
|
7287
|
-
const projectsRoot =
|
|
7391
|
+
const projectsRoot = join11(cfg, "projects");
|
|
7288
7392
|
const userTitles = loadUserTitles(ACCOUNTS_DIR ?? null);
|
|
7289
7393
|
const rows = [];
|
|
7290
7394
|
for (const { path: path2 } of enumerateJsonls(projectsRoot)) {
|
|
7291
7395
|
const sessionId = basename2(path2).replace(/\.jsonl$/, "");
|
|
7292
7396
|
const projectDir = dirname(path2);
|
|
7293
|
-
const meta = readSidecarMeta(
|
|
7397
|
+
const meta = readSidecarMeta(join11(projectDir, `${sessionId}.meta.json`));
|
|
7294
7398
|
if (!isReaderChannelSession(meta.role, meta.channel)) continue;
|
|
7295
7399
|
let body = "";
|
|
7296
7400
|
try {
|
|
7297
|
-
body =
|
|
7401
|
+
body = readFileSync12(path2, "utf8");
|
|
7298
7402
|
} catch {
|
|
7299
7403
|
}
|
|
7300
7404
|
const { title } = resolveTitle(body, sessionId, userTitles);
|
|
@@ -7311,7 +7415,8 @@ app4.get("/conversations", requireAdminSession, async (c) => {
|
|
|
7311
7415
|
channel: meta.channel,
|
|
7312
7416
|
senderId: meta.senderId,
|
|
7313
7417
|
lastMessageAt,
|
|
7314
|
-
modelGated
|
|
7418
|
+
modelGated,
|
|
7419
|
+
model: lastAssistantModel(body)
|
|
7315
7420
|
});
|
|
7316
7421
|
}
|
|
7317
7422
|
const selected = selectReaderChannelSessions(rows);
|
|
@@ -7377,15 +7482,31 @@ function resumeOffset(lastEventId, fileEnd) {
|
|
|
7377
7482
|
if (!Number.isFinite(n) || n < 0) return 0;
|
|
7378
7483
|
return Math.min(n, fileEnd);
|
|
7379
7484
|
}
|
|
7485
|
+
function enrichDownloads(turn) {
|
|
7486
|
+
if (turn.kind !== "agent-file") return turn;
|
|
7487
|
+
const downloads = turn.files.map((f) => {
|
|
7488
|
+
const rel = relative2(DATA_ROOT, f);
|
|
7489
|
+
return !rel || rel.startsWith("..") || isAbsolute(rel) ? null : rel;
|
|
7490
|
+
});
|
|
7491
|
+
return { ...turn, downloads };
|
|
7492
|
+
}
|
|
7493
|
+
function enrichAdminAttachments(turn) {
|
|
7494
|
+
if (turn.kind !== "operator-inbound") return turn;
|
|
7495
|
+
if (adminAttachmentIds(turn.text).length === 0) return turn;
|
|
7496
|
+
const account = resolveAccount();
|
|
7497
|
+
if (!account) return turn;
|
|
7498
|
+
const attachments = adminAttachmentsFromText(turn.text, resolve11(DATA_ROOT, "accounts", account.accountId, "uploads"));
|
|
7499
|
+
return attachments.length > 0 ? { ...turn, attachments } : turn;
|
|
7500
|
+
}
|
|
7380
7501
|
app4.get("/stream", requireAdminSession, (c) => {
|
|
7381
7502
|
const sessionId = c.req.query("sessionId") ?? "";
|
|
7382
7503
|
const projectDir = c.req.query("projectDir") ?? "";
|
|
7383
7504
|
const cfg = claudeConfigDir();
|
|
7384
|
-
const projectsRoot = cfg ? resolve11(
|
|
7505
|
+
const projectsRoot = cfg ? resolve11(join11(cfg, "projects")) : null;
|
|
7385
7506
|
if (!cfg || !projectsRoot || !SESSION_ID_RE2.test(sessionId)) {
|
|
7386
7507
|
return c.json({ error: "bad session reference" }, 400);
|
|
7387
7508
|
}
|
|
7388
|
-
const jsonlPath = resolve11(
|
|
7509
|
+
const jsonlPath = resolve11(join11(projectDir, `${sessionId}.jsonl`));
|
|
7389
7510
|
if (!jsonlPath.startsWith(projectsRoot + sep3)) {
|
|
7390
7511
|
return c.json({ error: "bad session reference" }, 400);
|
|
7391
7512
|
}
|
|
@@ -7408,6 +7529,7 @@ data: ${JSON.stringify(turn)}
|
|
|
7408
7529
|
})();
|
|
7409
7530
|
let offset = resumeOffset(lastEventId, fileEnd0);
|
|
7410
7531
|
console.log(`[wa-stream] op=open conn=${connId} sessionId=${sessionId} channel=reader resume=${offset}`);
|
|
7532
|
+
const queuedPendingSuppress = /* @__PURE__ */ new Map();
|
|
7411
7533
|
const drain = () => {
|
|
7412
7534
|
try {
|
|
7413
7535
|
const { buf, end } = readFrom(jsonlPath, offset);
|
|
@@ -7415,8 +7537,8 @@ data: ${JSON.stringify(turn)}
|
|
|
7415
7537
|
const { lines, nextOffset } = splitNewLines(buf, offset);
|
|
7416
7538
|
offset = nextOffset;
|
|
7417
7539
|
let n = 0;
|
|
7418
|
-
for (const turn of parseTranscript(lines)) {
|
|
7419
|
-
send(controller, turn, offset);
|
|
7540
|
+
for (const turn of parseTranscript(lines, queuedPendingSuppress)) {
|
|
7541
|
+
send(controller, enrichAdminAttachments(enrichDownloads(turn)), offset);
|
|
7420
7542
|
if (turn.kind === "agent-error") {
|
|
7421
7543
|
console.log(`[wa-stream] op=agent-error conn=${connId} sessionId=${sessionId} code=${turn.code} source=jsonl`);
|
|
7422
7544
|
}
|
|
@@ -7431,8 +7553,8 @@ data: ${JSON.stringify(turn)}
|
|
|
7431
7553
|
const { buf, end } = readFrom(jsonlPath, 0);
|
|
7432
7554
|
offset = end;
|
|
7433
7555
|
let n = 0;
|
|
7434
|
-
for (const turn of parseTranscript(buf.toString("utf8").split("\n"))) {
|
|
7435
|
-
send(controller, turn, offset);
|
|
7556
|
+
for (const turn of parseTranscript(buf.toString("utf8").split("\n"), queuedPendingSuppress)) {
|
|
7557
|
+
send(controller, enrichAdminAttachments(enrichDownloads(turn)), offset);
|
|
7436
7558
|
if (turn.kind === "agent-error") {
|
|
7437
7559
|
console.log(`[wa-stream] op=agent-error conn=${connId} sessionId=${sessionId} code=${turn.code} source=jsonl`);
|
|
7438
7560
|
}
|
|
@@ -7487,7 +7609,7 @@ app4.get("/directives", requireAdminSession, (c) => {
|
|
|
7487
7609
|
if (!dir || !existsSync7(dir)) return c.json({ entries: [] });
|
|
7488
7610
|
let names;
|
|
7489
7611
|
try {
|
|
7490
|
-
names =
|
|
7612
|
+
names = readdirSync7(dir).filter((n) => DIRECTIVE_NAME_RE.test(n));
|
|
7491
7613
|
} catch {
|
|
7492
7614
|
return c.json({ entries: [] });
|
|
7493
7615
|
}
|
|
@@ -7497,7 +7619,7 @@ app4.get("/directives", requireAdminSession, (c) => {
|
|
|
7497
7619
|
const pid = Number(pidPart);
|
|
7498
7620
|
let len = 0;
|
|
7499
7621
|
try {
|
|
7500
|
-
len = statSync4(
|
|
7622
|
+
len = statSync4(join11(dir, name)).size;
|
|
7501
7623
|
} catch {
|
|
7502
7624
|
}
|
|
7503
7625
|
return { name, secs, pid, ts: Number.isFinite(secs) ? new Date(secs * 1e3).toISOString() : null, len };
|
|
@@ -7512,11 +7634,11 @@ app4.get("/directive", requireAdminSession, (c) => {
|
|
|
7512
7634
|
}
|
|
7513
7635
|
const dir = directiveStoreDir(sessionId);
|
|
7514
7636
|
if (!dir) return c.json({ error: "no account" }, 404);
|
|
7515
|
-
const path2 = resolve11(
|
|
7637
|
+
const path2 = resolve11(join11(dir, name));
|
|
7516
7638
|
if (!path2.startsWith(dir + sep3)) return c.json({ error: "bad reference" }, 400);
|
|
7517
7639
|
let body;
|
|
7518
7640
|
try {
|
|
7519
|
-
body =
|
|
7641
|
+
body = readFileSync12(path2, "utf8");
|
|
7520
7642
|
} catch {
|
|
7521
7643
|
return c.json({ error: "not found" }, 404);
|
|
7522
7644
|
}
|
|
@@ -7525,8 +7647,8 @@ app4.get("/directive", requireAdminSession, (c) => {
|
|
|
7525
7647
|
var whatsapp_reader_default = app4;
|
|
7526
7648
|
|
|
7527
7649
|
// server/routes/public-reader.ts
|
|
7528
|
-
import { statSync as statSync5, openSync as openSync2, readSync as readSync2, closeSync as closeSync2, watch as watch2 } from "fs";
|
|
7529
|
-
import { basename as basename3, dirname as dirname2, join as
|
|
7650
|
+
import { statSync as statSync5, openSync as openSync2, readSync as readSync2, closeSync as closeSync2, watch as watch2, readFileSync as readFileSync13, readdirSync as readdirSync8 } from "fs";
|
|
7651
|
+
import { basename as basename3, dirname as dirname2, join as join12, resolve as resolve12, sep as sep4 } from "path";
|
|
7530
7652
|
|
|
7531
7653
|
// app/lib/whatsapp-reader/delivered-kinds.ts
|
|
7532
7654
|
var PUBLIC_DELIVERED_KINDS = /* @__PURE__ */ new Set([
|
|
@@ -7563,6 +7685,13 @@ function resolveVisitorSession(rows, visitorPersonId) {
|
|
|
7563
7685
|
(best, r) => (r.startedAt ?? "") > (best.startedAt ?? "") ? r : best
|
|
7564
7686
|
);
|
|
7565
7687
|
}
|
|
7688
|
+
function resolveOpenVisitorSession(rows, visitorId, agentSlug) {
|
|
7689
|
+
const matches = rows.filter(
|
|
7690
|
+
(r) => r.role === "public" && r.channel === "webchat" && r.personId === null && r.visitorId === visitorId && r.agentSlug === agentSlug
|
|
7691
|
+
);
|
|
7692
|
+
if (matches.length === 0) return null;
|
|
7693
|
+
return matches.reduce((best, r) => (r.startedAt ?? "") > (best.startedAt ?? "") ? r : best);
|
|
7694
|
+
}
|
|
7566
7695
|
|
|
7567
7696
|
// server/routes/public-reader.ts
|
|
7568
7697
|
var app5 = new Hono();
|
|
@@ -7572,19 +7701,31 @@ function parseAccessSessionId(cookieHeader) {
|
|
|
7572
7701
|
const part = cookieHeader.split(";").map((p) => p.trim()).find((p) => p.startsWith("__access_session="));
|
|
7573
7702
|
return part ? part.slice("__access_session=".length) : null;
|
|
7574
7703
|
}
|
|
7704
|
+
var VISITOR_UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
7705
|
+
function parseVisitorCookie(cookieHeader) {
|
|
7706
|
+
if (!cookieHeader) return null;
|
|
7707
|
+
const m = cookieHeader.match(/(?:^|;\s*)maxy_visitor=([^;]*)/);
|
|
7708
|
+
if (!m) return null;
|
|
7709
|
+
const v = decodeURIComponent(m[1]).trim();
|
|
7710
|
+
return VISITOR_UUID_RE.test(v) ? v : null;
|
|
7711
|
+
}
|
|
7575
7712
|
function resolveVisitor(c) {
|
|
7576
|
-
const
|
|
7577
|
-
const
|
|
7578
|
-
|
|
7713
|
+
const cookie = c.req.header("cookie");
|
|
7714
|
+
const grantId = parseAccessSessionId(cookie);
|
|
7715
|
+
const session = grantId ? getAccessSession(grantId) : null;
|
|
7716
|
+
if (session && session.personId) return { kind: "person", personId: session.personId };
|
|
7717
|
+
const visitorId = parseVisitorCookie(cookie);
|
|
7718
|
+
if (visitorId) return { kind: "visitor", visitorId };
|
|
7719
|
+
return null;
|
|
7579
7720
|
}
|
|
7580
7721
|
function enumeratePublicRows() {
|
|
7581
7722
|
const cfg = claudeConfigDir();
|
|
7582
7723
|
if (!cfg) return [];
|
|
7583
7724
|
const rows = [];
|
|
7584
|
-
for (const { path: path2 } of enumerateJsonls(
|
|
7725
|
+
for (const { path: path2 } of enumerateJsonls(join12(cfg, "projects"))) {
|
|
7585
7726
|
const sessionId = basename3(path2).replace(/\.jsonl$/, "");
|
|
7586
7727
|
const projectDir = dirname2(path2);
|
|
7587
|
-
const meta = readSidecarMeta(
|
|
7728
|
+
const meta = readSidecarMeta(join12(projectDir, `${sessionId}.meta.json`));
|
|
7588
7729
|
rows.push({
|
|
7589
7730
|
sessionId,
|
|
7590
7731
|
projectDir,
|
|
@@ -7592,7 +7733,9 @@ function enumeratePublicRows() {
|
|
|
7592
7733
|
channel: meta.channel,
|
|
7593
7734
|
personId: meta.personId,
|
|
7594
7735
|
senderId: meta.senderId,
|
|
7595
|
-
startedAt: meta.startedAt
|
|
7736
|
+
startedAt: meta.startedAt,
|
|
7737
|
+
visitorId: meta.visitorId,
|
|
7738
|
+
agentSlug: meta.agentSlug
|
|
7596
7739
|
});
|
|
7597
7740
|
}
|
|
7598
7741
|
return rows;
|
|
@@ -7600,18 +7743,43 @@ function enumeratePublicRows() {
|
|
|
7600
7743
|
app5.get("/session", (c) => {
|
|
7601
7744
|
const visitor = resolveVisitor(c);
|
|
7602
7745
|
if (!visitor) {
|
|
7603
|
-
console.error(`${TAG20} op=reader-refused reason=no-
|
|
7746
|
+
console.error(`${TAG20} op=reader-refused reason=no-anchor path=/session`);
|
|
7604
7747
|
return c.json({ error: "gate-required" }, 401);
|
|
7605
7748
|
}
|
|
7606
|
-
const
|
|
7749
|
+
const rows = enumeratePublicRows();
|
|
7750
|
+
let found = null;
|
|
7751
|
+
if (visitor.kind === "person") {
|
|
7752
|
+
found = resolveVisitorSession(rows, visitor.personId);
|
|
7753
|
+
} else {
|
|
7754
|
+
const account = resolveAccount();
|
|
7755
|
+
const agentParam = c.req.query("agent");
|
|
7756
|
+
let effectiveSlug;
|
|
7757
|
+
if (agentParam) {
|
|
7758
|
+
effectiveSlug = account && validateAgentSlug(agentParam) && isActiveAgentSlug(account.accountDir, agentParam) ? agentParam : null;
|
|
7759
|
+
} else {
|
|
7760
|
+
effectiveSlug = account ? resolveDefaultAgentSlug(account.accountDir) : null;
|
|
7761
|
+
}
|
|
7762
|
+
found = effectiveSlug ? resolveOpenVisitorSession(rows, visitor.visitorId, effectiveSlug) : null;
|
|
7763
|
+
}
|
|
7607
7764
|
if (found) {
|
|
7608
|
-
console.log(`${TAG20} op=session-resume key=${(found.senderId ?? "").slice(0, 8)} sessionId=${found.sessionId.slice(0, 8)}`);
|
|
7765
|
+
console.log(`${TAG20} op=session-resume anchor=${visitor.kind} key=${(found.senderId ?? "").slice(0, 8)} sessionId=${found.sessionId.slice(0, 8)}`);
|
|
7609
7766
|
return c.json({ sessionId: found.sessionId, projectDir: found.projectDir, sessionKey: found.senderId });
|
|
7610
7767
|
}
|
|
7611
7768
|
const sessionKey = crypto.randomUUID();
|
|
7612
|
-
console.log(`${TAG20} op=session-new key=${sessionKey.slice(0, 8)}`);
|
|
7769
|
+
console.log(`${TAG20} op=session-new anchor=${visitor.kind} key=${sessionKey.slice(0, 8)}`);
|
|
7613
7770
|
return c.json({ sessionId: null, projectDir: null, sessionKey });
|
|
7614
7771
|
});
|
|
7772
|
+
function publicUploadsDir(accountId, sessionId) {
|
|
7773
|
+
return resolve12(DATA_ROOT, "accounts", accountId, "uploads", "public", sessionId);
|
|
7774
|
+
}
|
|
7775
|
+
function enrichPublicAttachments(turns, sessionAttachments, cursor) {
|
|
7776
|
+
for (const turn of turns) {
|
|
7777
|
+
if (turn.kind !== "operator-inbound" || !turn.uploadCount) continue;
|
|
7778
|
+
const slice = sessionAttachments.slice(cursor.consumed, cursor.consumed + turn.uploadCount);
|
|
7779
|
+
cursor.consumed += turn.uploadCount;
|
|
7780
|
+
if (slice.length > 0) turn.attachments = slice;
|
|
7781
|
+
}
|
|
7782
|
+
}
|
|
7615
7783
|
function readFrom2(path2, from) {
|
|
7616
7784
|
const end = statSync5(path2).size;
|
|
7617
7785
|
if (end <= from) return { buf: Buffer.alloc(0), end: from };
|
|
@@ -7627,37 +7795,43 @@ function readFrom2(path2, from) {
|
|
|
7627
7795
|
app5.get("/stream", (c) => {
|
|
7628
7796
|
const visitor = resolveVisitor(c);
|
|
7629
7797
|
if (!visitor) {
|
|
7630
|
-
console.error(`${TAG20} op=reader-refused reason=no-
|
|
7798
|
+
console.error(`${TAG20} op=reader-refused reason=no-anchor path=/stream`);
|
|
7631
7799
|
return c.json({ error: "gate-required" }, 401);
|
|
7632
7800
|
}
|
|
7633
7801
|
const sessionId = c.req.query("sessionId") ?? "";
|
|
7634
7802
|
const projectDir = c.req.query("projectDir") ?? "";
|
|
7635
7803
|
const cfg = claudeConfigDir();
|
|
7636
|
-
const projectsRoot = cfg ? resolve12(
|
|
7804
|
+
const projectsRoot = cfg ? resolve12(join12(cfg, "projects")) : null;
|
|
7637
7805
|
if (!cfg || !projectsRoot || !SESSION_ID_RE2.test(sessionId)) {
|
|
7638
7806
|
return c.json({ error: "bad session reference" }, 400);
|
|
7639
7807
|
}
|
|
7640
|
-
const jsonlPath = resolve12(
|
|
7808
|
+
const jsonlPath = resolve12(join12(projectDir, `${sessionId}.jsonl`));
|
|
7641
7809
|
if (!jsonlPath.startsWith(projectsRoot + sep4)) {
|
|
7642
7810
|
return c.json({ error: "bad session reference" }, 400);
|
|
7643
7811
|
}
|
|
7644
|
-
const meta = readSidecarMeta(
|
|
7645
|
-
|
|
7646
|
-
|
|
7812
|
+
const meta = readSidecarMeta(join12(projectDir, `${sessionId}.meta.json`));
|
|
7813
|
+
const isPublicWebchat = meta.role === "public" && meta.channel === "webchat";
|
|
7814
|
+
const owns = visitor.kind === "person" ? meta.personId === visitor.personId : meta.personId === null && meta.visitorId === visitor.visitorId;
|
|
7815
|
+
if (!(isPublicWebchat && owns)) {
|
|
7816
|
+
console.error(`${TAG20} op=reader-refused reason=not-owner anchor=${visitor.kind} sessionId=${sessionId.slice(0, 8)}`);
|
|
7647
7817
|
return c.json({ error: "forbidden" }, 403);
|
|
7648
7818
|
}
|
|
7649
7819
|
const senderShort = (meta.senderId ?? "").slice(0, 8);
|
|
7650
7820
|
const encoder = new TextEncoder();
|
|
7651
|
-
console.log(`${TAG20} op=reader-open key=${senderShort} mode=delivered-only sessionId=${sessionId.slice(0, 8)}`);
|
|
7821
|
+
console.log(`${TAG20} op=reader-open anchor=${visitor.kind} key=${senderShort} mode=delivered-only sessionId=${sessionId.slice(0, 8)}`);
|
|
7652
7822
|
const send = (controller, turn, id) => controller.enqueue(encoder.encode(`id: ${id}
|
|
7653
7823
|
data: ${JSON.stringify(turn)}
|
|
7654
7824
|
|
|
7655
7825
|
`));
|
|
7826
|
+
const account = resolveAccount();
|
|
7827
|
+
const uploadsDir = account ? publicUploadsDir(account.accountId, sessionId) : null;
|
|
7828
|
+
const attachmentCursor = { consumed: 0 };
|
|
7656
7829
|
const readable = new ReadableStream({
|
|
7657
7830
|
start(controller) {
|
|
7658
7831
|
let offset = 0;
|
|
7659
7832
|
const emit = (lines) => {
|
|
7660
7833
|
const { turns, dropped } = filterDeliveredFrames(lines);
|
|
7834
|
+
if (uploadsDir) enrichPublicAttachments(turns, listSessionAttachmentsInDir(uploadsDir), attachmentCursor);
|
|
7661
7835
|
for (const turn of turns) send(controller, turn, offset);
|
|
7662
7836
|
if (dropped > 0) console.log(`${TAG20} op=reader-filtered key=${senderShort} dropped=${dropped}`);
|
|
7663
7837
|
};
|
|
@@ -7706,24 +7880,83 @@ data: ${JSON.stringify(turn)}
|
|
|
7706
7880
|
headers: { "Content-Type": "text/event-stream", "Cache-Control": "no-cache", Connection: "keep-alive" }
|
|
7707
7881
|
});
|
|
7708
7882
|
});
|
|
7883
|
+
var ATT_TAG = "[public-attachment]";
|
|
7884
|
+
var ATTACHMENT_ID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
7885
|
+
app5.get("/attachment/:attachmentId", (c) => {
|
|
7886
|
+
const visitor = resolveVisitor(c);
|
|
7887
|
+
const attachmentId = c.req.param("attachmentId");
|
|
7888
|
+
const sessionId = c.req.query("sessionId") ?? "";
|
|
7889
|
+
const ses = sessionId.slice(0, 8);
|
|
7890
|
+
const at = attachmentId.slice(0, 8);
|
|
7891
|
+
if (!visitor) {
|
|
7892
|
+
console.error(`${ATT_TAG} op=denied session=${ses} att=${at} reason=no-anchor`);
|
|
7893
|
+
return new Response("Unauthorized", { status: 401 });
|
|
7894
|
+
}
|
|
7895
|
+
console.log(`${ATT_TAG} op=request session=${ses} att=${at}`);
|
|
7896
|
+
if (!ATTACHMENT_ID_RE.test(attachmentId) || !SESSION_ID_RE2.test(sessionId)) {
|
|
7897
|
+
console.error(`${ATT_TAG} op=denied session=${ses} att=${at} reason=bad-id`);
|
|
7898
|
+
return new Response("Not found", { status: 404 });
|
|
7899
|
+
}
|
|
7900
|
+
const row = enumeratePublicRows().find((r) => r.sessionId === sessionId);
|
|
7901
|
+
const owns = row != null && row.role === "public" && row.channel === "webchat" && (visitor.kind === "person" ? row.personId === visitor.personId : row.personId === null && row.visitorId === visitor.visitorId);
|
|
7902
|
+
if (!owns) {
|
|
7903
|
+
console.error(`${ATT_TAG} op=denied session=${ses} att=${at} reason=foreign-session`);
|
|
7904
|
+
return new Response("Not found", { status: 404 });
|
|
7905
|
+
}
|
|
7906
|
+
const account = resolveAccount();
|
|
7907
|
+
if (!account) {
|
|
7908
|
+
console.error(`${ATT_TAG} op=denied session=${ses} att=${at} reason=not-found`);
|
|
7909
|
+
return new Response("Not found", { status: 404 });
|
|
7910
|
+
}
|
|
7911
|
+
const uploadsRoot = publicUploadsDir(account.accountId, sessionId);
|
|
7912
|
+
const dir = resolve12(uploadsRoot, attachmentId);
|
|
7913
|
+
if (!dir.startsWith(uploadsRoot + sep4)) {
|
|
7914
|
+
console.error(`${ATT_TAG} op=denied session=${ses} att=${at} reason=bad-id`);
|
|
7915
|
+
return new Response("Not found", { status: 404 });
|
|
7916
|
+
}
|
|
7917
|
+
const meta = readAttachmentMeta(dir);
|
|
7918
|
+
if (!meta) {
|
|
7919
|
+
console.error(`${ATT_TAG} op=denied session=${ses} att=${at} reason=not-found`);
|
|
7920
|
+
return new Response("Not found", { status: 404 });
|
|
7921
|
+
}
|
|
7922
|
+
let dataFile;
|
|
7923
|
+
let buffer;
|
|
7924
|
+
try {
|
|
7925
|
+
dataFile = readdirSync8(dir).find((f) => !f.endsWith(".meta.json"));
|
|
7926
|
+
if (!dataFile) throw new Error("no data file");
|
|
7927
|
+
buffer = readFileSync13(resolve12(dir, dataFile));
|
|
7928
|
+
} catch {
|
|
7929
|
+
console.error(`${ATT_TAG} op=denied session=${ses} att=${at} reason=not-found`);
|
|
7930
|
+
return new Response("Not found", { status: 404 });
|
|
7931
|
+
}
|
|
7932
|
+
console.log(`${ATT_TAG} op=served session=${ses} att=${at} bytes=${buffer.length} mime=${meta.mimeType}`);
|
|
7933
|
+
const safeName = meta.filename.replace(/["\\\r\n]/g, "_");
|
|
7934
|
+
return new Response(new Uint8Array(buffer), {
|
|
7935
|
+
headers: {
|
|
7936
|
+
"Content-Type": meta.mimeType,
|
|
7937
|
+
"Content-Disposition": `inline; filename="${safeName}"`,
|
|
7938
|
+
"Cache-Control": "private, max-age=3600"
|
|
7939
|
+
}
|
|
7940
|
+
});
|
|
7941
|
+
});
|
|
7709
7942
|
var public_reader_default = app5;
|
|
7710
7943
|
|
|
7711
7944
|
// server/routes/webchat.ts
|
|
7712
7945
|
import { basename as basename4, dirname as dirname3 } from "path";
|
|
7713
|
-
import { join as
|
|
7714
|
-
import { existsSync as existsSync9, readdirSync as
|
|
7946
|
+
import { join as join15 } from "path";
|
|
7947
|
+
import { existsSync as existsSync9, readdirSync as readdirSync10, readFileSync as readFileSync16, renameSync as renameSync3, statSync as statSync6, writeFileSync as writeFileSync7 } from "fs";
|
|
7715
7948
|
|
|
7716
7949
|
// server/canonical-webchat-override.ts
|
|
7717
|
-
import { readFileSync as
|
|
7718
|
-
import { join as
|
|
7950
|
+
import { readFileSync as readFileSync14, writeFileSync as writeFileSync5, renameSync } from "fs";
|
|
7951
|
+
import { join as join13 } from "path";
|
|
7719
7952
|
var FILE = "canonical-webchat-session.json";
|
|
7720
7953
|
var UUID = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;
|
|
7721
7954
|
function canonicalOverridePath(accountDir) {
|
|
7722
|
-
return
|
|
7955
|
+
return join13(accountDir, FILE);
|
|
7723
7956
|
}
|
|
7724
7957
|
function readCanonicalOverrideId(accountDir) {
|
|
7725
7958
|
try {
|
|
7726
|
-
const raw = JSON.parse(
|
|
7959
|
+
const raw = JSON.parse(readFileSync14(canonicalOverridePath(accountDir), "utf8"));
|
|
7727
7960
|
return typeof raw.sessionId === "string" && UUID.test(raw.sessionId) ? raw.sessionId : null;
|
|
7728
7961
|
} catch {
|
|
7729
7962
|
return null;
|
|
@@ -7749,8 +7982,8 @@ function adminSessionIdFor(accountId, senderId, personId) {
|
|
|
7749
7982
|
}
|
|
7750
7983
|
|
|
7751
7984
|
// ../services/claude-session-manager/src/sidecar-store.ts
|
|
7752
|
-
import { existsSync as existsSync8, mkdirSync as mkdirSync2, readdirSync as
|
|
7753
|
-
import { join as
|
|
7985
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync2, readdirSync as readdirSync9, readFileSync as readFileSync15, renameSync as renameSync2, unlinkSync, writeFileSync as writeFileSync6 } from "fs";
|
|
7986
|
+
import { join as join14 } from "path";
|
|
7754
7987
|
function escapeRegExp(s) {
|
|
7755
7988
|
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
7756
7989
|
}
|
|
@@ -7758,7 +7991,7 @@ function createSidecarStore(config) {
|
|
|
7758
7991
|
const { suffix, validate: validate2 } = config;
|
|
7759
7992
|
const suffixRe = new RegExp(`${escapeRegExp(suffix)}$`);
|
|
7760
7993
|
function pathFor(sessionsDir, id) {
|
|
7761
|
-
return
|
|
7994
|
+
return join14(sessionsDir, `${id}${suffix}`);
|
|
7762
7995
|
}
|
|
7763
7996
|
function write(sessionsDir, id, record) {
|
|
7764
7997
|
mkdirSync2(sessionsDir, { recursive: true });
|
|
@@ -7779,7 +8012,7 @@ function createSidecarStore(config) {
|
|
|
7779
8012
|
function readFileAt(path2) {
|
|
7780
8013
|
let raw;
|
|
7781
8014
|
try {
|
|
7782
|
-
raw =
|
|
8015
|
+
raw = readFileSync15(path2, "utf8");
|
|
7783
8016
|
} catch {
|
|
7784
8017
|
return null;
|
|
7785
8018
|
}
|
|
@@ -7795,17 +8028,17 @@ function createSidecarStore(config) {
|
|
|
7795
8028
|
function readAll(sessionsDir, onSkip) {
|
|
7796
8029
|
let names;
|
|
7797
8030
|
try {
|
|
7798
|
-
names =
|
|
8031
|
+
names = readdirSync9(sessionsDir, { withFileTypes: true }).filter((entry) => entry.isFile()).map((entry) => entry.name);
|
|
7799
8032
|
} catch {
|
|
7800
8033
|
return [];
|
|
7801
8034
|
}
|
|
7802
8035
|
const out = [];
|
|
7803
8036
|
for (const name of names) {
|
|
7804
8037
|
if (!suffixRe.test(name)) continue;
|
|
7805
|
-
const path2 =
|
|
8038
|
+
const path2 = join14(sessionsDir, name);
|
|
7806
8039
|
let raw;
|
|
7807
8040
|
try {
|
|
7808
|
-
raw =
|
|
8041
|
+
raw = readFileSync15(path2, "utf8");
|
|
7809
8042
|
} catch {
|
|
7810
8043
|
onSkip?.(name, "unreadable");
|
|
7811
8044
|
continue;
|
|
@@ -7879,10 +8112,10 @@ var UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9
|
|
|
7879
8112
|
function locateSession(sessionId) {
|
|
7880
8113
|
const cfg = claudeConfigDir();
|
|
7881
8114
|
if (!cfg) return { projectDir: null, channel: null };
|
|
7882
|
-
for (const { path: path2 } of enumerateJsonls(
|
|
8115
|
+
for (const { path: path2 } of enumerateJsonls(join15(cfg, "projects"))) {
|
|
7883
8116
|
if (basename4(path2) === `${sessionId}.jsonl`) {
|
|
7884
8117
|
const dir = dirname3(path2);
|
|
7885
|
-
const meta = readSidecarMeta(
|
|
8118
|
+
const meta = readSidecarMeta(join15(dir, `${sessionId}.meta.json`));
|
|
7886
8119
|
return { projectDir: dir, channel: meta.channel };
|
|
7887
8120
|
}
|
|
7888
8121
|
}
|
|
@@ -7891,15 +8124,15 @@ function locateSession(sessionId) {
|
|
|
7891
8124
|
function locateSidecar(sessionId) {
|
|
7892
8125
|
const cfg = claudeConfigDir();
|
|
7893
8126
|
if (!cfg) return null;
|
|
7894
|
-
const projectsRoot =
|
|
8127
|
+
const projectsRoot = join15(cfg, "projects");
|
|
7895
8128
|
let slugs;
|
|
7896
8129
|
try {
|
|
7897
|
-
slugs =
|
|
8130
|
+
slugs = readdirSync10(projectsRoot, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
|
|
7898
8131
|
} catch {
|
|
7899
8132
|
return null;
|
|
7900
8133
|
}
|
|
7901
8134
|
for (const slug of slugs) {
|
|
7902
|
-
const metaPath =
|
|
8135
|
+
const metaPath = join15(projectsRoot, slug, `${sessionId}.meta.json`);
|
|
7903
8136
|
if (existsSync9(metaPath)) {
|
|
7904
8137
|
return { channel: readSidecarMeta(metaPath).channel };
|
|
7905
8138
|
}
|
|
@@ -7912,7 +8145,7 @@ function sessionSidecarExists(sessionId) {
|
|
|
7912
8145
|
function jsonlSizeBytes(projectDir, sessionId) {
|
|
7913
8146
|
if (projectDir === null) return null;
|
|
7914
8147
|
try {
|
|
7915
|
-
return statSync6(
|
|
8148
|
+
return statSync6(join15(projectDir, `${sessionId}.jsonl`)).size;
|
|
7916
8149
|
} catch {
|
|
7917
8150
|
return null;
|
|
7918
8151
|
}
|
|
@@ -7981,10 +8214,10 @@ function latestAdminWebchatSessionId() {
|
|
|
7981
8214
|
const cfg = claudeConfigDir();
|
|
7982
8215
|
if (!cfg) return null;
|
|
7983
8216
|
let best = null;
|
|
7984
|
-
for (const { path: path2, isSubagent, archived } of enumerateJsonls(
|
|
8217
|
+
for (const { path: path2, isSubagent, archived } of enumerateJsonls(join15(cfg, "projects"))) {
|
|
7985
8218
|
if (isSubagent || archived) continue;
|
|
7986
8219
|
const id = basename4(path2).slice(0, -".jsonl".length);
|
|
7987
|
-
const meta = readSidecarMeta(
|
|
8220
|
+
const meta = readSidecarMeta(join15(dirname3(path2), `${id}.meta.json`));
|
|
7988
8221
|
if (meta.role !== "admin" || meta.channel !== "webchat") continue;
|
|
7989
8222
|
let mtimeMs;
|
|
7990
8223
|
try {
|
|
@@ -8213,9 +8446,9 @@ ${note}` : note;
|
|
|
8213
8446
|
console.error(`[webchat:settings] op=${op} outcome=refused value=${value} reason=account-unresolved`);
|
|
8214
8447
|
return c.json({ error: "account unresolved" }, 503);
|
|
8215
8448
|
}
|
|
8216
|
-
const accountJsonPath =
|
|
8449
|
+
const accountJsonPath = join15(account.accountDir, "account.json");
|
|
8217
8450
|
try {
|
|
8218
|
-
const parsed = JSON.parse(
|
|
8451
|
+
const parsed = JSON.parse(readFileSync16(accountJsonPath, "utf8"));
|
|
8219
8452
|
if (op === "model") parsed.adminModel = value;
|
|
8220
8453
|
else if (op === "effort") parsed.effort = value;
|
|
8221
8454
|
else parsed.adminPermissionMode = value;
|
|
@@ -8252,7 +8485,7 @@ ${note}` : note;
|
|
|
8252
8485
|
const cfg2 = claudeConfigDir();
|
|
8253
8486
|
if (cfg2) {
|
|
8254
8487
|
const source2 = resolveBridgeSource(
|
|
8255
|
-
|
|
8488
|
+
join15(cfg2, "sessions"),
|
|
8256
8489
|
target,
|
|
8257
8490
|
(id) => locateSession(id).projectDir !== null
|
|
8258
8491
|
);
|
|
@@ -8281,7 +8514,7 @@ ${note}` : note;
|
|
|
8281
8514
|
const cfg = claudeConfigDir();
|
|
8282
8515
|
let projectDir = null;
|
|
8283
8516
|
if (cfg) {
|
|
8284
|
-
for (const { path: path2 } of enumerateJsonls(
|
|
8517
|
+
for (const { path: path2 } of enumerateJsonls(join15(cfg, "projects"))) {
|
|
8285
8518
|
if (basename4(path2) === `${sessionId}.jsonl`) {
|
|
8286
8519
|
projectDir = dirname3(path2);
|
|
8287
8520
|
break;
|
|
@@ -8298,8 +8531,8 @@ ${note}` : note;
|
|
|
8298
8531
|
import { resolve as resolve13 } from "path";
|
|
8299
8532
|
|
|
8300
8533
|
// app/lib/claude-agent/specialist-roster.ts
|
|
8301
|
-
import { existsSync as existsSync10, readFileSync as
|
|
8302
|
-
import { join as
|
|
8534
|
+
import { existsSync as existsSync10, readFileSync as readFileSync17, readdirSync as readdirSync11 } from "fs";
|
|
8535
|
+
import { join as join16 } from "path";
|
|
8303
8536
|
function field(fm, name) {
|
|
8304
8537
|
const m = fm.match(new RegExp(`^${name}:\\s*(.*?)\\r?$`, "m"));
|
|
8305
8538
|
if (!m) return null;
|
|
@@ -8311,14 +8544,14 @@ function field(fm, name) {
|
|
|
8311
8544
|
}
|
|
8312
8545
|
function readSpecialistRoster(specialistsDir) {
|
|
8313
8546
|
if (!existsSync10(specialistsDir)) return { specialists: [], skipped: [] };
|
|
8314
|
-
const entries =
|
|
8547
|
+
const entries = readdirSync11(specialistsDir);
|
|
8315
8548
|
const specialists = [];
|
|
8316
8549
|
const skipped = [];
|
|
8317
8550
|
for (const file of entries.sort()) {
|
|
8318
8551
|
if (!file.endsWith(".md")) continue;
|
|
8319
8552
|
let raw;
|
|
8320
8553
|
try {
|
|
8321
|
-
raw =
|
|
8554
|
+
raw = readFileSync17(join16(specialistsDir, file), "utf-8");
|
|
8322
8555
|
} catch (err) {
|
|
8323
8556
|
skipped.push({ file, reason: err instanceof Error ? err.message : String(err) });
|
|
8324
8557
|
continue;
|
|
@@ -8435,11 +8668,11 @@ var webchat_greeting_default = app6;
|
|
|
8435
8668
|
|
|
8436
8669
|
// server/routes/telegram.ts
|
|
8437
8670
|
import { homedir } from "os";
|
|
8438
|
-
import { resolve as
|
|
8439
|
-
import { existsSync as existsSync11, readFileSync as
|
|
8671
|
+
import { resolve as resolve17, join as join17 } from "path";
|
|
8672
|
+
import { existsSync as existsSync11, readFileSync as readFileSync18 } from "fs";
|
|
8440
8673
|
|
|
8441
8674
|
// app/lib/channel-pty-bridge/bridge.ts
|
|
8442
|
-
import { resolve as
|
|
8675
|
+
import { resolve as resolve16 } from "path";
|
|
8443
8676
|
|
|
8444
8677
|
// app/lib/channel-pty-bridge/manager-client.ts
|
|
8445
8678
|
function managerBase2() {
|
|
@@ -8470,6 +8703,7 @@ async function managerSpawn(opts) {
|
|
|
8470
8703
|
specialistDomains: opts.specialistDomains,
|
|
8471
8704
|
sliceToken: opts.sliceToken,
|
|
8472
8705
|
personId: opts.personId,
|
|
8706
|
+
visitorId: opts.visitorId,
|
|
8473
8707
|
name: opts.name,
|
|
8474
8708
|
webchatChannel: opts.webchatChannel
|
|
8475
8709
|
})
|
|
@@ -8878,81 +9112,114 @@ async function fanOut(subscribers, text, onError, tag) {
|
|
|
8878
9112
|
);
|
|
8879
9113
|
}
|
|
8880
9114
|
|
|
9115
|
+
// app/lib/channel-pty-bridge/file-delivery.ts
|
|
9116
|
+
var SEND_USER_FILE = "SendUserFile";
|
|
9117
|
+
function makeFileDelivery(opts) {
|
|
9118
|
+
const { entry, tag, channel, sendFile } = opts;
|
|
9119
|
+
let failedFiles = [];
|
|
9120
|
+
let attempts = 0;
|
|
9121
|
+
return {
|
|
9122
|
+
isFileDeliveryTool(toolName) {
|
|
9123
|
+
return toolName === SEND_USER_FILE;
|
|
9124
|
+
},
|
|
9125
|
+
async onFileToolUse(use) {
|
|
9126
|
+
if (use.toolName !== SEND_USER_FILE) return;
|
|
9127
|
+
const input = use.input ?? {};
|
|
9128
|
+
const files = Array.isArray(input.files) ? input.files.filter((f) => typeof f === "string") : [];
|
|
9129
|
+
const caption = typeof input.caption === "string" ? input.caption : void 0;
|
|
9130
|
+
for (let i = 0; i < files.length; i++) {
|
|
9131
|
+
attempts++;
|
|
9132
|
+
const result = await sendFile(files[i], i === 0 ? caption : void 0);
|
|
9133
|
+
const name = files[i].split("/").pop() ?? files[i];
|
|
9134
|
+
console.error(`${tag} op=file-forward channel=${channel} file=${name} outcome=${result.ok ? "ok" : "fail"}`);
|
|
9135
|
+
if (!result.ok) failedFiles.push(files[i]);
|
|
9136
|
+
}
|
|
9137
|
+
},
|
|
9138
|
+
onTurnEnd(firedTools) {
|
|
9139
|
+
const failed = failedFiles;
|
|
9140
|
+
const tried = attempts;
|
|
9141
|
+
failedFiles = [];
|
|
9142
|
+
attempts = 0;
|
|
9143
|
+
const sid = entry.sessionId.slice(0, 8);
|
|
9144
|
+
for (const file of failed) {
|
|
9145
|
+
console.error(
|
|
9146
|
+
`${tag} file-delivery-unreconciled sender=${entry.senderId} sessionId=${sid} tool=${SEND_USER_FILE} file=${file}`
|
|
9147
|
+
);
|
|
9148
|
+
}
|
|
9149
|
+
if (firedTools.includes(SEND_USER_FILE) && tried === 0) {
|
|
9150
|
+
console.error(
|
|
9151
|
+
`${tag} file-delivery-unreconciled sender=${entry.senderId} sessionId=${sid} tool=${SEND_USER_FILE}`
|
|
9152
|
+
);
|
|
9153
|
+
}
|
|
9154
|
+
}
|
|
9155
|
+
};
|
|
9156
|
+
}
|
|
9157
|
+
|
|
8881
9158
|
// app/lib/whatsapp/inbound/file-delivery-bridge.ts
|
|
8882
9159
|
var TAG22 = "[whatsapp-adaptor]";
|
|
8883
|
-
var
|
|
9160
|
+
var SEND_USER_FILE2 = "SendUserFile";
|
|
8884
9161
|
var WHATSAPP_SEND_DOCUMENT = "whatsapp-send-document";
|
|
8885
9162
|
function platformRoot() {
|
|
8886
9163
|
return process.env.MAXY_PLATFORM_ROOT || "";
|
|
8887
9164
|
}
|
|
9165
|
+
function makeWhatsAppSendFile(entry) {
|
|
9166
|
+
return async (filePath, caption) => {
|
|
9167
|
+
let maxyAccountId;
|
|
9168
|
+
try {
|
|
9169
|
+
maxyAccountId = resolvePlatformAccountId();
|
|
9170
|
+
} catch (err) {
|
|
9171
|
+
console.error(
|
|
9172
|
+
`${TAG22} file-delivery reject reason=account-unresolved sender=${entry.senderId} message=${err instanceof Error ? err.message : String(err)}`
|
|
9173
|
+
);
|
|
9174
|
+
return { ok: false, error: "account-unresolved" };
|
|
9175
|
+
}
|
|
9176
|
+
const result = await sendWhatsAppDocument({
|
|
9177
|
+
to: entry.senderId,
|
|
9178
|
+
filePath,
|
|
9179
|
+
caption,
|
|
9180
|
+
accountId: entry.accountId,
|
|
9181
|
+
maxyAccountId,
|
|
9182
|
+
platformRoot: platformRoot()
|
|
9183
|
+
});
|
|
9184
|
+
if (result.ok) return { ok: true };
|
|
9185
|
+
console.error(
|
|
9186
|
+
`${TAG22} file-delivery reject reason=send-failed sender=${entry.senderId} status=${result.status} message=${result.error}`
|
|
9187
|
+
);
|
|
9188
|
+
return { ok: false, error: result.error };
|
|
9189
|
+
};
|
|
9190
|
+
}
|
|
8888
9191
|
function makeWhatsAppFileDelivery(entry) {
|
|
9192
|
+
const shared = makeFileDelivery({
|
|
9193
|
+
entry,
|
|
9194
|
+
tag: TAG22,
|
|
9195
|
+
channel: "whatsapp",
|
|
9196
|
+
sendFile: makeWhatsAppSendFile(entry)
|
|
9197
|
+
});
|
|
8889
9198
|
let turnStartedAt = null;
|
|
8890
|
-
let failedFiles = [];
|
|
8891
|
-
let sendUserFileAttempts = 0;
|
|
8892
9199
|
let routeCalls = [];
|
|
8893
9200
|
return {
|
|
8894
9201
|
isFileDeliveryTool(toolName) {
|
|
8895
|
-
return toolName ===
|
|
9202
|
+
return toolName === SEND_USER_FILE2 || toolName === WHATSAPP_SEND_DOCUMENT;
|
|
8896
9203
|
},
|
|
8897
9204
|
async onFileToolUse(use) {
|
|
8898
9205
|
if (turnStartedAt === null) turnStartedAt = Date.now();
|
|
8899
9206
|
if (use.toolName === WHATSAPP_SEND_DOCUMENT) {
|
|
8900
|
-
const
|
|
9207
|
+
const input = use.input ?? {};
|
|
8901
9208
|
routeCalls.push({
|
|
8902
|
-
to: typeof
|
|
8903
|
-
filePath: typeof
|
|
9209
|
+
to: typeof input.to === "string" ? input.to : void 0,
|
|
9210
|
+
filePath: typeof input.filePath === "string" ? input.filePath : void 0
|
|
8904
9211
|
});
|
|
8905
9212
|
return;
|
|
8906
9213
|
}
|
|
8907
|
-
|
|
8908
|
-
const input = use.input ?? {};
|
|
8909
|
-
const files = Array.isArray(input.files) ? input.files.filter((f) => typeof f === "string") : [];
|
|
8910
|
-
const caption = typeof input.caption === "string" ? input.caption : void 0;
|
|
8911
|
-
if (files.length === 0) return;
|
|
8912
|
-
let maxyAccountId;
|
|
8913
|
-
try {
|
|
8914
|
-
maxyAccountId = resolvePlatformAccountId();
|
|
8915
|
-
} catch (err) {
|
|
8916
|
-
console.error(
|
|
8917
|
-
`${TAG22} file-delivery reject reason=account-unresolved sender=${entry.senderId} message=${err instanceof Error ? err.message : String(err)}`
|
|
8918
|
-
);
|
|
8919
|
-
return;
|
|
8920
|
-
}
|
|
8921
|
-
for (let i = 0; i < files.length; i++) {
|
|
8922
|
-
sendUserFileAttempts++;
|
|
8923
|
-
const result = await sendWhatsAppDocument({
|
|
8924
|
-
to: entry.senderId,
|
|
8925
|
-
filePath: files[i],
|
|
8926
|
-
caption: i === 0 ? caption : void 0,
|
|
8927
|
-
accountId: entry.accountId,
|
|
8928
|
-
maxyAccountId,
|
|
8929
|
-
platformRoot: platformRoot()
|
|
8930
|
-
});
|
|
8931
|
-
if (!result.ok) {
|
|
8932
|
-
failedFiles.push(files[i]);
|
|
8933
|
-
console.error(
|
|
8934
|
-
`${TAG22} file-delivery reject reason=send-failed sender=${entry.senderId} status=${result.status} message=${result.error}`
|
|
8935
|
-
);
|
|
8936
|
-
}
|
|
8937
|
-
}
|
|
9214
|
+
await shared.onFileToolUse(use);
|
|
8938
9215
|
},
|
|
8939
9216
|
onTurnEnd(firedTools) {
|
|
8940
9217
|
const startedAt = turnStartedAt ?? 0;
|
|
8941
|
-
const failed = failedFiles;
|
|
8942
|
-
const attempts = sendUserFileAttempts;
|
|
8943
9218
|
const routes = routeCalls;
|
|
8944
9219
|
turnStartedAt = null;
|
|
8945
|
-
failedFiles = [];
|
|
8946
|
-
sendUserFileAttempts = 0;
|
|
8947
9220
|
routeCalls = [];
|
|
8948
9221
|
const sid = entry.sessionId.slice(0, 8);
|
|
8949
|
-
|
|
8950
|
-
console.error(
|
|
8951
|
-
`${TAG22} file-delivery-unreconciled sender=${entry.senderId} sessionId=${sid} tool=${SEND_USER_FILE} file=${file}`
|
|
8952
|
-
);
|
|
8953
|
-
}
|
|
8954
|
-
const okAt = documentOutboundAt(entry.senderId);
|
|
8955
|
-
const documentWentOut = okAt !== void 0 && okAt >= startedAt;
|
|
9222
|
+
shared.onTurnEnd(firedTools);
|
|
8956
9223
|
for (const call of routes) {
|
|
8957
9224
|
const routeAt = call.to !== void 0 && call.filePath !== void 0 ? routeDocumentOutboundAt(call.to, call.filePath) : void 0;
|
|
8958
9225
|
const delivered = routeAt !== void 0 && routeAt >= startedAt;
|
|
@@ -8963,21 +9230,186 @@ function makeWhatsAppFileDelivery(entry) {
|
|
|
8963
9230
|
);
|
|
8964
9231
|
}
|
|
8965
9232
|
}
|
|
8966
|
-
|
|
8967
|
-
|
|
8968
|
-
|
|
8969
|
-
|
|
9233
|
+
}
|
|
9234
|
+
};
|
|
9235
|
+
}
|
|
9236
|
+
|
|
9237
|
+
// app/lib/telegram/outbound/send-document.ts
|
|
9238
|
+
import { realpathSync as realpathSync4 } from "fs";
|
|
9239
|
+
import { readFile as readFile2, stat as stat3 } from "fs/promises";
|
|
9240
|
+
import { resolve as resolve14, basename as basename5 } from "path";
|
|
9241
|
+
var TAG23 = "[telegram:outbound]";
|
|
9242
|
+
var TELEGRAM_DOCUMENT_MAX_BYTES = 50 * 1024 * 1024;
|
|
9243
|
+
async function sendTelegramDocument(input) {
|
|
9244
|
+
const { botToken, chatId, filePath, caption, maxyAccountId, platformRoot: platformRoot4 } = input;
|
|
9245
|
+
if (!botToken || !filePath) {
|
|
9246
|
+
return { ok: false, status: 400, error: "Missing required fields: botToken, filePath" };
|
|
9247
|
+
}
|
|
9248
|
+
if (!maxyAccountId || !platformRoot4) {
|
|
9249
|
+
return { ok: false, status: 400, error: "Cannot validate file path: missing account or platform context" };
|
|
9250
|
+
}
|
|
9251
|
+
const accountDir = resolve14(platformRoot4, "..", "data/accounts", maxyAccountId);
|
|
9252
|
+
let resolvedPath;
|
|
9253
|
+
try {
|
|
9254
|
+
resolvedPath = realpathSync4(filePath);
|
|
9255
|
+
const accountResolved = realpathSync4(accountDir);
|
|
9256
|
+
if (!resolvedPath.startsWith(accountResolved + "/")) {
|
|
9257
|
+
console.error(`${TAG23} document REJECTED reason=outside_account_directory`);
|
|
9258
|
+
return { ok: false, status: 403, error: "Access denied: file is outside the account directory" };
|
|
9259
|
+
}
|
|
9260
|
+
} catch (err) {
|
|
9261
|
+
const code = err.code;
|
|
9262
|
+
if (code === "ENOENT") {
|
|
9263
|
+
console.error(`${TAG23} document ENOENT path=${filePath}`);
|
|
9264
|
+
return { ok: false, status: 404, error: `File not found: ${filePath}` };
|
|
9265
|
+
}
|
|
9266
|
+
console.error(`${TAG23} document path error: ${String(err)}`);
|
|
9267
|
+
return { ok: false, status: 500, error: String(err) };
|
|
9268
|
+
}
|
|
9269
|
+
const fileStat = await stat3(resolvedPath);
|
|
9270
|
+
if (fileStat.size > TELEGRAM_DOCUMENT_MAX_BYTES) {
|
|
9271
|
+
return {
|
|
9272
|
+
ok: false,
|
|
9273
|
+
status: 400,
|
|
9274
|
+
error: `File exceeds 50 MB limit (${(fileStat.size / 1024 / 1024).toFixed(1)} MB)`
|
|
9275
|
+
};
|
|
9276
|
+
}
|
|
9277
|
+
const filename = basename5(resolvedPath);
|
|
9278
|
+
const buffer = Buffer.from(await readFile2(resolvedPath));
|
|
9279
|
+
const form = new FormData();
|
|
9280
|
+
form.append("chat_id", String(chatId));
|
|
9281
|
+
if (caption) form.append("caption", caption);
|
|
9282
|
+
form.append("document", new File([buffer], filename, { type: detectMimeType(resolvedPath) }));
|
|
9283
|
+
let ok = false;
|
|
9284
|
+
let messageId;
|
|
9285
|
+
let error = "send failed";
|
|
9286
|
+
try {
|
|
9287
|
+
const res = await fetch(`https://api.telegram.org/bot${botToken}/sendDocument`, {
|
|
9288
|
+
method: "POST",
|
|
9289
|
+
body: form
|
|
9290
|
+
});
|
|
9291
|
+
const data = await res.json();
|
|
9292
|
+
ok = data.ok;
|
|
9293
|
+
messageId = data.result?.message_id;
|
|
9294
|
+
if (!data.ok) error = data.description ?? "Unknown Telegram error";
|
|
9295
|
+
} catch (e) {
|
|
9296
|
+
error = e instanceof Error ? e.message : String(e);
|
|
9297
|
+
}
|
|
9298
|
+
console.error(
|
|
9299
|
+
`${TAG23} sent document to=${chatId} file=${filename} bytes=${fileStat.size} ok=${ok}` + (messageId !== void 0 ? ` id=${messageId}` : "")
|
|
9300
|
+
);
|
|
9301
|
+
return ok ? { ok: true, messageId } : { ok: false, status: 500, error };
|
|
9302
|
+
}
|
|
9303
|
+
|
|
9304
|
+
// app/lib/telegram/outbound/file-delivery.ts
|
|
9305
|
+
var TAG24 = "[telegram:outbound]";
|
|
9306
|
+
function platformRoot2() {
|
|
9307
|
+
return process.env.MAXY_PLATFORM_ROOT || "";
|
|
9308
|
+
}
|
|
9309
|
+
function makeTelegramSendFile(entry) {
|
|
9310
|
+
let botToken;
|
|
9311
|
+
return async (filePath, caption) => {
|
|
9312
|
+
if (botToken === void 0) {
|
|
9313
|
+
const tg = resolveAccount()?.config.telegram;
|
|
9314
|
+
botToken = (entry.role === "admin" ? tg?.adminBotToken : tg?.publicBotToken) ?? null;
|
|
9315
|
+
}
|
|
9316
|
+
if (!botToken) {
|
|
9317
|
+
console.error(`${TAG24} file-delivery reject reason=no-bot-token sender=${entry.senderId} role=${entry.role}`);
|
|
9318
|
+
return { ok: false, error: "no-bot-token" };
|
|
9319
|
+
}
|
|
9320
|
+
if (entry.replyTarget == null) {
|
|
9321
|
+
console.error(`${TAG24} file-delivery reject reason=no-reply-target sender=${entry.senderId} role=${entry.role}`);
|
|
9322
|
+
return { ok: false, error: "no-reply-target" };
|
|
9323
|
+
}
|
|
9324
|
+
let maxyAccountId;
|
|
9325
|
+
try {
|
|
9326
|
+
maxyAccountId = resolvePlatformAccountId();
|
|
9327
|
+
} catch (err) {
|
|
9328
|
+
console.error(
|
|
9329
|
+
`${TAG24} file-delivery reject reason=account-unresolved sender=${entry.senderId} message=${err instanceof Error ? err.message : String(err)}`
|
|
9330
|
+
);
|
|
9331
|
+
return { ok: false, error: "account-unresolved" };
|
|
9332
|
+
}
|
|
9333
|
+
const result = await sendTelegramDocument({
|
|
9334
|
+
botToken,
|
|
9335
|
+
chatId: Number(entry.replyTarget),
|
|
9336
|
+
filePath,
|
|
9337
|
+
caption,
|
|
9338
|
+
maxyAccountId,
|
|
9339
|
+
platformRoot: platformRoot2()
|
|
9340
|
+
});
|
|
9341
|
+
return result.ok ? { ok: true } : { ok: false, error: result.error };
|
|
9342
|
+
};
|
|
9343
|
+
}
|
|
9344
|
+
function makeTelegramFileDelivery(entry) {
|
|
9345
|
+
return makeFileDelivery({
|
|
9346
|
+
entry,
|
|
9347
|
+
tag: TAG24,
|
|
9348
|
+
channel: "telegram",
|
|
9349
|
+
sendFile: makeTelegramSendFile(entry)
|
|
9350
|
+
});
|
|
9351
|
+
}
|
|
9352
|
+
|
|
9353
|
+
// app/lib/webchat/file-delivery.ts
|
|
9354
|
+
import { realpathSync as realpathSync5 } from "fs";
|
|
9355
|
+
import { resolve as resolve15 } from "path";
|
|
9356
|
+
var TAG25 = "[webchat-adaptor]";
|
|
9357
|
+
function platformRoot3() {
|
|
9358
|
+
return process.env.MAXY_PLATFORM_ROOT || "";
|
|
9359
|
+
}
|
|
9360
|
+
function makeWebchatSendFile(entry) {
|
|
9361
|
+
return async (filePath) => {
|
|
9362
|
+
let maxyAccountId;
|
|
9363
|
+
try {
|
|
9364
|
+
maxyAccountId = resolvePlatformAccountId();
|
|
9365
|
+
} catch (err) {
|
|
9366
|
+
console.error(
|
|
9367
|
+
`${TAG25} file-delivery reject reason=account-unresolved sender=${entry.senderId} message=${err instanceof Error ? err.message : String(err)}`
|
|
9368
|
+
);
|
|
9369
|
+
return { ok: false, error: "account-unresolved" };
|
|
9370
|
+
}
|
|
9371
|
+
const accountDir = resolve15(platformRoot3(), "..", "data/accounts", maxyAccountId);
|
|
9372
|
+
try {
|
|
9373
|
+
const resolved = realpathSync5(filePath);
|
|
9374
|
+
const accountResolved = realpathSync5(accountDir);
|
|
9375
|
+
if (!resolved.startsWith(accountResolved + "/")) {
|
|
9376
|
+
console.error(`${TAG25} file-delivery reject reason=outside_account_directory sender=${entry.senderId}`);
|
|
9377
|
+
return { ok: false, error: "outside-account" };
|
|
8970
9378
|
}
|
|
9379
|
+
return { ok: true };
|
|
9380
|
+
} catch (err) {
|
|
9381
|
+
const code = err.code;
|
|
9382
|
+
console.error(
|
|
9383
|
+
`${TAG25} file-delivery reject reason=${code === "ENOENT" ? "not-found" : "path-error"} sender=${entry.senderId}`
|
|
9384
|
+
);
|
|
9385
|
+
return { ok: false, error: code === "ENOENT" ? "not-found" : "path-error" };
|
|
8971
9386
|
}
|
|
8972
9387
|
};
|
|
8973
9388
|
}
|
|
9389
|
+
function makeWebchatFileDelivery(entry) {
|
|
9390
|
+
return makeFileDelivery({
|
|
9391
|
+
entry,
|
|
9392
|
+
tag: TAG25,
|
|
9393
|
+
channel: "webchat",
|
|
9394
|
+
sendFile: makeWebchatSendFile(entry)
|
|
9395
|
+
});
|
|
9396
|
+
}
|
|
8974
9397
|
|
|
8975
9398
|
// app/lib/channel-pty-bridge/bridge.ts
|
|
8976
9399
|
function tagFor(channel) {
|
|
8977
9400
|
return `[${channel}-adaptor]`;
|
|
8978
9401
|
}
|
|
8979
9402
|
function fileDeliveryFor(entry) {
|
|
8980
|
-
|
|
9403
|
+
switch (entry.channel) {
|
|
9404
|
+
case "whatsapp":
|
|
9405
|
+
return makeWhatsAppFileDelivery(entry);
|
|
9406
|
+
case "telegram":
|
|
9407
|
+
return makeTelegramFileDelivery(entry);
|
|
9408
|
+
case "webchat":
|
|
9409
|
+
return makeWebchatFileDelivery(entry);
|
|
9410
|
+
case "email":
|
|
9411
|
+
return null;
|
|
9412
|
+
}
|
|
8981
9413
|
}
|
|
8982
9414
|
function publicIdleMs() {
|
|
8983
9415
|
return Number(process.env.CHANNEL_PTY_IDLE_MS ?? String(5 * 6e4));
|
|
@@ -8995,6 +9427,7 @@ async function ensureEntry(input) {
|
|
|
8995
9427
|
const sliceTokenValue = input.sliceToken ?? "";
|
|
8996
9428
|
const personIdValue = input.personId ?? null;
|
|
8997
9429
|
const nameValue = input.name ?? null;
|
|
9430
|
+
const replyTargetValue = input.replyTarget ?? null;
|
|
8998
9431
|
const key = indexKey(input.accountId, input.agentSlug, input.senderId, sliceTokenValue, personIdValue);
|
|
8999
9432
|
const existing = index.get(key);
|
|
9000
9433
|
const tag = tagFor(input.channel);
|
|
@@ -9016,6 +9449,7 @@ async function ensureEntry(input) {
|
|
|
9016
9449
|
});
|
|
9017
9450
|
existing.followerRunning = true;
|
|
9018
9451
|
}
|
|
9452
|
+
existing.replyTarget = replyTargetValue;
|
|
9019
9453
|
return existing;
|
|
9020
9454
|
}
|
|
9021
9455
|
let spawned;
|
|
@@ -9036,7 +9470,7 @@ async function ensureEntry(input) {
|
|
|
9036
9470
|
});
|
|
9037
9471
|
} else {
|
|
9038
9472
|
console.error(`${tag} route role=${input.role} target=public-spawn senderId=${input.senderId}`);
|
|
9039
|
-
const attachmentDir =
|
|
9473
|
+
const attachmentDir = resolve16(DATA_ROOT, "accounts", input.accountId, "uploads", "public", input.senderId);
|
|
9040
9474
|
spawned = await managerSpawn({
|
|
9041
9475
|
senderId: input.senderId,
|
|
9042
9476
|
role: input.role,
|
|
@@ -9072,7 +9506,8 @@ async function ensureEntry(input) {
|
|
|
9072
9506
|
followerRunning: false,
|
|
9073
9507
|
sliceToken: sliceTokenValue,
|
|
9074
9508
|
personId: personIdValue,
|
|
9075
|
-
name: nameValue
|
|
9509
|
+
name: nameValue,
|
|
9510
|
+
replyTarget: replyTargetValue
|
|
9076
9511
|
};
|
|
9077
9512
|
entry.followerAbort = startFollower({
|
|
9078
9513
|
entry,
|
|
@@ -9124,7 +9559,8 @@ async function writeInput(entry, text) {
|
|
|
9124
9559
|
agentSlug: entry.agentSlug,
|
|
9125
9560
|
sliceToken: entry.sliceToken.length > 0 ? entry.sliceToken : void 0,
|
|
9126
9561
|
personId: entry.personId,
|
|
9127
|
-
name: entry.name
|
|
9562
|
+
name: entry.name,
|
|
9563
|
+
replyTarget: entry.replyTarget ?? void 0
|
|
9128
9564
|
});
|
|
9129
9565
|
if (!respawned) return false;
|
|
9130
9566
|
for (const cb of entry.subscribers) respawned.subscribers.add(cb);
|
|
@@ -9164,16 +9600,17 @@ async function dispatchOnce(input) {
|
|
|
9164
9600
|
agentSlug: input.agentSlug,
|
|
9165
9601
|
sliceToken: input.sliceToken,
|
|
9166
9602
|
personId: input.personId,
|
|
9167
|
-
name: input.name
|
|
9603
|
+
name: input.name,
|
|
9604
|
+
replyTarget: input.replyTarget
|
|
9168
9605
|
});
|
|
9169
9606
|
if (!entry) return { error: "spawn-failed" };
|
|
9170
9607
|
entry.lastInboundAt = Date.now();
|
|
9171
|
-
let
|
|
9608
|
+
let resolve34;
|
|
9172
9609
|
const turnPromise = new Promise((r) => {
|
|
9173
|
-
|
|
9610
|
+
resolve34 = r;
|
|
9174
9611
|
});
|
|
9175
9612
|
const listener = (text) => {
|
|
9176
|
-
|
|
9613
|
+
resolve34(text);
|
|
9177
9614
|
};
|
|
9178
9615
|
entry.subscribers.add(listener);
|
|
9179
9616
|
const writeAtMs = Date.now();
|
|
@@ -9320,14 +9757,14 @@ function routeTelegramUpdate(input) {
|
|
|
9320
9757
|
}
|
|
9321
9758
|
|
|
9322
9759
|
// server/routes/telegram.ts
|
|
9323
|
-
var
|
|
9760
|
+
var TAG26 = "[telegram-inbound]";
|
|
9324
9761
|
var DISPATCH_TIMEOUT_MS = 12e4;
|
|
9325
9762
|
function configDirName() {
|
|
9326
|
-
const
|
|
9327
|
-
const brandPath =
|
|
9763
|
+
const platformRoot4 = process.env.MAXY_PLATFORM_ROOT ?? resolve17(process.cwd(), "..");
|
|
9764
|
+
const brandPath = join17(platformRoot4, "config", "brand.json");
|
|
9328
9765
|
if (existsSync11(brandPath)) {
|
|
9329
9766
|
try {
|
|
9330
|
-
return JSON.parse(
|
|
9767
|
+
return JSON.parse(readFileSync18(brandPath, "utf-8")).configDir ?? ".maxy";
|
|
9331
9768
|
} catch {
|
|
9332
9769
|
}
|
|
9333
9770
|
}
|
|
@@ -9335,7 +9772,7 @@ function configDirName() {
|
|
|
9335
9772
|
}
|
|
9336
9773
|
function secretPath(botType) {
|
|
9337
9774
|
const filename = botType === "admin" ? ".telegram-admin-webhook-secret" : ".telegram-webhook-secret";
|
|
9338
|
-
return
|
|
9775
|
+
return resolve17(homedir(), configDirName(), filename);
|
|
9339
9776
|
}
|
|
9340
9777
|
async function sendTelegram(botToken, chatId, text) {
|
|
9341
9778
|
try {
|
|
@@ -9355,18 +9792,18 @@ app7.post("/", async (c) => {
|
|
|
9355
9792
|
const botParam = c.req.query("bot");
|
|
9356
9793
|
const botType = botParam === "admin" ? "admin" : botParam === "public" ? "public" : null;
|
|
9357
9794
|
if (!botType) {
|
|
9358
|
-
console.error(`${
|
|
9795
|
+
console.error(`${TAG26} op=reject reason=missing-bot-param`);
|
|
9359
9796
|
return c.json({ ok: false }, 400);
|
|
9360
9797
|
}
|
|
9361
9798
|
const sp = secretPath(botType);
|
|
9362
9799
|
if (!existsSync11(sp)) {
|
|
9363
|
-
console.error(`${
|
|
9800
|
+
console.error(`${TAG26} op=reject reason=no-secret-file botType=${botType}`);
|
|
9364
9801
|
return c.json({ ok: false }, 401);
|
|
9365
9802
|
}
|
|
9366
|
-
const expected =
|
|
9803
|
+
const expected = readFileSync18(sp, "utf-8").trim();
|
|
9367
9804
|
const got = c.req.header("x-telegram-bot-api-secret-token") ?? "";
|
|
9368
9805
|
if (got !== expected) {
|
|
9369
|
-
console.error(`${
|
|
9806
|
+
console.error(`${TAG26} op=reject reason=bad-secret botType=${botType}`);
|
|
9370
9807
|
return c.json({ ok: false }, 401);
|
|
9371
9808
|
}
|
|
9372
9809
|
let update;
|
|
@@ -9377,25 +9814,25 @@ app7.post("/", async (c) => {
|
|
|
9377
9814
|
}
|
|
9378
9815
|
const account = resolveAccount();
|
|
9379
9816
|
if (!account) {
|
|
9380
|
-
console.error(`${
|
|
9817
|
+
console.error(`${TAG26} op=reject reason=no-account`);
|
|
9381
9818
|
return c.json({ ok: true }, 200);
|
|
9382
9819
|
}
|
|
9383
9820
|
const decision = routeTelegramUpdate({ update, botType, config: account.config.telegram ?? {} });
|
|
9384
9821
|
if (decision.kind === "ignore") {
|
|
9385
|
-
console.error(`${
|
|
9822
|
+
console.error(`${TAG26} op=ignore reason=${decision.reason}`);
|
|
9386
9823
|
return c.json({ ok: true }, 200);
|
|
9387
9824
|
}
|
|
9388
9825
|
if (decision.kind === "denied") {
|
|
9389
|
-
console.error(`${
|
|
9826
|
+
console.error(`${TAG26} op=denied reason=${decision.reason} agentType=${decision.agentType}`);
|
|
9390
9827
|
return c.json({ ok: true }, 200);
|
|
9391
9828
|
}
|
|
9392
9829
|
const role = decision.agentType === "admin" ? "admin" : "public";
|
|
9393
9830
|
const agentSlug = role === "admin" ? "admin" : resolveDefaultAgentSlug(account.accountDir);
|
|
9394
9831
|
if (!agentSlug) {
|
|
9395
|
-
console.error(`${
|
|
9832
|
+
console.error(`${TAG26} op=reject reason=no-default-agent`);
|
|
9396
9833
|
return c.json({ ok: true }, 200);
|
|
9397
9834
|
}
|
|
9398
|
-
console.error(`${
|
|
9835
|
+
console.error(`${TAG26} op=update accountId=${account.accountId} from=${decision.senderId}`);
|
|
9399
9836
|
const botToken = botType === "admin" ? account.config.telegram?.adminBotToken : account.config.telegram?.publicBotToken;
|
|
9400
9837
|
const { senderId, chatId, text } = decision;
|
|
9401
9838
|
void (async () => {
|
|
@@ -9406,70 +9843,73 @@ app7.post("/", async (c) => {
|
|
|
9406
9843
|
channel: "telegram",
|
|
9407
9844
|
agentSlug,
|
|
9408
9845
|
text,
|
|
9846
|
+
// Task 924 — the file adapter delivers SendUserFile to this chat id, so a
|
|
9847
|
+
// group turn's document reaches the group rather than the sender's DM.
|
|
9848
|
+
replyTarget: String(chatId),
|
|
9409
9849
|
timeoutMs: DISPATCH_TIMEOUT_MS
|
|
9410
9850
|
});
|
|
9411
9851
|
if ("error" in result) {
|
|
9412
|
-
console.error(`${
|
|
9852
|
+
console.error(`${TAG26} op=dispatch-failed from=${senderId} error=${result.error}`);
|
|
9413
9853
|
return;
|
|
9414
9854
|
}
|
|
9415
9855
|
if (!botToken) {
|
|
9416
9856
|
const reason = account.config.telegram ? "no-bot-token" : "no-telegram-config";
|
|
9417
|
-
console.error(`${
|
|
9857
|
+
console.error(`${TAG26} op=reply-dropped reason=${reason} botType=${botType}`);
|
|
9418
9858
|
return;
|
|
9419
9859
|
}
|
|
9420
9860
|
const sent = await sendTelegram(botToken, chatId, result.turnText);
|
|
9421
|
-
console.error(`${
|
|
9861
|
+
console.error(`${TAG26} op=reply-sent from=${senderId} ok=${sent.ok}${sent.ok ? "" : ` error=${sent.error}`}`);
|
|
9422
9862
|
})();
|
|
9423
9863
|
return c.json({ ok: true }, 200);
|
|
9424
9864
|
});
|
|
9425
9865
|
var telegram_default = app7;
|
|
9426
9866
|
|
|
9427
9867
|
// server/routes/quickbooks.ts
|
|
9428
|
-
import { join as
|
|
9429
|
-
import { existsSync as existsSync12, readFileSync as
|
|
9430
|
-
var
|
|
9868
|
+
import { join as join18 } from "path";
|
|
9869
|
+
import { existsSync as existsSync12, readFileSync as readFileSync19, writeFileSync as writeFileSync8, mkdirSync as mkdirSync3, rmSync, renameSync as renameSync4 } from "fs";
|
|
9870
|
+
var TAG27 = "[quickbooks]";
|
|
9431
9871
|
var INTUIT_TOKEN_URL = "https://oauth.platform.intuit.com/oauth2/v1/tokens/bearer";
|
|
9432
9872
|
var STATE_RE = /^[A-Za-z0-9_-]+$/;
|
|
9433
9873
|
function pendingPath(accountDir, state) {
|
|
9434
|
-
return
|
|
9874
|
+
return join18(accountDir, "secrets", "quickbooks-pending", `${state}.json`);
|
|
9435
9875
|
}
|
|
9436
9876
|
function storePath(accountDir) {
|
|
9437
|
-
return
|
|
9877
|
+
return join18(accountDir, "secrets", "quickbooks.json");
|
|
9438
9878
|
}
|
|
9439
9879
|
async function completeConsent(args) {
|
|
9440
9880
|
const { accountDir, code, realmId } = args;
|
|
9441
9881
|
const state = args.state;
|
|
9442
9882
|
if (!state || !STATE_RE.test(state) || !existsSync12(pendingPath(accountDir, state))) {
|
|
9443
|
-
console.error(`${
|
|
9883
|
+
console.error(`${TAG27} op=callback state=${state ?? ""} stateValid=false realmId=${realmId ?? ""}`);
|
|
9444
9884
|
return { ok: false, status: 400, message: "Invalid or unknown authorization state.", stateValid: false };
|
|
9445
9885
|
}
|
|
9446
9886
|
const claimFile = `${pendingPath(accountDir, state)}.claimed`;
|
|
9447
9887
|
try {
|
|
9448
9888
|
renameSync4(pendingPath(accountDir, state), claimFile);
|
|
9449
9889
|
} catch {
|
|
9450
|
-
console.error(`${
|
|
9890
|
+
console.error(`${TAG27} op=callback state=${state} stateValid=false realmId=${realmId ?? ""}`);
|
|
9451
9891
|
return { ok: false, status: 400, message: "Invalid or unknown authorization state.", stateValid: false };
|
|
9452
9892
|
}
|
|
9453
9893
|
try {
|
|
9454
9894
|
let pending;
|
|
9455
9895
|
try {
|
|
9456
|
-
pending = JSON.parse(
|
|
9896
|
+
pending = JSON.parse(readFileSync19(claimFile, "utf-8"));
|
|
9457
9897
|
} catch {
|
|
9458
|
-
console.error(`${
|
|
9898
|
+
console.error(`${TAG27} op=callback state=${state} stateValid=false realmId=${realmId ?? ""}`);
|
|
9459
9899
|
return { ok: false, status: 400, message: "Authorization state could not be read.", stateValid: false };
|
|
9460
9900
|
}
|
|
9461
9901
|
if (Date.now() > pending.expiresAt) {
|
|
9462
|
-
console.error(`${
|
|
9902
|
+
console.error(`${TAG27} op=callback state=${state} stateValid=false realmId=${realmId ?? ""}`);
|
|
9463
9903
|
return { ok: false, status: 400, message: "Authorization request expired. Generate a new consent link.", stateValid: false };
|
|
9464
9904
|
}
|
|
9465
|
-
console.error(`${
|
|
9905
|
+
console.error(`${TAG27} op=callback state=${state} stateValid=true realmId=${realmId ?? ""}`);
|
|
9466
9906
|
if (!code || !realmId) {
|
|
9467
9907
|
return { ok: false, status: 400, message: "Missing code or realmId in the callback.", stateValid: true };
|
|
9468
9908
|
}
|
|
9469
9909
|
if (!existsSync12(storePath(accountDir))) {
|
|
9470
9910
|
return { ok: false, status: 500, message: "No QuickBooks credentials are stored for this account.", stateValid: true };
|
|
9471
9911
|
}
|
|
9472
|
-
const store2 = JSON.parse(
|
|
9912
|
+
const store2 = JSON.parse(readFileSync19(storePath(accountDir), "utf-8"));
|
|
9473
9913
|
const form = new URLSearchParams({
|
|
9474
9914
|
grant_type: "authorization_code",
|
|
9475
9915
|
code,
|
|
@@ -9486,12 +9926,12 @@ async function completeConsent(args) {
|
|
|
9486
9926
|
});
|
|
9487
9927
|
const text = await res.text();
|
|
9488
9928
|
if (!res.ok) {
|
|
9489
|
-
console.error(`${
|
|
9929
|
+
console.error(`${TAG27} op=token-exchange realmId=${realmId} persisted=false`);
|
|
9490
9930
|
return { ok: false, status: 502, message: `Token exchange failed (${res.status}). Generate a new consent link to retry.`, stateValid: true };
|
|
9491
9931
|
}
|
|
9492
9932
|
const body = JSON.parse(text);
|
|
9493
9933
|
if (!body.refresh_token) {
|
|
9494
|
-
console.error(`${
|
|
9934
|
+
console.error(`${TAG27} op=token-exchange realmId=${realmId} persisted=false`);
|
|
9495
9935
|
return { ok: false, status: 502, message: "Token exchange returned no refresh token.", stateValid: true };
|
|
9496
9936
|
}
|
|
9497
9937
|
const now = Date.now();
|
|
@@ -9501,10 +9941,10 @@ async function completeConsent(args) {
|
|
|
9501
9941
|
refreshTokenExpiry: now + (body.x_refresh_token_expires_in ?? 864e4) * 1e3,
|
|
9502
9942
|
lastRefresh: now
|
|
9503
9943
|
};
|
|
9504
|
-
mkdirSync3(
|
|
9944
|
+
mkdirSync3(join18(accountDir, "secrets"), { recursive: true });
|
|
9505
9945
|
writeFileSync8(storePath(accountDir), JSON.stringify(store2, null, 2), { mode: 384 });
|
|
9506
|
-
const persisted = JSON.parse(
|
|
9507
|
-
console.error(`${
|
|
9946
|
+
const persisted = JSON.parse(readFileSync19(storePath(accountDir), "utf-8")).connections?.[realmId]?.refreshToken === body.refresh_token;
|
|
9947
|
+
console.error(`${TAG27} op=token-exchange realmId=${realmId} persisted=${persisted}`);
|
|
9508
9948
|
return { ok: persisted, status: persisted ? 200 : 500, message: persisted ? `Connected company ${realmId}.` : "Failed to persist the connection.", stateValid: true };
|
|
9509
9949
|
} finally {
|
|
9510
9950
|
rmSync(claimFile, { force: true });
|
|
@@ -9517,7 +9957,7 @@ var app8 = new Hono();
|
|
|
9517
9957
|
app8.get("/callback", async (c) => {
|
|
9518
9958
|
const account = resolveAccount();
|
|
9519
9959
|
if (!account) {
|
|
9520
|
-
console.error(`${
|
|
9960
|
+
console.error(`${TAG27} op=callback stateValid=false realmId= reason=no-account`);
|
|
9521
9961
|
return c.html(page("QuickBooks", "This install has no account configured."), 500);
|
|
9522
9962
|
}
|
|
9523
9963
|
const result = await completeConsent({
|
|
@@ -9534,12 +9974,12 @@ var quickbooks_default = app8;
|
|
|
9534
9974
|
|
|
9535
9975
|
// server/routes/onboarding.ts
|
|
9536
9976
|
import { spawn, spawnSync as spawnSync2, execFileSync as execFileSync2 } from "child_process";
|
|
9537
|
-
import { openSync as openSync3, closeSync as closeSync3, writeFileSync as writeFileSync10, writeSync, existsSync as existsSync14, readFileSync as
|
|
9977
|
+
import { openSync as openSync3, closeSync as closeSync3, writeFileSync as writeFileSync10, writeSync, existsSync as existsSync14, readFileSync as readFileSync21, unlinkSync as unlinkSync2 } from "fs";
|
|
9538
9978
|
import { createHash as createHash3, randomUUID as randomUUID8 } from "crypto";
|
|
9539
9979
|
|
|
9540
9980
|
// ../lib/admins-write/src/index.ts
|
|
9541
|
-
import { existsSync as existsSync13, readFileSync as
|
|
9542
|
-
import { dirname as dirname4, join as
|
|
9981
|
+
import { existsSync as existsSync13, readFileSync as readFileSync20, writeFileSync as writeFileSync9, renameSync as renameSync5, mkdirSync as mkdirSync4, readdirSync as readdirSync12, statSync as statSync7 } from "fs";
|
|
9982
|
+
import { dirname as dirname4, join as join19 } from "path";
|
|
9543
9983
|
function logLine(input, result) {
|
|
9544
9984
|
const userIdShort = input.userId.slice(0, 8);
|
|
9545
9985
|
console.error(
|
|
@@ -9557,7 +9997,7 @@ function writeAdminEntry(input) {
|
|
|
9557
9997
|
try {
|
|
9558
9998
|
let users = [];
|
|
9559
9999
|
if (existsSync13(input.usersFile)) {
|
|
9560
|
-
const raw =
|
|
10000
|
+
const raw = readFileSync20(input.usersFile, "utf-8").trim();
|
|
9561
10001
|
if (raw) {
|
|
9562
10002
|
const parsed = JSON.parse(raw);
|
|
9563
10003
|
if (!Array.isArray(parsed)) {
|
|
@@ -9581,11 +10021,11 @@ function writeAdminEntry(input) {
|
|
|
9581
10021
|
return result;
|
|
9582
10022
|
}
|
|
9583
10023
|
try {
|
|
9584
|
-
const accountJsonPath =
|
|
10024
|
+
const accountJsonPath = join19(input.accountDir, "account.json");
|
|
9585
10025
|
if (!existsSync13(accountJsonPath)) {
|
|
9586
10026
|
throw new Error(`account.json not found at ${accountJsonPath}`);
|
|
9587
10027
|
}
|
|
9588
|
-
const config = JSON.parse(
|
|
10028
|
+
const config = JSON.parse(readFileSync20(accountJsonPath, "utf-8"));
|
|
9589
10029
|
const admins = config.admins ?? [];
|
|
9590
10030
|
if (admins.some((a) => a.userId === input.userId)) {
|
|
9591
10031
|
result.accountJsonResult = "noop";
|
|
@@ -9611,7 +10051,7 @@ function checkAdminAuthInvariant(input) {
|
|
|
9611
10051
|
const usersUserIds = /* @__PURE__ */ new Set();
|
|
9612
10052
|
if (existsSync13(input.usersFile)) {
|
|
9613
10053
|
try {
|
|
9614
|
-
const raw =
|
|
10054
|
+
const raw = readFileSync20(input.usersFile, "utf-8").trim();
|
|
9615
10055
|
if (raw) {
|
|
9616
10056
|
const users = JSON.parse(raw);
|
|
9617
10057
|
for (const u of users) {
|
|
@@ -9627,7 +10067,7 @@ function checkAdminAuthInvariant(input) {
|
|
|
9627
10067
|
if (existsSync13(input.accountsDir)) {
|
|
9628
10068
|
let entries;
|
|
9629
10069
|
try {
|
|
9630
|
-
entries =
|
|
10070
|
+
entries = readdirSync12(input.accountsDir);
|
|
9631
10071
|
} catch (err) {
|
|
9632
10072
|
const msg = err instanceof Error ? err.message : String(err);
|
|
9633
10073
|
console.error(`[${input.tag}] accounts-dir unreadable accountsDir=${input.accountsDir} error=${msg}`);
|
|
@@ -9636,17 +10076,17 @@ function checkAdminAuthInvariant(input) {
|
|
|
9636
10076
|
}
|
|
9637
10077
|
for (const entry of entries) {
|
|
9638
10078
|
if (entry.startsWith(".")) continue;
|
|
9639
|
-
const accountDir =
|
|
10079
|
+
const accountDir = join19(input.accountsDir, entry);
|
|
9640
10080
|
try {
|
|
9641
10081
|
if (!statSync7(accountDir).isDirectory()) continue;
|
|
9642
10082
|
} catch {
|
|
9643
10083
|
continue;
|
|
9644
10084
|
}
|
|
9645
|
-
const accountJsonPath =
|
|
10085
|
+
const accountJsonPath = join19(accountDir, "account.json");
|
|
9646
10086
|
if (!existsSync13(accountJsonPath)) continue;
|
|
9647
10087
|
let admins = [];
|
|
9648
10088
|
try {
|
|
9649
|
-
const config = JSON.parse(
|
|
10089
|
+
const config = JSON.parse(readFileSync20(accountJsonPath, "utf-8"));
|
|
9650
10090
|
admins = config.admins ?? [];
|
|
9651
10091
|
} catch (err) {
|
|
9652
10092
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -9687,7 +10127,7 @@ function hashPin2(pin) {
|
|
|
9687
10127
|
}
|
|
9688
10128
|
function readUsersFile2() {
|
|
9689
10129
|
if (!existsSync14(USERS_FILE)) return null;
|
|
9690
|
-
const raw =
|
|
10130
|
+
const raw = readFileSync21(USERS_FILE, "utf-8").trim();
|
|
9691
10131
|
if (!raw) return [];
|
|
9692
10132
|
return JSON.parse(raw);
|
|
9693
10133
|
}
|
|
@@ -9849,7 +10289,7 @@ app9.post("/set-pin", async (c) => {
|
|
|
9849
10289
|
let installOwner = null;
|
|
9850
10290
|
if (existsSync14(INSTALL_OWNER_FILE)) {
|
|
9851
10291
|
try {
|
|
9852
|
-
const raw =
|
|
10292
|
+
const raw = readFileSync21(INSTALL_OWNER_FILE, "utf-8").trim();
|
|
9853
10293
|
if (raw.length > 0) installOwner = raw;
|
|
9854
10294
|
} catch (err) {
|
|
9855
10295
|
console.error(`[set-pin] install-owner-read-failed path=${INSTALL_OWNER_FILE} error=${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -9931,8 +10371,8 @@ var onboarding_default = app9;
|
|
|
9931
10371
|
|
|
9932
10372
|
// server/routes/client-error.ts
|
|
9933
10373
|
import { appendFileSync, existsSync as existsSync15, renameSync as renameSync6, statSync as statSync8 } from "fs";
|
|
9934
|
-
import { join as
|
|
9935
|
-
var CLIENT_ERRORS_LOG =
|
|
10374
|
+
import { join as join20 } from "path";
|
|
10375
|
+
var CLIENT_ERRORS_LOG = join20(LOG_DIR, "client-errors.log");
|
|
9936
10376
|
var MAX_LOG_SIZE = 10 * 1024 * 1024;
|
|
9937
10377
|
var MAX_BODY_SIZE = 8 * 1024;
|
|
9938
10378
|
var MAX_STACK_LEN = 2e3;
|
|
@@ -10257,17 +10697,17 @@ app11.post("/", async (c) => {
|
|
|
10257
10697
|
var session_default = app11;
|
|
10258
10698
|
|
|
10259
10699
|
// server/routes/admin/logs.ts
|
|
10260
|
-
import { existsSync as existsSync17, readdirSync as
|
|
10261
|
-
import { resolve as
|
|
10700
|
+
import { existsSync as existsSync17, readdirSync as readdirSync13, readFileSync as readFileSync22, statSync as statSync9 } from "fs";
|
|
10701
|
+
import { resolve as resolve18, basename as basename6 } from "path";
|
|
10262
10702
|
|
|
10263
10703
|
// app/lib/logs-read-resolve.ts
|
|
10264
10704
|
import { existsSync as existsSync16 } from "fs";
|
|
10265
|
-
import { join as
|
|
10705
|
+
import { join as join21 } from "path";
|
|
10266
10706
|
function resolveSessionLogPaths(filename, logDirs) {
|
|
10267
10707
|
const tried = [filename];
|
|
10268
10708
|
const hits = [];
|
|
10269
10709
|
for (const dir of logDirs) {
|
|
10270
|
-
const fullPath =
|
|
10710
|
+
const fullPath = join21(dir, filename);
|
|
10271
10711
|
if (existsSync16(fullPath)) {
|
|
10272
10712
|
hits.push({ path: fullPath, dir });
|
|
10273
10713
|
}
|
|
@@ -10285,18 +10725,18 @@ app12.get("/", async (c) => {
|
|
|
10285
10725
|
const cacheKeyParam = c.req.query("cacheKey");
|
|
10286
10726
|
const download = c.req.query("download") === "1";
|
|
10287
10727
|
const account = resolveAccount();
|
|
10288
|
-
const accountLogDir = account ?
|
|
10728
|
+
const accountLogDir = account ? resolve18(account.accountDir, "logs") : null;
|
|
10289
10729
|
const logDirs = [];
|
|
10290
10730
|
if (accountLogDir) logDirs.push(accountLogDir);
|
|
10291
10731
|
logDirs.push(LOG_DIR);
|
|
10292
10732
|
if (fileParam) {
|
|
10293
|
-
const safe =
|
|
10733
|
+
const safe = basename6(fileParam);
|
|
10294
10734
|
const searched = [];
|
|
10295
10735
|
for (const dir of logDirs) {
|
|
10296
|
-
const filePath =
|
|
10736
|
+
const filePath = resolve18(dir, safe);
|
|
10297
10737
|
searched.push(filePath);
|
|
10298
10738
|
try {
|
|
10299
|
-
const buffer =
|
|
10739
|
+
const buffer = readFileSync22(filePath);
|
|
10300
10740
|
const onDiskBytes = statSync9(filePath).size;
|
|
10301
10741
|
const headers = {
|
|
10302
10742
|
"Content-Type": "text/plain; charset=utf-8",
|
|
@@ -10360,8 +10800,8 @@ app12.get("/", async (c) => {
|
|
|
10360
10800
|
if (hit) {
|
|
10361
10801
|
console.info(`[admin/logs] resolved cacheKey=${cacheKeySlice} sessionId=${sessionIdSlice} via=${primaryId === cacheKey ? "cacheKey" : primaryId === sessionKeyFromConv ? "reverse-lookup" : "sessionId-fallback"}`);
|
|
10362
10802
|
try {
|
|
10363
|
-
const filename =
|
|
10364
|
-
const buffer =
|
|
10803
|
+
const filename = basename6(hit.path);
|
|
10804
|
+
const buffer = readFileSync22(hit.path);
|
|
10365
10805
|
const onDiskBytes = statSync9(hit.path).size;
|
|
10366
10806
|
const headers = {
|
|
10367
10807
|
"Content-Type": "text/plain; charset=utf-8",
|
|
@@ -10399,16 +10839,16 @@ app12.get("/", async (c) => {
|
|
|
10399
10839
|
if (!existsSync17(dir)) continue;
|
|
10400
10840
|
let files;
|
|
10401
10841
|
try {
|
|
10402
|
-
files =
|
|
10842
|
+
files = readdirSync13(dir).filter((f) => f.endsWith(".log"));
|
|
10403
10843
|
} catch (err) {
|
|
10404
10844
|
const reason = err instanceof Error ? err.message : String(err);
|
|
10405
10845
|
console.warn(`[admin/logs] readdir-fail dir=${dir} reason=${reason}`);
|
|
10406
10846
|
continue;
|
|
10407
10847
|
}
|
|
10408
|
-
files.filter((f) => !seen.has(f)).map((f) => ({ name: f, mtime: statSync9(
|
|
10848
|
+
files.filter((f) => !seen.has(f)).map((f) => ({ name: f, mtime: statSync9(resolve18(dir, f)).mtimeMs })).sort((a, b) => b.mtime - a.mtime).forEach(({ name }) => {
|
|
10409
10849
|
seen.add(name);
|
|
10410
10850
|
try {
|
|
10411
|
-
const content =
|
|
10851
|
+
const content = readFileSync22(resolve18(dir, name));
|
|
10412
10852
|
const tail = content.length > TAIL_BYTES ? content.subarray(content.length - TAIL_BYTES).toString("utf-8") : content.toString("utf-8");
|
|
10413
10853
|
logs[name] = tail.trim() || "(empty)";
|
|
10414
10854
|
} catch (err) {
|
|
@@ -10446,9 +10886,9 @@ app13.get("/", (c) => {
|
|
|
10446
10886
|
var claude_info_default = app13;
|
|
10447
10887
|
|
|
10448
10888
|
// server/routes/admin/attachment.ts
|
|
10449
|
-
import { readFile as
|
|
10889
|
+
import { readFile as readFile3, readdir } from "fs/promises";
|
|
10450
10890
|
import { existsSync as existsSync18 } from "fs";
|
|
10451
|
-
import { resolve as
|
|
10891
|
+
import { resolve as resolve19 } from "path";
|
|
10452
10892
|
var app14 = new Hono();
|
|
10453
10893
|
app14.get("/:attachmentId", requireAdminSession, async (c) => {
|
|
10454
10894
|
const attachmentId = c.req.param("attachmentId");
|
|
@@ -10460,17 +10900,17 @@ app14.get("/:attachmentId", requireAdminSession, async (c) => {
|
|
|
10460
10900
|
if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/.test(attachmentId)) {
|
|
10461
10901
|
return new Response("Not found", { status: 404 });
|
|
10462
10902
|
}
|
|
10463
|
-
const dir =
|
|
10903
|
+
const dir = resolve19(DATA_ROOT, "accounts", accountId, "uploads", attachmentId);
|
|
10464
10904
|
if (!existsSync18(dir)) {
|
|
10465
10905
|
return new Response("Not found", { status: 404 });
|
|
10466
10906
|
}
|
|
10467
|
-
const metaPath =
|
|
10907
|
+
const metaPath = resolve19(dir, `${attachmentId}.meta.json`);
|
|
10468
10908
|
if (!existsSync18(metaPath)) {
|
|
10469
10909
|
return new Response("Not found", { status: 404 });
|
|
10470
10910
|
}
|
|
10471
10911
|
let meta;
|
|
10472
10912
|
try {
|
|
10473
|
-
meta = JSON.parse(await
|
|
10913
|
+
meta = JSON.parse(await readFile3(metaPath, "utf-8"));
|
|
10474
10914
|
} catch {
|
|
10475
10915
|
return new Response("Not found", { status: 404 });
|
|
10476
10916
|
}
|
|
@@ -10479,8 +10919,8 @@ app14.get("/:attachmentId", requireAdminSession, async (c) => {
|
|
|
10479
10919
|
if (!dataFile) {
|
|
10480
10920
|
return new Response("Not found", { status: 404 });
|
|
10481
10921
|
}
|
|
10482
|
-
const filePath =
|
|
10483
|
-
const buffer = await
|
|
10922
|
+
const filePath = resolve19(dir, dataFile);
|
|
10923
|
+
const buffer = await readFile3(filePath);
|
|
10484
10924
|
return new Response(new Uint8Array(buffer), {
|
|
10485
10925
|
headers: {
|
|
10486
10926
|
"Content-Type": meta.mimeType,
|
|
@@ -10492,24 +10932,24 @@ app14.get("/:attachmentId", requireAdminSession, async (c) => {
|
|
|
10492
10932
|
var attachment_default = app14;
|
|
10493
10933
|
|
|
10494
10934
|
// server/routes/admin/agents.ts
|
|
10495
|
-
import { resolve as
|
|
10496
|
-
import { readdirSync as
|
|
10935
|
+
import { resolve as resolve20 } from "path";
|
|
10936
|
+
import { readdirSync as readdirSync14, readFileSync as readFileSync23, existsSync as existsSync19, rmSync as rmSync2 } from "fs";
|
|
10497
10937
|
var app15 = new Hono();
|
|
10498
10938
|
app15.get("/", (c) => {
|
|
10499
10939
|
const account = resolveAccount();
|
|
10500
10940
|
if (!account) return c.json({ agents: [] });
|
|
10501
|
-
const agentsDir =
|
|
10941
|
+
const agentsDir = resolve20(account.accountDir, "agents");
|
|
10502
10942
|
if (!existsSync19(agentsDir)) return c.json({ agents: [] });
|
|
10503
10943
|
const agents = [];
|
|
10504
10944
|
try {
|
|
10505
|
-
const entries =
|
|
10945
|
+
const entries = readdirSync14(agentsDir, { withFileTypes: true });
|
|
10506
10946
|
for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
10507
10947
|
if (!entry.isDirectory()) continue;
|
|
10508
10948
|
if (entry.name === "admin") continue;
|
|
10509
|
-
const configPath2 =
|
|
10949
|
+
const configPath2 = resolve20(agentsDir, entry.name, "config.json");
|
|
10510
10950
|
if (!existsSync19(configPath2)) continue;
|
|
10511
10951
|
try {
|
|
10512
|
-
const config = JSON.parse(
|
|
10952
|
+
const config = JSON.parse(readFileSync23(configPath2, "utf-8"));
|
|
10513
10953
|
agents.push({
|
|
10514
10954
|
slug: entry.name,
|
|
10515
10955
|
displayName: config.displayName ?? entry.name,
|
|
@@ -10535,7 +10975,7 @@ app15.delete("/:slug", async (c) => {
|
|
|
10535
10975
|
if (slug.includes("/") || slug.includes("..") || slug.includes("\\")) {
|
|
10536
10976
|
return c.json({ error: "Invalid agent slug" }, 400);
|
|
10537
10977
|
}
|
|
10538
|
-
const agentDir =
|
|
10978
|
+
const agentDir = resolve20(account.accountDir, "agents", slug);
|
|
10539
10979
|
if (!existsSync19(agentDir)) {
|
|
10540
10980
|
return c.json({ error: "Agent not found" }, 404);
|
|
10541
10981
|
}
|
|
@@ -10565,7 +11005,7 @@ app15.post("/:slug/project", async (c) => {
|
|
|
10565
11005
|
if (slug.includes("/") || slug.includes("..") || slug.includes("\\")) {
|
|
10566
11006
|
return c.json({ error: "Invalid agent slug" }, 400);
|
|
10567
11007
|
}
|
|
10568
|
-
const agentDir =
|
|
11008
|
+
const agentDir = resolve20(account.accountDir, "agents", slug);
|
|
10569
11009
|
if (!existsSync19(agentDir)) {
|
|
10570
11010
|
return c.json({ error: "Agent not found on disk" }, 404);
|
|
10571
11011
|
}
|
|
@@ -11268,8 +11708,8 @@ app16.put("/:id/label", requireAdminSession, async (c) => {
|
|
|
11268
11708
|
var sessions_default = app16;
|
|
11269
11709
|
|
|
11270
11710
|
// app/lib/claude-agent/spawn-context.ts
|
|
11271
|
-
import { existsSync as existsSync21, readFileSync as
|
|
11272
|
-
import { dirname as dirname5, resolve as
|
|
11711
|
+
import { existsSync as existsSync21, readFileSync as readFileSync24, readdirSync as readdirSync15, statSync as statSync10 } from "fs";
|
|
11712
|
+
import { dirname as dirname5, resolve as resolve21, join as join22 } from "path";
|
|
11273
11713
|
async function resolveOwnerProfileBlock(accountId, userId) {
|
|
11274
11714
|
if (!userId) return { ok: false, reason: "missing-user-id" };
|
|
11275
11715
|
try {
|
|
@@ -11310,14 +11750,14 @@ function frontmatterField(manifest, field2) {
|
|
|
11310
11750
|
function extractPluginToolDescriptions(pluginDir) {
|
|
11311
11751
|
const out = /* @__PURE__ */ new Map();
|
|
11312
11752
|
const candidates = [
|
|
11313
|
-
|
|
11314
|
-
|
|
11753
|
+
join22(pluginDir, "mcp/dist/index.js"),
|
|
11754
|
+
join22(pluginDir, "mcp/src/index.ts")
|
|
11315
11755
|
];
|
|
11316
11756
|
let raw = null;
|
|
11317
11757
|
for (const path2 of candidates) {
|
|
11318
11758
|
if (!existsSync21(path2)) continue;
|
|
11319
11759
|
try {
|
|
11320
|
-
raw =
|
|
11760
|
+
raw = readFileSync24(path2, "utf-8");
|
|
11321
11761
|
break;
|
|
11322
11762
|
} catch {
|
|
11323
11763
|
}
|
|
@@ -11366,24 +11806,24 @@ function parseSkillPathsFromFrontmatter(fm) {
|
|
|
11366
11806
|
}
|
|
11367
11807
|
function listPluginDirs() {
|
|
11368
11808
|
const out = /* @__PURE__ */ new Map();
|
|
11369
|
-
const platformPlugins =
|
|
11809
|
+
const platformPlugins = resolve21(PLATFORM_ROOT, "plugins");
|
|
11370
11810
|
if (existsSync21(platformPlugins)) {
|
|
11371
|
-
for (const name of
|
|
11372
|
-
const dir =
|
|
11811
|
+
for (const name of readdirSync15(platformPlugins)) {
|
|
11812
|
+
const dir = join22(platformPlugins, name);
|
|
11373
11813
|
try {
|
|
11374
11814
|
if (statSync10(dir).isDirectory()) out.set(name, dir);
|
|
11375
11815
|
} catch {
|
|
11376
11816
|
}
|
|
11377
11817
|
}
|
|
11378
11818
|
}
|
|
11379
|
-
const premiumRoot =
|
|
11819
|
+
const premiumRoot = resolve21(dirname5(PLATFORM_ROOT), "premium-plugins");
|
|
11380
11820
|
if (existsSync21(premiumRoot)) {
|
|
11381
|
-
for (const bundle of
|
|
11382
|
-
const bundlePlugins =
|
|
11821
|
+
for (const bundle of readdirSync15(premiumRoot)) {
|
|
11822
|
+
const bundlePlugins = join22(premiumRoot, bundle, "plugins");
|
|
11383
11823
|
if (!existsSync21(bundlePlugins)) continue;
|
|
11384
11824
|
try {
|
|
11385
|
-
for (const name of
|
|
11386
|
-
const dir =
|
|
11825
|
+
for (const name of readdirSync15(bundlePlugins)) {
|
|
11826
|
+
const dir = join22(bundlePlugins, name);
|
|
11387
11827
|
try {
|
|
11388
11828
|
if (statSync10(dir).isDirectory()) out.set(name, dir);
|
|
11389
11829
|
} catch {
|
|
@@ -11396,10 +11836,10 @@ function listPluginDirs() {
|
|
|
11396
11836
|
return out;
|
|
11397
11837
|
}
|
|
11398
11838
|
function readEnabledPlugins(accountId) {
|
|
11399
|
-
const accountFile =
|
|
11839
|
+
const accountFile = resolve21(PLATFORM_ROOT, "..", "data/accounts", accountId, "account.json");
|
|
11400
11840
|
if (!existsSync21(accountFile)) return /* @__PURE__ */ new Set();
|
|
11401
11841
|
try {
|
|
11402
|
-
const parsed = JSON.parse(
|
|
11842
|
+
const parsed = JSON.parse(readFileSync24(accountFile, "utf-8"));
|
|
11403
11843
|
return new Set(
|
|
11404
11844
|
Array.isArray(parsed.enabledPlugins) ? parsed.enabledPlugins.filter((p) => typeof p === "string") : []
|
|
11405
11845
|
);
|
|
@@ -11411,7 +11851,7 @@ function readFrontmatter(path2) {
|
|
|
11411
11851
|
if (!existsSync21(path2)) return null;
|
|
11412
11852
|
let raw;
|
|
11413
11853
|
try {
|
|
11414
|
-
raw =
|
|
11854
|
+
raw = readFileSync24(path2, "utf-8");
|
|
11415
11855
|
} catch {
|
|
11416
11856
|
return null;
|
|
11417
11857
|
}
|
|
@@ -11426,7 +11866,7 @@ function computeActivePlugins(accountId) {
|
|
|
11426
11866
|
for (const name of Array.from(enabled).sort()) {
|
|
11427
11867
|
const dir = pluginDirs.get(name);
|
|
11428
11868
|
if (!dir) continue;
|
|
11429
|
-
const fm = readFrontmatter(
|
|
11869
|
+
const fm = readFrontmatter(join22(dir, "PLUGIN.md"));
|
|
11430
11870
|
if (!fm) continue;
|
|
11431
11871
|
const description = frontmatterField(`---
|
|
11432
11872
|
${fm}
|
|
@@ -11443,7 +11883,7 @@ ${fm}
|
|
|
11443
11883
|
`plugin-manifest-tool-coverage plugin=${name} tools=${tools.length} with-desc=${withDesc}`
|
|
11444
11884
|
);
|
|
11445
11885
|
if (toolNames.length > 0 && descMap.size === 0) {
|
|
11446
|
-
const hasSource = existsSync21(
|
|
11886
|
+
const hasSource = existsSync21(join22(dir, "mcp/dist/index.js")) || existsSync21(join22(dir, "mcp/src/index.ts"));
|
|
11447
11887
|
if (hasSource) {
|
|
11448
11888
|
console.warn(
|
|
11449
11889
|
`[spawn-context] WARN plugin=${name} mcp source present but tool-description regex matched zero entries \u2014 SDK upgrade may have changed the registration shape`
|
|
@@ -11453,7 +11893,7 @@ ${fm}
|
|
|
11453
11893
|
const skillPaths = parseSkillPathsFromFrontmatter(fm);
|
|
11454
11894
|
const skills = [];
|
|
11455
11895
|
for (const relPath of skillPaths) {
|
|
11456
|
-
const skillPath =
|
|
11896
|
+
const skillPath = join22(dir, relPath);
|
|
11457
11897
|
const skillFm = readFrontmatter(skillPath);
|
|
11458
11898
|
if (!skillFm) continue;
|
|
11459
11899
|
const skillName = frontmatterField(`---
|
|
@@ -11469,11 +11909,11 @@ ${skillFm}
|
|
|
11469
11909
|
return out;
|
|
11470
11910
|
}
|
|
11471
11911
|
function computeSpecialistDomains(accountId) {
|
|
11472
|
-
const specialistsDir =
|
|
11912
|
+
const specialistsDir = resolve21(PLATFORM_ROOT, "..", "data/accounts", accountId, "specialists", "agents");
|
|
11473
11913
|
if (!existsSync21(specialistsDir)) return [];
|
|
11474
11914
|
let entries;
|
|
11475
11915
|
try {
|
|
11476
|
-
entries =
|
|
11916
|
+
entries = readdirSync15(specialistsDir);
|
|
11477
11917
|
} catch {
|
|
11478
11918
|
return [];
|
|
11479
11919
|
}
|
|
@@ -11497,7 +11937,7 @@ function computeSpecialistDomains(accountId) {
|
|
|
11497
11937
|
const out = [];
|
|
11498
11938
|
for (const file of entries.sort()) {
|
|
11499
11939
|
if (!file.endsWith(".md")) continue;
|
|
11500
|
-
const fm = readFrontmatter(
|
|
11940
|
+
const fm = readFrontmatter(join22(specialistsDir, file));
|
|
11501
11941
|
if (!fm) continue;
|
|
11502
11942
|
const wrapped = `---
|
|
11503
11943
|
${fm}
|
|
@@ -11515,11 +11955,11 @@ ${fm}
|
|
|
11515
11955
|
return out;
|
|
11516
11956
|
}
|
|
11517
11957
|
function computeDormantPlugins(accountId) {
|
|
11518
|
-
const accountFile =
|
|
11958
|
+
const accountFile = resolve21(PLATFORM_ROOT, "..", "data/accounts", accountId, "account.json");
|
|
11519
11959
|
if (!existsSync21(accountFile)) return [];
|
|
11520
11960
|
let enabled;
|
|
11521
11961
|
try {
|
|
11522
|
-
const raw =
|
|
11962
|
+
const raw = readFileSync24(accountFile, "utf-8");
|
|
11523
11963
|
const parsed = JSON.parse(raw);
|
|
11524
11964
|
enabled = new Set(
|
|
11525
11965
|
Array.isArray(parsed.enabledPlugins) ? parsed.enabledPlugins.filter((p) => typeof p === "string") : []
|
|
@@ -11530,22 +11970,22 @@ function computeDormantPlugins(accountId) {
|
|
|
11530
11970
|
);
|
|
11531
11971
|
return [];
|
|
11532
11972
|
}
|
|
11533
|
-
const pluginsDir =
|
|
11973
|
+
const pluginsDir = resolve21(PLATFORM_ROOT, "plugins");
|
|
11534
11974
|
if (!existsSync21(pluginsDir)) return [];
|
|
11535
11975
|
let entries;
|
|
11536
11976
|
try {
|
|
11537
|
-
entries =
|
|
11977
|
+
entries = readdirSync15(pluginsDir);
|
|
11538
11978
|
} catch {
|
|
11539
11979
|
return [];
|
|
11540
11980
|
}
|
|
11541
11981
|
const out = [];
|
|
11542
11982
|
for (const name of entries) {
|
|
11543
11983
|
if (enabled.has(name)) continue;
|
|
11544
|
-
const manifestPath =
|
|
11984
|
+
const manifestPath = resolve21(pluginsDir, name, "PLUGIN.md");
|
|
11545
11985
|
if (!existsSync21(manifestPath)) continue;
|
|
11546
11986
|
let manifest;
|
|
11547
11987
|
try {
|
|
11548
|
-
manifest =
|
|
11988
|
+
manifest = readFileSync24(manifestPath, "utf-8");
|
|
11549
11989
|
} catch {
|
|
11550
11990
|
continue;
|
|
11551
11991
|
}
|
|
@@ -11559,13 +11999,13 @@ function computeDormantPlugins(accountId) {
|
|
|
11559
11999
|
}
|
|
11560
12000
|
|
|
11561
12001
|
// server/routes/admin/claude-sessions.ts
|
|
11562
|
-
import { existsSync as existsSync22, readFileSync as
|
|
11563
|
-
import { resolve as
|
|
12002
|
+
import { existsSync as existsSync22, readFileSync as readFileSync25 } from "fs";
|
|
12003
|
+
import { resolve as resolve22 } from "path";
|
|
11564
12004
|
function readTunnelState(brandConfigDir) {
|
|
11565
12005
|
const statePath = `${process.env.HOME ?? ""}/${brandConfigDir}/cloudflared/tunnel.state`;
|
|
11566
12006
|
if (!existsSync22(statePath)) return null;
|
|
11567
12007
|
try {
|
|
11568
|
-
const parsed = JSON.parse(
|
|
12008
|
+
const parsed = JSON.parse(readFileSync25(statePath, "utf-8"));
|
|
11569
12009
|
const tunnelId = typeof parsed.tunnelId === "string" ? parsed.tunnelId : null;
|
|
11570
12010
|
const tunnelName = typeof parsed.tunnelName === "string" ? parsed.tunnelName : null;
|
|
11571
12011
|
const domain = typeof parsed.domain === "string" ? parsed.domain : null;
|
|
@@ -11576,10 +12016,10 @@ function readTunnelState(brandConfigDir) {
|
|
|
11576
12016
|
}
|
|
11577
12017
|
}
|
|
11578
12018
|
function resolveTunnelUrl() {
|
|
11579
|
-
const
|
|
12019
|
+
const platformRoot4 = process.env.MAXY_PLATFORM_ROOT ?? process.env.PLATFORM_ROOT ?? resolve22(process.cwd(), "..");
|
|
11580
12020
|
let brand;
|
|
11581
12021
|
try {
|
|
11582
|
-
brand = JSON.parse(
|
|
12022
|
+
brand = JSON.parse(readFileSync25(resolve22(platformRoot4, "config", "brand.json"), "utf-8"));
|
|
11583
12023
|
} catch {
|
|
11584
12024
|
return null;
|
|
11585
12025
|
}
|
|
@@ -11590,7 +12030,7 @@ function resolveTunnelUrl() {
|
|
|
11590
12030
|
if (!state) return null;
|
|
11591
12031
|
return `https://${hostname2}.${state.domain}`;
|
|
11592
12032
|
}
|
|
11593
|
-
var
|
|
12033
|
+
var TAG28 = "[claude-session-manager:wrapper]";
|
|
11594
12034
|
async function refuseIfClaudeAuthDead(c, route, sessionId) {
|
|
11595
12035
|
const auth = await ensureAuth();
|
|
11596
12036
|
if (auth.status !== "dead" && auth.status !== "missing") return null;
|
|
@@ -11608,12 +12048,12 @@ async function performSpawnWithInitialMessage(args) {
|
|
|
11608
12048
|
const aboutOwner = await resolveOwnerProfileBlock(args.senderId, args.userId);
|
|
11609
12049
|
const ownerMs = Date.now() - ownerStart;
|
|
11610
12050
|
const aboutOwnerStatus = aboutOwner == null ? "absent" : "ok" in aboutOwner && aboutOwner.ok === false ? `unresolved:${aboutOwner.reason}` : "ok";
|
|
11611
|
-
console.log(`${
|
|
12051
|
+
console.log(`${TAG28} about-owner-resolved status=${aboutOwnerStatus} ms=${ownerMs}`);
|
|
11612
12052
|
const dormantPlugins = computeDormantPlugins(args.senderId);
|
|
11613
12053
|
const activePlugins = computeActivePlugins(args.senderId);
|
|
11614
12054
|
const specialistDomains = computeSpecialistDomains(args.senderId);
|
|
11615
12055
|
const tunnelUrl = resolveTunnelUrl();
|
|
11616
|
-
console.log(`${
|
|
12056
|
+
console.log(`${TAG28} tunnel-url-resolved value=${tunnelUrl ?? "null"}`);
|
|
11617
12057
|
const upstreamPayload = JSON.stringify({
|
|
11618
12058
|
senderId: args.senderId,
|
|
11619
12059
|
// Task 205 — pass userId through to the manager so it lands as
|
|
@@ -11640,24 +12080,24 @@ async function performSpawnWithInitialMessage(args) {
|
|
|
11640
12080
|
// unshapely values.
|
|
11641
12081
|
conversationNodeId: args.conversationNodeId
|
|
11642
12082
|
});
|
|
11643
|
-
console.log(`${
|
|
12083
|
+
console.log(`${TAG28} forward-spawn-start managerBase=${managerBase("claude-session-manager:wrapper")} bytes=${upstreamPayload.length} hidden=${args.hidden} specialist=${args.specialist ?? "none"}`);
|
|
11644
12084
|
const forwardStart = Date.now();
|
|
11645
12085
|
const upstream = await fetch(`${managerBase("claude-session-manager:wrapper")}/public-spawn`, {
|
|
11646
12086
|
method: "POST",
|
|
11647
12087
|
headers: { "content-type": "application/json" },
|
|
11648
12088
|
body: upstreamPayload
|
|
11649
12089
|
}).catch((err) => {
|
|
11650
|
-
console.error(`${
|
|
12090
|
+
console.error(`${TAG28} fetch-failed op=spawn message=${err instanceof Error ? err.message : String(err)} ms=${Date.now() - forwardStart}`);
|
|
11651
12091
|
return null;
|
|
11652
12092
|
});
|
|
11653
12093
|
if (!upstream) return {
|
|
11654
12094
|
response: new Response(JSON.stringify({ error: "manager-unreachable" }), { status: 503, headers: { "content-type": "application/json" } }),
|
|
11655
12095
|
claudeSessionId: null
|
|
11656
12096
|
};
|
|
11657
|
-
console.log(`${
|
|
12097
|
+
console.log(`${TAG28} forward-spawn-done status=${upstream.status} ms=${Date.now() - forwardStart}`);
|
|
11658
12098
|
if (args.initialMessage) {
|
|
11659
12099
|
const inputBytes = Buffer.byteLength(args.initialMessage, "utf8");
|
|
11660
|
-
console.log(`${
|
|
12100
|
+
console.log(`${TAG28} initial-message-inlined bytes=${inputBytes}`);
|
|
11661
12101
|
}
|
|
11662
12102
|
const bodyText = await upstream.text().catch(() => "");
|
|
11663
12103
|
let claudeSessionId = null;
|
|
@@ -11692,7 +12132,7 @@ app17.post("/", async (c) => {
|
|
|
11692
12132
|
if (refusal) return refusal;
|
|
11693
12133
|
const senderId = getAccountIdForSession(cacheKey) ?? "";
|
|
11694
12134
|
if (!senderId) {
|
|
11695
|
-
console.error(`${
|
|
12135
|
+
console.error(`${TAG28} reject reason=no-account-id cacheKey-prefix=${cacheKey.slice(0, 8)}`);
|
|
11696
12136
|
return c.json({ error: "admin-account-not-resolved" }, 500);
|
|
11697
12137
|
}
|
|
11698
12138
|
const userId = getUserIdForSession(cacheKey) ?? void 0;
|
|
@@ -11701,7 +12141,7 @@ app17.post("/", async (c) => {
|
|
|
11701
12141
|
const permissionMode = typeof body.permissionMode === "string" ? body.permissionMode : void 0;
|
|
11702
12142
|
const specialist = typeof body.specialist === "string" && /^[A-Za-z0-9_-]{1,64}$/.test(body.specialist) ? body.specialist : void 0;
|
|
11703
12143
|
const model = typeof body.model === "string" && /^[A-Za-z0-9._-]{1,64}$/.test(body.model) ? body.model : void 0;
|
|
11704
|
-
console.log(`${
|
|
12144
|
+
console.log(`${TAG28} spawn-request-in surface=cookie accountId=${senderId.slice(0, 8)} userId=${userId ? userId.slice(0, 8) : "absent"} channel=${channel} permissionMode=${permissionMode ?? "default"} specialist=${specialist ?? "none"} model=${model ?? "default"} initialMessage=${initialMessage ? "yes" : "no"}`);
|
|
11705
12145
|
const conversationNodeId = cacheKey ? getSessionIdForSession(cacheKey) : void 0;
|
|
11706
12146
|
const { response, claudeSessionId } = await performSpawnWithInitialMessage({
|
|
11707
12147
|
senderId,
|
|
@@ -11720,13 +12160,13 @@ app17.post("/", async (c) => {
|
|
|
11720
12160
|
claudeSessionId,
|
|
11721
12161
|
senderId
|
|
11722
12162
|
);
|
|
11723
|
-
console.log(`${
|
|
12163
|
+
console.log(`${TAG28} route-done surface=cookie status=${response.status} route-ms=${Date.now() - routeStart}`);
|
|
11724
12164
|
return response;
|
|
11725
12165
|
});
|
|
11726
12166
|
var claude_sessions_default = app17;
|
|
11727
12167
|
|
|
11728
12168
|
// server/routes/admin/log-ingest.ts
|
|
11729
|
-
var
|
|
12169
|
+
var TAG29 = "[log-ingest]";
|
|
11730
12170
|
var TAG_PATTERN = /^[A-Za-z0-9_:-]{1,32}$/;
|
|
11731
12171
|
var LEVELS = /* @__PURE__ */ new Set(["debug", "info", "warn", "error"]);
|
|
11732
12172
|
var MAX_LINE_BYTES = 4096;
|
|
@@ -11737,7 +12177,7 @@ function isLoopbackAddr(addr) {
|
|
|
11737
12177
|
app18.post("/", async (c) => {
|
|
11738
12178
|
const remoteAddr = c.env?.incoming?.socket?.remoteAddress ?? "";
|
|
11739
12179
|
if (!isLoopbackAddr(remoteAddr)) {
|
|
11740
|
-
console.error(`${
|
|
12180
|
+
console.error(`${TAG29} reject reason=non-loopback remoteAddr=${remoteAddr}`);
|
|
11741
12181
|
return c.json({ error: "log-ingest-loopback-only" }, 403);
|
|
11742
12182
|
}
|
|
11743
12183
|
const xffHeader = c.env?.incoming?.headers?.["x-forwarded-for"];
|
|
@@ -11746,7 +12186,7 @@ app18.post("/", async (c) => {
|
|
|
11746
12186
|
const tokens = xffRaw.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
|
|
11747
12187
|
const offender = tokens.find((t) => !isLoopbackAddr(t));
|
|
11748
12188
|
if (offender !== void 0) {
|
|
11749
|
-
console.error(`${
|
|
12189
|
+
console.error(`${TAG29} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
|
|
11750
12190
|
return c.json({ error: "log-ingest-loopback-only" }, 403);
|
|
11751
12191
|
}
|
|
11752
12192
|
}
|
|
@@ -11788,18 +12228,18 @@ var ALLOWED_EVENTS = /* @__PURE__ */ new Set([
|
|
|
11788
12228
|
]);
|
|
11789
12229
|
var app19 = new Hono();
|
|
11790
12230
|
app19.post("/", async (c) => {
|
|
11791
|
-
const
|
|
12231
|
+
const TAG38 = "[admin:events]";
|
|
11792
12232
|
let body;
|
|
11793
12233
|
try {
|
|
11794
12234
|
body = await c.req.json();
|
|
11795
12235
|
} catch (err) {
|
|
11796
12236
|
const detail = err instanceof Error ? err.message : String(err);
|
|
11797
|
-
console.error(`${
|
|
12237
|
+
console.error(`${TAG38} reject reason=body-not-json detail=${detail}`);
|
|
11798
12238
|
return c.json({ ok: false, detail: "Request body was not valid JSON" }, 400);
|
|
11799
12239
|
}
|
|
11800
12240
|
const event = typeof body.event === "string" ? body.event : "";
|
|
11801
12241
|
if (!ALLOWED_EVENTS.has(event)) {
|
|
11802
|
-
console.error(`${
|
|
12242
|
+
console.error(`${TAG38} reject reason=event-not-allowed event=${JSON.stringify(event)}`);
|
|
11803
12243
|
return c.json({ ok: false, detail: `Event "${event}" is not allowed` }, 400);
|
|
11804
12244
|
}
|
|
11805
12245
|
const rawFields = body.fields && typeof body.fields === "object" ? body.fields : {};
|
|
@@ -11822,10 +12262,10 @@ var events_default = app19;
|
|
|
11822
12262
|
|
|
11823
12263
|
// server/routes/admin/files.ts
|
|
11824
12264
|
import { createReadStream as createReadStream2, createWriteStream as createWriteStream2, existsSync as existsSync23 } from "fs";
|
|
11825
|
-
import { readdir as readdir3, readFile as
|
|
11826
|
-
import { realpathSync as
|
|
12265
|
+
import { readdir as readdir3, readFile as readFile5, stat as stat5, mkdir as mkdir3, unlink as unlink2, rename } from "fs/promises";
|
|
12266
|
+
import { realpathSync as realpathSync6 } from "fs";
|
|
11827
12267
|
import { randomUUID as randomUUID10 } from "crypto";
|
|
11828
|
-
import { basename as
|
|
12268
|
+
import { basename as basename8, dirname as dirname6, join as join24, resolve as resolve24, sep as sep6 } from "path";
|
|
11829
12269
|
import { Readable as Readable2 } from "stream";
|
|
11830
12270
|
|
|
11831
12271
|
// ../lib/graph-trash/src/index.ts
|
|
@@ -12143,7 +12583,7 @@ async function cascadeDeleteDocument(params) {
|
|
|
12143
12583
|
|
|
12144
12584
|
// app/lib/file-index.ts
|
|
12145
12585
|
import * as fsp from "fs/promises";
|
|
12146
|
-
import { resolve as
|
|
12586
|
+
import { resolve as resolve23, relative as relative3, join as join23, basename as basename7, extname as extname2, sep as sep5 } from "path";
|
|
12147
12587
|
import { tmpdir as tmpdir2 } from "os";
|
|
12148
12588
|
import { execFile as execFile2 } from "child_process";
|
|
12149
12589
|
import { promisify as promisify2 } from "util";
|
|
@@ -12225,7 +12665,7 @@ async function extractPdf(absolute) {
|
|
|
12225
12665
|
return stdout.trim();
|
|
12226
12666
|
}
|
|
12227
12667
|
async function ocrPdf(absolute) {
|
|
12228
|
-
const outPdf =
|
|
12668
|
+
const outPdf = join23(tmpdir2(), `file-index-ocr-${process.pid}-${Date.now()}.pdf`);
|
|
12229
12669
|
try {
|
|
12230
12670
|
await execFileAsync2(
|
|
12231
12671
|
"ocrmypdf",
|
|
@@ -12276,18 +12716,18 @@ async function extractFileContent(absolute) {
|
|
|
12276
12716
|
return { content: "", route: "name-only" };
|
|
12277
12717
|
} catch (err) {
|
|
12278
12718
|
const msg = err instanceof Error ? err.message : String(err);
|
|
12279
|
-
console.error(`[file-index] extract-failed path="${
|
|
12719
|
+
console.error(`[file-index] extract-failed path="${relative3(DATA_ROOT, absolute)}" err="${msg}"`);
|
|
12280
12720
|
return { content: "", route: "name-only" };
|
|
12281
12721
|
}
|
|
12282
12722
|
}
|
|
12283
12723
|
async function readDisplayName(absolute) {
|
|
12284
|
-
const dir = absolute.slice(0, absolute.length -
|
|
12285
|
-
const base =
|
|
12724
|
+
const dir = absolute.slice(0, absolute.length - basename7(absolute).length);
|
|
12725
|
+
const base = basename7(absolute);
|
|
12286
12726
|
const dot = base.lastIndexOf(".");
|
|
12287
12727
|
const stem = dot === -1 ? base : base.slice(0, dot);
|
|
12288
12728
|
if (base === `${stem}.meta.json`) return null;
|
|
12289
12729
|
try {
|
|
12290
|
-
const raw = await fsp.readFile(
|
|
12730
|
+
const raw = await fsp.readFile(join23(dir, `${stem}.meta.json`), "utf-8");
|
|
12291
12731
|
const meta = JSON.parse(raw);
|
|
12292
12732
|
const dn = meta.displayName ?? meta.filename;
|
|
12293
12733
|
return typeof dn === "string" && dn.length > 0 ? dn : null;
|
|
@@ -12301,14 +12741,14 @@ async function walkSubtree(root, dataRoot, out) {
|
|
|
12301
12741
|
entries = await fsp.readdir(root, { withFileTypes: true });
|
|
12302
12742
|
} catch (err) {
|
|
12303
12743
|
if (err.code === "ENOENT") return { clean: true };
|
|
12304
|
-
console.error(`[file-index] walk-error dir="${
|
|
12744
|
+
console.error(`[file-index] walk-error dir="${relative3(dataRoot, root)}" err="${err.message}"`);
|
|
12305
12745
|
return { clean: false };
|
|
12306
12746
|
}
|
|
12307
12747
|
let clean = true;
|
|
12308
12748
|
for (const entry of entries) {
|
|
12309
12749
|
if (entry.isSymbolicLink()) continue;
|
|
12310
12750
|
if (entry.isDirectory() && entry.name === ".uploads-tmp") continue;
|
|
12311
|
-
const abs =
|
|
12751
|
+
const abs = join23(root, entry.name);
|
|
12312
12752
|
if (entry.isDirectory()) {
|
|
12313
12753
|
const sub = await walkSubtree(abs, dataRoot, out);
|
|
12314
12754
|
if (!sub.clean) clean = false;
|
|
@@ -12317,12 +12757,12 @@ async function walkSubtree(root, dataRoot, out) {
|
|
|
12317
12757
|
const st = await fsp.stat(abs);
|
|
12318
12758
|
out.push({
|
|
12319
12759
|
absolute: abs,
|
|
12320
|
-
relativePath:
|
|
12760
|
+
relativePath: relative3(dataRoot, abs).split(sep5).join("/"),
|
|
12321
12761
|
sizeBytes: st.size,
|
|
12322
12762
|
modifiedAt: st.mtime.toISOString()
|
|
12323
12763
|
});
|
|
12324
12764
|
} catch (err) {
|
|
12325
|
-
console.error(`[file-index] stat-error path="${
|
|
12765
|
+
console.error(`[file-index] stat-error path="${relative3(dataRoot, abs)}" err="${err.message}"`);
|
|
12326
12766
|
clean = false;
|
|
12327
12767
|
}
|
|
12328
12768
|
}
|
|
@@ -12420,7 +12860,7 @@ async function cascadeTrashLinkedKnowledgeDocs(session, accountId, relativePaths
|
|
|
12420
12860
|
return { kds, sections, chunks };
|
|
12421
12861
|
}
|
|
12422
12862
|
async function buildArtifact(accountId, wf, embed2) {
|
|
12423
|
-
const name =
|
|
12863
|
+
const name = basename7(wf.absolute);
|
|
12424
12864
|
const { content, route } = await extractFileContent(wf.absolute);
|
|
12425
12865
|
const displayName = await readDisplayName(wf.absolute);
|
|
12426
12866
|
const embedInput = `${name}
|
|
@@ -12463,7 +12903,7 @@ async function reconcileFileIndex(accountId, opts) {
|
|
|
12463
12903
|
return withSession(opts, async (session) => {
|
|
12464
12904
|
const walked = [];
|
|
12465
12905
|
const subtreeRoots = [
|
|
12466
|
-
|
|
12906
|
+
resolve23(dataRoot, "accounts", accountId)
|
|
12467
12907
|
];
|
|
12468
12908
|
let clean = true;
|
|
12469
12909
|
for (const root of subtreeRoots) {
|
|
@@ -12546,7 +12986,7 @@ async function reconcileFileIndexDebounced(accountId, opts) {
|
|
|
12546
12986
|
async function indexFile(accountId, relativePath, opts) {
|
|
12547
12987
|
const dataRoot = opts?.dataRoot ?? DATA_ROOT;
|
|
12548
12988
|
const embed2 = opts?.embed ?? embed;
|
|
12549
|
-
const absolute =
|
|
12989
|
+
const absolute = resolve23(dataRoot, relativePath);
|
|
12550
12990
|
await withSession(opts, async (session) => {
|
|
12551
12991
|
const st = await fsp.stat(absolute);
|
|
12552
12992
|
const wf = {
|
|
@@ -12581,7 +13021,7 @@ var UPLOAD_TMP_DIR = ".uploads-tmp";
|
|
|
12581
13021
|
var UUID_RE3 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
12582
13022
|
async function readMeta(absDir, baseName) {
|
|
12583
13023
|
try {
|
|
12584
|
-
const raw = await
|
|
13024
|
+
const raw = await readFile5(join24(absDir, `${baseName}.meta.json`), "utf8");
|
|
12585
13025
|
const parsed = JSON.parse(raw);
|
|
12586
13026
|
if (typeof parsed?.filename === "string") {
|
|
12587
13027
|
return { filename: parsed.filename, mimeType: typeof parsed.mimeType === "string" ? parsed.mimeType : void 0 };
|
|
@@ -12592,7 +13032,7 @@ async function readMeta(absDir, baseName) {
|
|
|
12592
13032
|
}
|
|
12593
13033
|
async function readAccountNames() {
|
|
12594
13034
|
const map = /* @__PURE__ */ new Map();
|
|
12595
|
-
const accountsDir =
|
|
13035
|
+
const accountsDir = resolve24(DATA_ROOT, "accounts");
|
|
12596
13036
|
let names;
|
|
12597
13037
|
try {
|
|
12598
13038
|
names = await readdir3(accountsDir);
|
|
@@ -12601,9 +13041,9 @@ async function readAccountNames() {
|
|
|
12601
13041
|
}
|
|
12602
13042
|
for (const name of names) {
|
|
12603
13043
|
if (!UUID_RE3.test(name)) continue;
|
|
12604
|
-
const configPath2 =
|
|
13044
|
+
const configPath2 = resolve24(accountsDir, name, "account.json");
|
|
12605
13045
|
try {
|
|
12606
|
-
const raw = await
|
|
13046
|
+
const raw = await readFile5(configPath2, "utf8");
|
|
12607
13047
|
const parsed = JSON.parse(raw);
|
|
12608
13048
|
if (typeof parsed?.name === "string" && parsed.name.length > 0) {
|
|
12609
13049
|
map.set(name, parsed.name);
|
|
@@ -12619,7 +13059,7 @@ async function readAccountNames() {
|
|
|
12619
13059
|
}
|
|
12620
13060
|
async function enrich(absolute, entry, accountNames) {
|
|
12621
13061
|
if (entry.kind === "directory" && UUID_RE3.test(entry.name)) {
|
|
12622
|
-
const meta = await readMeta(
|
|
13062
|
+
const meta = await readMeta(join24(absolute, entry.name), entry.name);
|
|
12623
13063
|
if (meta?.filename) {
|
|
12624
13064
|
entry.displayName = meta.filename;
|
|
12625
13065
|
entry.mimeType = meta.mimeType;
|
|
@@ -12765,7 +13205,7 @@ app20.get("/", requireAdminSession, async (c) => {
|
|
|
12765
13205
|
return c.json({ error: "Not found" }, 404);
|
|
12766
13206
|
}
|
|
12767
13207
|
try {
|
|
12768
|
-
const info = await
|
|
13208
|
+
const info = await stat5(absolute);
|
|
12769
13209
|
if (!info.isDirectory()) {
|
|
12770
13210
|
return c.json({ error: "Path is not a directory \u2014 use /api/admin/files/download for files" }, 400);
|
|
12771
13211
|
}
|
|
@@ -12783,8 +13223,8 @@ app20.get("/", requireAdminSession, async (c) => {
|
|
|
12783
13223
|
const childRel = relPath === "" || relPath === "." ? name : `${relPath}/${name}`;
|
|
12784
13224
|
const protectedEntry = isProtectedFromDeletion(childRel).protected;
|
|
12785
13225
|
try {
|
|
12786
|
-
const entryPath =
|
|
12787
|
-
const s = await
|
|
13226
|
+
const entryPath = join24(absolute, name);
|
|
13227
|
+
const s = await stat5(entryPath);
|
|
12788
13228
|
entries.push({
|
|
12789
13229
|
name,
|
|
12790
13230
|
kind: s.isDirectory() ? "directory" : s.isFile() ? "file" : "other",
|
|
@@ -12846,11 +13286,11 @@ app20.get("/download", requireAdminSession, async (c) => {
|
|
|
12846
13286
|
return c.json({ error: "Not found" }, 404);
|
|
12847
13287
|
}
|
|
12848
13288
|
try {
|
|
12849
|
-
const info = await
|
|
13289
|
+
const info = await stat5(absolute);
|
|
12850
13290
|
if (!info.isFile()) {
|
|
12851
13291
|
return c.json({ error: "Path is not a file" }, 400);
|
|
12852
13292
|
}
|
|
12853
|
-
const filename =
|
|
13293
|
+
const filename = basename8(absolute);
|
|
12854
13294
|
const mimeType = detectMimeType(absolute);
|
|
12855
13295
|
const nodeStream = createReadStream2(absolute);
|
|
12856
13296
|
const webStream = Readable2.toWeb(nodeStream);
|
|
@@ -12889,14 +13329,14 @@ app20.post("/upload", requireAdminSession, async (c) => {
|
|
|
12889
13329
|
return c.json({ error: "Account not found for session" }, 401);
|
|
12890
13330
|
}
|
|
12891
13331
|
const rawName = c.req.query("filename") ?? "";
|
|
12892
|
-
const safeName =
|
|
13332
|
+
const safeName = basename8(rawName).replace(/[\0/\\]/g, "_");
|
|
12893
13333
|
if (!safeName) return c.json({ error: "filename query param required" }, 400);
|
|
12894
13334
|
const uid = `${Date.now()}-${randomUUID10().slice(0, 8)}`;
|
|
12895
13335
|
const declaredBytes = c.req.header("content-length") ?? "unknown";
|
|
12896
13336
|
const rawRelpath = c.req.query("relpath") ?? "";
|
|
12897
13337
|
const relpath = rawRelpath !== "" ? rawRelpath : null;
|
|
12898
13338
|
const token = c.req.query("token") ?? "";
|
|
12899
|
-
const finalName = relpath ?
|
|
13339
|
+
const finalName = relpath ? basename8(relpath).replace(/[\0/\\]/g, "_") : `${Date.now()}-${safeName}`;
|
|
12900
13340
|
const mimeType = (c.req.header("content-type") ?? "").split(";")[0].trim();
|
|
12901
13341
|
if (!SUPPORTED_MIME_TYPES.has(mimeType)) {
|
|
12902
13342
|
console.error(`[data-upload] op=reject-mime uid=${uid} mime="${mimeType}" token=${token} rel="${relpath ?? ""}"`);
|
|
@@ -12908,7 +13348,7 @@ app20.post("/upload", requireAdminSession, async (c) => {
|
|
|
12908
13348
|
const base = resolveOwnAccountWrite(rawDir, accountId, "POST /api/admin/files/upload");
|
|
12909
13349
|
if (!base.ok) return c.json({ error: base.error }, base.status);
|
|
12910
13350
|
const relDir = relpath ? dirname6(relpath) : ".";
|
|
12911
|
-
const destRel = relDir === "." ? base.relative :
|
|
13351
|
+
const destRel = relDir === "." ? base.relative : join24(base.relative, relDir);
|
|
12912
13352
|
if (crossesForeignAccountPartition(destRel, accountId)) {
|
|
12913
13353
|
console.error(`[data] account-scope-blocked endpoint="POST /api/admin/files/upload" path="${destRel}" account=${accountId.slice(0, 8)}\u2026`);
|
|
12914
13354
|
return c.json({ error: "Not found" }, 404);
|
|
@@ -12917,17 +13357,17 @@ app20.post("/upload", requireAdminSession, async (c) => {
|
|
|
12917
13357
|
console.error(`[data] write-scope-blocked endpoint="POST /api/admin/files/upload" path="${destRel}" account=${accountId.slice(0, 8)}\u2026`);
|
|
12918
13358
|
return c.json({ error: "Path is outside your account folder" }, 403);
|
|
12919
13359
|
}
|
|
12920
|
-
const destDir = relDir === "." ? base.absolute :
|
|
12921
|
-
const destPath =
|
|
13360
|
+
const destDir = relDir === "." ? base.absolute : resolve24(base.absolute, relDir);
|
|
13361
|
+
const destPath = resolve24(destDir, finalName);
|
|
12922
13362
|
if (relpath) {
|
|
12923
13363
|
const firstCreated = await mkdir3(destDir, { recursive: true });
|
|
12924
13364
|
if (firstCreated) console.error(`[data-upload] op=mkdir uid=${uid} token=${token} dir="${destRel}"`);
|
|
12925
13365
|
} else {
|
|
12926
|
-
const info = await
|
|
13366
|
+
const info = await stat5(destDir).catch(() => null);
|
|
12927
13367
|
if (!info || !info.isDirectory()) return c.json({ error: "Upload destination is not a directory" }, 400);
|
|
12928
13368
|
}
|
|
12929
|
-
const dataRootReal =
|
|
12930
|
-
const destDirReal =
|
|
13369
|
+
const dataRootReal = realpathSync6(DATA_ROOT);
|
|
13370
|
+
const destDirReal = realpathSync6(destDir);
|
|
12931
13371
|
if (destDirReal !== dataRootReal && !destDirReal.startsWith(dataRootReal + sep6)) {
|
|
12932
13372
|
console.error(`[data-upload] op=path-traversal-blocked uid=${uid} requested="${destRel}" resolved="${destDirReal}"`);
|
|
12933
13373
|
return c.json({ error: "Upload destination escapes DATA_ROOT" }, 403);
|
|
@@ -12936,9 +13376,9 @@ app20.post("/upload", requireAdminSession, async (c) => {
|
|
|
12936
13376
|
if (!body) return c.json({ error: "Empty request body" }, 400);
|
|
12937
13377
|
console.error(`[data-upload] op=request uid=${uid} account=${accountId} dir="${destRel}" token=${token} rel="${relpath ?? ""}" declaredBytes=${declaredBytes}`);
|
|
12938
13378
|
const ceiling = dataUploadCeiling();
|
|
12939
|
-
const tmpDir =
|
|
13379
|
+
const tmpDir = resolve24(DATA_ROOT, "accounts", accountId, UPLOAD_TMP_DIR);
|
|
12940
13380
|
await mkdir3(tmpDir, { recursive: true });
|
|
12941
|
-
const tmpPath =
|
|
13381
|
+
const tmpPath = resolve24(tmpDir, `${uid}.part`);
|
|
12942
13382
|
const out = createWriteStream2(tmpPath);
|
|
12943
13383
|
const t0 = Date.now();
|
|
12944
13384
|
let received = 0;
|
|
@@ -13020,7 +13460,7 @@ app20.post("/upload", requireAdminSession, async (c) => {
|
|
|
13020
13460
|
});
|
|
13021
13461
|
});
|
|
13022
13462
|
async function sweepStaleUploadTemps() {
|
|
13023
|
-
const accountsRoot =
|
|
13463
|
+
const accountsRoot = resolve24(DATA_ROOT, "accounts");
|
|
13024
13464
|
let accounts;
|
|
13025
13465
|
try {
|
|
13026
13466
|
accounts = await readdir3(accountsRoot, { withFileTypes: true });
|
|
@@ -13030,7 +13470,7 @@ async function sweepStaleUploadTemps() {
|
|
|
13030
13470
|
let removed = 0;
|
|
13031
13471
|
for (const acc of accounts) {
|
|
13032
13472
|
if (!acc.isDirectory()) continue;
|
|
13033
|
-
const tmpDir =
|
|
13473
|
+
const tmpDir = resolve24(accountsRoot, acc.name, UPLOAD_TMP_DIR);
|
|
13034
13474
|
let entries;
|
|
13035
13475
|
try {
|
|
13036
13476
|
entries = await readdir3(tmpDir);
|
|
@@ -13039,7 +13479,7 @@ async function sweepStaleUploadTemps() {
|
|
|
13039
13479
|
}
|
|
13040
13480
|
for (const name of entries) {
|
|
13041
13481
|
try {
|
|
13042
|
-
await unlink2(
|
|
13482
|
+
await unlink2(resolve24(tmpDir, name));
|
|
13043
13483
|
removed++;
|
|
13044
13484
|
} catch {
|
|
13045
13485
|
}
|
|
@@ -13069,14 +13509,14 @@ app20.delete("/", requireAdminSession, async (c) => {
|
|
|
13069
13509
|
console.error(`[data] account-scope-blocked endpoint="DELETE /api/admin/files" path="${relPath}" account=${accountId.slice(0, 8)}\u2026`);
|
|
13070
13510
|
return c.json({ error: "Not found" }, 404);
|
|
13071
13511
|
}
|
|
13072
|
-
const base =
|
|
13512
|
+
const base = basename8(absolute);
|
|
13073
13513
|
const protection = isProtectedFromDeletion(relPath);
|
|
13074
13514
|
if (protection.protected) {
|
|
13075
13515
|
console.error(`[data] file-delete blocked path="${relPath}" reason="protected" rule="${protection.rule}"`);
|
|
13076
13516
|
return c.json({ error: "Protected file \u2014 refusing to delete" }, 403);
|
|
13077
13517
|
}
|
|
13078
13518
|
try {
|
|
13079
|
-
const info = await
|
|
13519
|
+
const info = await stat5(absolute);
|
|
13080
13520
|
if (info.isDirectory()) {
|
|
13081
13521
|
return c.json({ error: "Directory deletion not supported" }, 400);
|
|
13082
13522
|
}
|
|
@@ -13085,7 +13525,7 @@ app20.delete("/", requireAdminSession, async (c) => {
|
|
|
13085
13525
|
}
|
|
13086
13526
|
const dot = base.lastIndexOf(".");
|
|
13087
13527
|
const stem = dot === -1 ? base : base.slice(0, dot);
|
|
13088
|
-
const sidecarPath = UUID_RE3.test(stem) && base !== `${stem}.meta.json` ?
|
|
13528
|
+
const sidecarPath = UUID_RE3.test(stem) && base !== `${stem}.meta.json` ? join24(dirname6(absolute), `${stem}.meta.json`) : null;
|
|
13089
13529
|
await unlink2(absolute);
|
|
13090
13530
|
if (sidecarPath) {
|
|
13091
13531
|
try {
|
|
@@ -13142,10 +13582,10 @@ app20.post("/folder", requireAdminSession, async (c) => {
|
|
|
13142
13582
|
if (!isValidEntryName(name)) return c.json({ error: "Invalid folder name" }, 400);
|
|
13143
13583
|
const parent = resolveOwnAccountWrite(rawPath, accountId, "POST /api/admin/files/folder");
|
|
13144
13584
|
if (!parent.ok) return c.json({ error: parent.error }, parent.status);
|
|
13145
|
-
const newAbs =
|
|
13585
|
+
const newAbs = resolve24(parent.absolute, name);
|
|
13146
13586
|
const newRel = `${parent.relative}/${name}`;
|
|
13147
13587
|
try {
|
|
13148
|
-
const info = await
|
|
13588
|
+
const info = await stat5(parent.absolute);
|
|
13149
13589
|
if (!info.isDirectory()) return c.json({ error: "Parent is not a directory" }, 400);
|
|
13150
13590
|
await mkdir3(newAbs, { recursive: false });
|
|
13151
13591
|
} catch (err) {
|
|
@@ -13181,7 +13621,7 @@ app20.post("/rename", requireAdminSession, async (c) => {
|
|
|
13181
13621
|
console.error(`[data] file-rename blocked path="${src.relative}" reason="protected"`);
|
|
13182
13622
|
return c.json({ error: "Protected entry \u2014 refusing to rename" }, 403);
|
|
13183
13623
|
}
|
|
13184
|
-
const destAbs =
|
|
13624
|
+
const destAbs = resolve24(dirname6(src.absolute), newName);
|
|
13185
13625
|
const newRel = `${dirname6(src.relative)}/${newName}`.replace(/^\.\//, "");
|
|
13186
13626
|
if (isProtectedFromRename(newRel)) {
|
|
13187
13627
|
console.error(`[data] file-rename blocked path="${newRel}" reason="protected-target"`);
|
|
@@ -13190,7 +13630,7 @@ app20.post("/rename", requireAdminSession, async (c) => {
|
|
|
13190
13630
|
try {
|
|
13191
13631
|
let collision = true;
|
|
13192
13632
|
try {
|
|
13193
|
-
await
|
|
13633
|
+
await stat5(destAbs);
|
|
13194
13634
|
} catch (e) {
|
|
13195
13635
|
if (e.code === "ENOENT") collision = false;
|
|
13196
13636
|
}
|
|
@@ -14149,8 +14589,6 @@ var FILTER_TOP_LEVEL_LABELS = Object.freeze(
|
|
|
14149
14589
|
"Job",
|
|
14150
14590
|
"QuoteDocument",
|
|
14151
14591
|
"InboundInvoice",
|
|
14152
|
-
"SubContractor",
|
|
14153
|
-
"SubInvoice",
|
|
14154
14592
|
"WhatsAppGroup",
|
|
14155
14593
|
"AdminConversation",
|
|
14156
14594
|
"PublicConversation",
|
|
@@ -15218,14 +15656,14 @@ var WRITE_CYPHER = `
|
|
|
15218
15656
|
var graph_default_view_default = app26;
|
|
15219
15657
|
|
|
15220
15658
|
// server/routes/admin/sidebar-artefacts.ts
|
|
15221
|
-
import { readdir as readdir4, stat as
|
|
15222
|
-
import { resolve as
|
|
15659
|
+
import { readdir as readdir4, stat as stat6 } from "fs/promises";
|
|
15660
|
+
import { resolve as resolve25, relative as relative4, isAbsolute as isAbsolute2, sep as sep7, basename as basename9 } from "path";
|
|
15223
15661
|
import { existsSync as existsSync24 } from "fs";
|
|
15224
15662
|
var LIMIT = 50;
|
|
15225
15663
|
var ADMIN_AGENT_FILES = ["IDENTITY.md", "SOUL.md", "KNOWLEDGE.md"];
|
|
15226
15664
|
function toDataRootRelPath(absPath) {
|
|
15227
15665
|
if (absPath !== DATA_ROOT && !absPath.startsWith(DATA_ROOT + sep7)) return null;
|
|
15228
|
-
return
|
|
15666
|
+
return relative4(DATA_ROOT, absPath);
|
|
15229
15667
|
}
|
|
15230
15668
|
var app27 = new Hono();
|
|
15231
15669
|
app27.get("/", requireAdminSession, async (c) => {
|
|
@@ -15239,7 +15677,7 @@ app27.get("/", requireAdminSession, async (c) => {
|
|
|
15239
15677
|
if (accountFiles === null) {
|
|
15240
15678
|
return c.json({ error: "Failed to load artefacts" }, 500);
|
|
15241
15679
|
}
|
|
15242
|
-
const accountDir =
|
|
15680
|
+
const accountDir = resolve25(ACCOUNTS_DIR, accountId);
|
|
15243
15681
|
const agents = await fetchAgentTemplateRows(accountDir);
|
|
15244
15682
|
const artefacts = [...accountFiles, ...agents].sort(
|
|
15245
15683
|
(a, b) => (b.updatedAt ?? "").localeCompare(a.updatedAt ?? "")
|
|
@@ -15272,7 +15710,7 @@ async function fetchAccountFileArtefacts(accountId) {
|
|
|
15272
15710
|
const modifiedAt = r.get("modifiedAt") ?? "";
|
|
15273
15711
|
return {
|
|
15274
15712
|
id: `account-file:${relativePath}`,
|
|
15275
|
-
name: displayName ??
|
|
15713
|
+
name: displayName ?? basename9(relativePath),
|
|
15276
15714
|
kind: "account-file",
|
|
15277
15715
|
updatedAt: modifiedAt,
|
|
15278
15716
|
mimeType,
|
|
@@ -15291,8 +15729,8 @@ async function fetchAccountFileArtefacts(accountId) {
|
|
|
15291
15729
|
async function fetchAgentTemplateRows(accountDir) {
|
|
15292
15730
|
const rows = [];
|
|
15293
15731
|
for (const filename of ADMIN_AGENT_FILES) {
|
|
15294
|
-
const overridePath =
|
|
15295
|
-
const bundledPath =
|
|
15732
|
+
const overridePath = resolve25(accountDir, "agents", "admin", filename);
|
|
15733
|
+
const bundledPath = resolve25(PLATFORM_ROOT, "templates", "agents", "admin", filename);
|
|
15296
15734
|
const labelStem = filename.replace(/\.md$/, "");
|
|
15297
15735
|
const row = await readAgentTemplateRow({
|
|
15298
15736
|
id: `agent-template:admin:${filename}`,
|
|
@@ -15305,12 +15743,12 @@ async function fetchAgentTemplateRows(accountDir) {
|
|
|
15305
15743
|
});
|
|
15306
15744
|
if (row) rows.push(row);
|
|
15307
15745
|
}
|
|
15308
|
-
const overrideDir =
|
|
15309
|
-
const bundledDir =
|
|
15746
|
+
const overrideDir = resolve25(accountDir, "specialists", "agents");
|
|
15747
|
+
const bundledDir = resolve25(PLATFORM_ROOT, "templates", "specialists", "agents");
|
|
15310
15748
|
const specialistNames = await unionSpecialistFilenames(overrideDir, bundledDir);
|
|
15311
15749
|
for (const filename of specialistNames) {
|
|
15312
|
-
const overridePath =
|
|
15313
|
-
const bundledPath =
|
|
15750
|
+
const overridePath = resolve25(overrideDir, filename);
|
|
15751
|
+
const bundledPath = resolve25(bundledDir, filename);
|
|
15314
15752
|
const row = await readAgentTemplateRow({
|
|
15315
15753
|
id: `agent-template:specialist:${filename}`,
|
|
15316
15754
|
displayName: filename.replace(/\.md$/, ""),
|
|
@@ -15364,7 +15802,7 @@ async function readAgentTemplateRow(inp) {
|
|
|
15364
15802
|
}
|
|
15365
15803
|
if (!chosenPath) return null;
|
|
15366
15804
|
try {
|
|
15367
|
-
const st = await
|
|
15805
|
+
const st = await stat6(chosenPath);
|
|
15368
15806
|
const dlPath = toDataRootRelPath(chosenPath);
|
|
15369
15807
|
return {
|
|
15370
15808
|
id: inp.id,
|
|
@@ -15389,8 +15827,8 @@ async function readAgentTemplateRow(inp) {
|
|
|
15389
15827
|
}
|
|
15390
15828
|
}
|
|
15391
15829
|
function isWithin(target, root) {
|
|
15392
|
-
const rel =
|
|
15393
|
-
return !rel.startsWith("..") && !
|
|
15830
|
+
const rel = relative4(root, target);
|
|
15831
|
+
return !rel.startsWith("..") && !isAbsolute2(rel);
|
|
15394
15832
|
}
|
|
15395
15833
|
var sidebar_artefacts_default = app27;
|
|
15396
15834
|
|
|
@@ -15676,6 +16114,8 @@ function isSelectableModel(id) {
|
|
|
15676
16114
|
}
|
|
15677
16115
|
|
|
15678
16116
|
// server/routes/admin/session-reseat.ts
|
|
16117
|
+
var RESEAT_PERMISSION_MODES = /* @__PURE__ */ new Set(["default", "acceptEdits", "plan", "auto", "bypassPermissions"]);
|
|
16118
|
+
var RESEAT_EFFORT_LEVELS = /* @__PURE__ */ new Set(["low", "medium", "high", "xhigh"]);
|
|
15679
16119
|
function resolveReseatPlan(body, mintId = randomUUID12, adminUserId, authenticatedName) {
|
|
15680
16120
|
const sessionId = mintId();
|
|
15681
16121
|
const key = `session:${sessionId}`;
|
|
@@ -15689,6 +16129,8 @@ function resolveReseatPlan(body, mintId = randomUUID12, adminUserId, authenticat
|
|
|
15689
16129
|
webchatChannel: webchatChannelDescriptor(key)
|
|
15690
16130
|
};
|
|
15691
16131
|
if (body.name) payload.name = body.name;
|
|
16132
|
+
if (body.permissionMode) payload.permissionMode = body.permissionMode;
|
|
16133
|
+
if (body.effort) payload.effort = body.effort;
|
|
15692
16134
|
if (adminUserId) payload.adminUserId = adminUserId;
|
|
15693
16135
|
if (authenticatedName) payload.authenticatedName = authenticatedName;
|
|
15694
16136
|
return { sessionId, payload };
|
|
@@ -15706,6 +16148,10 @@ app33.post("/", requireAdminSession, async (c) => {
|
|
|
15706
16148
|
const model = typeof body.model === "string" && isSelectableModel(body.model) ? body.model : null;
|
|
15707
16149
|
if (fromSessionId === null) return c.json({ error: "fromSessionId must be a valid session id" }, 400);
|
|
15708
16150
|
if (model === null) return c.json({ error: "model must be a selectable model" }, 400);
|
|
16151
|
+
const permissionMode = typeof body.permissionMode === "string" ? RESEAT_PERMISSION_MODES.has(body.permissionMode) ? body.permissionMode : null : void 0;
|
|
16152
|
+
const effort = typeof body.effort === "string" ? RESEAT_EFFORT_LEVELS.has(body.effort) ? body.effort : null : void 0;
|
|
16153
|
+
if (permissionMode === null) return c.json({ error: "permissionMode must be a composer-writable mode" }, 400);
|
|
16154
|
+
if (effort === null) return c.json({ error: "effort must be an accepted level" }, 400);
|
|
15709
16155
|
if (reseatsInFlight.has(fromSessionId)) {
|
|
15710
16156
|
return c.json({ error: "re-seat already in progress for this session" }, 409);
|
|
15711
16157
|
}
|
|
@@ -15715,9 +16161,14 @@ app33.post("/", requireAdminSession, async (c) => {
|
|
|
15715
16161
|
const adminUserId = cacheKey ? getUserIdForSession(cacheKey) : void 0;
|
|
15716
16162
|
const accountId = cacheKey ? getAccountIdForSession(cacheKey) : void 0;
|
|
15717
16163
|
const authenticatedName = accountId ? await resolveAuthenticatedName(accountId, adminUserId) : void 0;
|
|
15718
|
-
const plan = resolveReseatPlan(
|
|
16164
|
+
const plan = resolveReseatPlan(
|
|
16165
|
+
{ fromSessionId, model, ...permissionMode ? { permissionMode } : {}, ...effort ? { effort } : {} },
|
|
16166
|
+
randomUUID12,
|
|
16167
|
+
adminUserId,
|
|
16168
|
+
authenticatedName
|
|
16169
|
+
);
|
|
15719
16170
|
const reseatReqId = randomUUID12();
|
|
15720
|
-
console.log(`[chat-reseat] op=request reseatReqId=${reseatReqId} from=${fromSessionId} to-model=${model} new=${plan.sessionId}`);
|
|
16171
|
+
console.log(`[chat-reseat] op=request reseatReqId=${reseatReqId} from=${fromSessionId} to-model=${model} to-mode=${permissionMode ?? "none"} to-effort=${effort ?? "none"} new=${plan.sessionId}`);
|
|
15721
16172
|
let res;
|
|
15722
16173
|
try {
|
|
15723
16174
|
res = await fetch(`${managerBase("session-reseat:wrapper")}/rc-spawn`, {
|
|
@@ -15769,7 +16220,7 @@ app33.post("/", requireAdminSession, async (c) => {
|
|
|
15769
16220
|
var session_reseat_default = app33;
|
|
15770
16221
|
|
|
15771
16222
|
// server/routes/admin/system-stats.ts
|
|
15772
|
-
import { readFile as
|
|
16223
|
+
import { readFile as readFile6 } from "fs/promises";
|
|
15773
16224
|
import { cpus, loadavg, totalmem, freemem } from "os";
|
|
15774
16225
|
function parseCpuStat(blob) {
|
|
15775
16226
|
const line = blob.split("\n", 1)[0] ?? "";
|
|
@@ -15813,11 +16264,11 @@ function parseMeminfo(blob) {
|
|
|
15813
16264
|
}
|
|
15814
16265
|
var SAMPLE_DELAY_MS = 100;
|
|
15815
16266
|
async function sampleLinux() {
|
|
15816
|
-
const a = parseCpuStat(await
|
|
16267
|
+
const a = parseCpuStat(await readFile6("/proc/stat", "utf-8"));
|
|
15817
16268
|
await new Promise((r) => setTimeout(r, SAMPLE_DELAY_MS));
|
|
15818
|
-
const b = parseCpuStat(await
|
|
15819
|
-
const meminfo = parseMeminfo(await
|
|
15820
|
-
const loadavgRaw = (await
|
|
16269
|
+
const b = parseCpuStat(await readFile6("/proc/stat", "utf-8"));
|
|
16270
|
+
const meminfo = parseMeminfo(await readFile6("/proc/meminfo", "utf-8"));
|
|
16271
|
+
const loadavgRaw = (await readFile6("/proc/loadavg", "utf-8")).trim();
|
|
15821
16272
|
const [l1, l5, l15] = loadavgRaw.split(/\s+/, 3).map(Number);
|
|
15822
16273
|
if (![l1, l5, l15].every(Number.isFinite)) {
|
|
15823
16274
|
throw new Error(`malformed /proc/loadavg: ${loadavgRaw}`);
|
|
@@ -15890,24 +16341,24 @@ app34.get("/", async (c) => {
|
|
|
15890
16341
|
var system_stats_default = app34;
|
|
15891
16342
|
|
|
15892
16343
|
// server/routes/admin/health.ts
|
|
15893
|
-
import { existsSync as existsSync25, readFileSync as
|
|
15894
|
-
import { resolve as
|
|
15895
|
-
var PLATFORM_ROOT6 = process.env.MAXY_PLATFORM_ROOT ??
|
|
16344
|
+
import { existsSync as existsSync25, readFileSync as readFileSync26 } from "fs";
|
|
16345
|
+
import { resolve as resolve26, join as join25 } from "path";
|
|
16346
|
+
var PLATFORM_ROOT6 = process.env.MAXY_PLATFORM_ROOT ?? resolve26(process.cwd(), "..");
|
|
15896
16347
|
var brandHostname = "maxy";
|
|
15897
|
-
var brandJsonPath =
|
|
16348
|
+
var brandJsonPath = join25(PLATFORM_ROOT6, "config", "brand.json");
|
|
15898
16349
|
if (existsSync25(brandJsonPath)) {
|
|
15899
16350
|
try {
|
|
15900
|
-
const brand = JSON.parse(
|
|
16351
|
+
const brand = JSON.parse(readFileSync26(brandJsonPath, "utf-8"));
|
|
15901
16352
|
if (brand.hostname) brandHostname = brand.hostname;
|
|
15902
16353
|
} catch {
|
|
15903
16354
|
}
|
|
15904
16355
|
}
|
|
15905
|
-
var VERSION_FILE =
|
|
16356
|
+
var VERSION_FILE = resolve26(PLATFORM_ROOT6, `config/.${brandHostname}-version`);
|
|
15906
16357
|
var PROCESS_STARTED_AT = (/* @__PURE__ */ new Date()).toISOString();
|
|
15907
16358
|
var PROBE_TIMEOUT_MS = 1e3;
|
|
15908
16359
|
function readVersion() {
|
|
15909
16360
|
if (!existsSync25(VERSION_FILE)) return "unknown";
|
|
15910
|
-
return
|
|
16361
|
+
return readFileSync26(VERSION_FILE, "utf-8").trim() || "unknown";
|
|
15911
16362
|
}
|
|
15912
16363
|
async function probeConversationDb() {
|
|
15913
16364
|
let session;
|
|
@@ -15959,7 +16410,7 @@ var health_default2 = app35;
|
|
|
15959
16410
|
|
|
15960
16411
|
// server/routes/admin/linkedin-ingest.ts
|
|
15961
16412
|
import { randomUUID as randomUUID13 } from "crypto";
|
|
15962
|
-
var
|
|
16413
|
+
var TAG30 = "[linkedin-ingest-route]";
|
|
15963
16414
|
var UUID2 = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;
|
|
15964
16415
|
var ISO = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,3})?(?:Z|[+-]\d{2}:\d{2})$/;
|
|
15965
16416
|
var LINKEDIN_URL = /^https:\/\/www\.linkedin\.com\//;
|
|
@@ -16063,29 +16514,29 @@ app36.post("/", requireAdminSession, async (c) => {
|
|
|
16063
16514
|
try {
|
|
16064
16515
|
body = await c.req.json();
|
|
16065
16516
|
} catch {
|
|
16066
|
-
console.error(
|
|
16517
|
+
console.error(TAG30 + " rejected status=400 reason=schema:body-not-json");
|
|
16067
16518
|
return c.json({ ok: false, error: "schema", reason: "body-not-json" }, 400);
|
|
16068
16519
|
}
|
|
16069
16520
|
const v = validate(body);
|
|
16070
16521
|
if (!v.ok) {
|
|
16071
|
-
console.error(
|
|
16522
|
+
console.error(TAG30 + " rejected status=" + v.status + " reason=" + v.reason + " missing=" + v.missing.join(","));
|
|
16072
16523
|
return c.json({ ok: false, error: v.error, reason: v.reason, missing: v.missing }, v.status);
|
|
16073
16524
|
}
|
|
16074
16525
|
const envelope = v.envelope;
|
|
16075
16526
|
const cacheKey = c.var.cacheKey ?? "";
|
|
16076
16527
|
const senderId = getAccountIdForSession(cacheKey) ?? "";
|
|
16077
16528
|
if (!senderId) {
|
|
16078
|
-
console.error(
|
|
16529
|
+
console.error(TAG30 + " rejected status=500 reason=admin-account-not-resolved");
|
|
16079
16530
|
return c.json({ ok: false, error: "admin-account-not-resolved" }, 500);
|
|
16080
16531
|
}
|
|
16081
16532
|
const payloadBytes = JSON.stringify(envelope).length;
|
|
16082
16533
|
console.log(
|
|
16083
|
-
|
|
16534
|
+
TAG30 + " received kind=" + envelope.kind + " account=" + senderId.slice(0, 8) + " pageUrl=" + envelope.pageUrl + " dispatchId=" + envelope.dispatchId + " payloadBytes=" + payloadBytes
|
|
16084
16535
|
);
|
|
16085
16536
|
const initialMessage = buildInitialMessage(envelope);
|
|
16086
16537
|
const spawnStart = Date.now();
|
|
16087
16538
|
const sessionId = randomUUID13();
|
|
16088
|
-
console.log(
|
|
16539
|
+
console.log(TAG30 + " route target=rc-spawn dispatchId=" + envelope.dispatchId + " sessionId=" + sessionId.slice(0, 8));
|
|
16089
16540
|
const spawned = await managerRcSpawn({
|
|
16090
16541
|
sessionId,
|
|
16091
16542
|
initialMessage,
|
|
@@ -16094,12 +16545,12 @@ app36.post("/", requireAdminSession, async (c) => {
|
|
|
16094
16545
|
});
|
|
16095
16546
|
if ("error" in spawned) {
|
|
16096
16547
|
console.error(
|
|
16097
|
-
|
|
16548
|
+
TAG30 + " dispatch-failed dispatchId=" + envelope.dispatchId + " status=" + spawned.status + " ms=" + (Date.now() - spawnStart) + " message=" + spawned.error
|
|
16098
16549
|
);
|
|
16099
16550
|
return c.json({ ok: false, error: "dispatch-failed", upstreamStatus: spawned.status, detail: spawned.error }, 502);
|
|
16100
16551
|
}
|
|
16101
16552
|
console.log(
|
|
16102
|
-
|
|
16553
|
+
TAG30 + " dispatched dispatchId=" + envelope.dispatchId + " taskId=" + spawned.sessionId + " ms=" + (Date.now() - spawnStart)
|
|
16103
16554
|
);
|
|
16104
16555
|
return c.json({ ok: true, dispatchId: envelope.dispatchId, taskId: spawned.sessionId }, 202);
|
|
16105
16556
|
});
|
|
@@ -16107,7 +16558,7 @@ var linkedin_ingest_default = app36;
|
|
|
16107
16558
|
|
|
16108
16559
|
// server/routes/admin/post-turn-context.ts
|
|
16109
16560
|
import neo4j3 from "neo4j-driver";
|
|
16110
|
-
var
|
|
16561
|
+
var TAG31 = "[post-turn-context]";
|
|
16111
16562
|
var STRIPPED_PROPERTIES2 = /* @__PURE__ */ new Set([
|
|
16112
16563
|
"embedding",
|
|
16113
16564
|
"passwordHash",
|
|
@@ -16149,7 +16600,7 @@ var app37 = new Hono();
|
|
|
16149
16600
|
app37.get("/", async (c) => {
|
|
16150
16601
|
const remoteAddr = c.env?.incoming?.socket?.remoteAddress ?? "";
|
|
16151
16602
|
if (!isLoopbackAddr2(remoteAddr)) {
|
|
16152
|
-
console.error(`${
|
|
16603
|
+
console.error(`${TAG31} reject reason=non-loopback remoteAddr=${remoteAddr}`);
|
|
16153
16604
|
return c.json({ error: "post-turn-context-loopback-only" }, 403);
|
|
16154
16605
|
}
|
|
16155
16606
|
const xffHeader = c.env?.incoming?.headers?.["x-forwarded-for"];
|
|
@@ -16158,7 +16609,7 @@ app37.get("/", async (c) => {
|
|
|
16158
16609
|
const tokens = xffRaw.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
|
|
16159
16610
|
const offender = tokens.find((t) => !isLoopbackAddr2(t));
|
|
16160
16611
|
if (offender !== void 0) {
|
|
16161
|
-
console.error(`${
|
|
16612
|
+
console.error(`${TAG31} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
|
|
16162
16613
|
return c.json({ error: "post-turn-context-loopback-only" }, 403);
|
|
16163
16614
|
}
|
|
16164
16615
|
}
|
|
@@ -16190,7 +16641,7 @@ app37.get("/", async (c) => {
|
|
|
16190
16641
|
writes.sort((a, b) => a.sortKey.localeCompare(b.sortKey));
|
|
16191
16642
|
const total = Date.now() - started;
|
|
16192
16643
|
console.log(
|
|
16193
|
-
`${
|
|
16644
|
+
`${TAG31} sessionId=${sessionId} accountId=${accountId} writes=${writes.length} ms=${total}`
|
|
16194
16645
|
);
|
|
16195
16646
|
return c.json({
|
|
16196
16647
|
writes: writes.map(({ elementId, labels, properties }) => ({ elementId, labels, properties }))
|
|
@@ -16199,7 +16650,7 @@ app37.get("/", async (c) => {
|
|
|
16199
16650
|
const elapsed = Date.now() - started;
|
|
16200
16651
|
const message = err instanceof Error ? err.message : String(err);
|
|
16201
16652
|
console.error(
|
|
16202
|
-
`${
|
|
16653
|
+
`${TAG31} neo4j-unreachable sessionId=${sessionId} ms=${elapsed} err="${message}"`
|
|
16203
16654
|
);
|
|
16204
16655
|
return c.json({ error: `post-turn-context unavailable: ${message}` }, 503);
|
|
16205
16656
|
} finally {
|
|
@@ -16210,7 +16661,7 @@ var post_turn_context_default = app37;
|
|
|
16210
16661
|
|
|
16211
16662
|
// server/routes/admin/public-session-context.ts
|
|
16212
16663
|
import neo4j4 from "neo4j-driver";
|
|
16213
|
-
var
|
|
16664
|
+
var TAG32 = "[public-session-context]";
|
|
16214
16665
|
var STRIPPED_PROPERTIES3 = /* @__PURE__ */ new Set([
|
|
16215
16666
|
"embedding",
|
|
16216
16667
|
"passwordHash",
|
|
@@ -16252,7 +16703,7 @@ var app38 = new Hono();
|
|
|
16252
16703
|
app38.get("/", async (c) => {
|
|
16253
16704
|
const remoteAddr = c.env?.incoming?.socket?.remoteAddress ?? "";
|
|
16254
16705
|
if (!isLoopbackAddr3(remoteAddr)) {
|
|
16255
|
-
console.error(`${
|
|
16706
|
+
console.error(`${TAG32} reject reason=non-loopback remoteAddr=${remoteAddr}`);
|
|
16256
16707
|
return c.json({ error: "public-session-context-loopback-only" }, 403);
|
|
16257
16708
|
}
|
|
16258
16709
|
const xffHeader = c.env?.incoming?.headers?.["x-forwarded-for"];
|
|
@@ -16261,7 +16712,7 @@ app38.get("/", async (c) => {
|
|
|
16261
16712
|
const tokens = xffRaw.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
|
|
16262
16713
|
const offender = tokens.find((t) => !isLoopbackAddr3(t));
|
|
16263
16714
|
if (offender !== void 0) {
|
|
16264
|
-
console.error(`${
|
|
16715
|
+
console.error(`${TAG32} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
|
|
16265
16716
|
return c.json({ error: "public-session-context-loopback-only" }, 403);
|
|
16266
16717
|
}
|
|
16267
16718
|
}
|
|
@@ -16292,7 +16743,7 @@ app38.get("/", async (c) => {
|
|
|
16292
16743
|
writes.sort((a, b) => a.sortKey.localeCompare(b.sortKey));
|
|
16293
16744
|
const total = Date.now() - started;
|
|
16294
16745
|
console.log(
|
|
16295
|
-
`${
|
|
16746
|
+
`${TAG32} sliceToken=${sliceToken.slice(0, 8)} writes=${writes.length} ms=${total}`
|
|
16296
16747
|
);
|
|
16297
16748
|
return c.json({
|
|
16298
16749
|
writes: writes.map(({ elementId, labels, properties }) => ({ elementId, labels, properties }))
|
|
@@ -16301,7 +16752,7 @@ app38.get("/", async (c) => {
|
|
|
16301
16752
|
const elapsed = Date.now() - started;
|
|
16302
16753
|
const message = err instanceof Error ? err.message : String(err);
|
|
16303
16754
|
console.error(
|
|
16304
|
-
`${
|
|
16755
|
+
`${TAG32} neo4j-unreachable sliceToken=${sliceToken.slice(0, 8)} ms=${elapsed} err="${message}"`
|
|
16305
16756
|
);
|
|
16306
16757
|
return c.json({ error: `public-session-context unavailable: ${message}` }, 503);
|
|
16307
16758
|
} finally {
|
|
@@ -16311,7 +16762,7 @@ app38.get("/", async (c) => {
|
|
|
16311
16762
|
var public_session_context_default = app38;
|
|
16312
16763
|
|
|
16313
16764
|
// server/routes/admin/public-session-exit.ts
|
|
16314
|
-
var
|
|
16765
|
+
var TAG33 = "[public-session-exit-route]";
|
|
16315
16766
|
function isLoopbackAddr4(addr) {
|
|
16316
16767
|
return addr === "127.0.0.1" || addr === "::1" || addr === "::ffff:127.0.0.1";
|
|
16317
16768
|
}
|
|
@@ -16319,7 +16770,7 @@ var app39 = new Hono();
|
|
|
16319
16770
|
app39.post("/", async (c) => {
|
|
16320
16771
|
const remoteAddr = c.env?.incoming?.socket?.remoteAddress ?? "";
|
|
16321
16772
|
if (!isLoopbackAddr4(remoteAddr)) {
|
|
16322
|
-
console.error(`${
|
|
16773
|
+
console.error(`${TAG33} reject reason=non-loopback remoteAddr=${remoteAddr}`);
|
|
16323
16774
|
return c.json({ error: "public-session-exit-loopback-only" }, 403);
|
|
16324
16775
|
}
|
|
16325
16776
|
const xffHeader = c.env?.incoming?.headers?.["x-forwarded-for"];
|
|
@@ -16328,7 +16779,7 @@ app39.post("/", async (c) => {
|
|
|
16328
16779
|
const tokens = xffRaw.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
|
|
16329
16780
|
const offender = tokens.find((t) => !isLoopbackAddr4(t));
|
|
16330
16781
|
if (offender !== void 0) {
|
|
16331
|
-
console.error(`${
|
|
16782
|
+
console.error(`${TAG33} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
|
|
16332
16783
|
return c.json({ error: "public-session-exit-loopback-only" }, 403);
|
|
16333
16784
|
}
|
|
16334
16785
|
}
|
|
@@ -16346,7 +16797,7 @@ app39.post("/", async (c) => {
|
|
|
16346
16797
|
var public_session_exit_default = app39;
|
|
16347
16798
|
|
|
16348
16799
|
// server/routes/admin/access-session-evict.ts
|
|
16349
|
-
var
|
|
16800
|
+
var TAG34 = "[access-session-evict]";
|
|
16350
16801
|
function isLoopbackAddr5(addr) {
|
|
16351
16802
|
return addr === "127.0.0.1" || addr === "::1" || addr === "::ffff:127.0.0.1";
|
|
16352
16803
|
}
|
|
@@ -16354,7 +16805,7 @@ var app40 = new Hono();
|
|
|
16354
16805
|
app40.post("/", async (c) => {
|
|
16355
16806
|
const remoteAddr = c.env?.incoming?.socket?.remoteAddress ?? "";
|
|
16356
16807
|
if (!isLoopbackAddr5(remoteAddr)) {
|
|
16357
|
-
console.error(`${
|
|
16808
|
+
console.error(`${TAG34} reject reason=non-loopback remoteAddr=${remoteAddr}`);
|
|
16358
16809
|
return c.json({ error: "access-session-evict-loopback-only" }, 403);
|
|
16359
16810
|
}
|
|
16360
16811
|
const xffHeader = c.env?.incoming?.headers?.["x-forwarded-for"];
|
|
@@ -16363,7 +16814,7 @@ app40.post("/", async (c) => {
|
|
|
16363
16814
|
const tokens = xffRaw.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
|
|
16364
16815
|
const offender = tokens.find((t) => !isLoopbackAddr5(t));
|
|
16365
16816
|
if (offender !== void 0) {
|
|
16366
|
-
console.error(`${
|
|
16817
|
+
console.error(`${TAG34} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
|
|
16367
16818
|
return c.json({ error: "access-session-evict-loopback-only" }, 403);
|
|
16368
16819
|
}
|
|
16369
16820
|
}
|
|
@@ -16376,13 +16827,13 @@ app40.post("/", async (c) => {
|
|
|
16376
16827
|
const grantId = typeof body.grantId === "string" ? body.grantId.trim() : "";
|
|
16377
16828
|
if (!grantId) return c.json({ error: "grantId required" }, 400);
|
|
16378
16829
|
const dropped = evictAccessSessionsByGrant(grantId);
|
|
16379
|
-
console.log(`${
|
|
16830
|
+
console.log(`${TAG34} grantId=${grantId} dropped=${dropped}`);
|
|
16380
16831
|
return c.json({ ok: true, dropped });
|
|
16381
16832
|
});
|
|
16382
16833
|
var access_session_evict_default = app40;
|
|
16383
16834
|
|
|
16384
16835
|
// server/routes/admin/enrol-person.ts
|
|
16385
|
-
var
|
|
16836
|
+
var TAG35 = "[enrol]";
|
|
16386
16837
|
function isLoopbackAddr6(addr) {
|
|
16387
16838
|
return addr === "127.0.0.1" || addr === "::1" || addr === "::ffff:127.0.0.1";
|
|
16388
16839
|
}
|
|
@@ -16391,7 +16842,7 @@ var app41 = new Hono();
|
|
|
16391
16842
|
app41.post("/", async (c) => {
|
|
16392
16843
|
const remoteAddr = c.env?.incoming?.socket?.remoteAddress ?? "";
|
|
16393
16844
|
if (!isLoopbackAddr6(remoteAddr)) {
|
|
16394
|
-
console.error(`${
|
|
16845
|
+
console.error(`${TAG35} reject reason=non-loopback remoteAddr=${remoteAddr}`);
|
|
16395
16846
|
return c.json({ error: "enrol-person-loopback-only" }, 403);
|
|
16396
16847
|
}
|
|
16397
16848
|
const xffHeader = c.env?.incoming?.headers?.["x-forwarded-for"];
|
|
@@ -16400,7 +16851,7 @@ app41.post("/", async (c) => {
|
|
|
16400
16851
|
const tokens = xffRaw.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
|
|
16401
16852
|
const offender = tokens.find((t) => !isLoopbackAddr6(t));
|
|
16402
16853
|
if (offender !== void 0) {
|
|
16403
|
-
console.error(`${
|
|
16854
|
+
console.error(`${TAG35} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
|
|
16404
16855
|
return c.json({ error: "enrol-person-loopback-only" }, 403);
|
|
16405
16856
|
}
|
|
16406
16857
|
}
|
|
@@ -16425,7 +16876,7 @@ app41.post("/", async (c) => {
|
|
|
16425
16876
|
}
|
|
16426
16877
|
const accountId = process.env.ACCOUNT_ID ?? "";
|
|
16427
16878
|
if (!accountId) {
|
|
16428
|
-
console.error(`${
|
|
16879
|
+
console.error(`${TAG35} op=person result=no-account-id`);
|
|
16429
16880
|
return c.json({ error: "no-account-id" }, 500);
|
|
16430
16881
|
}
|
|
16431
16882
|
let personId;
|
|
@@ -16433,7 +16884,7 @@ app41.post("/", async (c) => {
|
|
|
16433
16884
|
personId = await enrolPerson({ accountId, phone, email, agentSlug });
|
|
16434
16885
|
} catch (err) {
|
|
16435
16886
|
console.error(
|
|
16436
|
-
`${
|
|
16887
|
+
`${TAG35} op=person result=write-failed agentSlug=${agentSlug} contact=${maskContact(email)} err="${err instanceof Error ? err.message : String(err)}"`
|
|
16437
16888
|
);
|
|
16438
16889
|
return c.json({ error: "enrol-failed" }, 500);
|
|
16439
16890
|
}
|
|
@@ -16443,11 +16894,11 @@ app41.post("/", async (c) => {
|
|
|
16443
16894
|
if (g) grant = { grantId: g.grantId, status: g.status, contactValue: g.contactValue };
|
|
16444
16895
|
} catch (err) {
|
|
16445
16896
|
console.error(
|
|
16446
|
-
`${
|
|
16897
|
+
`${TAG35} op=person result=grant-lookup-failed contact=${maskContact(email)} err="${err instanceof Error ? err.message : String(err)}"`
|
|
16447
16898
|
);
|
|
16448
16899
|
}
|
|
16449
16900
|
console.log(
|
|
16450
|
-
`${
|
|
16901
|
+
`${TAG35} op=person personId=${personId} account=${accountId} agentSlug=${agentSlug} contact=${maskContact(email)}`
|
|
16451
16902
|
);
|
|
16452
16903
|
return c.json({ personId, grant }, 200);
|
|
16453
16904
|
});
|
|
@@ -16523,7 +16974,7 @@ app43.route("/enrol-person", enrol_person_default);
|
|
|
16523
16974
|
var admin_default = app43;
|
|
16524
16975
|
|
|
16525
16976
|
// server/routes/access/verify-token.ts
|
|
16526
|
-
var
|
|
16977
|
+
var TAG36 = "[access-verify]";
|
|
16527
16978
|
var MINT_TAG = "[access-session-mint]";
|
|
16528
16979
|
var COOKIE_NAME = "__access_session";
|
|
16529
16980
|
var app44 = new Hono();
|
|
@@ -16542,39 +16993,39 @@ app44.post("/", async (c) => {
|
|
|
16542
16993
|
}
|
|
16543
16994
|
const rateMsg = checkAccessRateLimit(ip, agentSlug);
|
|
16544
16995
|
if (rateMsg) {
|
|
16545
|
-
console.error(`${
|
|
16996
|
+
console.error(`${TAG36} grantId=- agentSlug=${agentSlug} result=rate-limited ip=${ip}`);
|
|
16546
16997
|
return c.json({ error: rateMsg }, 429);
|
|
16547
16998
|
}
|
|
16548
16999
|
const grant = await findGrantByMagicToken(token);
|
|
16549
17000
|
if (!grant) {
|
|
16550
17001
|
recordAccessFailedAttempt(ip, agentSlug);
|
|
16551
|
-
console.error(`${
|
|
17002
|
+
console.error(`${TAG36} grantId=- agentSlug=${agentSlug} result=notfound ip=${ip}`);
|
|
16552
17003
|
return c.json({ error: "invalid-or-expired-link" }, 401);
|
|
16553
17004
|
}
|
|
16554
17005
|
if (grant.agentSlug !== agentSlug) {
|
|
16555
17006
|
recordAccessFailedAttempt(ip, agentSlug);
|
|
16556
17007
|
console.error(
|
|
16557
|
-
`${
|
|
17008
|
+
`${TAG36} grantId=${grant.grantId} agentSlug=${agentSlug} result=agent-mismatch grantAgent=${grant.agentSlug} ip=${ip}`
|
|
16558
17009
|
);
|
|
16559
17010
|
return c.json({ error: "invalid-or-expired-link" }, 401);
|
|
16560
17011
|
}
|
|
16561
17012
|
if (grant.status === "expired" || grant.status === "revoked") {
|
|
16562
17013
|
recordAccessFailedAttempt(ip, agentSlug);
|
|
16563
17014
|
console.error(
|
|
16564
|
-
`${
|
|
17015
|
+
`${TAG36} grantId=${grant.grantId} agentSlug=${agentSlug} result=expired status=${grant.status} ip=${ip}`
|
|
16565
17016
|
);
|
|
16566
17017
|
return c.json({ error: "access-no-longer-valid" }, 401);
|
|
16567
17018
|
}
|
|
16568
17019
|
if (grant.expiresAt !== null && grant.expiresAt < Date.now()) {
|
|
16569
17020
|
recordAccessFailedAttempt(ip, agentSlug);
|
|
16570
17021
|
console.error(
|
|
16571
|
-
`${
|
|
17022
|
+
`${TAG36} grantId=${grant.grantId} agentSlug=${agentSlug} result=expired reason=expiresAt-past ip=${ip}`
|
|
16572
17023
|
);
|
|
16573
17024
|
return c.json({ error: "access-no-longer-valid" }, 401);
|
|
16574
17025
|
}
|
|
16575
17026
|
if (!grant.sliceToken) {
|
|
16576
17027
|
console.error(
|
|
16577
|
-
`${
|
|
17028
|
+
`${TAG36} grantId=${grant.grantId} agentSlug=${agentSlug} result=no-slice-token reason=schema-violation`
|
|
16578
17029
|
);
|
|
16579
17030
|
return c.json({ error: "grant-misconfigured" }, 500);
|
|
16580
17031
|
}
|
|
@@ -16590,12 +17041,12 @@ app44.post("/", async (c) => {
|
|
|
16590
17041
|
await consumeMagicTokenAndActivate(grant.grantId);
|
|
16591
17042
|
} catch (err) {
|
|
16592
17043
|
console.error(
|
|
16593
|
-
`${
|
|
17044
|
+
`${TAG36} grantId=${grant.grantId} agentSlug=${agentSlug} result=consume-failed err="${err instanceof Error ? err.message : String(err)}"`
|
|
16594
17045
|
);
|
|
16595
17046
|
return c.json({ error: "verification-failed" }, 500);
|
|
16596
17047
|
}
|
|
16597
17048
|
clearAccessRateLimit(ip, agentSlug);
|
|
16598
|
-
console.log(`${
|
|
17049
|
+
console.log(`${TAG36} grantId=${grant.grantId} agentSlug=${agentSlug} result=ok ip=${ip}`);
|
|
16599
17050
|
console.log(
|
|
16600
17051
|
`${MINT_TAG} grantId=${grant.grantId} sliceToken=${grant.sliceToken.slice(0, 8)} agentSlug=${agentSlug} personId=${grant.personId ?? "none"}`
|
|
16601
17052
|
);
|
|
@@ -16613,9 +17064,9 @@ var verify_token_default = app44;
|
|
|
16613
17064
|
|
|
16614
17065
|
// app/lib/access-email.ts
|
|
16615
17066
|
import { spawn as spawn2 } from "child_process";
|
|
16616
|
-
import { resolve as
|
|
16617
|
-
var PLATFORM_ROOT7 = process.env.MAXY_PLATFORM_ROOT ??
|
|
16618
|
-
var SEND_SCRIPT =
|
|
17067
|
+
import { resolve as resolve27 } from "path";
|
|
17068
|
+
var PLATFORM_ROOT7 = process.env.MAXY_PLATFORM_ROOT ?? resolve27(process.cwd(), "..");
|
|
17069
|
+
var SEND_SCRIPT = resolve27(
|
|
16619
17070
|
PLATFORM_ROOT7,
|
|
16620
17071
|
"plugins",
|
|
16621
17072
|
"email",
|
|
@@ -16671,7 +17122,7 @@ async function sendMagicLinkEmail(payload) {
|
|
|
16671
17122
|
}
|
|
16672
17123
|
|
|
16673
17124
|
// server/routes/access/request-magic-link.ts
|
|
16674
|
-
var
|
|
17125
|
+
var TAG37 = "[access-request-link]";
|
|
16675
17126
|
var app45 = new Hono();
|
|
16676
17127
|
var VISITOR_MESSAGE = "If that email is on the invite list, a fresh link is on the way.";
|
|
16677
17128
|
app45.post("/", async (c) => {
|
|
@@ -16688,18 +17139,18 @@ app45.post("/", async (c) => {
|
|
|
16688
17139
|
}
|
|
16689
17140
|
const rateMsg = checkRequestLinkRateLimit(contactValue);
|
|
16690
17141
|
if (rateMsg) {
|
|
16691
|
-
console.error(`${
|
|
17142
|
+
console.error(`${TAG37} contactValue=${maskContact(contactValue)} result=rate-limited`);
|
|
16692
17143
|
return c.json({ error: rateMsg }, 429);
|
|
16693
17144
|
}
|
|
16694
17145
|
recordRequestLinkAttempt(contactValue);
|
|
16695
17146
|
const accountId = process.env.ACCOUNT_ID ?? "";
|
|
16696
17147
|
if (!accountId) {
|
|
16697
|
-
console.error(`${
|
|
17148
|
+
console.error(`${TAG37} contactValue=${maskContact(contactValue)} result=no-account-id`);
|
|
16698
17149
|
return c.json({ message: VISITOR_MESSAGE }, 200);
|
|
16699
17150
|
}
|
|
16700
17151
|
const grant = await findActiveGrantByContact(contactValue, agentSlug, accountId);
|
|
16701
17152
|
if (!grant) {
|
|
16702
|
-
console.log(`${
|
|
17153
|
+
console.log(`${TAG37} contactValue=${maskContact(contactValue)} result=notfound`);
|
|
16703
17154
|
return c.json({ message: VISITOR_MESSAGE }, 200);
|
|
16704
17155
|
}
|
|
16705
17156
|
let token;
|
|
@@ -16707,7 +17158,7 @@ app45.post("/", async (c) => {
|
|
|
16707
17158
|
token = await generateNewMagicToken(grant.grantId);
|
|
16708
17159
|
} catch (err) {
|
|
16709
17160
|
console.error(
|
|
16710
|
-
`${
|
|
17161
|
+
`${TAG37} contactValue=${maskContact(contactValue)} result=mint-failed err="${err instanceof Error ? err.message : String(err)}"`
|
|
16711
17162
|
);
|
|
16712
17163
|
return c.json({ message: VISITOR_MESSAGE }, 200);
|
|
16713
17164
|
}
|
|
@@ -16739,12 +17190,12 @@ app45.post("/", async (c) => {
|
|
|
16739
17190
|
});
|
|
16740
17191
|
if (!sendResult.ok) {
|
|
16741
17192
|
console.error(
|
|
16742
|
-
`${
|
|
17193
|
+
`${TAG37} contactValue=${maskContact(contactValue)} result=send-failed err="${sendResult.error}"`
|
|
16743
17194
|
);
|
|
16744
17195
|
return c.json({ message: VISITOR_MESSAGE }, 200);
|
|
16745
17196
|
}
|
|
16746
17197
|
console.log(
|
|
16747
|
-
`${
|
|
17198
|
+
`${TAG37} contactValue=${maskContact(contactValue)} result=ok messageId=${sendResult.messageId}`
|
|
16748
17199
|
);
|
|
16749
17200
|
return c.json({ message: VISITOR_MESSAGE }, 200);
|
|
16750
17201
|
});
|
|
@@ -16757,8 +17208,8 @@ app46.route("/request-magic-link", request_magic_link_default);
|
|
|
16757
17208
|
var access_default = app46;
|
|
16758
17209
|
|
|
16759
17210
|
// server/routes/sites.ts
|
|
16760
|
-
import { existsSync as existsSync26, readFileSync as
|
|
16761
|
-
import { resolve as
|
|
17211
|
+
import { existsSync as existsSync26, readFileSync as readFileSync27, realpathSync as realpathSync7, statSync as statSync11 } from "fs";
|
|
17212
|
+
import { resolve as resolve28 } from "path";
|
|
16762
17213
|
var SAFE_SEG_RE = /^[a-z0-9_][a-z0-9_.-]{0,99}$/i;
|
|
16763
17214
|
var MIME = {
|
|
16764
17215
|
".html": "text/html; charset=utf-8",
|
|
@@ -16815,19 +17266,19 @@ app47.get("/:rel{.*}", (c) => {
|
|
|
16815
17266
|
}
|
|
16816
17267
|
segments.push(seg);
|
|
16817
17268
|
}
|
|
16818
|
-
const rootDir =
|
|
16819
|
-
let filePath = segments.length === 0 ? rootDir :
|
|
17269
|
+
const rootDir = resolve28(account.accountDir, "sites");
|
|
17270
|
+
let filePath = segments.length === 0 ? rootDir : resolve28(rootDir, ...segments);
|
|
16820
17271
|
if (filePath !== rootDir && !filePath.startsWith(rootDir + "/")) {
|
|
16821
17272
|
console.error(`[sites] path-traversal-rejected path=${reqPath} reason=escape status=403`);
|
|
16822
17273
|
return c.text("Forbidden", 403);
|
|
16823
17274
|
}
|
|
16824
|
-
let
|
|
17275
|
+
let stat8;
|
|
16825
17276
|
try {
|
|
16826
|
-
|
|
17277
|
+
stat8 = existsSync26(filePath) ? statSync11(filePath) : null;
|
|
16827
17278
|
} catch {
|
|
16828
|
-
|
|
17279
|
+
stat8 = null;
|
|
16829
17280
|
}
|
|
16830
|
-
if (
|
|
17281
|
+
if (stat8?.isDirectory() && !reqPath.endsWith("/")) {
|
|
16831
17282
|
const search = new URL(c.req.url).search;
|
|
16832
17283
|
const target = `${reqPath}/${search}`;
|
|
16833
17284
|
console.log(
|
|
@@ -16835,8 +17286,8 @@ app47.get("/:rel{.*}", (c) => {
|
|
|
16835
17286
|
);
|
|
16836
17287
|
return c.redirect(target, 301);
|
|
16837
17288
|
}
|
|
16838
|
-
if (
|
|
16839
|
-
filePath =
|
|
17289
|
+
if (stat8?.isDirectory()) {
|
|
17290
|
+
filePath = resolve28(filePath, "index.html");
|
|
16840
17291
|
}
|
|
16841
17292
|
if (!filePath.startsWith(rootDir + "/")) {
|
|
16842
17293
|
console.error(`[sites] path-traversal-rejected path=${reqPath} reason=escape status=403`);
|
|
@@ -16849,8 +17300,8 @@ app47.get("/:rel{.*}", (c) => {
|
|
|
16849
17300
|
let realPath;
|
|
16850
17301
|
let realRoot;
|
|
16851
17302
|
try {
|
|
16852
|
-
realPath =
|
|
16853
|
-
realRoot =
|
|
17303
|
+
realPath = realpathSync7(filePath);
|
|
17304
|
+
realRoot = realpathSync7(rootDir);
|
|
16854
17305
|
} catch {
|
|
16855
17306
|
console.error(`[sites] not-found path=${reqPath} status=404`);
|
|
16856
17307
|
return c.text("Not found", 404);
|
|
@@ -16861,7 +17312,7 @@ app47.get("/:rel{.*}", (c) => {
|
|
|
16861
17312
|
}
|
|
16862
17313
|
let body;
|
|
16863
17314
|
try {
|
|
16864
|
-
body =
|
|
17315
|
+
body = readFileSync27(realPath);
|
|
16865
17316
|
} catch (err) {
|
|
16866
17317
|
const code = err?.code;
|
|
16867
17318
|
if (code === "EISDIR") {
|
|
@@ -16897,7 +17348,7 @@ var sites_default = app47;
|
|
|
16897
17348
|
|
|
16898
17349
|
// app/lib/visitor-token.ts
|
|
16899
17350
|
import { createHmac, randomBytes, timingSafeEqual } from "crypto";
|
|
16900
|
-
import { mkdirSync as mkdirSync6, readFileSync as
|
|
17351
|
+
import { mkdirSync as mkdirSync6, readFileSync as readFileSync28, writeFileSync as writeFileSync11 } from "fs";
|
|
16901
17352
|
import { dirname as dirname7 } from "path";
|
|
16902
17353
|
var TOKEN_PREFIX = "v1.";
|
|
16903
17354
|
var SECRET_BYTES = 32;
|
|
@@ -16906,7 +17357,7 @@ var cachedSecret = null;
|
|
|
16906
17357
|
function getSecret() {
|
|
16907
17358
|
if (cachedSecret) return cachedSecret;
|
|
16908
17359
|
try {
|
|
16909
|
-
const hex2 =
|
|
17360
|
+
const hex2 = readFileSync28(VISITOR_TOKEN_SECRET_FILE, "utf-8").trim();
|
|
16910
17361
|
if (hex2.length === SECRET_BYTES * 2) {
|
|
16911
17362
|
cachedSecret = Buffer.from(hex2, "hex");
|
|
16912
17363
|
return cachedSecret;
|
|
@@ -16920,7 +17371,7 @@ function getSecret() {
|
|
|
16920
17371
|
console.log(`[visitor-token] secret minted path=${VISITOR_TOKEN_SECRET_FILE}`);
|
|
16921
17372
|
} catch {
|
|
16922
17373
|
}
|
|
16923
|
-
const hex =
|
|
17374
|
+
const hex = readFileSync28(VISITOR_TOKEN_SECRET_FILE, "utf-8").trim();
|
|
16924
17375
|
cachedSecret = Buffer.from(hex, "hex");
|
|
16925
17376
|
return cachedSecret;
|
|
16926
17377
|
}
|
|
@@ -16973,19 +17424,19 @@ var VISITOR_COOKIE_NAME = "mxy_v";
|
|
|
16973
17424
|
var VISITOR_COOKIE_MAX_AGE_SECONDS = Math.floor(DEFAULT_TTL_MS / 1e3);
|
|
16974
17425
|
|
|
16975
17426
|
// app/lib/brand-config.ts
|
|
16976
|
-
import { existsSync as existsSync27, readFileSync as
|
|
16977
|
-
import { join as
|
|
17427
|
+
import { existsSync as existsSync27, readFileSync as readFileSync29 } from "fs";
|
|
17428
|
+
import { join as join26 } from "path";
|
|
16978
17429
|
var cached2 = null;
|
|
16979
17430
|
var cachedAttempted = false;
|
|
16980
17431
|
function readBrandConfig() {
|
|
16981
17432
|
if (cachedAttempted) return cached2;
|
|
16982
17433
|
cachedAttempted = true;
|
|
16983
|
-
const
|
|
16984
|
-
if (!
|
|
16985
|
-
const brandPath =
|
|
17434
|
+
const platformRoot4 = process.env.MAXY_PLATFORM_ROOT;
|
|
17435
|
+
if (!platformRoot4) return null;
|
|
17436
|
+
const brandPath = join26(platformRoot4, "config", "brand.json");
|
|
16986
17437
|
if (!existsSync27(brandPath)) return null;
|
|
16987
17438
|
try {
|
|
16988
|
-
cached2 = JSON.parse(
|
|
17439
|
+
cached2 = JSON.parse(readFileSync29(brandPath, "utf-8"));
|
|
16989
17440
|
return cached2;
|
|
16990
17441
|
} catch {
|
|
16991
17442
|
return null;
|
|
@@ -17473,19 +17924,19 @@ async function writeEvent(opts) {
|
|
|
17473
17924
|
var visitor_event_default = app50;
|
|
17474
17925
|
|
|
17475
17926
|
// server/routes/session.ts
|
|
17476
|
-
import { resolve as
|
|
17927
|
+
import { resolve as resolve29 } from "path";
|
|
17477
17928
|
import { existsSync as existsSync28, writeFileSync as writeFileSync12, mkdirSync as mkdirSync7 } from "fs";
|
|
17478
17929
|
var UUID_RE4 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
17479
17930
|
function writeBrandingCache(accountId, agentSlug, branding) {
|
|
17480
17931
|
try {
|
|
17481
|
-
const cacheDir =
|
|
17932
|
+
const cacheDir = resolve29(MAXY_DIR, "branding-cache", accountId);
|
|
17482
17933
|
mkdirSync7(cacheDir, { recursive: true });
|
|
17483
|
-
writeFileSync12(
|
|
17934
|
+
writeFileSync12(resolve29(cacheDir, `${agentSlug}.json`), JSON.stringify(branding), "utf-8");
|
|
17484
17935
|
} catch (err) {
|
|
17485
17936
|
console.error(`[branding] cache write failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
17486
17937
|
}
|
|
17487
17938
|
}
|
|
17488
|
-
function
|
|
17939
|
+
function parseVisitorCookie2(cookieHeader) {
|
|
17489
17940
|
if (!cookieHeader) return null;
|
|
17490
17941
|
const match = cookieHeader.match(/(?:^|;\s*)maxy_visitor=([^;]*)/);
|
|
17491
17942
|
if (!match) return null;
|
|
@@ -17534,7 +17985,7 @@ app51.post("/", async (c) => {
|
|
|
17534
17985
|
}
|
|
17535
17986
|
const account = resolveAccount();
|
|
17536
17987
|
const cookieHeader = c.req.header("cookie");
|
|
17537
|
-
const visitorId =
|
|
17988
|
+
const visitorId = parseVisitorCookie2(cookieHeader);
|
|
17538
17989
|
const groupSlug = body.group_slug && typeof body.group_slug === "string" ? body.group_slug : null;
|
|
17539
17990
|
let groupInfo = null;
|
|
17540
17991
|
if (groupSlug) {
|
|
@@ -17568,8 +18019,8 @@ app51.post("/", async (c) => {
|
|
|
17568
18019
|
}
|
|
17569
18020
|
let agentConfig = null;
|
|
17570
18021
|
if (account) {
|
|
17571
|
-
const agentDir =
|
|
17572
|
-
if (!existsSync28(agentDir) || !existsSync28(
|
|
18022
|
+
const agentDir = resolve29(account.accountDir, "agents", agentSlug);
|
|
18023
|
+
if (!existsSync28(agentDir) || !existsSync28(resolve29(agentDir, "config.json"))) {
|
|
17573
18024
|
return c.json({ error: "Agent not found" }, 404);
|
|
17574
18025
|
}
|
|
17575
18026
|
agentConfig = resolveAgentConfig(account.accountDir, agentSlug);
|
|
@@ -17849,7 +18300,7 @@ function startGraphHealthTimer() {
|
|
|
17849
18300
|
|
|
17850
18301
|
// app/lib/file-watcher.ts
|
|
17851
18302
|
import * as fsp2 from "fs/promises";
|
|
17852
|
-
import { resolve as
|
|
18303
|
+
import { resolve as resolve30, sep as sep8 } from "path";
|
|
17853
18304
|
var ACCOUNT_UUID_RE2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
17854
18305
|
var DEFAULT_COALESCE_MS = 500;
|
|
17855
18306
|
var ROOTS = ["accounts"];
|
|
@@ -17868,7 +18319,7 @@ async function startFileWatcher(opts = {}) {
|
|
|
17868
18319
|
const dropFn = opts.drop ?? dropFileIndex;
|
|
17869
18320
|
for (const r of ROOTS) {
|
|
17870
18321
|
try {
|
|
17871
|
-
await fsp2.mkdir(
|
|
18322
|
+
await fsp2.mkdir(resolve30(dataRoot, r), { recursive: true });
|
|
17872
18323
|
} catch (err) {
|
|
17873
18324
|
console.error(
|
|
17874
18325
|
`[file-watcher] start-failed root="${r}" err="${err.message}" \u2014 index will be maintained by the 5-min reconcile backstop only`
|
|
@@ -17889,7 +18340,7 @@ async function startFileWatcher(opts = {}) {
|
|
|
17889
18340
|
timers.set(relativePath, t);
|
|
17890
18341
|
}
|
|
17891
18342
|
async function runHook(relativePath, accountId) {
|
|
17892
|
-
const absolute =
|
|
18343
|
+
const absolute = resolve30(dataRoot, relativePath);
|
|
17893
18344
|
let exists = false;
|
|
17894
18345
|
try {
|
|
17895
18346
|
const st = await fsp2.stat(absolute);
|
|
@@ -17913,7 +18364,7 @@ async function startFileWatcher(opts = {}) {
|
|
|
17913
18364
|
}
|
|
17914
18365
|
}
|
|
17915
18366
|
async function watchRoot(rootName) {
|
|
17916
|
-
const absRoot =
|
|
18367
|
+
const absRoot = resolve30(dataRoot, rootName);
|
|
17917
18368
|
try {
|
|
17918
18369
|
const iter = fsp2.watch(absRoot, { recursive: true, signal: controller.signal });
|
|
17919
18370
|
for await (const event of iter) {
|
|
@@ -17952,7 +18403,7 @@ async function startFileWatcher(opts = {}) {
|
|
|
17952
18403
|
|
|
17953
18404
|
// app/lib/migrate-uploads.ts
|
|
17954
18405
|
import { mkdir as mkdir5, readdir as readdir5, rename as rename2, rm as rm3 } from "fs/promises";
|
|
17955
|
-
import { dirname as dirname8, relative as
|
|
18406
|
+
import { dirname as dirname8, relative as relative5, resolve as resolve31 } from "path";
|
|
17956
18407
|
var ACCOUNT_UUID_RE3 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
17957
18408
|
async function walkFiles(dir) {
|
|
17958
18409
|
const out = [];
|
|
@@ -17963,7 +18414,7 @@ async function walkFiles(dir) {
|
|
|
17963
18414
|
return out;
|
|
17964
18415
|
}
|
|
17965
18416
|
for (const e of entries) {
|
|
17966
|
-
const abs =
|
|
18417
|
+
const abs = resolve31(dir, e.name);
|
|
17967
18418
|
if (e.isDirectory()) {
|
|
17968
18419
|
out.push(...await walkFiles(abs));
|
|
17969
18420
|
} else if (e.isFile()) {
|
|
@@ -17982,13 +18433,13 @@ async function relocateTree(srcDir, destDir, dataRoot, accountId, session) {
|
|
|
17982
18433
|
const files = await walkFiles(srcDir);
|
|
17983
18434
|
let moved = 0;
|
|
17984
18435
|
for (const oldAbs of files) {
|
|
17985
|
-
const suffix =
|
|
17986
|
-
const newAbs =
|
|
18436
|
+
const suffix = relative5(srcDir, oldAbs);
|
|
18437
|
+
const newAbs = resolve31(destDir, suffix);
|
|
17987
18438
|
await mkdir5(dirname8(newAbs), { recursive: true });
|
|
17988
18439
|
await rename2(oldAbs, newAbs);
|
|
17989
18440
|
moved++;
|
|
17990
|
-
const oldRel =
|
|
17991
|
-
const newRel =
|
|
18441
|
+
const oldRel = relative5(dataRoot, oldAbs);
|
|
18442
|
+
const newRel = relative5(dataRoot, newAbs);
|
|
17992
18443
|
let linked = false;
|
|
17993
18444
|
if (accountId && session) {
|
|
17994
18445
|
const fa = await session.run(
|
|
@@ -18010,7 +18461,7 @@ async function relocateTree(srcDir, destDir, dataRoot, accountId, session) {
|
|
|
18010
18461
|
}
|
|
18011
18462
|
async function migrateUploads(opts = {}) {
|
|
18012
18463
|
const dataRoot = opts.dataRoot ?? DATA_ROOT;
|
|
18013
|
-
const oldRoot =
|
|
18464
|
+
const oldRoot = resolve31(dataRoot, "uploads");
|
|
18014
18465
|
let topEntries;
|
|
18015
18466
|
try {
|
|
18016
18467
|
topEntries = await readdir5(oldRoot, { withFileTypes: true });
|
|
@@ -18031,8 +18482,8 @@ async function migrateUploads(opts = {}) {
|
|
|
18031
18482
|
const name = entry.name;
|
|
18032
18483
|
if (ACCOUNT_UUID_RE3.test(name)) {
|
|
18033
18484
|
moved += await relocateTree(
|
|
18034
|
-
|
|
18035
|
-
|
|
18485
|
+
resolve31(oldRoot, name),
|
|
18486
|
+
resolve31(dataRoot, "accounts", name, "uploads"),
|
|
18036
18487
|
dataRoot,
|
|
18037
18488
|
name,
|
|
18038
18489
|
session
|
|
@@ -18044,8 +18495,8 @@ async function migrateUploads(opts = {}) {
|
|
|
18044
18495
|
continue;
|
|
18045
18496
|
}
|
|
18046
18497
|
moved += await relocateTree(
|
|
18047
|
-
|
|
18048
|
-
|
|
18498
|
+
resolve31(oldRoot, "public"),
|
|
18499
|
+
resolve31(dataRoot, "accounts", installAccountId, "uploads", "public"),
|
|
18049
18500
|
dataRoot,
|
|
18050
18501
|
null,
|
|
18051
18502
|
// public uploads carry no graph nodes
|
|
@@ -18074,9 +18525,9 @@ async function migrateUploads(opts = {}) {
|
|
|
18074
18525
|
}
|
|
18075
18526
|
|
|
18076
18527
|
// app/lib/migrate-admin-webchat-sidecars.ts
|
|
18077
|
-
import { readdir as readdir6, readFile as
|
|
18528
|
+
import { readdir as readdir6, readFile as readFile7, rename as rename3, writeFile as writeFile4, unlink as unlink3 } from "fs/promises";
|
|
18078
18529
|
import { existsSync as existsSync29 } from "fs";
|
|
18079
|
-
import { join as
|
|
18530
|
+
import { join as join27 } from "path";
|
|
18080
18531
|
var ADMIN_ROLE2 = "admin";
|
|
18081
18532
|
var WEBCHAT_CHANNEL2 = "webchat";
|
|
18082
18533
|
var SESSION_META_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\.meta\.json$/i;
|
|
@@ -18087,7 +18538,7 @@ var defaultResolveName = async (accountId, userId) => {
|
|
|
18087
18538
|
async function readRaw(path2) {
|
|
18088
18539
|
let raw;
|
|
18089
18540
|
try {
|
|
18090
|
-
raw = await
|
|
18541
|
+
raw = await readFile7(path2, "utf8");
|
|
18091
18542
|
} catch {
|
|
18092
18543
|
return null;
|
|
18093
18544
|
}
|
|
@@ -18121,7 +18572,7 @@ async function collectLiveSidecars(projectsRoot) {
|
|
|
18121
18572
|
}
|
|
18122
18573
|
for (const slug of slugs) {
|
|
18123
18574
|
if (!slug.isDirectory()) continue;
|
|
18124
|
-
const slugDir =
|
|
18575
|
+
const slugDir = join27(projectsRoot, slug.name);
|
|
18125
18576
|
let entries;
|
|
18126
18577
|
try {
|
|
18127
18578
|
entries = await readdir6(slugDir, { withFileTypes: true });
|
|
@@ -18130,9 +18581,9 @@ async function collectLiveSidecars(projectsRoot) {
|
|
|
18130
18581
|
}
|
|
18131
18582
|
for (const entry of entries) {
|
|
18132
18583
|
if (entry.isFile() && SESSION_META_RE.test(entry.name)) {
|
|
18133
|
-
out.push(
|
|
18584
|
+
out.push(join27(slugDir, entry.name));
|
|
18134
18585
|
} else if (entry.isDirectory() && entry.name === "subagents") {
|
|
18135
|
-
const subDir =
|
|
18586
|
+
const subDir = join27(slugDir, entry.name);
|
|
18136
18587
|
let subs;
|
|
18137
18588
|
try {
|
|
18138
18589
|
subs = await readdir6(subDir, { withFileTypes: true });
|
|
@@ -18140,7 +18591,7 @@ async function collectLiveSidecars(projectsRoot) {
|
|
|
18140
18591
|
continue;
|
|
18141
18592
|
}
|
|
18142
18593
|
for (const s of subs) {
|
|
18143
|
-
if (s.isFile() && SESSION_META_RE.test(s.name)) out.push(
|
|
18594
|
+
if (s.isFile() && SESSION_META_RE.test(s.name)) out.push(join27(subDir, s.name));
|
|
18144
18595
|
}
|
|
18145
18596
|
}
|
|
18146
18597
|
}
|
|
@@ -18152,7 +18603,7 @@ function shortId(path2) {
|
|
|
18152
18603
|
return base.slice(0, 8);
|
|
18153
18604
|
}
|
|
18154
18605
|
async function migrateAdminWebchatSidecars(opts = {}) {
|
|
18155
|
-
const projectsRoot = opts.projectsRoot ?? (process.env.CLAUDE_CONFIG_DIR ?
|
|
18606
|
+
const projectsRoot = opts.projectsRoot ?? (process.env.CLAUDE_CONFIG_DIR ? join27(process.env.CLAUDE_CONFIG_DIR, "projects") : null) ?? "";
|
|
18156
18607
|
const usersFile = opts.usersFile ?? USERS_FILE;
|
|
18157
18608
|
const accountId = opts.accountId ?? resolveAccount()?.accountId ?? null;
|
|
18158
18609
|
const resolveName = opts.resolveName ?? defaultResolveName;
|
|
@@ -18959,16 +19410,16 @@ var WebchatGateway = class {
|
|
|
18959
19410
|
* The public /api/chat route awaits this to return the reply on the same SSE
|
|
18960
19411
|
* response, preserving the pre-756 POST→single-blob browser contract. */
|
|
18961
19412
|
awaitReply(key, timeoutMs) {
|
|
18962
|
-
return new Promise((
|
|
19413
|
+
return new Promise((resolve34) => {
|
|
18963
19414
|
const timer2 = setTimeout(() => {
|
|
18964
19415
|
this.replyAwaiters.delete(key);
|
|
18965
|
-
|
|
19416
|
+
resolve34({ timeout: true });
|
|
18966
19417
|
}, timeoutMs);
|
|
18967
19418
|
if (timer2.unref) timer2.unref();
|
|
18968
19419
|
this.replyAwaiters.set(key, (r) => {
|
|
18969
19420
|
clearTimeout(timer2);
|
|
18970
19421
|
this.replyAwaiters.delete(key);
|
|
18971
|
-
|
|
19422
|
+
resolve34(r);
|
|
18972
19423
|
});
|
|
18973
19424
|
});
|
|
18974
19425
|
}
|
|
@@ -19104,7 +19555,7 @@ var WebchatGateway = class {
|
|
|
19104
19555
|
|
|
19105
19556
|
// app/lib/webchat/gateway/public-spawn-request.ts
|
|
19106
19557
|
function buildPublicWebchatSpawnRequest(input) {
|
|
19107
|
-
const { agentSlug, sliceToken, personId, name } = input.public;
|
|
19558
|
+
const { agentSlug, sliceToken, personId, visitorId, name } = input.public;
|
|
19108
19559
|
return {
|
|
19109
19560
|
senderId: input.key,
|
|
19110
19561
|
role: "public",
|
|
@@ -19115,6 +19566,7 @@ function buildPublicWebchatSpawnRequest(input) {
|
|
|
19115
19566
|
// public agents carry neither (mirrors the bridge's open-mode key).
|
|
19116
19567
|
...sliceToken ? { sliceToken } : {},
|
|
19117
19568
|
...personId ? { personId } : {},
|
|
19569
|
+
...visitorId ? { visitorId } : {},
|
|
19118
19570
|
name,
|
|
19119
19571
|
webchatChannel: { key: input.key, gatewayUrl: input.gatewayUrl, serverPath: input.serverPath }
|
|
19120
19572
|
};
|
|
@@ -19138,7 +19590,9 @@ function startNativeFileFollower(input) {
|
|
|
19138
19590
|
followerRunning: false,
|
|
19139
19591
|
sliceToken: "",
|
|
19140
19592
|
personId: null,
|
|
19141
|
-
name: null
|
|
19593
|
+
name: null,
|
|
19594
|
+
// WhatsApp addresses the document to senderId; no separate reply target.
|
|
19595
|
+
replyTarget: null
|
|
19142
19596
|
};
|
|
19143
19597
|
const sid = input.senderId.slice(0, 8);
|
|
19144
19598
|
let lastComposingAt = Number.NEGATIVE_INFINITY;
|
|
@@ -19237,8 +19691,8 @@ function broadcastAdminShutdown(reason) {
|
|
|
19237
19691
|
|
|
19238
19692
|
// ../lib/entitlement/src/index.ts
|
|
19239
19693
|
import { createPublicKey, createHash as createHash5, verify as cryptoVerify } from "crypto";
|
|
19240
|
-
import { existsSync as existsSync30, readFileSync as
|
|
19241
|
-
import { resolve as
|
|
19694
|
+
import { existsSync as existsSync30, readFileSync as readFileSync30, statSync as statSync12 } from "fs";
|
|
19695
|
+
import { resolve as resolve32 } from "path";
|
|
19242
19696
|
|
|
19243
19697
|
// ../lib/entitlement/src/canonicalize.ts
|
|
19244
19698
|
function canonicalize(value) {
|
|
@@ -19273,7 +19727,7 @@ var PUBKEY_SHA256 = "8eee6bcb33545fd13b16d3199a5735ca5db5062834c7b49dfe4f23801d9
|
|
|
19273
19727
|
var GRACE_DAYS = 7;
|
|
19274
19728
|
var GRACE_MS = GRACE_DAYS * 24 * 60 * 60 * 1e3;
|
|
19275
19729
|
function pubkeyPath(brand) {
|
|
19276
|
-
return
|
|
19730
|
+
return resolve32(brand.platformRoot, "lib", "entitlement", "rubytech-pubkey.pem");
|
|
19277
19731
|
}
|
|
19278
19732
|
var memo = null;
|
|
19279
19733
|
function memoKey(mtimeMs, account) {
|
|
@@ -19285,12 +19739,12 @@ function resolveEntitlement(brand, account) {
|
|
|
19285
19739
|
if (brand.commercialMode !== true) {
|
|
19286
19740
|
return logResolved(implicitTrust(account), null);
|
|
19287
19741
|
}
|
|
19288
|
-
const entitlementPath =
|
|
19742
|
+
const entitlementPath = resolve32(brand.configDir, "entitlement.json");
|
|
19289
19743
|
if (!existsSync30(entitlementPath)) {
|
|
19290
19744
|
return logResolved(anonymousFallback("missing"), { reason: "missing" });
|
|
19291
19745
|
}
|
|
19292
|
-
const
|
|
19293
|
-
const key = memoKey(
|
|
19746
|
+
const stat8 = statSync12(entitlementPath);
|
|
19747
|
+
const key = memoKey(stat8.mtimeMs, account);
|
|
19294
19748
|
if (memo && memo.key === key) {
|
|
19295
19749
|
return memo.result;
|
|
19296
19750
|
}
|
|
@@ -19301,7 +19755,7 @@ function resolveEntitlement(brand, account) {
|
|
|
19301
19755
|
function verifyAndResolve(brand, entitlementPath, account) {
|
|
19302
19756
|
let pubkeyPem;
|
|
19303
19757
|
try {
|
|
19304
|
-
pubkeyPem =
|
|
19758
|
+
pubkeyPem = readFileSync30(pubkeyPath(brand), "utf-8");
|
|
19305
19759
|
} catch (err) {
|
|
19306
19760
|
return logResolved(anonymousFallback("pubkey-missing"), {
|
|
19307
19761
|
reason: "pubkey-missing"
|
|
@@ -19315,7 +19769,7 @@ function verifyAndResolve(brand, entitlementPath, account) {
|
|
|
19315
19769
|
}
|
|
19316
19770
|
let envelope;
|
|
19317
19771
|
try {
|
|
19318
|
-
envelope = JSON.parse(
|
|
19772
|
+
envelope = JSON.parse(readFileSync30(entitlementPath, "utf-8"));
|
|
19319
19773
|
} catch {
|
|
19320
19774
|
return logResolved(anonymousFallback("malformed"), { reason: "malformed" });
|
|
19321
19775
|
}
|
|
@@ -19476,14 +19930,14 @@ function clientFrom(c) {
|
|
|
19476
19930
|
);
|
|
19477
19931
|
}
|
|
19478
19932
|
var PLATFORM_ROOT8 = process.env.MAXY_PLATFORM_ROOT || "";
|
|
19479
|
-
var BRAND_JSON_PATH = PLATFORM_ROOT8 ?
|
|
19933
|
+
var BRAND_JSON_PATH = PLATFORM_ROOT8 ? join28(PLATFORM_ROOT8, "config", "brand.json") : "";
|
|
19480
19934
|
var BRAND = { productName: "Maxy", hostname: "maxy", configDir: ".maxy", marketingUrl: "https://getmaxy.com" };
|
|
19481
19935
|
if (BRAND_JSON_PATH && !existsSync31(BRAND_JSON_PATH)) {
|
|
19482
19936
|
console.error(`[brand] WARNING: brand.json not found at ${BRAND_JSON_PATH} \u2014 using Maxy defaults`);
|
|
19483
19937
|
}
|
|
19484
19938
|
if (BRAND_JSON_PATH && existsSync31(BRAND_JSON_PATH)) {
|
|
19485
19939
|
try {
|
|
19486
|
-
const parsed = JSON.parse(
|
|
19940
|
+
const parsed = JSON.parse(readFileSync31(BRAND_JSON_PATH, "utf-8"));
|
|
19487
19941
|
BRAND = { ...BRAND, ...parsed };
|
|
19488
19942
|
} catch (err) {
|
|
19489
19943
|
console.error(`[brand] Failed to parse brand.json: ${err.message}`);
|
|
@@ -19502,11 +19956,11 @@ var brandLoginOpts = {
|
|
|
19502
19956
|
bodyFont: BRAND.defaultFonts?.body,
|
|
19503
19957
|
logoContainsName: !!BRAND.logoContainsName
|
|
19504
19958
|
};
|
|
19505
|
-
var ALIAS_DOMAINS_PATH =
|
|
19959
|
+
var ALIAS_DOMAINS_PATH = join28(homedir3(), BRAND.configDir, "alias-domains.json");
|
|
19506
19960
|
function loadAliasDomains() {
|
|
19507
19961
|
try {
|
|
19508
19962
|
if (!existsSync31(ALIAS_DOMAINS_PATH)) return null;
|
|
19509
|
-
const parsed = JSON.parse(
|
|
19963
|
+
const parsed = JSON.parse(readFileSync31(ALIAS_DOMAINS_PATH, "utf-8"));
|
|
19510
19964
|
if (!Array.isArray(parsed)) {
|
|
19511
19965
|
console.error("[alias-domains] malformed alias-domains.json \u2014 expected array");
|
|
19512
19966
|
return null;
|
|
@@ -19534,7 +19988,7 @@ var app52 = new Hono();
|
|
|
19534
19988
|
var nativeFileFollowers = /* @__PURE__ */ new Map();
|
|
19535
19989
|
var waGateway = new WaGateway({
|
|
19536
19990
|
gatewayUrl: `http://127.0.0.1:${process.env.MAXY_UI_INTERNAL_PORT ?? ""}`,
|
|
19537
|
-
serverPath: process.env.MAXY_WA_CHANNEL_SERVER_PATH ??
|
|
19991
|
+
serverPath: process.env.MAXY_WA_CHANNEL_SERVER_PATH ?? resolve33(process.env.MAXY_PLATFORM_ROOT ?? join28(__dirname, ".."), "services/whatsapp-channel/dist/server.js"),
|
|
19538
19992
|
// Task 751 — file delivery on the native channel: resolve the platform
|
|
19539
19993
|
// account + path validation here and funnel through the shared send core.
|
|
19540
19994
|
sendDocument: async ({ senderId, accountId, filePath, caption }) => {
|
|
@@ -19550,7 +20004,7 @@ var waGateway = new WaGateway({
|
|
|
19550
20004
|
caption,
|
|
19551
20005
|
accountId,
|
|
19552
20006
|
maxyAccountId,
|
|
19553
|
-
platformRoot:
|
|
20007
|
+
platformRoot: resolve33(process.env.MAXY_PLATFORM_ROOT ?? join28(__dirname, ".."))
|
|
19554
20008
|
});
|
|
19555
20009
|
return result.ok ? { ok: true, messageId: result.messageId } : { ok: false, error: result.error };
|
|
19556
20010
|
},
|
|
@@ -19973,8 +20427,8 @@ app52.get("/agent-assets/:slug/:filename", (c) => {
|
|
|
19973
20427
|
console.error(`[agent-assets] no-account slug=${slug} file=${filename}`);
|
|
19974
20428
|
return c.text("Not found", 404);
|
|
19975
20429
|
}
|
|
19976
|
-
const filePath =
|
|
19977
|
-
const expectedDir =
|
|
20430
|
+
const filePath = resolve33(account.accountDir, "agents", slug, "assets", filename);
|
|
20431
|
+
const expectedDir = resolve33(account.accountDir, "agents", slug, "assets");
|
|
19978
20432
|
if (!filePath.startsWith(expectedDir + "/")) {
|
|
19979
20433
|
console.error(`[agent-assets] path-traversal-rejected slug=${slug} file=${filename}`);
|
|
19980
20434
|
return c.text("Forbidden", 403);
|
|
@@ -19986,7 +20440,7 @@ app52.get("/agent-assets/:slug/:filename", (c) => {
|
|
|
19986
20440
|
const ext = "." + filename.split(".").pop()?.toLowerCase();
|
|
19987
20441
|
const contentType = IMAGE_MIME[ext] || "application/octet-stream";
|
|
19988
20442
|
console.log(`[agent-assets] serve slug=${slug} file=${filename} status=200`);
|
|
19989
|
-
const body =
|
|
20443
|
+
const body = readFileSync31(filePath);
|
|
19990
20444
|
return c.body(body, 200, {
|
|
19991
20445
|
"Content-Type": contentType,
|
|
19992
20446
|
"Cache-Control": "public, max-age=3600"
|
|
@@ -20003,8 +20457,8 @@ app52.get("/generated/:filename", (c) => {
|
|
|
20003
20457
|
console.error(`[generated] serve file=${filename} status=404`);
|
|
20004
20458
|
return c.text("Not found", 404);
|
|
20005
20459
|
}
|
|
20006
|
-
const filePath =
|
|
20007
|
-
const expectedDir =
|
|
20460
|
+
const filePath = resolve33(account.accountDir, "generated", filename);
|
|
20461
|
+
const expectedDir = resolve33(account.accountDir, "generated");
|
|
20008
20462
|
if (!filePath.startsWith(expectedDir + "/")) {
|
|
20009
20463
|
console.error(`[generated] serve file=${filename} status=403`);
|
|
20010
20464
|
return c.text("Forbidden", 403);
|
|
@@ -20016,7 +20470,7 @@ app52.get("/generated/:filename", (c) => {
|
|
|
20016
20470
|
const ext = "." + filename.split(".").pop()?.toLowerCase();
|
|
20017
20471
|
const contentType = IMAGE_MIME[ext] || "application/octet-stream";
|
|
20018
20472
|
console.log(`[generated] serve file=${filename} status=200`);
|
|
20019
|
-
const body =
|
|
20473
|
+
const body = readFileSync31(filePath);
|
|
20020
20474
|
return c.body(body, 200, {
|
|
20021
20475
|
"Content-Type": contentType,
|
|
20022
20476
|
"Cache-Control": "public, max-age=86400"
|
|
@@ -20031,7 +20485,7 @@ var brandLogoPath = "/brand/maxy-monochrome.png";
|
|
|
20031
20485
|
var brandIconPath = "/brand/maxy-monochrome.png";
|
|
20032
20486
|
if (BRAND_JSON_PATH && existsSync31(BRAND_JSON_PATH)) {
|
|
20033
20487
|
try {
|
|
20034
|
-
const fullBrand = JSON.parse(
|
|
20488
|
+
const fullBrand = JSON.parse(readFileSync31(BRAND_JSON_PATH, "utf-8"));
|
|
20035
20489
|
if (fullBrand.assets?.logo) brandLogoPath = `/brand/${fullBrand.assets.logo}`;
|
|
20036
20490
|
brandIconPath = fullBrand.assets?.icon ? `/brand/${fullBrand.assets.icon}` : brandLogoPath;
|
|
20037
20491
|
} catch {
|
|
@@ -20047,11 +20501,11 @@ var brandScript = `<script>window.__BRAND__=${JSON.stringify({
|
|
|
20047
20501
|
})}</script>`;
|
|
20048
20502
|
var brandThemeColor = BRAND.defaultColors?.primary ?? "#000000";
|
|
20049
20503
|
var brandBackgroundColor = BRAND.defaultColors?.background ?? "#ffffff";
|
|
20050
|
-
var brandIconFsPath =
|
|
20504
|
+
var brandIconFsPath = resolve33(process.cwd(), "public", brandIconPath.replace(/^\//, ""));
|
|
20051
20505
|
var brandIconExists = existsSync31(brandIconFsPath);
|
|
20052
20506
|
var SW_SOURCE = (() => {
|
|
20053
20507
|
try {
|
|
20054
|
-
return
|
|
20508
|
+
return readFileSync31(resolve33(process.cwd(), "public", "sw.js"), "utf-8");
|
|
20055
20509
|
} catch {
|
|
20056
20510
|
return null;
|
|
20057
20511
|
}
|
|
@@ -20059,9 +20513,9 @@ var SW_SOURCE = (() => {
|
|
|
20059
20513
|
function readInstalledVersion() {
|
|
20060
20514
|
try {
|
|
20061
20515
|
if (!PLATFORM_ROOT8) return "unknown";
|
|
20062
|
-
const versionFile =
|
|
20516
|
+
const versionFile = join28(PLATFORM_ROOT8, "config", `.${BRAND.hostname}-version`);
|
|
20063
20517
|
if (!existsSync31(versionFile)) return "unknown";
|
|
20064
|
-
const content =
|
|
20518
|
+
const content = readFileSync31(versionFile, "utf-8").trim();
|
|
20065
20519
|
return content || "unknown";
|
|
20066
20520
|
} catch {
|
|
20067
20521
|
return "unknown";
|
|
@@ -20102,7 +20556,7 @@ var clientErrorReporterScript = `<script>
|
|
|
20102
20556
|
function cachedHtml(file) {
|
|
20103
20557
|
let html = htmlCache.get(file);
|
|
20104
20558
|
if (!html) {
|
|
20105
|
-
html =
|
|
20559
|
+
html = readFileSync31(resolve33(process.cwd(), "public", file), "utf-8");
|
|
20106
20560
|
const productNameEsc = escapeHtml(BRAND.productName);
|
|
20107
20561
|
html = html.replace(/<title>([^<]*)<\/title>/, (_match, inner) => `<title>${escapeHtml(inner).replace(/Maxy/g, productNameEsc)}</title>`);
|
|
20108
20562
|
html = html.replace('href="/favicon.ico"', `href="${escapeHtml(brandFaviconPath)}"`);
|
|
@@ -20120,13 +20574,13 @@ ${clientErrorReporterScript}
|
|
|
20120
20574
|
}
|
|
20121
20575
|
var brandedHtmlCache = /* @__PURE__ */ new Map();
|
|
20122
20576
|
function loadBrandingCache(agentSlug) {
|
|
20123
|
-
const configDir2 =
|
|
20577
|
+
const configDir2 = join28(homedir3(), BRAND.configDir);
|
|
20124
20578
|
try {
|
|
20125
20579
|
const accountId = getDefaultAccountId();
|
|
20126
20580
|
if (!accountId) return null;
|
|
20127
|
-
const cachePath =
|
|
20581
|
+
const cachePath = join28(configDir2, "branding-cache", accountId, `${agentSlug}.json`);
|
|
20128
20582
|
if (!existsSync31(cachePath)) return null;
|
|
20129
|
-
return JSON.parse(
|
|
20583
|
+
return JSON.parse(readFileSync31(cachePath, "utf-8"));
|
|
20130
20584
|
} catch {
|
|
20131
20585
|
return null;
|
|
20132
20586
|
}
|
|
@@ -20226,7 +20680,7 @@ app52.use("/vnc-popout.html", logViewerFetch);
|
|
|
20226
20680
|
app52.get("/vnc-popout.html", (c) => {
|
|
20227
20681
|
let html = htmlCache.get("vnc-popout.html");
|
|
20228
20682
|
if (!html) {
|
|
20229
|
-
html =
|
|
20683
|
+
html = readFileSync31(resolve33(process.cwd(), "public", "vnc-popout.html"), "utf-8");
|
|
20230
20684
|
const name = escapeHtml(BRAND.productName);
|
|
20231
20685
|
html = html.replace("<title>Browser \u2014 Maxy</title>", `<title>${name}</title>`);
|
|
20232
20686
|
html = html.replace("</head>", ` ${brandScript}
|
|
@@ -20438,7 +20892,7 @@ async function runAdminUserReconcileTick() {
|
|
|
20438
20892
|
console.error("[adminuser-self-heal] skip reason=no-users-file");
|
|
20439
20893
|
return;
|
|
20440
20894
|
}
|
|
20441
|
-
const usersRaw =
|
|
20895
|
+
const usersRaw = readFileSync31(USERS_FILE, "utf-8").trim();
|
|
20442
20896
|
if (!usersRaw) {
|
|
20443
20897
|
console.error("[adminuser-self-heal] skip reason=empty-users-file");
|
|
20444
20898
|
return;
|
|
@@ -20490,7 +20944,7 @@ try {
|
|
|
20490
20944
|
} catch (err) {
|
|
20491
20945
|
console.error(`[graph-health] account-enumeration unavailable reason=${err instanceof Error ? err.message : String(err)}`);
|
|
20492
20946
|
}
|
|
20493
|
-
var configDirForWhatsApp =
|
|
20947
|
+
var configDirForWhatsApp = basename10(MAXY_DIR) || ".maxy";
|
|
20494
20948
|
var bootAccount = resolveAccount();
|
|
20495
20949
|
if (bootAccount) {
|
|
20496
20950
|
migrateRemovedConfigKeys(bootAccount.accountDir);
|
|
@@ -20552,7 +21006,7 @@ if (bootAccountConfig?.whatsapp) {
|
|
|
20552
21006
|
}
|
|
20553
21007
|
init({
|
|
20554
21008
|
configDir: configDirForWhatsApp,
|
|
20555
|
-
platformRoot:
|
|
21009
|
+
platformRoot: resolve33(process.env.MAXY_PLATFORM_ROOT ?? join28(__dirname, "..")),
|
|
20556
21010
|
accountConfig: bootAccountConfig,
|
|
20557
21011
|
onMessage: async (msg) => {
|
|
20558
21012
|
if (msg.isOwnerMirror) {
|