@rubytech/create-maxy-code 0.1.442 → 0.1.444
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/dist/__tests__/converge-client-admins.test.js +50 -0
- package/dist/converge-client-admins.js +67 -0
- package/dist/index.js +8 -0
- package/package.json +1 -1
- package/payload/platform/docs/superpowers/plans/2026-07-13-account-schema-ontology-projection.md +547 -0
- package/payload/platform/docs/superpowers/plans/2026-07-14-graph-top-level-labels-ontology-single-source.md +458 -0
- package/payload/platform/docs/superpowers/specs/2026-07-13-account-schema-ontology-projection-design.md +178 -0
- package/payload/platform/docs/superpowers/specs/2026-07-14-graph-top-level-labels-ontology-single-source-design.md +119 -0
- package/payload/platform/plugins/admin/.claude-plugin/plugin.json +1 -1
- package/payload/platform/plugins/admin/PLUGIN.md +7 -1
- package/payload/platform/plugins/admin/hooks/__tests__/fs-schema-guard.test.sh +19 -2
- package/payload/platform/plugins/admin/mcp/dist/index.js +165 -24
- package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/admin/mcp/dist/tools/account-lifecycle.d.ts +37 -7
- package/payload/platform/plugins/admin/mcp/dist/tools/account-lifecycle.d.ts.map +1 -1
- package/payload/platform/plugins/admin/mcp/dist/tools/account-lifecycle.js +85 -7
- package/payload/platform/plugins/admin/mcp/dist/tools/account-lifecycle.js.map +1 -1
- package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +9 -5
- package/payload/platform/plugins/docs/references/admin-session.md +2 -0
- package/payload/platform/plugins/docs/references/admin-ui.md +5 -1
- package/payload/platform/plugins/docs/references/cross-account-authority.md +3 -3
- package/payload/platform/plugins/memory/PLUGIN.md +2 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/resolve-active-vertical.test.js +12 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/resolve-active-vertical.test.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/resolve-active-vertical.d.ts +9 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/resolve-active-vertical.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/resolve-active-vertical.js +19 -2
- package/payload/platform/plugins/memory/mcp/dist/lib/resolve-active-vertical.js.map +1 -1
- package/payload/platform/plugins/memory/references/schema-estate-agent.md +8 -3
- package/payload/platform/plugins/outlook/references/auth.md +8 -0
- package/payload/platform/plugins/outlook/skills/outlook/SKILL.md +3 -0
- package/payload/platform/plugins/whatsapp/mcp/dist/__tests__/format-attachment-suffix.test.d.ts +2 -0
- package/payload/platform/plugins/whatsapp/mcp/dist/__tests__/format-attachment-suffix.test.d.ts.map +1 -0
- package/payload/platform/plugins/whatsapp/mcp/dist/__tests__/format-attachment-suffix.test.js +44 -0
- package/payload/platform/plugins/whatsapp/mcp/dist/__tests__/format-attachment-suffix.test.js.map +1 -0
- package/payload/platform/plugins/whatsapp/mcp/dist/format.d.ts +7 -0
- package/payload/platform/plugins/whatsapp/mcp/dist/format.d.ts.map +1 -1
- package/payload/platform/plugins/whatsapp/mcp/dist/format.js +27 -0
- package/payload/platform/plugins/whatsapp/mcp/dist/format.js.map +1 -1
- package/payload/platform/plugins/whatsapp/mcp/dist/index.js +6 -19
- package/payload/platform/plugins/whatsapp/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/whatsapp/references/channels-whatsapp.md +3 -1
- package/payload/platform/plugins/work/mcp/dist/tools/work-create.d.ts.map +1 -1
- package/payload/platform/plugins/work/mcp/dist/tools/work-create.js +10 -7
- package/payload/platform/plugins/work/mcp/dist/tools/work-create.js.map +1 -1
- package/payload/platform/scripts/__tests__/account-schema-owned-dirs.test.sh +58 -0
- package/payload/platform/scripts/__tests__/provision-role-stamp.test.sh +6 -0
- package/payload/platform/scripts/lib/account-schema-owned-dirs.py +143 -10
- package/payload/platform/scripts/lib/provision-account-dir.sh +5 -0
- package/payload/platform/scripts/name-glsmith-owner.sh +239 -0
- package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js +2 -0
- package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/http-server.d.ts +1 -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 +99 -8
- package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/index.js +0 -13
- package/payload/platform/services/claude-session-manager/dist/index.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/jsonl-path.d.ts +8 -0
- package/payload/platform/services/claude-session-manager/dist/jsonl-path.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/jsonl-path.js +11 -0
- package/payload/platform/services/claude-session-manager/dist/jsonl-path.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/passive-admin-assignment.d.ts +20 -0
- package/payload/platform/services/claude-session-manager/dist/passive-admin-assignment.d.ts.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/passive-admin-assignment.js +66 -0
- package/payload/platform/services/claude-session-manager/dist/passive-admin-assignment.js.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/passive-spawn-tools-tail.d.ts +19 -0
- package/payload/platform/services/claude-session-manager/dist/passive-spawn-tools-tail.d.ts.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/passive-spawn-tools-tail.js +50 -0
- package/payload/platform/services/claude-session-manager/dist/passive-spawn-tools-tail.js.map +1 -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 +14 -1
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.js.map +1 -1
- package/payload/server/public/assets/{chat-DhD-NES_.js → chat-Dq-X-ia8.js} +1 -1
- package/payload/server/public/assets/{operator-zcIfeB14.js → operator-88m0DMl2.js} +1 -1
- package/payload/server/public/assets/page-BRfxFXyi.js +32 -0
- package/payload/server/public/assets/{public-C0ehZCdE.js → public-BX2MjsFY.js} +1 -1
- package/payload/server/public/chat.html +2 -2
- package/payload/server/public/operator.html +2 -2
- package/payload/server/public/public.html +2 -2
- package/payload/server/server.js +406 -182
- package/payload/platform/plugins/memory/references/schema-trades.md +0 -34
- package/payload/server/public/assets/page-6fCnIb45.js +0 -32
package/payload/server/server.js
CHANGED
|
@@ -1413,10 +1413,10 @@ var serveStatic = (options = { root: "" }) => {
|
|
|
1413
1413
|
};
|
|
1414
1414
|
|
|
1415
1415
|
// server/index.ts
|
|
1416
|
-
import { readFileSync as
|
|
1416
|
+
import { readFileSync as readFileSync37, existsSync as existsSync35, watchFile } from "fs";
|
|
1417
1417
|
import { spawn as spawn3 } from "child_process";
|
|
1418
1418
|
import { createHash as createHash7 } from "crypto";
|
|
1419
|
-
import { resolve as
|
|
1419
|
+
import { resolve as resolve34, join as join37, basename as basename12 } from "path";
|
|
1420
1420
|
import { homedir as homedir4 } from "os";
|
|
1421
1421
|
import { monitorEventLoopDelay } from "perf_hooks";
|
|
1422
1422
|
|
|
@@ -2129,7 +2129,7 @@ var credsSaveQueue = Promise.resolve();
|
|
|
2129
2129
|
async function drainCredsSaveQueue(timeoutMs = 5e3) {
|
|
2130
2130
|
console.error(`${TAG2} draining credential save queue\u2026`);
|
|
2131
2131
|
const timer2 = new Promise(
|
|
2132
|
-
(
|
|
2132
|
+
(resolve35) => setTimeout(() => resolve35("timeout"), timeoutMs)
|
|
2133
2133
|
);
|
|
2134
2134
|
const result = await Promise.race([
|
|
2135
2135
|
credsSaveQueue.then(() => "drained"),
|
|
@@ -2292,11 +2292,11 @@ async function createWaSocket(opts) {
|
|
|
2292
2292
|
return sock;
|
|
2293
2293
|
}
|
|
2294
2294
|
async function waitForConnection(sock) {
|
|
2295
|
-
return new Promise((
|
|
2295
|
+
return new Promise((resolve35, reject) => {
|
|
2296
2296
|
const handler = (update) => {
|
|
2297
2297
|
if (update.connection === "open") {
|
|
2298
2298
|
sock.ev.off("connection.update", handler);
|
|
2299
|
-
|
|
2299
|
+
resolve35();
|
|
2300
2300
|
}
|
|
2301
2301
|
if (update.connection === "close") {
|
|
2302
2302
|
sock.ev.off("connection.update", handler);
|
|
@@ -2401,7 +2401,7 @@ import { inspect as inspect2 } from "util";
|
|
|
2401
2401
|
|
|
2402
2402
|
// app/lib/whatsapp/with-timeout.ts
|
|
2403
2403
|
function withTimeout(label, promise, timeoutMs) {
|
|
2404
|
-
return new Promise((
|
|
2404
|
+
return new Promise((resolve35, reject) => {
|
|
2405
2405
|
const timer2 = setTimeout(() => {
|
|
2406
2406
|
reject(new Error(`${label} timed out after ${timeoutMs}ms`));
|
|
2407
2407
|
}, timeoutMs);
|
|
@@ -2409,7 +2409,7 @@ function withTimeout(label, promise, timeoutMs) {
|
|
|
2409
2409
|
promise.then(
|
|
2410
2410
|
(value) => {
|
|
2411
2411
|
clearTimeout(timer2);
|
|
2412
|
-
|
|
2412
|
+
resolve35(value);
|
|
2413
2413
|
},
|
|
2414
2414
|
(err) => {
|
|
2415
2415
|
clearTimeout(timer2);
|
|
@@ -3489,10 +3489,10 @@ function readConversationSummaries(platformAccountId) {
|
|
|
3489
3489
|
} catch {
|
|
3490
3490
|
continue;
|
|
3491
3491
|
}
|
|
3492
|
-
const
|
|
3493
|
-
if (
|
|
3492
|
+
const cached3 = summaryCache.get(file);
|
|
3493
|
+
if (cached3 && cached3.size === size && cached3.mtimeMs === mtimeMs) {
|
|
3494
3494
|
cacheHits++;
|
|
3495
|
-
out.set(remoteJid,
|
|
3495
|
+
out.set(remoteJid, cached3.summary);
|
|
3496
3496
|
continue;
|
|
3497
3497
|
}
|
|
3498
3498
|
let summary;
|
|
@@ -3556,8 +3556,8 @@ async function persistWhatsAppMessage(input) {
|
|
|
3556
3556
|
const { givenName, familyName } = splitName(input.pushName);
|
|
3557
3557
|
const prev = sessionWriteLocks.get(input.cacheKey);
|
|
3558
3558
|
let release;
|
|
3559
|
-
const mine = new Promise((
|
|
3560
|
-
release =
|
|
3559
|
+
const mine = new Promise((resolve35) => {
|
|
3560
|
+
release = resolve35;
|
|
3561
3561
|
});
|
|
3562
3562
|
const chained = (prev ?? Promise.resolve()).then(() => mine);
|
|
3563
3563
|
sessionWriteLocks.set(input.cacheKey, chained);
|
|
@@ -3700,8 +3700,8 @@ import { resolve as resolve3 } from "path";
|
|
|
3700
3700
|
var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
3701
3701
|
var cache = /* @__PURE__ */ new Map();
|
|
3702
3702
|
function enumerateValidAccountIds(accountsDir) {
|
|
3703
|
-
const
|
|
3704
|
-
if (
|
|
3703
|
+
const cached3 = cache.get(accountsDir);
|
|
3704
|
+
if (cached3 !== void 0) return cached3;
|
|
3705
3705
|
let names;
|
|
3706
3706
|
try {
|
|
3707
3707
|
names = readdirSync2(accountsDir);
|
|
@@ -4179,8 +4179,9 @@ function resolveStoredAttachment(accountId, attachmentId) {
|
|
|
4179
4179
|
return null;
|
|
4180
4180
|
}
|
|
4181
4181
|
if (!meta || typeof meta !== "object") return null;
|
|
4182
|
-
const { filename, mimeType } = meta;
|
|
4182
|
+
const { filename, mimeType, sizeBytes } = meta;
|
|
4183
4183
|
if (typeof filename !== "string" || typeof mimeType !== "string") return null;
|
|
4184
|
+
const resolvedSizeBytes = typeof sizeBytes === "number" ? sizeBytes : void 0;
|
|
4184
4185
|
let entries;
|
|
4185
4186
|
try {
|
|
4186
4187
|
entries = readdirSync3(dir, { withFileTypes: true });
|
|
@@ -4191,7 +4192,7 @@ function resolveStoredAttachment(accountId, attachmentId) {
|
|
|
4191
4192
|
if (!dataFile) return null;
|
|
4192
4193
|
const readableSibling = `${attachmentId}${EXTRACTED_TEXT_SUFFIX}`;
|
|
4193
4194
|
const readableTextPath = entries.some((e) => e.isFile() && e.name === readableSibling) ? resolve6(dir, readableSibling) : void 0;
|
|
4194
|
-
return { attachmentId, filename, mimeType, path: resolve6(dir, dataFile.name), readableTextPath };
|
|
4195
|
+
return { attachmentId, filename, mimeType, sizeBytes: resolvedSizeBytes, path: resolve6(dir, dataFile.name), readableTextPath };
|
|
4195
4196
|
}
|
|
4196
4197
|
async function writeAttachment(accountId, subPath, filename, mimeType, sizeBytes, buffer, explicitId) {
|
|
4197
4198
|
const attachmentId = explicitId ?? randomUUID3();
|
|
@@ -5259,11 +5260,11 @@ async function connectWithReconnect(conn) {
|
|
|
5259
5260
|
console.error(
|
|
5260
5261
|
`${TAG13} reconnecting account=${conn.accountId} in ${delay}ms (attempt ${decision.nextAttempts}/${maxAttempts})`
|
|
5261
5262
|
);
|
|
5262
|
-
await new Promise((
|
|
5263
|
-
const timer2 = setTimeout(
|
|
5263
|
+
await new Promise((resolve35) => {
|
|
5264
|
+
const timer2 = setTimeout(resolve35, delay);
|
|
5264
5265
|
conn.abortController.signal.addEventListener("abort", () => {
|
|
5265
5266
|
clearTimeout(timer2);
|
|
5266
|
-
|
|
5267
|
+
resolve35();
|
|
5267
5268
|
}, { once: true });
|
|
5268
5269
|
});
|
|
5269
5270
|
}
|
|
@@ -5271,16 +5272,16 @@ async function connectWithReconnect(conn) {
|
|
|
5271
5272
|
}
|
|
5272
5273
|
}
|
|
5273
5274
|
function waitForDisconnectEvent(conn) {
|
|
5274
|
-
return new Promise((
|
|
5275
|
+
return new Promise((resolve35) => {
|
|
5275
5276
|
if (!conn.sock) {
|
|
5276
|
-
|
|
5277
|
+
resolve35();
|
|
5277
5278
|
return;
|
|
5278
5279
|
}
|
|
5279
5280
|
const sock = conn.sock;
|
|
5280
5281
|
const handler = (update) => {
|
|
5281
5282
|
if (update.connection === "close") {
|
|
5282
5283
|
sock.ev.off("connection.update", handler);
|
|
5283
|
-
|
|
5284
|
+
resolve35();
|
|
5284
5285
|
}
|
|
5285
5286
|
};
|
|
5286
5287
|
sock.ev.on("connection.update", handler);
|
|
@@ -5602,8 +5603,8 @@ async function handleInboundMessage(conn, msg) {
|
|
|
5602
5603
|
const conversationKey = isGroup ? remoteJid : senderPhone;
|
|
5603
5604
|
const debounceKey = `${conn.accountId}:${conversationKey}:${senderPhone}`;
|
|
5604
5605
|
let resolvePending;
|
|
5605
|
-
const sttPending = new Promise((
|
|
5606
|
-
resolvePending =
|
|
5606
|
+
const sttPending = new Promise((resolve35) => {
|
|
5607
|
+
resolvePending = resolve35;
|
|
5607
5608
|
});
|
|
5608
5609
|
if (conn.debouncer) conn.debouncer.registerPending(debounceKey, sttPending);
|
|
5609
5610
|
try {
|
|
@@ -6031,20 +6032,20 @@ function buildX11Env(chromiumWrapperPath, transport = "vnc") {
|
|
|
6031
6032
|
|
|
6032
6033
|
// server/routes/health.ts
|
|
6033
6034
|
function checkPort(port2, timeoutMs = 500) {
|
|
6034
|
-
return new Promise((
|
|
6035
|
+
return new Promise((resolve35) => {
|
|
6035
6036
|
const socket = createConnection2(port2, "127.0.0.1");
|
|
6036
6037
|
socket.setTimeout(timeoutMs);
|
|
6037
6038
|
socket.once("connect", () => {
|
|
6038
6039
|
socket.destroy();
|
|
6039
|
-
|
|
6040
|
+
resolve35(true);
|
|
6040
6041
|
});
|
|
6041
6042
|
socket.once("error", () => {
|
|
6042
6043
|
socket.destroy();
|
|
6043
|
-
|
|
6044
|
+
resolve35(false);
|
|
6044
6045
|
});
|
|
6045
6046
|
socket.once("timeout", () => {
|
|
6046
6047
|
socket.destroy();
|
|
6047
|
-
|
|
6048
|
+
resolve35(false);
|
|
6048
6049
|
});
|
|
6049
6050
|
});
|
|
6050
6051
|
}
|
|
@@ -7321,8 +7322,8 @@ async function startLogin(opts) {
|
|
|
7321
7322
|
await clearAuth(authDir);
|
|
7322
7323
|
let resolveCode = null;
|
|
7323
7324
|
let rejectCode = null;
|
|
7324
|
-
const codePromise = new Promise((
|
|
7325
|
-
resolveCode =
|
|
7325
|
+
const codePromise = new Promise((resolve35, reject) => {
|
|
7326
|
+
resolveCode = resolve35;
|
|
7326
7327
|
rejectCode = reject;
|
|
7327
7328
|
});
|
|
7328
7329
|
const codeTimer = setTimeout(
|
|
@@ -7842,17 +7843,30 @@ function projectionFor(requestedAccountId) {
|
|
|
7842
7843
|
houseAccountManagers
|
|
7843
7844
|
});
|
|
7844
7845
|
}
|
|
7845
|
-
function
|
|
7846
|
+
function evaluateCallerScope(c, requestedAccountId) {
|
|
7846
7847
|
const callerAccountId = c.req.header("x-maxy-caller-account")?.trim() || null;
|
|
7847
7848
|
const houseAdminScope = c.req.header("x-maxy-house-admin-scope")?.trim() || null;
|
|
7848
7849
|
const decision = authorizeRecallRead({ requestedAccountId, callerAccountId, houseAdminScope });
|
|
7850
|
+
return { decision, callerAccountId, houseAdminScope };
|
|
7851
|
+
}
|
|
7852
|
+
function callerScopeDenial(c, requestedAccountId) {
|
|
7853
|
+
const { decision, callerAccountId, houseAdminScope } = evaluateCallerScope(c, requestedAccountId);
|
|
7849
7854
|
if (decision.authorized) return null;
|
|
7850
7855
|
console.error(`[whatsapp-read-tool] op=authz-deny requested=${requestedAccountId} caller=${callerAccountId ?? "none"} houseAdmin=${houseAdminScope ? "y" : "n"}`);
|
|
7851
7856
|
return c.json({ error: decision.reason }, 403);
|
|
7852
7857
|
}
|
|
7858
|
+
function houseSocketScopeDenial(c, op) {
|
|
7859
|
+
const houseAccountId = resolveAccount()?.accountId ?? "";
|
|
7860
|
+
const { decision, callerAccountId, houseAdminScope } = evaluateCallerScope(c, houseAccountId);
|
|
7861
|
+
if (decision.authorized) return null;
|
|
7862
|
+
console.error(`${TAG21} op=authz-deny route=${op} caller=${callerAccountId ?? "none"} houseAdmin=${houseAdminScope ? "y" : "n"}`);
|
|
7863
|
+
return c.json({ ok: false, error: "Not authorized. The WhatsApp connection and its settings are house-scoped; this session may only act on its own account." }, 403);
|
|
7864
|
+
}
|
|
7853
7865
|
var app2 = new Hono();
|
|
7854
7866
|
app2.get("/status", (c) => {
|
|
7855
7867
|
try {
|
|
7868
|
+
const denial = houseSocketScopeDenial(c, "status");
|
|
7869
|
+
if (denial) return denial;
|
|
7856
7870
|
const live = getStatus();
|
|
7857
7871
|
const liveIds = new Set(live.map((a) => a.accountId));
|
|
7858
7872
|
const activeLoginIds = new Set(listActiveLoginAccountIds());
|
|
@@ -7880,6 +7894,8 @@ app2.get("/status", (c) => {
|
|
|
7880
7894
|
});
|
|
7881
7895
|
app2.post("/login/start", async (c) => {
|
|
7882
7896
|
try {
|
|
7897
|
+
const denial = houseSocketScopeDenial(c, "login/start");
|
|
7898
|
+
if (denial) return denial;
|
|
7883
7899
|
const body = await c.req.json().catch(() => ({}));
|
|
7884
7900
|
const accountId = validateAccountId(body.accountId);
|
|
7885
7901
|
const force = body.force ?? false;
|
|
@@ -7899,6 +7915,8 @@ app2.post("/login/start", async (c) => {
|
|
|
7899
7915
|
});
|
|
7900
7916
|
app2.post("/login/wait", async (c) => {
|
|
7901
7917
|
try {
|
|
7918
|
+
const denial = houseSocketScopeDenial(c, "login/wait");
|
|
7919
|
+
if (denial) return denial;
|
|
7902
7920
|
const body = await c.req.json().catch(() => ({}));
|
|
7903
7921
|
const accountId = validateAccountId(body.accountId);
|
|
7904
7922
|
const timeoutMs = body.timeoutMs ?? 6e4;
|
|
@@ -7917,6 +7935,8 @@ app2.post("/login/wait", async (c) => {
|
|
|
7917
7935
|
});
|
|
7918
7936
|
app2.post("/disconnect", async (c) => {
|
|
7919
7937
|
try {
|
|
7938
|
+
const denial = houseSocketScopeDenial(c, "disconnect");
|
|
7939
|
+
if (denial) return denial;
|
|
7920
7940
|
const body = await c.req.json().catch(() => ({}));
|
|
7921
7941
|
const accountId = validateAccountId(body.accountId);
|
|
7922
7942
|
await stopConnection(accountId);
|
|
@@ -7928,6 +7948,8 @@ app2.post("/disconnect", async (c) => {
|
|
|
7928
7948
|
});
|
|
7929
7949
|
app2.post("/reconnect", async (c) => {
|
|
7930
7950
|
try {
|
|
7951
|
+
const denial = houseSocketScopeDenial(c, "reconnect");
|
|
7952
|
+
if (denial) return denial;
|
|
7931
7953
|
const body = await c.req.json().catch(() => ({}));
|
|
7932
7954
|
const accountId = validateAccountId(body.accountId);
|
|
7933
7955
|
await startConnection(accountId);
|
|
@@ -7944,6 +7966,8 @@ app2.post("/config", async (c) => {
|
|
|
7944
7966
|
if (!action || typeof action !== "string") {
|
|
7945
7967
|
return c.json({ ok: false, error: 'Missing required field "action".' }, 400);
|
|
7946
7968
|
}
|
|
7969
|
+
const denial = houseSocketScopeDenial(c, `config action=${action}`);
|
|
7970
|
+
if (denial) return denial;
|
|
7947
7971
|
const account = resolveAccount();
|
|
7948
7972
|
if (!account) {
|
|
7949
7973
|
return c.json({ ok: false, error: "No account configured." }, 500);
|
|
@@ -8983,6 +9007,16 @@ function asString(content) {
|
|
|
8983
9007
|
function tsOf(row) {
|
|
8984
9008
|
return typeof row.timestamp === "string" ? row.timestamp : null;
|
|
8985
9009
|
}
|
|
9010
|
+
function hasWebchatReply(content) {
|
|
9011
|
+
if (!Array.isArray(content)) return false;
|
|
9012
|
+
for (const block of content) {
|
|
9013
|
+
if (block && typeof block === "object") {
|
|
9014
|
+
const b = block;
|
|
9015
|
+
if (b.type === "tool_use" && b.name === WEBCHAT_REPLY_TOOL) return true;
|
|
9016
|
+
}
|
|
9017
|
+
}
|
|
9018
|
+
return false;
|
|
9019
|
+
}
|
|
8986
9020
|
function joinTextBlocks(content) {
|
|
8987
9021
|
if (!Array.isArray(content)) return "";
|
|
8988
9022
|
const parts = [];
|
|
@@ -9075,8 +9109,10 @@ function parseTranscript(lines, queuedPendingSuppress = /* @__PURE__ */ new Map(
|
|
|
9075
9109
|
out.push({ kind: "agent-error", code, raw, ts });
|
|
9076
9110
|
continue;
|
|
9077
9111
|
}
|
|
9112
|
+
if (hasWebchatReply(msg?.content)) turnCtx.repliedThisTurn = true;
|
|
9078
9113
|
const isEnd = msg?.stop_reason === "end_turn";
|
|
9079
|
-
|
|
9114
|
+
const promoteFinal = isEnd && turnCtx.lastChannelSource === "webchat" && !turnCtx.repliedThisTurn;
|
|
9115
|
+
out.push(...assistantTurns(msg?.content, ts, promoteFinal));
|
|
9080
9116
|
continue;
|
|
9081
9117
|
}
|
|
9082
9118
|
if (row.type === "queue-operation" && row.operation === "enqueue") {
|
|
@@ -9084,6 +9120,7 @@ function parseTranscript(lines, queuedPendingSuppress = /* @__PURE__ */ new Map(
|
|
|
9084
9120
|
if (content !== null && CHANNEL_WRAPPER2.test(content)) {
|
|
9085
9121
|
const u = unwrapChannel(content);
|
|
9086
9122
|
turnCtx.lastChannelSource = u.source;
|
|
9123
|
+
turnCtx.repliedThisTurn = false;
|
|
9087
9124
|
if (!isScheduleSource(content)) {
|
|
9088
9125
|
out.push(operatorInboundTurn(u, ts));
|
|
9089
9126
|
queuedPendingSuppress.set(u.text, (queuedPendingSuppress.get(u.text) ?? 0) + 1);
|
|
@@ -9097,6 +9134,7 @@ function parseTranscript(lines, queuedPendingSuppress = /* @__PURE__ */ new Map(
|
|
|
9097
9134
|
if (isChannelQueued) {
|
|
9098
9135
|
const u = unwrapChannel(att.prompt);
|
|
9099
9136
|
turnCtx.lastChannelSource = u.source;
|
|
9137
|
+
turnCtx.repliedThisTurn = false;
|
|
9100
9138
|
if (!isScheduleSource(att.prompt)) {
|
|
9101
9139
|
renderOrSuppressChannelInbound(u, ts, out, queuedPendingSuppress);
|
|
9102
9140
|
}
|
|
@@ -9126,6 +9164,7 @@ function parseTranscript(lines, queuedPendingSuppress = /* @__PURE__ */ new Map(
|
|
|
9126
9164
|
if (isChannel) {
|
|
9127
9165
|
const u = unwrapChannel(text);
|
|
9128
9166
|
turnCtx.lastChannelSource = u.source;
|
|
9167
|
+
turnCtx.repliedThisTurn = false;
|
|
9129
9168
|
if (!isScheduleSource(text)) {
|
|
9130
9169
|
renderOrSuppressChannelInbound(u, ts, out, queuedPendingSuppress);
|
|
9131
9170
|
}
|
|
@@ -9210,6 +9249,24 @@ function splitNewLines(buf, baseOffset) {
|
|
|
9210
9249
|
};
|
|
9211
9250
|
}
|
|
9212
9251
|
|
|
9252
|
+
// app/lib/whatsapp-reader/authorize-reply-file-ref.ts
|
|
9253
|
+
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;
|
|
9254
|
+
function accountPartitionOwner(relative7) {
|
|
9255
|
+
const segs = relative7.split("/").filter(Boolean);
|
|
9256
|
+
if (segs.length < 2 || segs[0] !== "accounts") return null;
|
|
9257
|
+
return ACCOUNT_UUID_RE2.test(segs[1]) ? segs[1] : null;
|
|
9258
|
+
}
|
|
9259
|
+
function authorizeReplyFileRef(input) {
|
|
9260
|
+
const { relative: relative7, sendAccountId, sessionAccountId } = input;
|
|
9261
|
+
const segs = relative7.split("/").filter(Boolean);
|
|
9262
|
+
const inAccounts = segs.length >= 2 && segs[0] === "accounts";
|
|
9263
|
+
if (inAccounts && segs[1] === sendAccountId) return { allow: true, via: "own" };
|
|
9264
|
+
if (inAccounts && sessionAccountId && segs[1] === sessionAccountId) return { allow: true, via: "session" };
|
|
9265
|
+
if (accountPartitionOwner(relative7)) return { allow: false, reason: "cross-account" };
|
|
9266
|
+
if (segs[0] === "public" || inAccounts && segs[1] === "public") return { allow: false, reason: "public" };
|
|
9267
|
+
return { allow: false, reason: "foreign" };
|
|
9268
|
+
}
|
|
9269
|
+
|
|
9213
9270
|
// server/routes/admin/manager-client.ts
|
|
9214
9271
|
var SESSION_ID_RE2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
9215
9272
|
function isSessionId(value) {
|
|
@@ -9244,6 +9301,11 @@ function isMimeAdmitted(mimeType, supportedSet) {
|
|
|
9244
9301
|
|
|
9245
9302
|
// server/routes/whatsapp-reader.ts
|
|
9246
9303
|
var app4 = new Hono();
|
|
9304
|
+
var DENY_COPY = {
|
|
9305
|
+
"cross-account": "This file belongs to another account. Open that account to send it.",
|
|
9306
|
+
public: "This file cannot be sent to a WhatsApp contact.",
|
|
9307
|
+
foreign: "This file cannot be sent to a WhatsApp contact."
|
|
9308
|
+
};
|
|
9247
9309
|
app4.get("/conversations", requireAdminSession, async (c) => {
|
|
9248
9310
|
const cacheKey = c.var.cacheKey;
|
|
9249
9311
|
const scopeAccountId = getAccountIdForSession(cacheKey) ?? null;
|
|
@@ -9891,7 +9953,9 @@ app4.post("/reply", requireAdminSession, async (c) => {
|
|
|
9891
9953
|
return c.json({ error: "invalid accountId" }, 400);
|
|
9892
9954
|
}
|
|
9893
9955
|
const validAccount = isValidAccountId(accountId);
|
|
9894
|
-
|
|
9956
|
+
const cacheKey = c.var.cacheKey;
|
|
9957
|
+
const sessionAccountId = getAccountIdForSession(cacheKey) ?? null;
|
|
9958
|
+
console.log(`${MANUAL_TAG} op=scope sendId=${sendId} accountId=${accountId} validAccount=${validAccount} sessionAccount=${sessionAccountId ?? "null"} src=session`);
|
|
9895
9959
|
if (!validAccount) {
|
|
9896
9960
|
exit("rejected");
|
|
9897
9961
|
return c.json({ error: "unknown account" }, 400);
|
|
@@ -9929,26 +9993,37 @@ app4.post("/reply", requireAdminSession, async (c) => {
|
|
|
9929
9993
|
}
|
|
9930
9994
|
}
|
|
9931
9995
|
const resolvedRefs = [];
|
|
9996
|
+
const acct8 = accountId.slice(0, 8);
|
|
9997
|
+
const sess8 = sessionAccountId ? sessionAccountId.slice(0, 8) : "";
|
|
9932
9998
|
for (const ref of fileRefs) {
|
|
9933
9999
|
const resolved = resolveDataPath(ref);
|
|
9934
|
-
|
|
9935
|
-
|
|
10000
|
+
if (!resolved.ok) {
|
|
10001
|
+
console.log(`${MANUAL_TAG} op=reply-file-ref sendId=${sendId} path=${ref} owner= sendAccount=${acct8} sessionAccount=${sess8} via=deny reason=unresolved ok=false`);
|
|
10002
|
+
exit("rejected");
|
|
10003
|
+
return c.json({ error: `device file not found: ${ref}`, reason: "file-ref-unresolved" }, 404);
|
|
10004
|
+
}
|
|
10005
|
+
const owner = accountPartitionOwner(resolved.relative);
|
|
10006
|
+
const owner8 = owner ? owner.slice(0, 8) : "";
|
|
10007
|
+
const decision = authorizeReplyFileRef({ relative: resolved.relative, sendAccountId: accountId, sessionAccountId });
|
|
10008
|
+
if (!decision.allow) {
|
|
10009
|
+
console.log(`${MANUAL_TAG} op=reply-file-ref sendId=${sendId} path=${ref} owner=${owner8} sendAccount=${acct8} sessionAccount=${sess8} via=deny reason=${decision.reason} ok=false`);
|
|
10010
|
+
exit("rejected");
|
|
10011
|
+
return c.json({ error: DENY_COPY[decision.reason], reason: `file-ref-${decision.reason}` }, 403);
|
|
10012
|
+
}
|
|
10013
|
+
const absolute = resolved.absolute;
|
|
9936
10014
|
let size = -1;
|
|
9937
|
-
|
|
9938
|
-
|
|
9939
|
-
|
|
9940
|
-
|
|
9941
|
-
|
|
9942
|
-
} catch {
|
|
9943
|
-
size = -1;
|
|
9944
|
-
}
|
|
10015
|
+
try {
|
|
10016
|
+
const st = statSync5(absolute);
|
|
10017
|
+
size = st.isFile() ? st.size : -1;
|
|
10018
|
+
} catch {
|
|
10019
|
+
size = -1;
|
|
9945
10020
|
}
|
|
9946
|
-
|
|
9947
|
-
|
|
9948
|
-
if (!ok) {
|
|
10021
|
+
if (size < 0) {
|
|
10022
|
+
console.log(`${MANUAL_TAG} op=reply-file-ref sendId=${sendId} path=${ref} owner=${owner8} sendAccount=${acct8} sessionAccount=${sess8} via=${decision.via} reason=missing ok=false`);
|
|
9949
10023
|
exit("rejected");
|
|
9950
10024
|
return c.json({ error: `device file not found: ${ref}`, reason: "file-ref-unresolved" }, 404);
|
|
9951
10025
|
}
|
|
10026
|
+
console.log(`${MANUAL_TAG} op=reply-file-ref sendId=${sendId} path=${ref} owner=${owner8} sendAccount=${acct8} sessionAccount=${sess8} via=${decision.via} reason=none ok=true`);
|
|
9952
10027
|
if (size > MAX_FILE_BYTES) {
|
|
9953
10028
|
exit("rejected");
|
|
9954
10029
|
return c.json({ error: `"${basename4(absolute)}" exceeds the 50 MB limit` }, 400);
|
|
@@ -10837,6 +10912,7 @@ function createWebchatRoutes(deps) {
|
|
|
10837
10912
|
let text;
|
|
10838
10913
|
let target;
|
|
10839
10914
|
let files = [];
|
|
10915
|
+
let clearSignal = false;
|
|
10840
10916
|
const contentType = c.req.header("content-type") ?? "";
|
|
10841
10917
|
const isMultipart = contentType.startsWith("multipart/form-data");
|
|
10842
10918
|
if (isMultipart) {
|
|
@@ -10857,6 +10933,7 @@ function createWebchatRoutes(deps) {
|
|
|
10857
10933
|
}
|
|
10858
10934
|
target = rawTarget;
|
|
10859
10935
|
}
|
|
10936
|
+
clearSignal = formData.get("clearSignal") === "true";
|
|
10860
10937
|
} else {
|
|
10861
10938
|
const body = await c.req.json().catch(() => null);
|
|
10862
10939
|
const typed = body?.text;
|
|
@@ -10871,14 +10948,25 @@ function createWebchatRoutes(deps) {
|
|
|
10871
10948
|
}
|
|
10872
10949
|
target = rawTarget;
|
|
10873
10950
|
}
|
|
10951
|
+
clearSignal = body?.clearSignal === true;
|
|
10874
10952
|
}
|
|
10875
10953
|
let deliveryKey = WEBCHAT_ADMIN_KEY;
|
|
10876
10954
|
let deliverySessionId;
|
|
10877
10955
|
let deliveryAccountId = accountId;
|
|
10878
10956
|
const explicitTarget = target !== void 0;
|
|
10957
|
+
let sendIdFromResolve;
|
|
10879
10958
|
if (target === void 0) {
|
|
10880
10959
|
const latest = latestAdminWebchatSessionId(requesterUserId, primaryUserId, scopeAccountId, multiAccount);
|
|
10881
|
-
|
|
10960
|
+
let sendTargetMode = "bootstrap";
|
|
10961
|
+
if (clearSignal) {
|
|
10962
|
+
sendTargetMode = "spawn-new";
|
|
10963
|
+
} else if (latest !== null) {
|
|
10964
|
+
target = latest;
|
|
10965
|
+
sendTargetMode = "resume-latest";
|
|
10966
|
+
}
|
|
10967
|
+
const sendId2 = ++sendSeq;
|
|
10968
|
+
sendIdFromResolve = sendId2;
|
|
10969
|
+
console.error(`[webchat:inbound] op=send-target-resolve seq=${sendId2} mode=${sendTargetMode} hadLatest=${latest !== null} clearSignal=${clearSignal}`);
|
|
10882
10970
|
}
|
|
10883
10971
|
if (explicitTarget && target !== void 0) {
|
|
10884
10972
|
const t = readTargetOwner(target);
|
|
@@ -10993,7 +11081,7 @@ ${note}` : note;
|
|
|
10993
11081
|
if (deliveredIntId !== null) {
|
|
10994
11082
|
console.log(`[webchat-interrupt] op=delivered intId=${deliveredIntId} sessionId=${deliverySessionId.slice(0, 8)}`);
|
|
10995
11083
|
}
|
|
10996
|
-
const sendId = ++sendSeq;
|
|
11084
|
+
const sendId = sendIdFromResolve ?? ++sendSeq;
|
|
10997
11085
|
let turnSessionId = deliverySessionId;
|
|
10998
11086
|
if (turnSessionId === void 0) {
|
|
10999
11087
|
let canonAccount;
|
|
@@ -11163,7 +11251,7 @@ ${note}` : note;
|
|
|
11163
11251
|
}
|
|
11164
11252
|
}
|
|
11165
11253
|
const indicators = await fetchComposerIndicators(sessionId);
|
|
11166
|
-
return c.json({ sessionId, projectDir, sizeBytes: jsonlSizeBytes(projectDir, sessionId), pendingPermissionPrompt: deps.pendingPromptFor?.(`session:${sessionId}`) ?? null, deliveryFailure: deps.deliveryFailureFor?.(`session:${sessionId}`) ?? null, ...indicators, ...readLevers(account) });
|
|
11254
|
+
return c.json({ sessionId, projectDir, source, sizeBytes: jsonlSizeBytes(projectDir, sessionId), pendingPermissionPrompt: deps.pendingPromptFor?.(`session:${sessionId}`) ?? null, deliveryFailure: deps.deliveryFailureFor?.(`session:${sessionId}`) ?? null, ...indicators, ...readLevers(account) });
|
|
11167
11255
|
});
|
|
11168
11256
|
app62.post("/permission-verdict", requireAdminSession, async (c) => {
|
|
11169
11257
|
const body = await c.req.json().catch(() => null);
|
|
@@ -17099,6 +17187,142 @@ function isHiddenByDefault(label) {
|
|
|
17099
17187
|
return HIDDEN_BY_DEFAULT_LABELS.has(label);
|
|
17100
17188
|
}
|
|
17101
17189
|
|
|
17190
|
+
// server/lib/top-level-labels.ts
|
|
17191
|
+
import { readdirSync as readdirSync19, readFileSync as readFileSync30 } from "fs";
|
|
17192
|
+
import { join as join30, resolve as resolve23 } from "path";
|
|
17193
|
+
var STATIC_TOP_LEVEL_LABELS = Object.freeze(
|
|
17194
|
+
/* @__PURE__ */ new Set([
|
|
17195
|
+
// Base-schema infra (schema-base.md has no top-level section).
|
|
17196
|
+
"LocalBusiness",
|
|
17197
|
+
"Service",
|
|
17198
|
+
"PriceSpecification",
|
|
17199
|
+
"OpeningHoursSpecification",
|
|
17200
|
+
"Organization",
|
|
17201
|
+
"Person",
|
|
17202
|
+
"UserProfile",
|
|
17203
|
+
"Preference",
|
|
17204
|
+
"AdminUser",
|
|
17205
|
+
"AccessGrant",
|
|
17206
|
+
"KnowledgeDocument",
|
|
17207
|
+
"DigitalDocument",
|
|
17208
|
+
"CreativeWork",
|
|
17209
|
+
"Question",
|
|
17210
|
+
"FAQPage",
|
|
17211
|
+
"DefinedTerm",
|
|
17212
|
+
"Review",
|
|
17213
|
+
"ImageObject",
|
|
17214
|
+
"Invoice",
|
|
17215
|
+
"Task",
|
|
17216
|
+
"Project",
|
|
17217
|
+
"Event",
|
|
17218
|
+
"Workflow",
|
|
17219
|
+
"Email",
|
|
17220
|
+
"EmailAccount",
|
|
17221
|
+
"Agent",
|
|
17222
|
+
// Knowledge-work vertical: schema-knowledge-work.md has no top-level
|
|
17223
|
+
// section yet.
|
|
17224
|
+
"Objective",
|
|
17225
|
+
"KeyResult",
|
|
17226
|
+
"Decision",
|
|
17227
|
+
"Risk",
|
|
17228
|
+
"Source",
|
|
17229
|
+
"Finding",
|
|
17230
|
+
"Hypothesis"
|
|
17231
|
+
])
|
|
17232
|
+
);
|
|
17233
|
+
function splitPipeRow(row) {
|
|
17234
|
+
let trimmed = row.trim();
|
|
17235
|
+
if (trimmed.startsWith("|")) trimmed = trimmed.slice(1);
|
|
17236
|
+
if (trimmed.endsWith("|")) trimmed = trimmed.slice(0, -1);
|
|
17237
|
+
const cells = [];
|
|
17238
|
+
let inBacktick = false;
|
|
17239
|
+
let start = 0;
|
|
17240
|
+
for (let i = 0; i < trimmed.length; i++) {
|
|
17241
|
+
const ch = trimmed[i];
|
|
17242
|
+
if (ch === "`") inBacktick = !inBacktick;
|
|
17243
|
+
else if (ch === "|" && !inBacktick) {
|
|
17244
|
+
cells.push(trimmed.slice(start, i).trim());
|
|
17245
|
+
start = i + 1;
|
|
17246
|
+
}
|
|
17247
|
+
}
|
|
17248
|
+
cells.push(trimmed.slice(start).trim());
|
|
17249
|
+
return cells;
|
|
17250
|
+
}
|
|
17251
|
+
var stripBackticks = (s) => s.replace(/`/g, "").trim();
|
|
17252
|
+
function parseTopLevelLabels(content) {
|
|
17253
|
+
const lines = content.split(/\r?\n/);
|
|
17254
|
+
let i = 0;
|
|
17255
|
+
while (i < lines.length) {
|
|
17256
|
+
if (lines[i].toLowerCase().startsWith("## top-level node types")) break;
|
|
17257
|
+
i++;
|
|
17258
|
+
}
|
|
17259
|
+
if (i >= lines.length) return [];
|
|
17260
|
+
i++;
|
|
17261
|
+
for (; i < lines.length - 1; i++) {
|
|
17262
|
+
const line = lines[i];
|
|
17263
|
+
if (line.startsWith("## ")) return [];
|
|
17264
|
+
if (!line.includes("|")) continue;
|
|
17265
|
+
const sep9 = lines[i + 1];
|
|
17266
|
+
if (!/^\s*\|?[\s:|-]+\|?\s*$/.test(sep9) || !sep9.includes("-")) continue;
|
|
17267
|
+
const headers = splitPipeRow(line).map((h) => stripBackticks(h).toLowerCase());
|
|
17268
|
+
const labelCol = headers.findIndex((h) => h === "neo4j label");
|
|
17269
|
+
if (labelCol === -1) return [];
|
|
17270
|
+
const out = [];
|
|
17271
|
+
let j = i + 2;
|
|
17272
|
+
for (; j < lines.length; j++) {
|
|
17273
|
+
const row = lines[j];
|
|
17274
|
+
if (!row.trim() || !row.includes("|")) break;
|
|
17275
|
+
const cells = splitPipeRow(row);
|
|
17276
|
+
const label = stripBackticks(cells[labelCol] ?? "");
|
|
17277
|
+
if (label) out.push(label);
|
|
17278
|
+
}
|
|
17279
|
+
return out;
|
|
17280
|
+
}
|
|
17281
|
+
return [];
|
|
17282
|
+
}
|
|
17283
|
+
function resolveReferencesDir() {
|
|
17284
|
+
if (process.env.MAXY_PLATFORM_ROOT) {
|
|
17285
|
+
return join30(process.env.MAXY_PLATFORM_ROOT, "plugins", "memory", "references");
|
|
17286
|
+
}
|
|
17287
|
+
return resolve23(import.meta.dirname, "../../../plugins/memory/references");
|
|
17288
|
+
}
|
|
17289
|
+
function parseTableTopLevelLabels(referencesDir) {
|
|
17290
|
+
const labels = /* @__PURE__ */ new Set();
|
|
17291
|
+
const contributingFiles = [];
|
|
17292
|
+
try {
|
|
17293
|
+
const fileNames = readdirSync19(referencesDir).filter((f) => f.startsWith("schema-") && f.endsWith(".md")).sort();
|
|
17294
|
+
for (const fileName of fileNames) {
|
|
17295
|
+
const content = readFileSync30(join30(referencesDir, fileName), "utf-8");
|
|
17296
|
+
const parsed = parseTopLevelLabels(content);
|
|
17297
|
+
if (parsed.length > 0) contributingFiles.push(fileName);
|
|
17298
|
+
for (const label of parsed) labels.add(label);
|
|
17299
|
+
}
|
|
17300
|
+
} catch (err) {
|
|
17301
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
17302
|
+
process.stderr.write(
|
|
17303
|
+
`[graph-page] op=top-level-labels source=ontology status=error dir=${referencesDir} reason="${message}"
|
|
17304
|
+
`
|
|
17305
|
+
);
|
|
17306
|
+
throw err;
|
|
17307
|
+
}
|
|
17308
|
+
return { labels, contributingFiles };
|
|
17309
|
+
}
|
|
17310
|
+
var cached = null;
|
|
17311
|
+
function getTopLevelLabelAllowlist(opts = {}) {
|
|
17312
|
+
if (!opts.referencesDir && cached) return cached;
|
|
17313
|
+
const referencesDir = opts.referencesDir ?? resolveReferencesDir();
|
|
17314
|
+
const { labels: derived, contributingFiles } = parseTableTopLevelLabels(referencesDir);
|
|
17315
|
+
const derivedCount = derived.size;
|
|
17316
|
+
for (const label of STATIC_TOP_LEVEL_LABELS) derived.add(label);
|
|
17317
|
+
const result = Object.freeze(derived);
|
|
17318
|
+
process.stderr.write(
|
|
17319
|
+
`[graph-page] op=top-level-labels source=ontology count=${derived.size} files=${contributingFiles.join("|") || "none"} derived=${derivedCount} static=${STATIC_TOP_LEVEL_LABELS.size}
|
|
17320
|
+
`
|
|
17321
|
+
);
|
|
17322
|
+
if (!opts.referencesDir) cached = result;
|
|
17323
|
+
return result;
|
|
17324
|
+
}
|
|
17325
|
+
|
|
17102
17326
|
// server/routes/admin/graph-subgraph.ts
|
|
17103
17327
|
var CEILING = 2e3;
|
|
17104
17328
|
var TOP_LEVEL_LIMIT = 1e3;
|
|
@@ -17220,7 +17444,7 @@ async function handleDefault(c, accountId) {
|
|
|
17220
17444
|
}
|
|
17221
17445
|
async function handleDefaultTopLevel(c, accountId, flags) {
|
|
17222
17446
|
const { includeTrashed, includeAgentActions } = flags;
|
|
17223
|
-
const labels = [...
|
|
17447
|
+
const labels = [...getTopLevelLabelAllowlist()].filter((l) => !HIDDEN_BY_DEFAULT_LABELS.has(l));
|
|
17224
17448
|
const agentActionLabels = [...AGENT_ACTION_LABELS];
|
|
17225
17449
|
const started = Date.now();
|
|
17226
17450
|
const session = getSession();
|
|
@@ -18031,7 +18255,7 @@ app26.get("/", requireAdminSession, async (c) => {
|
|
|
18031
18255
|
const started = Date.now();
|
|
18032
18256
|
const session = getSession();
|
|
18033
18257
|
try {
|
|
18034
|
-
const allowedSet = new Set(
|
|
18258
|
+
const allowedSet = new Set(getTopLevelLabelAllowlist());
|
|
18035
18259
|
if (includeAgentActions) {
|
|
18036
18260
|
for (const label of AGENT_ACTION_LABELS) allowedSet.add(label);
|
|
18037
18261
|
}
|
|
@@ -18193,12 +18417,12 @@ function validateBody(body) {
|
|
|
18193
18417
|
if (typeof lbl !== "string") {
|
|
18194
18418
|
return { ok: false, code: "shape", reason: "labels must be an array of strings" };
|
|
18195
18419
|
}
|
|
18196
|
-
if (!
|
|
18420
|
+
if (!getTopLevelLabelAllowlist().has(lbl)) {
|
|
18197
18421
|
return {
|
|
18198
18422
|
ok: false,
|
|
18199
18423
|
code: "not-eligible-chip",
|
|
18200
18424
|
label: lbl,
|
|
18201
|
-
reason: `label "${lbl}" is not eligible for default view \u2014 outside the chip allowlist
|
|
18425
|
+
reason: `label "${lbl}" is not eligible for default view \u2014 outside the chip allowlist`
|
|
18202
18426
|
};
|
|
18203
18427
|
}
|
|
18204
18428
|
seen.add(lbl);
|
|
@@ -18221,7 +18445,7 @@ var graph_default_view_default = app27;
|
|
|
18221
18445
|
|
|
18222
18446
|
// server/routes/admin/sidebar-artefacts.ts
|
|
18223
18447
|
import { readdir as readdir4, stat as stat5 } from "fs/promises";
|
|
18224
|
-
import { resolve as
|
|
18448
|
+
import { resolve as resolve24, relative as relative5, isAbsolute as isAbsolute2, sep as sep7, basename as basename10 } from "path";
|
|
18225
18449
|
import { existsSync as existsSync27 } from "fs";
|
|
18226
18450
|
var LIMIT = 50;
|
|
18227
18451
|
var ADMIN_AGENT_FILES = ["IDENTITY.md", "SOUL.md", "KNOWLEDGE.md"];
|
|
@@ -18241,7 +18465,7 @@ app28.get("/", requireAdminSession, async (c) => {
|
|
|
18241
18465
|
if (accountFiles === null) {
|
|
18242
18466
|
return c.json({ error: "Failed to load artefacts" }, 500);
|
|
18243
18467
|
}
|
|
18244
|
-
const accountDir =
|
|
18468
|
+
const accountDir = resolve24(ACCOUNTS_DIR, accountId);
|
|
18245
18469
|
const agents = await fetchAgentTemplateRows(accountDir);
|
|
18246
18470
|
const artefacts = [...accountFiles, ...agents].sort(
|
|
18247
18471
|
(a, b) => (b.updatedAt ?? "").localeCompare(a.updatedAt ?? "")
|
|
@@ -18293,8 +18517,8 @@ async function fetchAccountFileArtefacts(accountId) {
|
|
|
18293
18517
|
async function fetchAgentTemplateRows(accountDir) {
|
|
18294
18518
|
const rows = [];
|
|
18295
18519
|
for (const filename of ADMIN_AGENT_FILES) {
|
|
18296
|
-
const overridePath =
|
|
18297
|
-
const bundledPath =
|
|
18520
|
+
const overridePath = resolve24(accountDir, "agents", "admin", filename);
|
|
18521
|
+
const bundledPath = resolve24(PLATFORM_ROOT, "templates", "agents", "admin", filename);
|
|
18298
18522
|
const labelStem = filename.replace(/\.md$/, "");
|
|
18299
18523
|
const row = await readAgentTemplateRow({
|
|
18300
18524
|
id: `agent-template:admin:${filename}`,
|
|
@@ -18307,12 +18531,12 @@ async function fetchAgentTemplateRows(accountDir) {
|
|
|
18307
18531
|
});
|
|
18308
18532
|
if (row) rows.push(row);
|
|
18309
18533
|
}
|
|
18310
|
-
const overrideDir =
|
|
18311
|
-
const bundledDir =
|
|
18534
|
+
const overrideDir = resolve24(accountDir, "specialists", "agents");
|
|
18535
|
+
const bundledDir = resolve24(PLATFORM_ROOT, "templates", "specialists", "agents");
|
|
18312
18536
|
const specialistNames = await unionSpecialistFilenames(overrideDir, bundledDir);
|
|
18313
18537
|
for (const filename of specialistNames) {
|
|
18314
|
-
const overridePath =
|
|
18315
|
-
const bundledPath =
|
|
18538
|
+
const overridePath = resolve24(overrideDir, filename);
|
|
18539
|
+
const bundledPath = resolve24(bundledDir, filename);
|
|
18316
18540
|
const row = await readAgentTemplateRow({
|
|
18317
18541
|
id: `agent-template:specialist:${filename}`,
|
|
18318
18542
|
displayName: filename.replace(/\.md$/, ""),
|
|
@@ -18594,7 +18818,7 @@ var session_usage_default = app33;
|
|
|
18594
18818
|
|
|
18595
18819
|
// server/routes/admin/session-rc-spawn.ts
|
|
18596
18820
|
import { randomUUID as randomUUID10 } from "crypto";
|
|
18597
|
-
var
|
|
18821
|
+
var ACCOUNT_UUID_RE3 = /^[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}$/;
|
|
18598
18822
|
function resolveSpawnPlan(host, operatorDomains, body, mintId = randomUUID10, adminUserId, authenticatedName, callerAccountId) {
|
|
18599
18823
|
const operator = isOperatorHost(host, operatorDomains);
|
|
18600
18824
|
const resumeId = typeof body.sessionId === "string" && body.sessionId.length > 0 ? body.sessionId : void 0;
|
|
@@ -18605,8 +18829,8 @@ function resolveSpawnPlan(host, operatorDomains, body, mintId = randomUUID10, ad
|
|
|
18605
18829
|
const pickedModel = kind === "new" && typeof body.model === "string" && isSelectableModel(body.model) ? body.model : null;
|
|
18606
18830
|
const pickedEffort = kind === "new" && typeof body.effort === "string" && isNewSessionEffortLevel(body.effort) ? body.effort : null;
|
|
18607
18831
|
const paramsSource = pickedModel !== null && pickedEffort !== null ? "picker" : "default";
|
|
18608
|
-
const callerTargetAccountId = typeof callerAccountId === "string" &&
|
|
18609
|
-
const bodyTargetAccountId = typeof body.targetAccountId === "string" &&
|
|
18832
|
+
const callerTargetAccountId = typeof callerAccountId === "string" && ACCOUNT_UUID_RE3.test(callerAccountId) ? callerAccountId : void 0;
|
|
18833
|
+
const bodyTargetAccountId = typeof body.targetAccountId === "string" && ACCOUNT_UUID_RE3.test(body.targetAccountId) ? body.targetAccountId : void 0;
|
|
18610
18834
|
const targetAccountId = kind === "new" ? callerTargetAccountId : bodyTargetAccountId;
|
|
18611
18835
|
const payload = {};
|
|
18612
18836
|
let sessionId;
|
|
@@ -18957,24 +19181,24 @@ app36.get("/", async (c) => {
|
|
|
18957
19181
|
var system_stats_default = app36;
|
|
18958
19182
|
|
|
18959
19183
|
// server/routes/admin/health.ts
|
|
18960
|
-
import { existsSync as existsSync28, readFileSync as
|
|
18961
|
-
import { resolve as
|
|
18962
|
-
var PLATFORM_ROOT6 = process.env.MAXY_PLATFORM_ROOT ??
|
|
19184
|
+
import { existsSync as existsSync28, readFileSync as readFileSync31 } from "fs";
|
|
19185
|
+
import { resolve as resolve25, join as join31 } from "path";
|
|
19186
|
+
var PLATFORM_ROOT6 = process.env.MAXY_PLATFORM_ROOT ?? resolve25(process.cwd(), "..");
|
|
18963
19187
|
var brandHostname = "maxy";
|
|
18964
|
-
var brandJsonPath =
|
|
19188
|
+
var brandJsonPath = join31(PLATFORM_ROOT6, "config", "brand.json");
|
|
18965
19189
|
if (existsSync28(brandJsonPath)) {
|
|
18966
19190
|
try {
|
|
18967
|
-
const brand = JSON.parse(
|
|
19191
|
+
const brand = JSON.parse(readFileSync31(brandJsonPath, "utf-8"));
|
|
18968
19192
|
if (brand.hostname) brandHostname = brand.hostname;
|
|
18969
19193
|
} catch {
|
|
18970
19194
|
}
|
|
18971
19195
|
}
|
|
18972
|
-
var VERSION_FILE =
|
|
19196
|
+
var VERSION_FILE = resolve25(PLATFORM_ROOT6, `config/.${brandHostname}-version`);
|
|
18973
19197
|
var PROCESS_STARTED_AT = (/* @__PURE__ */ new Date()).toISOString();
|
|
18974
19198
|
var PROBE_TIMEOUT_MS = 1e3;
|
|
18975
19199
|
function readVersion() {
|
|
18976
19200
|
if (!existsSync28(VERSION_FILE)) return "unknown";
|
|
18977
|
-
return
|
|
19201
|
+
return readFileSync31(VERSION_FILE, "utf-8").trim() || "unknown";
|
|
18978
19202
|
}
|
|
18979
19203
|
async function probeConversationDb() {
|
|
18980
19204
|
let session;
|
|
@@ -19751,7 +19975,7 @@ var browser_default = app44;
|
|
|
19751
19975
|
|
|
19752
19976
|
// server/routes/admin/calendar.ts
|
|
19753
19977
|
import { existsSync as existsSync29 } from "fs";
|
|
19754
|
-
import { join as
|
|
19978
|
+
import { join as join33 } from "path";
|
|
19755
19979
|
|
|
19756
19980
|
// server/lib/calendar-ics.ts
|
|
19757
19981
|
var CRLF = "\r\n";
|
|
@@ -19850,8 +20074,8 @@ function countVEvents(ics) {
|
|
|
19850
20074
|
}
|
|
19851
20075
|
|
|
19852
20076
|
// server/lib/calendar-availability.ts
|
|
19853
|
-
import { readFileSync as
|
|
19854
|
-
import { join as
|
|
20077
|
+
import { readFileSync as readFileSync32 } from "fs";
|
|
20078
|
+
import { join as join32 } from "path";
|
|
19855
20079
|
var AVAILABILITY_FILENAME = "calendar-availability.json";
|
|
19856
20080
|
var REQUIRED_KEYS = [
|
|
19857
20081
|
"timezone",
|
|
@@ -19860,10 +20084,10 @@ var REQUIRED_KEYS = [
|
|
|
19860
20084
|
"weekly"
|
|
19861
20085
|
];
|
|
19862
20086
|
function readAvailabilityConfig(accountDir) {
|
|
19863
|
-
const path2 =
|
|
20087
|
+
const path2 = join32(accountDir, AVAILABILITY_FILENAME);
|
|
19864
20088
|
let raw;
|
|
19865
20089
|
try {
|
|
19866
|
-
raw =
|
|
20090
|
+
raw = readFileSync32(path2, "utf-8");
|
|
19867
20091
|
} catch {
|
|
19868
20092
|
throw new Error(`availability not configured: ${path2} not found`);
|
|
19869
20093
|
}
|
|
@@ -20222,7 +20446,7 @@ app45.get("/booking-link", requireAdminSession, (c) => {
|
|
|
20222
20446
|
console.error('[calendar] op=booking-link-fail err="no account configured"');
|
|
20223
20447
|
return c.json({ bookingDomain: null });
|
|
20224
20448
|
}
|
|
20225
|
-
if (!existsSync29(
|
|
20449
|
+
if (!existsSync29(join33(account.accountDir, AVAILABILITY_FILENAME))) {
|
|
20226
20450
|
console.log("[calendar] op=booking-link bookingDomain=none source=availability-config");
|
|
20227
20451
|
return c.json({ bookingDomain: null });
|
|
20228
20452
|
}
|
|
@@ -20696,9 +20920,9 @@ var verify_token_default = app52;
|
|
|
20696
20920
|
|
|
20697
20921
|
// app/lib/access-email.ts
|
|
20698
20922
|
import { spawn as spawn2 } from "child_process";
|
|
20699
|
-
import { resolve as
|
|
20700
|
-
var PLATFORM_ROOT7 = process.env.MAXY_PLATFORM_ROOT ??
|
|
20701
|
-
var SEND_SCRIPT =
|
|
20923
|
+
import { resolve as resolve26 } from "path";
|
|
20924
|
+
var PLATFORM_ROOT7 = process.env.MAXY_PLATFORM_ROOT ?? resolve26(process.cwd(), "..");
|
|
20925
|
+
var SEND_SCRIPT = resolve26(
|
|
20702
20926
|
PLATFORM_ROOT7,
|
|
20703
20927
|
"plugins",
|
|
20704
20928
|
"email",
|
|
@@ -20840,8 +21064,8 @@ app54.route("/request-magic-link", request_magic_link_default);
|
|
|
20840
21064
|
var access_default = app54;
|
|
20841
21065
|
|
|
20842
21066
|
// server/routes/sites.ts
|
|
20843
|
-
import { existsSync as existsSync30, readFileSync as
|
|
20844
|
-
import { resolve as
|
|
21067
|
+
import { existsSync as existsSync30, readFileSync as readFileSync33, realpathSync as realpathSync6, statSync as statSync12 } from "fs";
|
|
21068
|
+
import { resolve as resolve27 } from "path";
|
|
20845
21069
|
var SAFE_SEG_RE = /^[a-z0-9_][a-z0-9_.-]{0,99}$/i;
|
|
20846
21070
|
var MIME = {
|
|
20847
21071
|
".html": "text/html; charset=utf-8",
|
|
@@ -20898,8 +21122,8 @@ app55.get("/:rel{.*}", (c) => {
|
|
|
20898
21122
|
}
|
|
20899
21123
|
segments.push(seg);
|
|
20900
21124
|
}
|
|
20901
|
-
const rootDir =
|
|
20902
|
-
let filePath = segments.length === 0 ? rootDir :
|
|
21125
|
+
const rootDir = resolve27(account.accountDir, "sites");
|
|
21126
|
+
let filePath = segments.length === 0 ? rootDir : resolve27(rootDir, ...segments);
|
|
20903
21127
|
if (filePath !== rootDir && !filePath.startsWith(rootDir + "/")) {
|
|
20904
21128
|
console.error(`[sites] path-traversal-rejected path=${reqPath} reason=escape status=403`);
|
|
20905
21129
|
return c.text("Forbidden", 403);
|
|
@@ -20919,7 +21143,7 @@ app55.get("/:rel{.*}", (c) => {
|
|
|
20919
21143
|
return c.redirect(target, 301);
|
|
20920
21144
|
}
|
|
20921
21145
|
if (stat8?.isDirectory()) {
|
|
20922
|
-
filePath =
|
|
21146
|
+
filePath = resolve27(filePath, "index.html");
|
|
20923
21147
|
}
|
|
20924
21148
|
if (!filePath.startsWith(rootDir + "/")) {
|
|
20925
21149
|
console.error(`[sites] path-traversal-rejected path=${reqPath} reason=escape status=403`);
|
|
@@ -20944,7 +21168,7 @@ app55.get("/:rel{.*}", (c) => {
|
|
|
20944
21168
|
}
|
|
20945
21169
|
let body;
|
|
20946
21170
|
try {
|
|
20947
|
-
body =
|
|
21171
|
+
body = readFileSync33(realPath);
|
|
20948
21172
|
} catch (err) {
|
|
20949
21173
|
const code = err?.code;
|
|
20950
21174
|
if (code === "EISDIR") {
|
|
@@ -20980,7 +21204,7 @@ var sites_default = app55;
|
|
|
20980
21204
|
|
|
20981
21205
|
// app/lib/visitor-token.ts
|
|
20982
21206
|
import { createHmac, randomBytes, timingSafeEqual } from "crypto";
|
|
20983
|
-
import { mkdirSync as mkdirSync7, readFileSync as
|
|
21207
|
+
import { mkdirSync as mkdirSync7, readFileSync as readFileSync34, writeFileSync as writeFileSync11 } from "fs";
|
|
20984
21208
|
import { dirname as dirname11 } from "path";
|
|
20985
21209
|
var TOKEN_PREFIX = "v1.";
|
|
20986
21210
|
var SECRET_BYTES = 32;
|
|
@@ -20989,7 +21213,7 @@ var cachedSecret = null;
|
|
|
20989
21213
|
function getSecret() {
|
|
20990
21214
|
if (cachedSecret) return cachedSecret;
|
|
20991
21215
|
try {
|
|
20992
|
-
const hex2 =
|
|
21216
|
+
const hex2 = readFileSync34(VISITOR_TOKEN_SECRET_FILE, "utf-8").trim();
|
|
20993
21217
|
if (hex2.length === SECRET_BYTES * 2) {
|
|
20994
21218
|
cachedSecret = Buffer.from(hex2, "hex");
|
|
20995
21219
|
return cachedSecret;
|
|
@@ -21003,7 +21227,7 @@ function getSecret() {
|
|
|
21003
21227
|
console.log(`[visitor-token] secret minted path=${VISITOR_TOKEN_SECRET_FILE}`);
|
|
21004
21228
|
} catch {
|
|
21005
21229
|
}
|
|
21006
|
-
const hex =
|
|
21230
|
+
const hex = readFileSync34(VISITOR_TOKEN_SECRET_FILE, "utf-8").trim();
|
|
21007
21231
|
cachedSecret = Buffer.from(hex, "hex");
|
|
21008
21232
|
return cachedSecret;
|
|
21009
21233
|
}
|
|
@@ -21056,20 +21280,20 @@ var VISITOR_COOKIE_NAME = "mxy_v";
|
|
|
21056
21280
|
var VISITOR_COOKIE_MAX_AGE_SECONDS = Math.floor(DEFAULT_TTL_MS / 1e3);
|
|
21057
21281
|
|
|
21058
21282
|
// app/lib/brand-config.ts
|
|
21059
|
-
import { existsSync as existsSync31, readFileSync as
|
|
21060
|
-
import { join as
|
|
21061
|
-
var
|
|
21283
|
+
import { existsSync as existsSync31, readFileSync as readFileSync35 } from "fs";
|
|
21284
|
+
import { join as join34 } from "path";
|
|
21285
|
+
var cached2 = null;
|
|
21062
21286
|
var cachedAttempted = false;
|
|
21063
21287
|
function readBrandConfig() {
|
|
21064
|
-
if (cachedAttempted) return
|
|
21288
|
+
if (cachedAttempted) return cached2;
|
|
21065
21289
|
cachedAttempted = true;
|
|
21066
21290
|
const platformRoot3 = process.env.MAXY_PLATFORM_ROOT;
|
|
21067
21291
|
if (!platformRoot3) return null;
|
|
21068
|
-
const brandPath =
|
|
21292
|
+
const brandPath = join34(platformRoot3, "config", "brand.json");
|
|
21069
21293
|
if (!existsSync31(brandPath)) return null;
|
|
21070
21294
|
try {
|
|
21071
|
-
|
|
21072
|
-
return
|
|
21295
|
+
cached2 = JSON.parse(readFileSync35(brandPath, "utf-8"));
|
|
21296
|
+
return cached2;
|
|
21073
21297
|
} catch {
|
|
21074
21298
|
return null;
|
|
21075
21299
|
}
|
|
@@ -21556,14 +21780,14 @@ async function writeEvent(opts) {
|
|
|
21556
21780
|
var visitor_event_default = app58;
|
|
21557
21781
|
|
|
21558
21782
|
// server/routes/session.ts
|
|
21559
|
-
import { resolve as
|
|
21783
|
+
import { resolve as resolve28 } from "path";
|
|
21560
21784
|
import { existsSync as existsSync32, writeFileSync as writeFileSync12, mkdirSync as mkdirSync8 } from "fs";
|
|
21561
21785
|
var UUID_RE4 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
21562
21786
|
function writeBrandingCache(accountId, agentSlug, branding) {
|
|
21563
21787
|
try {
|
|
21564
|
-
const cacheDir =
|
|
21788
|
+
const cacheDir = resolve28(MAXY_DIR, "branding-cache", accountId);
|
|
21565
21789
|
mkdirSync8(cacheDir, { recursive: true });
|
|
21566
|
-
writeFileSync12(
|
|
21790
|
+
writeFileSync12(resolve28(cacheDir, `${agentSlug}.json`), JSON.stringify(branding), "utf-8");
|
|
21567
21791
|
} catch (err) {
|
|
21568
21792
|
console.error(`[branding] cache write failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
21569
21793
|
}
|
|
@@ -21640,8 +21864,8 @@ app59.post("/", async (c) => {
|
|
|
21640
21864
|
}
|
|
21641
21865
|
let agentConfig = null;
|
|
21642
21866
|
if (account) {
|
|
21643
|
-
const agentDir =
|
|
21644
|
-
if (!existsSync32(agentDir) || !existsSync32(
|
|
21867
|
+
const agentDir = resolve28(account.accountDir, "agents", agentSlug);
|
|
21868
|
+
if (!existsSync32(agentDir) || !existsSync32(resolve28(agentDir, "config.json"))) {
|
|
21645
21869
|
return c.json({ error: "Agent not found" }, 404);
|
|
21646
21870
|
}
|
|
21647
21871
|
agentConfig = resolveAgentConfig(account.accountDir, agentSlug);
|
|
@@ -22087,8 +22311,8 @@ function startGraphHealthTimer() {
|
|
|
22087
22311
|
|
|
22088
22312
|
// app/lib/file-watcher.ts
|
|
22089
22313
|
import * as fsp2 from "fs/promises";
|
|
22090
|
-
import { resolve as
|
|
22091
|
-
var
|
|
22314
|
+
import { resolve as resolve29, sep as sep8 } from "path";
|
|
22315
|
+
var ACCOUNT_UUID_RE4 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
22092
22316
|
var DEFAULT_COALESCE_MS = 500;
|
|
22093
22317
|
var ROOTS = ["accounts"];
|
|
22094
22318
|
function _routeEvent(rootName, filename) {
|
|
@@ -22096,7 +22320,7 @@ function _routeEvent(rootName, filename) {
|
|
|
22096
22320
|
const segs = filename.split(sep8).filter(Boolean);
|
|
22097
22321
|
if (segs.length < 2) return null;
|
|
22098
22322
|
const accountId = segs[0];
|
|
22099
|
-
if (!
|
|
22323
|
+
if (!ACCOUNT_UUID_RE4.test(accountId)) return null;
|
|
22100
22324
|
return { accountId, relativePath: `${rootName}/${segs.join("/")}` };
|
|
22101
22325
|
}
|
|
22102
22326
|
async function startFileWatcher(opts = {}) {
|
|
@@ -22106,7 +22330,7 @@ async function startFileWatcher(opts = {}) {
|
|
|
22106
22330
|
const dropFn = opts.drop ?? dropFileIndex;
|
|
22107
22331
|
for (const r of ROOTS) {
|
|
22108
22332
|
try {
|
|
22109
|
-
await fsp2.mkdir(
|
|
22333
|
+
await fsp2.mkdir(resolve29(dataRoot, r), { recursive: true });
|
|
22110
22334
|
} catch (err) {
|
|
22111
22335
|
console.error(
|
|
22112
22336
|
`[file-watcher] start-failed root="${r}" err="${err.message}" \u2014 index will be maintained by the 5-min reconcile backstop only`
|
|
@@ -22127,7 +22351,7 @@ async function startFileWatcher(opts = {}) {
|
|
|
22127
22351
|
timers.set(relativePath, t);
|
|
22128
22352
|
}
|
|
22129
22353
|
async function runHook(relativePath, accountId) {
|
|
22130
|
-
const absolute =
|
|
22354
|
+
const absolute = resolve29(dataRoot, relativePath);
|
|
22131
22355
|
let exists = false;
|
|
22132
22356
|
try {
|
|
22133
22357
|
const st = await fsp2.stat(absolute);
|
|
@@ -22151,7 +22375,7 @@ async function startFileWatcher(opts = {}) {
|
|
|
22151
22375
|
}
|
|
22152
22376
|
}
|
|
22153
22377
|
async function watchRoot(rootName) {
|
|
22154
|
-
const absRoot =
|
|
22378
|
+
const absRoot = resolve29(dataRoot, rootName);
|
|
22155
22379
|
try {
|
|
22156
22380
|
const iter = fsp2.watch(absRoot, { recursive: true, signal: controller.signal });
|
|
22157
22381
|
for await (const event of iter) {
|
|
@@ -22190,8 +22414,8 @@ async function startFileWatcher(opts = {}) {
|
|
|
22190
22414
|
|
|
22191
22415
|
// app/lib/migrate-uploads.ts
|
|
22192
22416
|
import { mkdir as mkdir5, readdir as readdir5, rename as rename2, rm as rm3 } from "fs/promises";
|
|
22193
|
-
import { dirname as dirname12, relative as relative6, resolve as
|
|
22194
|
-
var
|
|
22417
|
+
import { dirname as dirname12, relative as relative6, resolve as resolve30 } from "path";
|
|
22418
|
+
var ACCOUNT_UUID_RE5 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
22195
22419
|
async function walkFiles(dir) {
|
|
22196
22420
|
const out = [];
|
|
22197
22421
|
let entries;
|
|
@@ -22201,7 +22425,7 @@ async function walkFiles(dir) {
|
|
|
22201
22425
|
return out;
|
|
22202
22426
|
}
|
|
22203
22427
|
for (const e of entries) {
|
|
22204
|
-
const abs =
|
|
22428
|
+
const abs = resolve30(dir, e.name);
|
|
22205
22429
|
if (e.isDirectory()) {
|
|
22206
22430
|
out.push(...await walkFiles(abs));
|
|
22207
22431
|
} else if (e.isFile()) {
|
|
@@ -22221,7 +22445,7 @@ async function relocateTree(srcDir, destDir, dataRoot, accountId, session) {
|
|
|
22221
22445
|
let moved = 0;
|
|
22222
22446
|
for (const oldAbs of files) {
|
|
22223
22447
|
const suffix = relative6(srcDir, oldAbs);
|
|
22224
|
-
const newAbs =
|
|
22448
|
+
const newAbs = resolve30(destDir, suffix);
|
|
22225
22449
|
await mkdir5(dirname12(newAbs), { recursive: true });
|
|
22226
22450
|
await rename2(oldAbs, newAbs);
|
|
22227
22451
|
moved++;
|
|
@@ -22248,7 +22472,7 @@ async function relocateTree(srcDir, destDir, dataRoot, accountId, session) {
|
|
|
22248
22472
|
}
|
|
22249
22473
|
async function migrateUploads(opts = {}) {
|
|
22250
22474
|
const dataRoot = opts.dataRoot ?? DATA_ROOT;
|
|
22251
|
-
const oldRoot =
|
|
22475
|
+
const oldRoot = resolve30(dataRoot, "uploads");
|
|
22252
22476
|
let topEntries;
|
|
22253
22477
|
try {
|
|
22254
22478
|
topEntries = await readdir5(oldRoot, { withFileTypes: true });
|
|
@@ -22267,10 +22491,10 @@ async function migrateUploads(opts = {}) {
|
|
|
22267
22491
|
try {
|
|
22268
22492
|
for (const entry of topEntries) {
|
|
22269
22493
|
const name = entry.name;
|
|
22270
|
-
if (
|
|
22494
|
+
if (ACCOUNT_UUID_RE5.test(name)) {
|
|
22271
22495
|
moved += await relocateTree(
|
|
22272
|
-
|
|
22273
|
-
|
|
22496
|
+
resolve30(oldRoot, name),
|
|
22497
|
+
resolve30(dataRoot, "accounts", name, "uploads"),
|
|
22274
22498
|
dataRoot,
|
|
22275
22499
|
name,
|
|
22276
22500
|
session
|
|
@@ -22282,8 +22506,8 @@ async function migrateUploads(opts = {}) {
|
|
|
22282
22506
|
continue;
|
|
22283
22507
|
}
|
|
22284
22508
|
moved += await relocateTree(
|
|
22285
|
-
|
|
22286
|
-
|
|
22509
|
+
resolve30(oldRoot, "public"),
|
|
22510
|
+
resolve30(dataRoot, "accounts", installAccountId, "uploads", "public"),
|
|
22287
22511
|
dataRoot,
|
|
22288
22512
|
null,
|
|
22289
22513
|
// public uploads carry no graph nodes
|
|
@@ -22314,7 +22538,7 @@ async function migrateUploads(opts = {}) {
|
|
|
22314
22538
|
// app/lib/migrate-admin-webchat-sidecars.ts
|
|
22315
22539
|
import { readdir as readdir6, readFile as readFile6, rename as rename3, writeFile as writeFile4, unlink as unlink3 } from "fs/promises";
|
|
22316
22540
|
import { existsSync as existsSync33 } from "fs";
|
|
22317
|
-
import { join as
|
|
22541
|
+
import { join as join35 } from "path";
|
|
22318
22542
|
var ADMIN_ROLE2 = "admin";
|
|
22319
22543
|
var WEBCHAT_CHANNEL2 = "webchat";
|
|
22320
22544
|
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;
|
|
@@ -22359,7 +22583,7 @@ async function collectLiveSidecars(projectsRoot) {
|
|
|
22359
22583
|
}
|
|
22360
22584
|
for (const slug of slugs) {
|
|
22361
22585
|
if (!slug.isDirectory()) continue;
|
|
22362
|
-
const slugDir =
|
|
22586
|
+
const slugDir = join35(projectsRoot, slug.name);
|
|
22363
22587
|
let entries;
|
|
22364
22588
|
try {
|
|
22365
22589
|
entries = await readdir6(slugDir, { withFileTypes: true });
|
|
@@ -22368,9 +22592,9 @@ async function collectLiveSidecars(projectsRoot) {
|
|
|
22368
22592
|
}
|
|
22369
22593
|
for (const entry of entries) {
|
|
22370
22594
|
if (entry.isFile() && SESSION_META_RE.test(entry.name)) {
|
|
22371
|
-
out.push(
|
|
22595
|
+
out.push(join35(slugDir, entry.name));
|
|
22372
22596
|
} else if (entry.isDirectory() && entry.name === "subagents") {
|
|
22373
|
-
const subDir =
|
|
22597
|
+
const subDir = join35(slugDir, entry.name);
|
|
22374
22598
|
let subs;
|
|
22375
22599
|
try {
|
|
22376
22600
|
subs = await readdir6(subDir, { withFileTypes: true });
|
|
@@ -22378,7 +22602,7 @@ async function collectLiveSidecars(projectsRoot) {
|
|
|
22378
22602
|
continue;
|
|
22379
22603
|
}
|
|
22380
22604
|
for (const s of subs) {
|
|
22381
|
-
if (s.isFile() && SESSION_META_RE.test(s.name)) out.push(
|
|
22605
|
+
if (s.isFile() && SESSION_META_RE.test(s.name)) out.push(join35(subDir, s.name));
|
|
22382
22606
|
}
|
|
22383
22607
|
}
|
|
22384
22608
|
}
|
|
@@ -22390,7 +22614,7 @@ function shortId(path2) {
|
|
|
22390
22614
|
return base.slice(0, 8);
|
|
22391
22615
|
}
|
|
22392
22616
|
async function migrateAdminWebchatSidecars(opts = {}) {
|
|
22393
|
-
const projectsRoot = opts.projectsRoot ?? (process.env.CLAUDE_CONFIG_DIR ?
|
|
22617
|
+
const projectsRoot = opts.projectsRoot ?? (process.env.CLAUDE_CONFIG_DIR ? join35(process.env.CLAUDE_CONFIG_DIR, "projects") : null) ?? "";
|
|
22394
22618
|
const usersFile = opts.usersFile ?? USERS_FILE;
|
|
22395
22619
|
const accountId = opts.accountId ?? resolveAccount()?.accountId ?? null;
|
|
22396
22620
|
const resolveName = opts.resolveName ?? defaultResolveName;
|
|
@@ -23300,15 +23524,15 @@ function createWebchatChannelRoutes(deps) {
|
|
|
23300
23524
|
const finalBytes = typeof body?.finalBytes === "number" && Number.isFinite(body.finalBytes) ? body.finalBytes : 0;
|
|
23301
23525
|
const k = key.slice(0, 8);
|
|
23302
23526
|
const sid = sessionId.slice(0, 8);
|
|
23303
|
-
if (interactive && finalBytes > 0) {
|
|
23304
|
-
console.error(`[webchat-native] op=turn-end channel=webchat key=${k} sessionId=${sid} replied=${replied ? "yes" : "no"} delivered=fallback bytes=${finalBytes}`);
|
|
23305
|
-
return c.json({ ok: true, delivered: "fallback" });
|
|
23306
|
-
}
|
|
23307
23527
|
if (replied) {
|
|
23308
|
-
console.error(`[webchat-native] op=turn-end channel=webchat key=${k} sessionId=${sid} delivered=reply`);
|
|
23528
|
+
console.error(`[webchat-native] op=turn-end channel=webchat key=${k} sessionId=${sid} replied=yes delivered=reply bytes=${finalBytes}`);
|
|
23309
23529
|
return c.json({ ok: true, delivered: "reply" });
|
|
23310
23530
|
}
|
|
23311
|
-
|
|
23531
|
+
if (interactive && finalBytes > 0) {
|
|
23532
|
+
console.error(`[webchat-native] op=turn-end channel=webchat key=${k} sessionId=${sid} replied=no delivered=fallback bytes=${finalBytes}`);
|
|
23533
|
+
return c.json({ ok: true, delivered: "fallback" });
|
|
23534
|
+
}
|
|
23535
|
+
console.error(`[webchat-native] op=turn-end channel=webchat key=${k} sessionId=${sid} replied=no delivered=none`);
|
|
23312
23536
|
return c.json({ ok: true, delivered: "none" });
|
|
23313
23537
|
});
|
|
23314
23538
|
return app62;
|
|
@@ -23369,16 +23593,16 @@ var WebchatGateway = class _WebchatGateway {
|
|
|
23369
23593
|
* The public /api/chat route awaits this to return the reply on the same SSE
|
|
23370
23594
|
* response, preserving the pre-756 POST→single-blob browser contract. */
|
|
23371
23595
|
awaitReply(key, timeoutMs) {
|
|
23372
|
-
return new Promise((
|
|
23596
|
+
return new Promise((resolve35) => {
|
|
23373
23597
|
const timer2 = setTimeout(() => {
|
|
23374
23598
|
this.replyAwaiters.delete(key);
|
|
23375
|
-
|
|
23599
|
+
resolve35({ timeout: true });
|
|
23376
23600
|
}, timeoutMs);
|
|
23377
23601
|
if (timer2.unref) timer2.unref();
|
|
23378
23602
|
this.replyAwaiters.set(key, (r) => {
|
|
23379
23603
|
clearTimeout(timer2);
|
|
23380
23604
|
this.replyAwaiters.delete(key);
|
|
23381
|
-
|
|
23605
|
+
resolve35(r);
|
|
23382
23606
|
});
|
|
23383
23607
|
});
|
|
23384
23608
|
}
|
|
@@ -23389,11 +23613,11 @@ var WebchatGateway = class _WebchatGateway {
|
|
|
23389
23613
|
* terminal dialog stays answerable). */
|
|
23390
23614
|
awaitPermissionVerdict(p, timeoutMs) {
|
|
23391
23615
|
const k = _WebchatGateway.promptKey(p.key, p.requestId);
|
|
23392
|
-
return new Promise((
|
|
23616
|
+
return new Promise((resolve35) => {
|
|
23393
23617
|
this.pendingPrompts.get(k)?.resolve({ timeout: true });
|
|
23394
23618
|
const timer2 = setTimeout(() => {
|
|
23395
23619
|
this.pendingPrompts.delete(k);
|
|
23396
|
-
|
|
23620
|
+
resolve35({ timeout: true });
|
|
23397
23621
|
}, timeoutMs);
|
|
23398
23622
|
if (timer2.unref) timer2.unref();
|
|
23399
23623
|
this.pendingPrompts.set(k, {
|
|
@@ -23402,7 +23626,7 @@ var WebchatGateway = class _WebchatGateway {
|
|
|
23402
23626
|
resolve: (r) => {
|
|
23403
23627
|
clearTimeout(timer2);
|
|
23404
23628
|
this.pendingPrompts.delete(k);
|
|
23405
|
-
|
|
23629
|
+
resolve35(r);
|
|
23406
23630
|
}
|
|
23407
23631
|
});
|
|
23408
23632
|
console.error(`[webchat:perm] op=open key=${keyDisplay(p.key)} id=${p.requestId} tool=${p.toolName}`);
|
|
@@ -24398,7 +24622,7 @@ function buildTelegramSpawnRequest(input) {
|
|
|
24398
24622
|
// app/lib/telegram/outbound/send-document.ts
|
|
24399
24623
|
import { realpathSync as realpathSync7 } from "fs";
|
|
24400
24624
|
import { readFile as readFile7, stat as stat7 } from "fs/promises";
|
|
24401
|
-
import { resolve as
|
|
24625
|
+
import { resolve as resolve31, basename as basename11 } from "path";
|
|
24402
24626
|
var TAG42 = "[telegram:outbound]";
|
|
24403
24627
|
var TELEGRAM_DOCUMENT_MAX_BYTES = 50 * 1024 * 1024;
|
|
24404
24628
|
async function sendTelegramDocument(input) {
|
|
@@ -24409,7 +24633,7 @@ async function sendTelegramDocument(input) {
|
|
|
24409
24633
|
if (!maxyAccountId || !platformRoot3) {
|
|
24410
24634
|
return { ok: false, status: 400, error: "Cannot validate file path: missing account or platform context" };
|
|
24411
24635
|
}
|
|
24412
|
-
const accountDir =
|
|
24636
|
+
const accountDir = resolve31(platformRoot3, "..", "data/accounts", maxyAccountId);
|
|
24413
24637
|
let resolvedPath;
|
|
24414
24638
|
try {
|
|
24415
24639
|
resolvedPath = realpathSync7(filePath);
|
|
@@ -24536,12 +24760,12 @@ function startTelegramNativeFileFollower(input) {
|
|
|
24536
24760
|
}
|
|
24537
24761
|
|
|
24538
24762
|
// server/telegram-descriptor.ts
|
|
24539
|
-
import { resolve as
|
|
24763
|
+
import { resolve as resolve32, join as join36 } from "path";
|
|
24540
24764
|
function telegramGatewayUrl() {
|
|
24541
24765
|
return `http://127.0.0.1:${process.env.MAXY_UI_INTERNAL_PORT ?? ""}`;
|
|
24542
24766
|
}
|
|
24543
24767
|
function telegramServerPath() {
|
|
24544
|
-
return process.env.MAXY_TELEGRAM_CHANNEL_SERVER_PATH ??
|
|
24768
|
+
return process.env.MAXY_TELEGRAM_CHANNEL_SERVER_PATH ?? resolve32(process.env.MAXY_PLATFORM_ROOT ?? join36(__dirname, ".."), "services/telegram-channel/dist/server.js");
|
|
24545
24769
|
}
|
|
24546
24770
|
|
|
24547
24771
|
// app/lib/channel-pty-bridge/public-session-end-review.ts
|
|
@@ -24803,8 +25027,8 @@ function broadcastAdminShutdown(reason) {
|
|
|
24803
25027
|
|
|
24804
25028
|
// ../lib/entitlement/src/index.ts
|
|
24805
25029
|
import { createPublicKey, createHash as createHash6, verify as cryptoVerify } from "crypto";
|
|
24806
|
-
import { existsSync as existsSync34, readFileSync as
|
|
24807
|
-
import { resolve as
|
|
25030
|
+
import { existsSync as existsSync34, readFileSync as readFileSync36, statSync as statSync13 } from "fs";
|
|
25031
|
+
import { resolve as resolve33 } from "path";
|
|
24808
25032
|
|
|
24809
25033
|
// ../lib/entitlement/src/canonicalize.ts
|
|
24810
25034
|
function canonicalize(value) {
|
|
@@ -24839,7 +25063,7 @@ var PUBKEY_SHA256 = "8eee6bcb33545fd13b16d3199a5735ca5db5062834c7b49dfe4f23801d9
|
|
|
24839
25063
|
var GRACE_DAYS = 7;
|
|
24840
25064
|
var GRACE_MS = GRACE_DAYS * 24 * 60 * 60 * 1e3;
|
|
24841
25065
|
function pubkeyPath(brand) {
|
|
24842
|
-
return
|
|
25066
|
+
return resolve33(brand.platformRoot, "lib", "entitlement", "rubytech-pubkey.pem");
|
|
24843
25067
|
}
|
|
24844
25068
|
var memo = null;
|
|
24845
25069
|
function memoKey(mtimeMs, account) {
|
|
@@ -24851,7 +25075,7 @@ function resolveEntitlement(brand, account) {
|
|
|
24851
25075
|
if (brand.commercialMode !== true) {
|
|
24852
25076
|
return logResolved(implicitTrust(account), null);
|
|
24853
25077
|
}
|
|
24854
|
-
const entitlementPath =
|
|
25078
|
+
const entitlementPath = resolve33(brand.configDir, "entitlement.json");
|
|
24855
25079
|
if (!existsSync34(entitlementPath)) {
|
|
24856
25080
|
return logResolved(anonymousFallback("missing"), { reason: "missing" });
|
|
24857
25081
|
}
|
|
@@ -24867,7 +25091,7 @@ function resolveEntitlement(brand, account) {
|
|
|
24867
25091
|
function verifyAndResolve(brand, entitlementPath, account) {
|
|
24868
25092
|
let pubkeyPem;
|
|
24869
25093
|
try {
|
|
24870
|
-
pubkeyPem =
|
|
25094
|
+
pubkeyPem = readFileSync36(pubkeyPath(brand), "utf-8");
|
|
24871
25095
|
} catch (err) {
|
|
24872
25096
|
return logResolved(anonymousFallback("pubkey-missing"), {
|
|
24873
25097
|
reason: "pubkey-missing"
|
|
@@ -24881,7 +25105,7 @@ function verifyAndResolve(brand, entitlementPath, account) {
|
|
|
24881
25105
|
}
|
|
24882
25106
|
let envelope;
|
|
24883
25107
|
try {
|
|
24884
|
-
envelope = JSON.parse(
|
|
25108
|
+
envelope = JSON.parse(readFileSync36(entitlementPath, "utf-8"));
|
|
24885
25109
|
} catch {
|
|
24886
25110
|
return logResolved(anonymousFallback("malformed"), { reason: "malformed" });
|
|
24887
25111
|
}
|
|
@@ -25042,14 +25266,14 @@ function clientFrom(c) {
|
|
|
25042
25266
|
);
|
|
25043
25267
|
}
|
|
25044
25268
|
var PLATFORM_ROOT8 = process.env.MAXY_PLATFORM_ROOT || "";
|
|
25045
|
-
var BRAND_JSON_PATH = PLATFORM_ROOT8 ?
|
|
25269
|
+
var BRAND_JSON_PATH = PLATFORM_ROOT8 ? join37(PLATFORM_ROOT8, "config", "brand.json") : "";
|
|
25046
25270
|
var BRAND = { productName: "Maxy", hostname: "maxy", configDir: ".maxy", marketingUrl: "https://getmaxy.com" };
|
|
25047
25271
|
if (BRAND_JSON_PATH && !existsSync35(BRAND_JSON_PATH)) {
|
|
25048
25272
|
console.error(`[brand] WARNING: brand.json not found at ${BRAND_JSON_PATH} \u2014 using Maxy defaults`);
|
|
25049
25273
|
}
|
|
25050
25274
|
if (BRAND_JSON_PATH && existsSync35(BRAND_JSON_PATH)) {
|
|
25051
25275
|
try {
|
|
25052
|
-
const parsed = JSON.parse(
|
|
25276
|
+
const parsed = JSON.parse(readFileSync37(BRAND_JSON_PATH, "utf-8"));
|
|
25053
25277
|
BRAND = { ...BRAND, ...parsed };
|
|
25054
25278
|
} catch (err) {
|
|
25055
25279
|
console.error(`[brand] Failed to parse brand.json: ${err.message}`);
|
|
@@ -25081,11 +25305,11 @@ var brandLoginOpts = {
|
|
|
25081
25305
|
appleTouchIconPath: brandAppIcon512Path,
|
|
25082
25306
|
themeColor: BRAND.defaultColors?.primary
|
|
25083
25307
|
};
|
|
25084
|
-
var ALIAS_DOMAINS_PATH =
|
|
25308
|
+
var ALIAS_DOMAINS_PATH = join37(homedir4(), BRAND.configDir, "alias-domains.json");
|
|
25085
25309
|
function loadAliasDomains() {
|
|
25086
25310
|
try {
|
|
25087
25311
|
if (!existsSync35(ALIAS_DOMAINS_PATH)) return null;
|
|
25088
|
-
const parsed = JSON.parse(
|
|
25312
|
+
const parsed = JSON.parse(readFileSync37(ALIAS_DOMAINS_PATH, "utf-8"));
|
|
25089
25313
|
if (!Array.isArray(parsed)) {
|
|
25090
25314
|
console.error("[alias-domains] malformed alias-domains.json \u2014 expected array");
|
|
25091
25315
|
return null;
|
|
@@ -25148,7 +25372,7 @@ async function recordPassiveDispatch(input) {
|
|
|
25148
25372
|
var waGateway = new WaGateway({
|
|
25149
25373
|
fetchStandingRules: fetchAccountStandingRules,
|
|
25150
25374
|
gatewayUrl: `http://127.0.0.1:${process.env.MAXY_UI_INTERNAL_PORT ?? ""}`,
|
|
25151
|
-
serverPath: process.env.MAXY_WA_CHANNEL_SERVER_PATH ??
|
|
25375
|
+
serverPath: process.env.MAXY_WA_CHANNEL_SERVER_PATH ?? resolve34(process.env.MAXY_PLATFORM_ROOT ?? join37(__dirname, ".."), "services/whatsapp-channel/dist/server.js"),
|
|
25152
25376
|
// Task 751 / 1390 — file delivery on the native channel. `maxyAccountId` (the
|
|
25153
25377
|
// path-validation scope) is the sender's effective SESSION account, resolved
|
|
25154
25378
|
// once at the inbound gate and threaded here via the gateway's per-sender doc
|
|
@@ -25163,7 +25387,7 @@ var waGateway = new WaGateway({
|
|
|
25163
25387
|
caption,
|
|
25164
25388
|
accountId,
|
|
25165
25389
|
maxyAccountId,
|
|
25166
|
-
platformRoot:
|
|
25390
|
+
platformRoot: resolve34(process.env.MAXY_PLATFORM_ROOT ?? join37(__dirname, ".."))
|
|
25167
25391
|
});
|
|
25168
25392
|
return result.ok ? { ok: true, messageId: result.messageId } : { ok: false, error: result.error };
|
|
25169
25393
|
},
|
|
@@ -25761,8 +25985,8 @@ app61.get("/agent-assets/:slug/:filename", (c) => {
|
|
|
25761
25985
|
console.error(`[agent-assets] no-account slug=${slug} file=${filename}`);
|
|
25762
25986
|
return c.text("Not found", 404);
|
|
25763
25987
|
}
|
|
25764
|
-
const filePath =
|
|
25765
|
-
const expectedDir =
|
|
25988
|
+
const filePath = resolve34(account.accountDir, "agents", slug, "assets", filename);
|
|
25989
|
+
const expectedDir = resolve34(account.accountDir, "agents", slug, "assets");
|
|
25766
25990
|
if (!filePath.startsWith(expectedDir + "/")) {
|
|
25767
25991
|
console.error(`[agent-assets] path-traversal-rejected slug=${slug} file=${filename}`);
|
|
25768
25992
|
return c.text("Forbidden", 403);
|
|
@@ -25774,7 +25998,7 @@ app61.get("/agent-assets/:slug/:filename", (c) => {
|
|
|
25774
25998
|
const ext = "." + filename.split(".").pop()?.toLowerCase();
|
|
25775
25999
|
const contentType = IMAGE_MIME[ext] || "application/octet-stream";
|
|
25776
26000
|
console.log(`[agent-assets] serve slug=${slug} file=${filename} status=200`);
|
|
25777
|
-
const body =
|
|
26001
|
+
const body = readFileSync37(filePath);
|
|
25778
26002
|
return c.body(body, 200, {
|
|
25779
26003
|
"Content-Type": contentType,
|
|
25780
26004
|
"Cache-Control": "public, max-age=3600"
|
|
@@ -25791,8 +26015,8 @@ app61.get("/generated/:filename", (c) => {
|
|
|
25791
26015
|
console.error(`[generated] serve file=${filename} status=404`);
|
|
25792
26016
|
return c.text("Not found", 404);
|
|
25793
26017
|
}
|
|
25794
|
-
const filePath =
|
|
25795
|
-
const expectedDir =
|
|
26018
|
+
const filePath = resolve34(account.accountDir, "generated", filename);
|
|
26019
|
+
const expectedDir = resolve34(account.accountDir, "generated");
|
|
25796
26020
|
if (!filePath.startsWith(expectedDir + "/")) {
|
|
25797
26021
|
console.error(`[generated] serve file=${filename} status=403`);
|
|
25798
26022
|
return c.text("Forbidden", 403);
|
|
@@ -25804,7 +26028,7 @@ app61.get("/generated/:filename", (c) => {
|
|
|
25804
26028
|
const ext = "." + filename.split(".").pop()?.toLowerCase();
|
|
25805
26029
|
const contentType = IMAGE_MIME[ext] || "application/octet-stream";
|
|
25806
26030
|
console.log(`[generated] serve file=${filename} status=200`);
|
|
25807
|
-
const body =
|
|
26031
|
+
const body = readFileSync37(filePath);
|
|
25808
26032
|
return c.body(body, 200, {
|
|
25809
26033
|
"Content-Type": contentType,
|
|
25810
26034
|
"Cache-Control": "public, max-age=86400"
|
|
@@ -25819,7 +26043,7 @@ var brandLogoPath = "/brand/maxy-monochrome.png";
|
|
|
25819
26043
|
var brandIconPath = "/brand/maxy-monochrome.png";
|
|
25820
26044
|
if (BRAND_JSON_PATH && existsSync35(BRAND_JSON_PATH)) {
|
|
25821
26045
|
try {
|
|
25822
|
-
const fullBrand = JSON.parse(
|
|
26046
|
+
const fullBrand = JSON.parse(readFileSync37(BRAND_JSON_PATH, "utf-8"));
|
|
25823
26047
|
if (fullBrand.assets?.logo) brandLogoPath = `/brand/${fullBrand.assets.logo}`;
|
|
25824
26048
|
brandIconPath = fullBrand.assets?.icon ? `/brand/${fullBrand.assets.icon}` : brandLogoPath;
|
|
25825
26049
|
} catch {
|
|
@@ -25846,11 +26070,11 @@ var brandScript = `<script>window.__BRAND__=${JSON.stringify({
|
|
|
25846
26070
|
var brandThemeColor = BRAND.defaultColors?.primary ?? "#000000";
|
|
25847
26071
|
var brandBackgroundColor = BRAND.defaultColors?.background ?? "#ffffff";
|
|
25848
26072
|
var brandAppIconsExist = [brandAppIcon192Path, brandAppIcon512Path, brandMaskableIconPath].every(
|
|
25849
|
-
(p) => existsSync35(
|
|
26073
|
+
(p) => existsSync35(resolve34(process.cwd(), "public", p.replace(/^\//, "")))
|
|
25850
26074
|
);
|
|
25851
26075
|
var SW_SOURCE = (() => {
|
|
25852
26076
|
try {
|
|
25853
|
-
return
|
|
26077
|
+
return readFileSync37(resolve34(process.cwd(), "public", "sw.js"), "utf-8");
|
|
25854
26078
|
} catch {
|
|
25855
26079
|
return null;
|
|
25856
26080
|
}
|
|
@@ -25858,9 +26082,9 @@ var SW_SOURCE = (() => {
|
|
|
25858
26082
|
function readInstalledVersion() {
|
|
25859
26083
|
try {
|
|
25860
26084
|
if (!PLATFORM_ROOT8) return "unknown";
|
|
25861
|
-
const versionFile =
|
|
26085
|
+
const versionFile = join37(PLATFORM_ROOT8, "config", `.${BRAND.hostname}-version`);
|
|
25862
26086
|
if (!existsSync35(versionFile)) return "unknown";
|
|
25863
|
-
const content =
|
|
26087
|
+
const content = readFileSync37(versionFile, "utf-8").trim();
|
|
25864
26088
|
return content || "unknown";
|
|
25865
26089
|
} catch {
|
|
25866
26090
|
return "unknown";
|
|
@@ -25901,7 +26125,7 @@ var clientErrorReporterScript = `<script>
|
|
|
25901
26125
|
function cachedHtml(file) {
|
|
25902
26126
|
let html = htmlCache.get(file);
|
|
25903
26127
|
if (!html) {
|
|
25904
|
-
html =
|
|
26128
|
+
html = readFileSync37(resolve34(process.cwd(), "public", file), "utf-8");
|
|
25905
26129
|
const productNameEsc = escapeHtml(BRAND.productName);
|
|
25906
26130
|
html = html.replace(/<title>([^<]*)<\/title>/, (_match, inner) => `<title>${escapeHtml(inner).replace(/Maxy/g, productNameEsc)}</title>`);
|
|
25907
26131
|
html = html.replace('href="/favicon.ico"', `href="${escapeHtml(brandFaviconPath)}"`);
|
|
@@ -25918,13 +26142,13 @@ ${clientErrorReporterScript}
|
|
|
25918
26142
|
return html;
|
|
25919
26143
|
}
|
|
25920
26144
|
function loadBrandingCache(agentSlug) {
|
|
25921
|
-
const configDir2 =
|
|
26145
|
+
const configDir2 = join37(homedir4(), BRAND.configDir);
|
|
25922
26146
|
try {
|
|
25923
26147
|
const accountId = getDefaultAccountId();
|
|
25924
26148
|
if (!accountId) return null;
|
|
25925
|
-
const cachePath =
|
|
26149
|
+
const cachePath = join37(configDir2, "branding-cache", accountId, `${agentSlug}.json`);
|
|
25926
26150
|
if (!existsSync35(cachePath)) return null;
|
|
25927
|
-
return JSON.parse(
|
|
26151
|
+
return JSON.parse(readFileSync37(cachePath, "utf-8"));
|
|
25928
26152
|
} catch {
|
|
25929
26153
|
return null;
|
|
25930
26154
|
}
|
|
@@ -25941,11 +26165,11 @@ var brandedHtmlCache = /* @__PURE__ */ new Map();
|
|
|
25941
26165
|
function brandedPublicHtml(agentSlug) {
|
|
25942
26166
|
const baseHtml = cachedHtml("public.html");
|
|
25943
26167
|
if (!agentSlug) return baseHtml;
|
|
25944
|
-
const
|
|
26168
|
+
const cached3 = brandedHtmlCache.get(agentSlug);
|
|
25945
26169
|
const branding = loadBrandingCache(agentSlug);
|
|
25946
26170
|
if (!branding) return baseHtml;
|
|
25947
26171
|
const brandHash = JSON.stringify(branding).length;
|
|
25948
|
-
if (
|
|
26172
|
+
if (cached3 && cached3.mtime === brandHash) return cached3.html;
|
|
25949
26173
|
const title = escapeHtml(branding.name);
|
|
25950
26174
|
const description = branding.tagline ? escapeHtml(branding.tagline) : "";
|
|
25951
26175
|
const themeColor = branding.primaryColor || "";
|
|
@@ -26019,7 +26243,7 @@ app61.use("/vnc-popout.html", logViewerFetch);
|
|
|
26019
26243
|
app61.get("/vnc-popout.html", (c) => {
|
|
26020
26244
|
let html = htmlCache.get("vnc-popout.html");
|
|
26021
26245
|
if (!html) {
|
|
26022
|
-
html =
|
|
26246
|
+
html = readFileSync37(resolve34(process.cwd(), "public", "vnc-popout.html"), "utf-8");
|
|
26023
26247
|
const name = escapeHtml(BRAND.productName);
|
|
26024
26248
|
html = html.replace("<title>Browser \u2014 Maxy</title>", `<title>${name}</title>`);
|
|
26025
26249
|
html = html.replace("</head>", ` ${brandScript}
|
|
@@ -26151,8 +26375,8 @@ var hostname = process.env.HOSTNAME ?? "127.0.0.1";
|
|
|
26151
26375
|
var httpServer = serve({ fetch: app61.fetch, port, hostname });
|
|
26152
26376
|
console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
26153
26377
|
{
|
|
26154
|
-
const reconcilePlatformRoot = process.env.MAXY_PLATFORM_ROOT ??
|
|
26155
|
-
const reconcileScript =
|
|
26378
|
+
const reconcilePlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join37(__dirname, "..");
|
|
26379
|
+
const reconcileScript = resolve34(reconcilePlatformRoot, "plugins/scheduling/mcp/dist/scripts/reconcile-bookings.js");
|
|
26156
26380
|
const RECONCILE_INTERVAL_MS = 12e4;
|
|
26157
26381
|
const runReconcile = () => {
|
|
26158
26382
|
if (!existsSync35(reconcileScript)) return;
|
|
@@ -26173,8 +26397,8 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
26173
26397
|
loop.unref();
|
|
26174
26398
|
}
|
|
26175
26399
|
{
|
|
26176
|
-
const publishPlatformRoot = process.env.MAXY_PLATFORM_ROOT ??
|
|
26177
|
-
const publishScript =
|
|
26400
|
+
const publishPlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join37(__dirname, "..");
|
|
26401
|
+
const publishScript = resolve34(publishPlatformRoot, "plugins/scheduling/mcp/dist/scripts/publish-availability.js");
|
|
26178
26402
|
const PUBLISH_INTERVAL_MS = 3e5;
|
|
26179
26403
|
const currentAccount = () => {
|
|
26180
26404
|
try {
|
|
@@ -26203,21 +26427,21 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
26203
26427
|
}
|
|
26204
26428
|
};
|
|
26205
26429
|
const auditSnapshotAge = (account) => {
|
|
26206
|
-
const availPath =
|
|
26430
|
+
const availPath = resolve34(account.accountDir, "calendar-availability.json");
|
|
26207
26431
|
let hasBookingSite = false;
|
|
26208
26432
|
try {
|
|
26209
26433
|
if (existsSync35(availPath)) {
|
|
26210
|
-
const cfg = JSON.parse(
|
|
26434
|
+
const cfg = JSON.parse(readFileSync37(availPath, "utf-8"));
|
|
26211
26435
|
hasBookingSite = typeof cfg.bookingDbName === "string" && cfg.bookingDbName.length > 0;
|
|
26212
26436
|
}
|
|
26213
26437
|
} catch {
|
|
26214
26438
|
}
|
|
26215
26439
|
if (!hasBookingSite) return;
|
|
26216
|
-
const statePath =
|
|
26440
|
+
const statePath = resolve34(account.accountDir, "state", "booking-availability", "last-publish.json");
|
|
26217
26441
|
let lastSuccessAt = null;
|
|
26218
26442
|
if (existsSync35(statePath)) {
|
|
26219
26443
|
try {
|
|
26220
|
-
const rec = JSON.parse(
|
|
26444
|
+
const rec = JSON.parse(readFileSync37(statePath, "utf-8"));
|
|
26221
26445
|
lastSuccessAt = rec.lastSuccessAt ?? null;
|
|
26222
26446
|
} catch {
|
|
26223
26447
|
console.error(`[calendar-availability] op=audit accountId=${account.accountId} snapshotAgeMs=na reason=bad-state-file`);
|
|
@@ -26241,8 +26465,8 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
26241
26465
|
}
|
|
26242
26466
|
startTimeEntryCensus(() => getSession());
|
|
26243
26467
|
{
|
|
26244
|
-
const auditPlatformRoot = process.env.MAXY_PLATFORM_ROOT ??
|
|
26245
|
-
const auditScript =
|
|
26468
|
+
const auditPlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join37(__dirname, "..");
|
|
26469
|
+
const auditScript = resolve34(auditPlatformRoot, "plugins/connector/mcp/dist/scripts/audit-connectors.js");
|
|
26246
26470
|
const auditLogDir = process.env.LOG_DIR ?? LOG_DIR;
|
|
26247
26471
|
const CONNECTOR_AUDIT_INTERVAL_MS = 3e5;
|
|
26248
26472
|
const runConnectorAudit = () => {
|
|
@@ -26360,7 +26584,7 @@ async function runAdminUserReconcileTick() {
|
|
|
26360
26584
|
console.error("[adminuser-self-heal] skip reason=no-users-file");
|
|
26361
26585
|
return;
|
|
26362
26586
|
}
|
|
26363
|
-
const usersRaw =
|
|
26587
|
+
const usersRaw = readFileSync37(USERS_FILE, "utf-8").trim();
|
|
26364
26588
|
if (!usersRaw) {
|
|
26365
26589
|
console.error("[adminuser-self-heal] skip reason=empty-users-file");
|
|
26366
26590
|
return;
|
|
@@ -26390,7 +26614,7 @@ if (typeof adminUserReconcileTimer.unref === "function") adminUserReconcileTimer
|
|
|
26390
26614
|
var USERS_RECONCILE_INTERVAL_MS = 60 * 60 * 1e3;
|
|
26391
26615
|
function countUsersRows() {
|
|
26392
26616
|
if (!existsSync35(USERS_FILE)) return 0;
|
|
26393
|
-
const raw =
|
|
26617
|
+
const raw = readFileSync37(USERS_FILE, "utf-8").trim();
|
|
26394
26618
|
if (!raw) return 0;
|
|
26395
26619
|
const users = JSON.parse(raw);
|
|
26396
26620
|
return users.filter((u) => typeof u.userId === "string").length;
|
|
@@ -26510,7 +26734,7 @@ if (bootAccountConfig?.whatsapp) {
|
|
|
26510
26734
|
}
|
|
26511
26735
|
init({
|
|
26512
26736
|
configDir: configDirForWhatsApp,
|
|
26513
|
-
platformRoot:
|
|
26737
|
+
platformRoot: resolve34(process.env.MAXY_PLATFORM_ROOT ?? join37(__dirname, "..")),
|
|
26514
26738
|
accountConfig: bootAccountConfig,
|
|
26515
26739
|
onMessage: async (msg) => {
|
|
26516
26740
|
if (msg.isOwnerMirror) {
|