@rubytech/create-realagent-code 0.1.252 → 0.1.253
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/payload/platform/plugins/admin/skills/file-presentation/SKILL.md +4 -13
- package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +1 -2
- package/payload/platform/plugins/docs/references/deployment.md +0 -1
- package/payload/platform/plugins/scheduling/PLUGIN.md +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/index.js +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/index.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/index.js +10 -0
- package/payload/platform/services/claude-session-manager/dist/index.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/pty-census.d.ts +40 -0
- package/payload/platform/services/claude-session-manager/dist/pty-census.d.ts.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/pty-census.js +125 -0
- package/payload/platform/services/claude-session-manager/dist/pty-census.js.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/rc-daemon.d.ts +1 -0
- package/payload/platform/services/claude-session-manager/dist/rc-daemon.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/rc-daemon.js +8 -1
- package/payload/platform/services/claude-session-manager/dist/rc-daemon.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/system-prompt.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/system-prompt.js +0 -11
- package/payload/platform/services/claude-session-manager/dist/system-prompt.js.map +1 -1
- package/payload/server/server.js +439 -317
package/payload/server/server.js
CHANGED
|
@@ -4514,10 +4514,10 @@ var require_browser = __commonJS({
|
|
|
4514
4514
|
text = canvas;
|
|
4515
4515
|
canvas = void 0;
|
|
4516
4516
|
}
|
|
4517
|
-
return new Promise(function(
|
|
4517
|
+
return new Promise(function(resolve27, reject) {
|
|
4518
4518
|
try {
|
|
4519
4519
|
const data = QRCode2.create(text, opts);
|
|
4520
|
-
|
|
4520
|
+
resolve27(renderFunc(data, canvas, opts));
|
|
4521
4521
|
} catch (e) {
|
|
4522
4522
|
reject(e);
|
|
4523
4523
|
}
|
|
@@ -4599,11 +4599,11 @@ var require_server = __commonJS({
|
|
|
4599
4599
|
}
|
|
4600
4600
|
function render(renderFunc, text, params) {
|
|
4601
4601
|
if (!params.cb) {
|
|
4602
|
-
return new Promise(function(
|
|
4602
|
+
return new Promise(function(resolve27, reject) {
|
|
4603
4603
|
try {
|
|
4604
4604
|
const data = QRCode2.create(text, params.opts);
|
|
4605
4605
|
return renderFunc(data, params.opts, function(err, data2) {
|
|
4606
|
-
return err ? reject(err) :
|
|
4606
|
+
return err ? reject(err) : resolve27(data2);
|
|
4607
4607
|
});
|
|
4608
4608
|
} catch (e) {
|
|
4609
4609
|
reject(e);
|
|
@@ -5435,7 +5435,7 @@ var serveStatic = (options = { root: "" }) => {
|
|
|
5435
5435
|
|
|
5436
5436
|
// server/index.ts
|
|
5437
5437
|
import { readFileSync as readFileSync22, existsSync as existsSync23, watchFile } from "fs";
|
|
5438
|
-
import { resolve as
|
|
5438
|
+
import { resolve as resolve26, join as join17, basename as basename6 } from "path";
|
|
5439
5439
|
import { homedir as homedir2 } from "os";
|
|
5440
5440
|
import { monitorEventLoopDelay } from "perf_hooks";
|
|
5441
5441
|
|
|
@@ -5958,7 +5958,7 @@ var credsSaveQueue = Promise.resolve();
|
|
|
5958
5958
|
async function drainCredsSaveQueue(timeoutMs = 5e3) {
|
|
5959
5959
|
console.error(`${TAG2} draining credential save queue\u2026`);
|
|
5960
5960
|
const timer2 = new Promise(
|
|
5961
|
-
(
|
|
5961
|
+
(resolve27) => setTimeout(() => resolve27("timeout"), timeoutMs)
|
|
5962
5962
|
);
|
|
5963
5963
|
const result = await Promise.race([
|
|
5964
5964
|
credsSaveQueue.then(() => "drained"),
|
|
@@ -6086,11 +6086,11 @@ async function createWaSocket(opts) {
|
|
|
6086
6086
|
return sock;
|
|
6087
6087
|
}
|
|
6088
6088
|
async function waitForConnection(sock) {
|
|
6089
|
-
return new Promise((
|
|
6089
|
+
return new Promise((resolve27, reject) => {
|
|
6090
6090
|
const handler = (update) => {
|
|
6091
6091
|
if (update.connection === "open") {
|
|
6092
6092
|
sock.ev.off("connection.update", handler);
|
|
6093
|
-
|
|
6093
|
+
resolve27();
|
|
6094
6094
|
}
|
|
6095
6095
|
if (update.connection === "close") {
|
|
6096
6096
|
sock.ev.off("connection.update", handler);
|
|
@@ -6204,14 +6204,14 @@ ${inspected}`;
|
|
|
6204
6204
|
return inspect2(err, INSPECT_OPTS2);
|
|
6205
6205
|
}
|
|
6206
6206
|
function withTimeout(label, promise, timeoutMs) {
|
|
6207
|
-
return new Promise((
|
|
6207
|
+
return new Promise((resolve27, reject) => {
|
|
6208
6208
|
const timer2 = setTimeout(() => {
|
|
6209
6209
|
reject(new Error(`${label} timed out after ${timeoutMs}ms`));
|
|
6210
6210
|
}, timeoutMs);
|
|
6211
6211
|
promise.then(
|
|
6212
6212
|
(value) => {
|
|
6213
6213
|
clearTimeout(timer2);
|
|
6214
|
-
|
|
6214
|
+
resolve27(value);
|
|
6215
6215
|
},
|
|
6216
6216
|
(err) => {
|
|
6217
6217
|
clearTimeout(timer2);
|
|
@@ -6753,8 +6753,8 @@ async function persistWhatsAppMessage(input) {
|
|
|
6753
6753
|
const { givenName, familyName } = splitName(input.pushName);
|
|
6754
6754
|
const prev = sessionWriteLocks.get(input.cacheKey);
|
|
6755
6755
|
let release;
|
|
6756
|
-
const mine = new Promise((
|
|
6757
|
-
release =
|
|
6756
|
+
const mine = new Promise((resolve27) => {
|
|
6757
|
+
release = resolve27;
|
|
6758
6758
|
});
|
|
6759
6759
|
const chained = (prev ?? Promise.resolve()).then(() => mine);
|
|
6760
6760
|
sessionWriteLocks.set(input.cacheKey, chained);
|
|
@@ -7790,11 +7790,11 @@ async function connectWithReconnect(conn) {
|
|
|
7790
7790
|
console.error(
|
|
7791
7791
|
`${TAG12} reconnecting account=${conn.accountId} in ${delay}ms (attempt ${decision.nextAttempts}/${maxAttempts})`
|
|
7792
7792
|
);
|
|
7793
|
-
await new Promise((
|
|
7794
|
-
const timer2 = setTimeout(
|
|
7793
|
+
await new Promise((resolve27) => {
|
|
7794
|
+
const timer2 = setTimeout(resolve27, delay);
|
|
7795
7795
|
conn.abortController.signal.addEventListener("abort", () => {
|
|
7796
7796
|
clearTimeout(timer2);
|
|
7797
|
-
|
|
7797
|
+
resolve27();
|
|
7798
7798
|
}, { once: true });
|
|
7799
7799
|
});
|
|
7800
7800
|
}
|
|
@@ -7802,16 +7802,16 @@ async function connectWithReconnect(conn) {
|
|
|
7802
7802
|
}
|
|
7803
7803
|
}
|
|
7804
7804
|
function waitForDisconnectEvent(conn) {
|
|
7805
|
-
return new Promise((
|
|
7805
|
+
return new Promise((resolve27) => {
|
|
7806
7806
|
if (!conn.sock) {
|
|
7807
|
-
|
|
7807
|
+
resolve27();
|
|
7808
7808
|
return;
|
|
7809
7809
|
}
|
|
7810
7810
|
const sock = conn.sock;
|
|
7811
7811
|
const handler = (update) => {
|
|
7812
7812
|
if (update.connection === "close") {
|
|
7813
7813
|
sock.ev.off("connection.update", handler);
|
|
7814
|
-
|
|
7814
|
+
resolve27();
|
|
7815
7815
|
}
|
|
7816
7816
|
};
|
|
7817
7817
|
sock.ev.on("connection.update", handler);
|
|
@@ -8078,8 +8078,8 @@ async function handleInboundMessage(conn, msg) {
|
|
|
8078
8078
|
const conversationKey = isGroup ? remoteJid : senderPhone;
|
|
8079
8079
|
const debounceKey = `${conn.accountId}:${conversationKey}:${senderPhone}`;
|
|
8080
8080
|
let resolvePending;
|
|
8081
|
-
const sttPending = new Promise((
|
|
8082
|
-
resolvePending =
|
|
8081
|
+
const sttPending = new Promise((resolve27) => {
|
|
8082
|
+
resolvePending = resolve27;
|
|
8083
8083
|
});
|
|
8084
8084
|
if (conn.debouncer) conn.debouncer.registerPending(debounceKey, sttPending);
|
|
8085
8085
|
try {
|
|
@@ -8488,20 +8488,20 @@ function buildX11Env(chromiumWrapperPath, transport = "vnc") {
|
|
|
8488
8488
|
|
|
8489
8489
|
// server/routes/health.ts
|
|
8490
8490
|
function checkPort(port2, timeoutMs = 500) {
|
|
8491
|
-
return new Promise((
|
|
8491
|
+
return new Promise((resolve27) => {
|
|
8492
8492
|
const socket = createConnection2(port2, "127.0.0.1");
|
|
8493
8493
|
socket.setTimeout(timeoutMs);
|
|
8494
8494
|
socket.once("connect", () => {
|
|
8495
8495
|
socket.destroy();
|
|
8496
|
-
|
|
8496
|
+
resolve27(true);
|
|
8497
8497
|
});
|
|
8498
8498
|
socket.once("error", () => {
|
|
8499
8499
|
socket.destroy();
|
|
8500
|
-
|
|
8500
|
+
resolve27(false);
|
|
8501
8501
|
});
|
|
8502
8502
|
socket.once("timeout", () => {
|
|
8503
8503
|
socket.destroy();
|
|
8504
|
-
|
|
8504
|
+
resolve27(false);
|
|
8505
8505
|
});
|
|
8506
8506
|
});
|
|
8507
8507
|
}
|
|
@@ -8773,7 +8773,7 @@ async function transcribeVoiceNote(file, source) {
|
|
|
8773
8773
|
}
|
|
8774
8774
|
|
|
8775
8775
|
// app/lib/channel-pty-bridge/bridge.ts
|
|
8776
|
-
import { resolve as
|
|
8776
|
+
import { resolve as resolve5 } from "path";
|
|
8777
8777
|
|
|
8778
8778
|
// app/lib/channel-pty-bridge/manager-client.ts
|
|
8779
8779
|
function managerBase() {
|
|
@@ -9073,6 +9073,8 @@ function startFollower(opts) {
|
|
|
9073
9073
|
const reader = res.body.getReader();
|
|
9074
9074
|
const decoder = new TextDecoder("utf8");
|
|
9075
9075
|
let buffered = "";
|
|
9076
|
+
const fileDelivery = opts.fileDelivery ?? null;
|
|
9077
|
+
let firedFileTools = [];
|
|
9076
9078
|
while (!abort.signal.aborted) {
|
|
9077
9079
|
const { value, done } = await reader.read();
|
|
9078
9080
|
if (done) break;
|
|
@@ -9094,6 +9096,7 @@ function startFollower(opts) {
|
|
|
9094
9096
|
}
|
|
9095
9097
|
if (event.type === "user") {
|
|
9096
9098
|
entry.pendingTurnText = "";
|
|
9099
|
+
firedFileTools = [];
|
|
9097
9100
|
continue;
|
|
9098
9101
|
}
|
|
9099
9102
|
if (event.type !== "assistant") continue;
|
|
@@ -9103,6 +9106,15 @@ function startFollower(opts) {
|
|
|
9103
9106
|
for (const block of msg.content) {
|
|
9104
9107
|
if (block?.type === "text" && typeof block.text === "string") {
|
|
9105
9108
|
entry.pendingTurnText += block.text;
|
|
9109
|
+
} else if (fileDelivery && block?.type === "tool_use" && typeof block.name === "string" && fileDelivery.isFileDeliveryTool(block.name)) {
|
|
9110
|
+
firedFileTools.push(block.name);
|
|
9111
|
+
try {
|
|
9112
|
+
await fileDelivery.onFileToolUse({ toolName: block.name, input: block.input });
|
|
9113
|
+
} catch (err) {
|
|
9114
|
+
console.error(
|
|
9115
|
+
`${tag} file-delivery-error sessionId=${sid} tool=${block.name} message=${err instanceof Error ? err.message : String(err)}`
|
|
9116
|
+
);
|
|
9117
|
+
}
|
|
9106
9118
|
}
|
|
9107
9119
|
}
|
|
9108
9120
|
}
|
|
@@ -9112,6 +9124,17 @@ function startFollower(opts) {
|
|
|
9112
9124
|
if (flush.trim()) {
|
|
9113
9125
|
await fanOut(entry.subscribers, flush, opts.onError, tag);
|
|
9114
9126
|
}
|
|
9127
|
+
if (fileDelivery && firedFileTools.length > 0) {
|
|
9128
|
+
const fired = firedFileTools;
|
|
9129
|
+
firedFileTools = [];
|
|
9130
|
+
try {
|
|
9131
|
+
fileDelivery.onTurnEnd(fired);
|
|
9132
|
+
} catch (err) {
|
|
9133
|
+
console.error(
|
|
9134
|
+
`${tag} file-delivery-error sessionId=${sid} phase=turn-end message=${err instanceof Error ? err.message : String(err)}`
|
|
9135
|
+
);
|
|
9136
|
+
}
|
|
9137
|
+
}
|
|
9115
9138
|
}
|
|
9116
9139
|
}
|
|
9117
9140
|
}
|
|
@@ -9138,10 +9161,145 @@ async function fanOut(subscribers, text, onError, tag) {
|
|
|
9138
9161
|
);
|
|
9139
9162
|
}
|
|
9140
9163
|
|
|
9164
|
+
// app/lib/whatsapp/outbound/send-document.ts
|
|
9165
|
+
import { realpathSync as realpathSync2 } from "fs";
|
|
9166
|
+
import { readFile, stat as stat2 } from "fs/promises";
|
|
9167
|
+
import { resolve as resolve4, basename } from "path";
|
|
9168
|
+
var TAG15 = "[whatsapp:outbound]";
|
|
9169
|
+
var lastDocumentOutboundAt = /* @__PURE__ */ new Map();
|
|
9170
|
+
function normalizeJid(to) {
|
|
9171
|
+
return to.includes("@") ? to : toWhatsappJid(to);
|
|
9172
|
+
}
|
|
9173
|
+
function documentOutboundAt(to) {
|
|
9174
|
+
return lastDocumentOutboundAt.get(normalizeJid(to));
|
|
9175
|
+
}
|
|
9176
|
+
async function sendWhatsAppDocument(input) {
|
|
9177
|
+
const { to, filePath, caption, accountId, maxyAccountId, platformRoot: platformRoot2 } = input;
|
|
9178
|
+
if (!to || !filePath) {
|
|
9179
|
+
return { ok: false, status: 400, error: "Missing required fields: to, filePath" };
|
|
9180
|
+
}
|
|
9181
|
+
if (!maxyAccountId || !platformRoot2) {
|
|
9182
|
+
return { ok: false, status: 400, error: "Cannot validate file path: missing account or platform context" };
|
|
9183
|
+
}
|
|
9184
|
+
const accountDir = resolve4(platformRoot2, "..", "data/accounts", maxyAccountId);
|
|
9185
|
+
let resolvedPath;
|
|
9186
|
+
try {
|
|
9187
|
+
resolvedPath = realpathSync2(filePath);
|
|
9188
|
+
const accountResolved = realpathSync2(accountDir);
|
|
9189
|
+
if (!resolvedPath.startsWith(accountResolved + "/")) {
|
|
9190
|
+
const sanitised = filePath.replace(accountDir, "<account>/");
|
|
9191
|
+
console.error(`${TAG15} document REJECTED path=${sanitised} reason=outside_account_directory`);
|
|
9192
|
+
return { ok: false, status: 403, error: "Access denied: file is outside the account directory" };
|
|
9193
|
+
}
|
|
9194
|
+
} catch (err) {
|
|
9195
|
+
const code = err.code;
|
|
9196
|
+
if (code === "ENOENT") {
|
|
9197
|
+
console.error(`${TAG15} document ENOENT path=${filePath}`);
|
|
9198
|
+
return { ok: false, status: 404, error: `File not found: ${filePath}` };
|
|
9199
|
+
}
|
|
9200
|
+
console.error(`${TAG15} document path error: ${String(err)}`);
|
|
9201
|
+
return { ok: false, status: 500, error: String(err) };
|
|
9202
|
+
}
|
|
9203
|
+
const fileStat = await stat2(resolvedPath);
|
|
9204
|
+
if (fileStat.size > MAX_FILE_SIZE_BYTES) {
|
|
9205
|
+
return {
|
|
9206
|
+
ok: false,
|
|
9207
|
+
status: 400,
|
|
9208
|
+
error: `File exceeds 50 MB limit (${(fileStat.size / 1024 / 1024).toFixed(1)} MB)`
|
|
9209
|
+
};
|
|
9210
|
+
}
|
|
9211
|
+
const filename = basename(resolvedPath);
|
|
9212
|
+
const jid = normalizeJid(to);
|
|
9213
|
+
const sock = getSocket(accountId);
|
|
9214
|
+
if (!sock) {
|
|
9215
|
+
console.error(`${TAG15} sent document to=${jid} file=${filename} bytes=${fileStat.size} ok=false reason=not-connected`);
|
|
9216
|
+
return { ok: false, status: 503, error: `WhatsApp account "${accountId}" is not connected` };
|
|
9217
|
+
}
|
|
9218
|
+
const buffer = Buffer.from(await readFile(resolvedPath));
|
|
9219
|
+
const mimetype = detectMimeType(resolvedPath);
|
|
9220
|
+
const result = await sendMediaMessage(
|
|
9221
|
+
sock,
|
|
9222
|
+
to,
|
|
9223
|
+
{ type: "document", buffer, mimetype, filename, caption },
|
|
9224
|
+
{ accountId }
|
|
9225
|
+
);
|
|
9226
|
+
console.error(
|
|
9227
|
+
`${TAG15} sent document to=${jid} file=${filename} bytes=${fileStat.size} ok=${result.success}` + (result.messageId ? ` id=${result.messageId}` : "")
|
|
9228
|
+
);
|
|
9229
|
+
if (result.success) {
|
|
9230
|
+
lastDocumentOutboundAt.set(jid, Date.now());
|
|
9231
|
+
return { ok: true, messageId: result.messageId };
|
|
9232
|
+
}
|
|
9233
|
+
return { ok: false, status: 500, error: result.error ?? "send failed" };
|
|
9234
|
+
}
|
|
9235
|
+
|
|
9236
|
+
// app/lib/whatsapp/inbound/file-delivery-bridge.ts
|
|
9237
|
+
var TAG16 = "[whatsapp-adaptor]";
|
|
9238
|
+
var SEND_USER_FILE = "SendUserFile";
|
|
9239
|
+
var WHATSAPP_SEND_DOCUMENT = "whatsapp-send-document";
|
|
9240
|
+
function platformRoot() {
|
|
9241
|
+
return process.env.MAXY_PLATFORM_ROOT || "";
|
|
9242
|
+
}
|
|
9243
|
+
function makeWhatsAppFileDelivery(entry) {
|
|
9244
|
+
let turnStartedAt = null;
|
|
9245
|
+
return {
|
|
9246
|
+
isFileDeliveryTool(toolName) {
|
|
9247
|
+
return toolName === SEND_USER_FILE || toolName === WHATSAPP_SEND_DOCUMENT;
|
|
9248
|
+
},
|
|
9249
|
+
async onFileToolUse(use) {
|
|
9250
|
+
if (turnStartedAt === null) turnStartedAt = Date.now();
|
|
9251
|
+
if (use.toolName !== SEND_USER_FILE) return;
|
|
9252
|
+
const input = use.input ?? {};
|
|
9253
|
+
const files = Array.isArray(input.files) ? input.files.filter((f) => typeof f === "string") : [];
|
|
9254
|
+
const caption = typeof input.caption === "string" ? input.caption : void 0;
|
|
9255
|
+
if (files.length === 0) return;
|
|
9256
|
+
let maxyAccountId;
|
|
9257
|
+
try {
|
|
9258
|
+
maxyAccountId = resolvePlatformAccountId();
|
|
9259
|
+
} catch (err) {
|
|
9260
|
+
console.error(
|
|
9261
|
+
`${TAG16} file-delivery reject reason=account-unresolved sender=${entry.senderId} message=${err instanceof Error ? err.message : String(err)}`
|
|
9262
|
+
);
|
|
9263
|
+
return;
|
|
9264
|
+
}
|
|
9265
|
+
for (let i = 0; i < files.length; i++) {
|
|
9266
|
+
const result = await sendWhatsAppDocument({
|
|
9267
|
+
to: entry.senderId,
|
|
9268
|
+
filePath: files[i],
|
|
9269
|
+
caption: i === 0 ? caption : void 0,
|
|
9270
|
+
accountId: entry.accountId,
|
|
9271
|
+
maxyAccountId,
|
|
9272
|
+
platformRoot: platformRoot()
|
|
9273
|
+
});
|
|
9274
|
+
if (!result.ok) {
|
|
9275
|
+
console.error(
|
|
9276
|
+
`${TAG16} file-delivery reject reason=send-failed sender=${entry.senderId} status=${result.status} message=${result.error}`
|
|
9277
|
+
);
|
|
9278
|
+
}
|
|
9279
|
+
}
|
|
9280
|
+
},
|
|
9281
|
+
onTurnEnd(firedTools) {
|
|
9282
|
+
const startedAt = turnStartedAt ?? 0;
|
|
9283
|
+
turnStartedAt = null;
|
|
9284
|
+
const okAt = documentOutboundAt(entry.senderId);
|
|
9285
|
+
if (okAt !== void 0 && okAt >= startedAt) return;
|
|
9286
|
+
const sid = entry.sessionId.slice(0, 8);
|
|
9287
|
+
for (const tool of firedTools) {
|
|
9288
|
+
console.error(
|
|
9289
|
+
`${TAG16} file-delivery-unreconciled sender=${entry.senderId} sessionId=${sid} tool=${tool}`
|
|
9290
|
+
);
|
|
9291
|
+
}
|
|
9292
|
+
}
|
|
9293
|
+
};
|
|
9294
|
+
}
|
|
9295
|
+
|
|
9141
9296
|
// app/lib/channel-pty-bridge/bridge.ts
|
|
9142
9297
|
function tagFor(channel) {
|
|
9143
9298
|
return `[${channel}-adaptor]`;
|
|
9144
9299
|
}
|
|
9300
|
+
function fileDeliveryFor(entry) {
|
|
9301
|
+
return entry.channel === "whatsapp" ? makeWhatsAppFileDelivery(entry) : null;
|
|
9302
|
+
}
|
|
9145
9303
|
function publicIdleMs() {
|
|
9146
9304
|
return Number(process.env.CHANNEL_PTY_IDLE_MS ?? String(5 * 6e4));
|
|
9147
9305
|
}
|
|
@@ -9163,6 +9321,7 @@ async function ensureEntry(input) {
|
|
|
9163
9321
|
existing.followerAbort = startFollower({
|
|
9164
9322
|
entry: existing,
|
|
9165
9323
|
tag,
|
|
9324
|
+
fileDelivery: fileDeliveryFor(existing),
|
|
9166
9325
|
onClose: () => {
|
|
9167
9326
|
existing.followerRunning = false;
|
|
9168
9327
|
}
|
|
@@ -9182,7 +9341,7 @@ async function ensureEntry(input) {
|
|
|
9182
9341
|
});
|
|
9183
9342
|
} else {
|
|
9184
9343
|
console.error(`${tag} route role=${input.role} target=public-spawn senderId=${input.senderId}`);
|
|
9185
|
-
const attachmentDir =
|
|
9344
|
+
const attachmentDir = resolve5(ATTACHMENTS_ROOT, "public", input.senderId);
|
|
9186
9345
|
spawned = await managerSpawn({
|
|
9187
9346
|
senderId: input.senderId,
|
|
9188
9347
|
role: input.role,
|
|
@@ -9223,6 +9382,7 @@ async function ensureEntry(input) {
|
|
|
9223
9382
|
entry.followerAbort = startFollower({
|
|
9224
9383
|
entry,
|
|
9225
9384
|
tag,
|
|
9385
|
+
fileDelivery: fileDeliveryFor(entry),
|
|
9226
9386
|
onClose: () => {
|
|
9227
9387
|
entry.followerRunning = false;
|
|
9228
9388
|
}
|
|
@@ -9308,12 +9468,12 @@ async function dispatchOnce(input) {
|
|
|
9308
9468
|
});
|
|
9309
9469
|
if (!entry) return { error: "spawn-failed" };
|
|
9310
9470
|
entry.lastInboundAt = Date.now();
|
|
9311
|
-
let
|
|
9471
|
+
let resolve27;
|
|
9312
9472
|
const turnPromise = new Promise((r) => {
|
|
9313
|
-
|
|
9473
|
+
resolve27 = r;
|
|
9314
9474
|
});
|
|
9315
9475
|
const listener = (text) => {
|
|
9316
|
-
|
|
9476
|
+
resolve27(text);
|
|
9317
9477
|
};
|
|
9318
9478
|
entry.subscribers.add(listener);
|
|
9319
9479
|
const writeOk = await writeInput(entry, input.text);
|
|
@@ -9672,19 +9832,18 @@ app2.post("/", async (c) => {
|
|
|
9672
9832
|
var chat_default = app2;
|
|
9673
9833
|
|
|
9674
9834
|
// server/routes/whatsapp.ts
|
|
9675
|
-
import { join as join5, resolve as
|
|
9676
|
-
import {
|
|
9677
|
-
import { realpathSync as realpathSync2, readdirSync as readdirSync2, readFileSync as readFileSync6, existsSync as existsSync4 } from "fs";
|
|
9835
|
+
import { join as join5, resolve as resolve7 } from "path";
|
|
9836
|
+
import { readdirSync as readdirSync2, readFileSync as readFileSync6, existsSync as existsSync4 } from "fs";
|
|
9678
9837
|
|
|
9679
9838
|
// app/lib/whatsapp/login.ts
|
|
9680
9839
|
var import_qrcode = __toESM(require_lib(), 1);
|
|
9681
9840
|
import { randomUUID as randomUUID6 } from "crypto";
|
|
9682
|
-
var
|
|
9841
|
+
var TAG17 = "[whatsapp:login]";
|
|
9683
9842
|
async function renderQrTerminal(qr) {
|
|
9684
9843
|
try {
|
|
9685
9844
|
return await import_qrcode.default.toString(qr, { type: "utf8" });
|
|
9686
9845
|
} catch (err) {
|
|
9687
|
-
console.error(`${
|
|
9846
|
+
console.error(`${TAG17} terminal QR render failed: ${String(err)}`);
|
|
9688
9847
|
return void 0;
|
|
9689
9848
|
}
|
|
9690
9849
|
}
|
|
@@ -9694,7 +9853,7 @@ function closeSocket(sock) {
|
|
|
9694
9853
|
try {
|
|
9695
9854
|
sock.ws?.close?.();
|
|
9696
9855
|
} catch (err) {
|
|
9697
|
-
console.warn(`${
|
|
9856
|
+
console.warn(`${TAG17} socket close error during cleanup: ${String(err)}`);
|
|
9698
9857
|
}
|
|
9699
9858
|
}
|
|
9700
9859
|
function resetActiveLogin(accountId) {
|
|
@@ -9717,7 +9876,7 @@ async function loginConnectionLoop(accountId, login) {
|
|
|
9717
9876
|
const current = activeLogins.get(accountId);
|
|
9718
9877
|
if (current?.id === login.id) {
|
|
9719
9878
|
current.connected = true;
|
|
9720
|
-
console.error(`${
|
|
9879
|
+
console.error(`${TAG17} loginConnectionLoop: connected account=${accountId} attempt=${attempt}`);
|
|
9721
9880
|
}
|
|
9722
9881
|
return;
|
|
9723
9882
|
} catch (err) {
|
|
@@ -9727,7 +9886,7 @@ async function loginConnectionLoop(accountId, login) {
|
|
|
9727
9886
|
if (!classification.shouldRetry || attempt >= LOGIN_MAX_RECONNECTS) {
|
|
9728
9887
|
if (attempt >= LOGIN_MAX_RECONNECTS) {
|
|
9729
9888
|
console.error(
|
|
9730
|
-
`${
|
|
9889
|
+
`${TAG17} login reconnect attempts exhausted (${attempt}/${LOGIN_MAX_RECONNECTS}) \u2014 surfacing error to agent`
|
|
9731
9890
|
);
|
|
9732
9891
|
current.error = `Login failed after ${attempt} reconnect attempts: ${formatError(err)}`;
|
|
9733
9892
|
} else {
|
|
@@ -9739,7 +9898,7 @@ async function loginConnectionLoop(accountId, login) {
|
|
|
9739
9898
|
attempt++;
|
|
9740
9899
|
const delay = LOGIN_RECONNECT_DELAYS[attempt - 1] ?? 8e3;
|
|
9741
9900
|
console.error(
|
|
9742
|
-
`${
|
|
9901
|
+
`${TAG17} status=${classification.statusCode ?? "unknown"} restart required \u2014 reconnecting with saved creds (attempt ${attempt}/${LOGIN_MAX_RECONNECTS}) delay=${delay}ms`
|
|
9743
9902
|
);
|
|
9744
9903
|
closeSocket(current.sock);
|
|
9745
9904
|
await new Promise((r) => setTimeout(r, delay));
|
|
@@ -9750,7 +9909,7 @@ async function loginConnectionLoop(accountId, login) {
|
|
|
9750
9909
|
current.sock = newSock;
|
|
9751
9910
|
} catch (sockErr) {
|
|
9752
9911
|
console.error(
|
|
9753
|
-
`${
|
|
9912
|
+
`${TAG17} reconnect socket creation failed (attempt ${attempt}/${LOGIN_MAX_RECONNECTS}): ${String(sockErr)}`
|
|
9754
9913
|
);
|
|
9755
9914
|
current.error = `Reconnection failed: ${String(sockErr)}`;
|
|
9756
9915
|
return;
|
|
@@ -9764,7 +9923,7 @@ async function startLogin(opts) {
|
|
|
9764
9923
|
const hasAuth = await authExists(authDir);
|
|
9765
9924
|
const selfId = readSelfId(authDir);
|
|
9766
9925
|
console.error(
|
|
9767
|
-
`${
|
|
9926
|
+
`${TAG17} startLogin account=${accountId} force=${!!force} hasAuth=${hasAuth}` + (existing0 ? ` activeLogin={id=${existing0.id.slice(0, 8)},age=${Math.round((Date.now() - existing0.startedAt) / 1e3)}s,hasQr=${!!existing0.qr}}` : " activeLogin=none")
|
|
9768
9927
|
);
|
|
9769
9928
|
if (hasAuth && !force) {
|
|
9770
9929
|
const who = selfId.e164 ?? selfId.jid ?? "unknown";
|
|
@@ -9776,7 +9935,7 @@ async function startLogin(opts) {
|
|
|
9776
9935
|
await clearAuth(authDir);
|
|
9777
9936
|
const existing = activeLogins.get(accountId);
|
|
9778
9937
|
if (existing && isLoginFresh(existing) && existing.qrDataUrl && !force) {
|
|
9779
|
-
console.error(`${
|
|
9938
|
+
console.error(`${TAG17} startLogin account=${accountId} guard: returning existing QR (age=${Math.round((Date.now() - existing.startedAt) / 1e3)}s)`);
|
|
9780
9939
|
return {
|
|
9781
9940
|
qrDataUrl: existing.qrDataUrl,
|
|
9782
9941
|
qrRaw: existing.qr,
|
|
@@ -9785,13 +9944,13 @@ async function startLogin(opts) {
|
|
|
9785
9944
|
};
|
|
9786
9945
|
}
|
|
9787
9946
|
if (existing) {
|
|
9788
|
-
console.error(`${
|
|
9947
|
+
console.error(`${TAG17} startLogin account=${accountId} ${force ? "force override" : "stale/no-QR"}, resetting active login`);
|
|
9789
9948
|
}
|
|
9790
9949
|
resetActiveLogin(accountId);
|
|
9791
9950
|
let resolveQr = null;
|
|
9792
9951
|
let rejectQr = null;
|
|
9793
|
-
const qrPromise = new Promise((
|
|
9794
|
-
resolveQr =
|
|
9952
|
+
const qrPromise = new Promise((resolve27, reject) => {
|
|
9953
|
+
resolveQr = resolve27;
|
|
9795
9954
|
rejectQr = reject;
|
|
9796
9955
|
});
|
|
9797
9956
|
const qrTimer = setTimeout(
|
|
@@ -9807,14 +9966,14 @@ async function startLogin(opts) {
|
|
|
9807
9966
|
onQr: (qr2) => {
|
|
9808
9967
|
loginQrCount++;
|
|
9809
9968
|
if (pendingQr) {
|
|
9810
|
-
console.error(`${
|
|
9969
|
+
console.error(`${TAG17} QR rotation #${loginQrCount} received for account=${accountId} \u2014 not forwarded (initial QR already captured)`);
|
|
9811
9970
|
return;
|
|
9812
9971
|
}
|
|
9813
9972
|
pendingQr = qr2;
|
|
9814
9973
|
const current = activeLogins.get(accountId);
|
|
9815
9974
|
if (current && !current.qr) current.qr = qr2;
|
|
9816
9975
|
clearTimeout(qrTimer);
|
|
9817
|
-
console.error(`${
|
|
9976
|
+
console.error(`${TAG17} QR #${loginQrCount} received for account=${accountId} \u2014 forwarding to caller`);
|
|
9818
9977
|
resolveQr?.(qr2);
|
|
9819
9978
|
}
|
|
9820
9979
|
});
|
|
@@ -9834,7 +9993,7 @@ async function startLogin(opts) {
|
|
|
9834
9993
|
activeLogins.set(accountId, login);
|
|
9835
9994
|
if (pendingQr && !login.qr) login.qr = pendingQr;
|
|
9836
9995
|
loginConnectionLoop(accountId, login).catch((err) => {
|
|
9837
|
-
console.error(`${
|
|
9996
|
+
console.error(`${TAG17} loginConnectionLoop unexpected error: ${String(err)}`);
|
|
9838
9997
|
const current = activeLogins.get(accountId);
|
|
9839
9998
|
if (current?.id === login.id) {
|
|
9840
9999
|
current.error = `Unexpected login error: ${String(err)}`;
|
|
@@ -9860,7 +10019,7 @@ async function waitForLogin(opts) {
|
|
|
9860
10019
|
const { accountId, timeoutMs = 6e4 } = opts;
|
|
9861
10020
|
const login = activeLogins.get(accountId);
|
|
9862
10021
|
console.error(
|
|
9863
|
-
`${
|
|
10022
|
+
`${TAG17} waitForLogin account=${accountId} timeout=${timeoutMs}ms` + (login ? ` login={id=${login.id.slice(0, 8)},age=${Math.round((Date.now() - login.startedAt) / 1e3)}s,connected=${login.connected},hasQr=${!!login.qr}}` : " login=none")
|
|
9864
10023
|
);
|
|
9865
10024
|
if (!login) {
|
|
9866
10025
|
return { connected: false, message: "No active WhatsApp login in progress." };
|
|
@@ -9873,7 +10032,7 @@ async function waitForLogin(opts) {
|
|
|
9873
10032
|
while (Date.now() < deadline) {
|
|
9874
10033
|
if (login.connected) {
|
|
9875
10034
|
const selfId = readSelfId(login.authDir);
|
|
9876
|
-
console.error(`${
|
|
10035
|
+
console.error(`${TAG17} login complete for account=${accountId} phone=${selfId.e164 ?? "unknown"}`);
|
|
9877
10036
|
const sock = login.sock;
|
|
9878
10037
|
const authDir = login.authDir;
|
|
9879
10038
|
activeLogins.delete(accountId);
|
|
@@ -9893,17 +10052,17 @@ async function waitForLogin(opts) {
|
|
|
9893
10052
|
await new Promise((r) => setTimeout(r, 1e3));
|
|
9894
10053
|
}
|
|
9895
10054
|
const elapsed = Math.round((Date.now() - (deadline - timeoutMs)) / 1e3);
|
|
9896
|
-
console.error(`${
|
|
10055
|
+
console.error(`${TAG17} waitForLogin timeout account=${accountId} elapsed=${elapsed}s \u2014 cleaning up active login`);
|
|
9897
10056
|
resetActiveLogin(accountId);
|
|
9898
10057
|
return { connected: false, message: "Login timed out. Try generating a new QR." };
|
|
9899
10058
|
}
|
|
9900
10059
|
|
|
9901
10060
|
// app/lib/whatsapp/config-persist.ts
|
|
9902
10061
|
import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, existsSync as existsSync3 } from "fs";
|
|
9903
|
-
import { resolve as
|
|
9904
|
-
var
|
|
10062
|
+
import { resolve as resolve6, join as join4 } from "path";
|
|
10063
|
+
var TAG18 = "[whatsapp:config]";
|
|
9905
10064
|
function configPath(accountDir) {
|
|
9906
|
-
return
|
|
10065
|
+
return resolve6(accountDir, "account.json");
|
|
9907
10066
|
}
|
|
9908
10067
|
function readConfig(accountDir) {
|
|
9909
10068
|
const path2 = configPath(accountDir);
|
|
@@ -9918,9 +10077,9 @@ function reloadManagerConfig(accountDir) {
|
|
|
9918
10077
|
try {
|
|
9919
10078
|
const config = readConfig(accountDir);
|
|
9920
10079
|
reloadConfig(config);
|
|
9921
|
-
console.error(`${
|
|
10080
|
+
console.error(`${TAG18} reloaded manager config`);
|
|
9922
10081
|
} catch (err) {
|
|
9923
|
-
console.error(`${
|
|
10082
|
+
console.error(`${TAG18} manager config reload failed: ${String(err)}`);
|
|
9924
10083
|
}
|
|
9925
10084
|
}
|
|
9926
10085
|
var E164_PATTERN = /^\+\d{7,15}$/;
|
|
@@ -9946,25 +10105,25 @@ function persistAfterPairing(accountDir, accountId, selfPhone) {
|
|
|
9946
10105
|
const adminPhones = wa.adminPhones;
|
|
9947
10106
|
if (!adminPhones.includes(normalized)) {
|
|
9948
10107
|
adminPhones.push(normalized);
|
|
9949
|
-
console.error(`${
|
|
10108
|
+
console.error(`${TAG18} added selfPhone=${normalized} to adminPhones`);
|
|
9950
10109
|
}
|
|
9951
10110
|
} else {
|
|
9952
|
-
console.error(`${
|
|
10111
|
+
console.error(`${TAG18} skipping adminPhones \u2014 selfPhone is null account=${accountId}`);
|
|
9953
10112
|
}
|
|
9954
10113
|
const parsed = WhatsAppConfigSchema.safeParse(wa);
|
|
9955
10114
|
if (!parsed.success) {
|
|
9956
10115
|
const msg = parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ");
|
|
9957
|
-
console.error(`${
|
|
10116
|
+
console.error(`${TAG18} validation failed after pairing: ${msg}`);
|
|
9958
10117
|
return { ok: false, error: `Validation failed: ${msg}` };
|
|
9959
10118
|
}
|
|
9960
10119
|
config.whatsapp = parsed.data;
|
|
9961
10120
|
writeConfig(accountDir, config);
|
|
9962
|
-
console.error(`${
|
|
10121
|
+
console.error(`${TAG18} persisted after pairing account=${accountId} phone=${selfPhone ?? "null"}`);
|
|
9963
10122
|
reloadManagerConfig(accountDir);
|
|
9964
10123
|
return { ok: true };
|
|
9965
10124
|
} catch (err) {
|
|
9966
10125
|
const msg = err instanceof Error ? err.message : String(err);
|
|
9967
|
-
console.error(`${
|
|
10126
|
+
console.error(`${TAG18} persist failed account=${accountId}: ${msg}`);
|
|
9968
10127
|
return { ok: false, error: msg };
|
|
9969
10128
|
}
|
|
9970
10129
|
}
|
|
@@ -9994,12 +10153,12 @@ function addAdminPhone(accountDir, phone) {
|
|
|
9994
10153
|
}
|
|
9995
10154
|
config.whatsapp = parsed.data;
|
|
9996
10155
|
writeConfig(accountDir, config);
|
|
9997
|
-
console.error(`${
|
|
10156
|
+
console.error(`${TAG18} added admin phone=${normalized}`);
|
|
9998
10157
|
reloadManagerConfig(accountDir);
|
|
9999
10158
|
return { ok: true, message: `Added ${normalized} as admin phone. Messages from this number will route to the admin agent.` };
|
|
10000
10159
|
} catch (err) {
|
|
10001
10160
|
const msg = err instanceof Error ? err.message : String(err);
|
|
10002
|
-
console.error(`${
|
|
10161
|
+
console.error(`${TAG18} addAdminPhone failed: ${msg}`);
|
|
10003
10162
|
return { ok: false, error: msg };
|
|
10004
10163
|
}
|
|
10005
10164
|
}
|
|
@@ -10027,12 +10186,12 @@ function removeAdminPhone(accountDir, phone) {
|
|
|
10027
10186
|
}
|
|
10028
10187
|
config.whatsapp = parsed.data;
|
|
10029
10188
|
writeConfig(accountDir, config);
|
|
10030
|
-
console.error(`${
|
|
10189
|
+
console.error(`${TAG18} removed admin phone=${normalized}`);
|
|
10031
10190
|
reloadManagerConfig(accountDir);
|
|
10032
10191
|
return { ok: true, message: `Removed ${normalized} from admin phones. Messages from this number will now route to the public agent.` };
|
|
10033
10192
|
} catch (err) {
|
|
10034
10193
|
const msg = err instanceof Error ? err.message : String(err);
|
|
10035
|
-
console.error(`${
|
|
10194
|
+
console.error(`${TAG18} removeAdminPhone failed: ${msg}`);
|
|
10036
10195
|
return { ok: false, error: msg };
|
|
10037
10196
|
}
|
|
10038
10197
|
}
|
|
@@ -10070,12 +10229,12 @@ function setPublicAgent(accountDir, slug) {
|
|
|
10070
10229
|
}
|
|
10071
10230
|
config.whatsapp = parsed.data;
|
|
10072
10231
|
writeConfig(accountDir, config);
|
|
10073
|
-
console.error(`${
|
|
10232
|
+
console.error(`${TAG18} publicAgent set to ${trimmed}`);
|
|
10074
10233
|
reloadManagerConfig(accountDir);
|
|
10075
10234
|
return { ok: true, message: `Public agent set to "${trimmed}". WhatsApp messages from non-admin phones will be handled by this agent.` };
|
|
10076
10235
|
} catch (err) {
|
|
10077
10236
|
const msg = err instanceof Error ? err.message : String(err);
|
|
10078
|
-
console.error(`${
|
|
10237
|
+
console.error(`${TAG18} setPublicAgent failed: ${msg}`);
|
|
10079
10238
|
return { ok: false, error: msg };
|
|
10080
10239
|
}
|
|
10081
10240
|
}
|
|
@@ -10139,12 +10298,12 @@ function setGroupPublicAgent(accountDir, accountId, groupJid, slug) {
|
|
|
10139
10298
|
}
|
|
10140
10299
|
config.whatsapp = parsed.data;
|
|
10141
10300
|
writeConfig(accountDir, config);
|
|
10142
|
-
console.error(`${
|
|
10301
|
+
console.error(`${TAG18} setGroupPublicAgent account=${trimmedAccount} groupJid=${trimmedGroup} slug=${trimmedSlug}`);
|
|
10143
10302
|
reloadManagerConfig(accountDir);
|
|
10144
10303
|
return { ok: true, message: `Per-group public agent set to "${trimmedSlug}" for group ${trimmedGroup}.` };
|
|
10145
10304
|
} catch (err) {
|
|
10146
10305
|
const msg = err instanceof Error ? err.message : String(err);
|
|
10147
|
-
console.error(`${
|
|
10306
|
+
console.error(`${TAG18} setGroupPublicAgent failed: ${msg}`);
|
|
10148
10307
|
return { ok: false, error: msg };
|
|
10149
10308
|
}
|
|
10150
10309
|
}
|
|
@@ -10171,12 +10330,12 @@ function unsetGroupPublicAgent(accountDir, accountId, groupJid) {
|
|
|
10171
10330
|
}
|
|
10172
10331
|
config.whatsapp = parsed.data;
|
|
10173
10332
|
writeConfig(accountDir, config);
|
|
10174
|
-
console.error(`${
|
|
10333
|
+
console.error(`${TAG18} unsetGroupPublicAgent account=${trimmedAccount} groupJid=${trimmedGroup}`);
|
|
10175
10334
|
reloadManagerConfig(accountDir);
|
|
10176
10335
|
return { ok: true, message: `Per-group public agent override removed for group ${trimmedGroup}.` };
|
|
10177
10336
|
} catch (err) {
|
|
10178
10337
|
const msg = err instanceof Error ? err.message : String(err);
|
|
10179
|
-
console.error(`${
|
|
10338
|
+
console.error(`${TAG18} unsetGroupPublicAgent failed: ${msg}`);
|
|
10180
10339
|
return { ok: false, error: msg };
|
|
10181
10340
|
}
|
|
10182
10341
|
}
|
|
@@ -10197,17 +10356,17 @@ function updateConfig(accountDir, fields) {
|
|
|
10197
10356
|
const parsed = WhatsAppConfigSchema.safeParse(wa);
|
|
10198
10357
|
if (!parsed.success) {
|
|
10199
10358
|
const msg = parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ");
|
|
10200
|
-
console.error(`${
|
|
10359
|
+
console.error(`${TAG18} update validation failed: ${msg}`);
|
|
10201
10360
|
return { ok: false, error: `Validation failed: ${msg}` };
|
|
10202
10361
|
}
|
|
10203
10362
|
config.whatsapp = parsed.data;
|
|
10204
10363
|
writeConfig(accountDir, config);
|
|
10205
|
-
console.error(`${
|
|
10364
|
+
console.error(`${TAG18} updated fields=[${fieldNames.join(",")}]`);
|
|
10206
10365
|
reloadManagerConfig(accountDir);
|
|
10207
10366
|
return { ok: true, message: `Updated WhatsApp config: ${fieldNames.join(", ")}.` };
|
|
10208
10367
|
} catch (err) {
|
|
10209
10368
|
const msg = err instanceof Error ? err.message : String(err);
|
|
10210
|
-
console.error(`${
|
|
10369
|
+
console.error(`${TAG18} updateConfig failed: ${msg}`);
|
|
10211
10370
|
return { ok: false, error: msg };
|
|
10212
10371
|
}
|
|
10213
10372
|
}
|
|
@@ -10333,17 +10492,17 @@ function serializeWhatsAppSchema() {
|
|
|
10333
10492
|
}
|
|
10334
10493
|
|
|
10335
10494
|
// server/routes/whatsapp.ts
|
|
10336
|
-
var
|
|
10495
|
+
var TAG19 = "[whatsapp:api]";
|
|
10337
10496
|
var PLATFORM_ROOT4 = process.env.MAXY_PLATFORM_ROOT || "";
|
|
10338
10497
|
var app3 = new Hono();
|
|
10339
10498
|
app3.get("/status", (c) => {
|
|
10340
10499
|
try {
|
|
10341
10500
|
const status = getStatus();
|
|
10342
10501
|
const summary = status.map((a) => `${a.accountId}:${a.connected ? "up" : "down"}`).join(", ");
|
|
10343
|
-
console.error(`${
|
|
10502
|
+
console.error(`${TAG19} status accounts=${status.length} [${summary}]`);
|
|
10344
10503
|
return c.json({ accounts: status });
|
|
10345
10504
|
} catch (err) {
|
|
10346
|
-
console.error(`${
|
|
10505
|
+
console.error(`${TAG19} status error: ${String(err)}`);
|
|
10347
10506
|
return c.json({ error: String(err) }, 500);
|
|
10348
10507
|
}
|
|
10349
10508
|
});
|
|
@@ -10354,10 +10513,10 @@ app3.post("/login/start", async (c) => {
|
|
|
10354
10513
|
const force = body.force ?? false;
|
|
10355
10514
|
const authDir = join5(MAXY_DIR, "credentials", "whatsapp", accountId);
|
|
10356
10515
|
const result = await startLogin({ accountId, authDir, force });
|
|
10357
|
-
console.error(`${
|
|
10516
|
+
console.error(`${TAG19} login/start result account=${accountId} hasQr=${!!result.qrRaw}${result.selfPhone ? ` phone=${result.selfPhone}` : ""}`);
|
|
10358
10517
|
return c.json(result);
|
|
10359
10518
|
} catch (err) {
|
|
10360
|
-
console.error(`${
|
|
10519
|
+
console.error(`${TAG19} login/start error: ${String(err)}`);
|
|
10361
10520
|
return c.json({ error: String(err) }, 500);
|
|
10362
10521
|
}
|
|
10363
10522
|
});
|
|
@@ -10372,7 +10531,7 @@ app3.post("/login/wait", async (c) => {
|
|
|
10372
10531
|
try {
|
|
10373
10532
|
await registerLoginSocket(accountId, result.sock, result.authDir);
|
|
10374
10533
|
} catch (regErr) {
|
|
10375
|
-
console.error(`${
|
|
10534
|
+
console.error(`${TAG19} registerLoginSocket failed account=${accountId}: ${String(regErr)}`);
|
|
10376
10535
|
}
|
|
10377
10536
|
try {
|
|
10378
10537
|
const account = resolveAccount();
|
|
@@ -10380,16 +10539,16 @@ app3.post("/login/wait", async (c) => {
|
|
|
10380
10539
|
const persistResult = persistAfterPairing(account.accountDir, accountId, result.selfPhone ?? null);
|
|
10381
10540
|
configPersisted = persistResult.ok;
|
|
10382
10541
|
if (!persistResult.ok) {
|
|
10383
|
-
console.error(`${
|
|
10542
|
+
console.error(`${TAG19} config persist failed account=${accountId}: ${persistResult.error}`);
|
|
10384
10543
|
}
|
|
10385
10544
|
} else {
|
|
10386
|
-
console.error(`${
|
|
10545
|
+
console.error(`${TAG19} config persist skipped \u2014 no account resolved`);
|
|
10387
10546
|
}
|
|
10388
10547
|
} catch (persistErr) {
|
|
10389
|
-
console.error(`${
|
|
10548
|
+
console.error(`${TAG19} config persist error account=${accountId}: ${String(persistErr)}`);
|
|
10390
10549
|
}
|
|
10391
10550
|
}
|
|
10392
|
-
console.error(`${
|
|
10551
|
+
console.error(`${TAG19} login/wait result account=${accountId} connected=${result.connected}${result.selfPhone ? ` phone=${result.selfPhone}` : ""} configPersisted=${configPersisted}`);
|
|
10393
10552
|
return c.json({
|
|
10394
10553
|
connected: result.connected,
|
|
10395
10554
|
message: result.message,
|
|
@@ -10397,7 +10556,7 @@ app3.post("/login/wait", async (c) => {
|
|
|
10397
10556
|
configPersisted
|
|
10398
10557
|
});
|
|
10399
10558
|
} catch (err) {
|
|
10400
|
-
console.error(`${
|
|
10559
|
+
console.error(`${TAG19} login/wait error: ${String(err)}`);
|
|
10401
10560
|
return c.json({ error: String(err) }, 500);
|
|
10402
10561
|
}
|
|
10403
10562
|
});
|
|
@@ -10408,7 +10567,7 @@ app3.post("/disconnect", async (c) => {
|
|
|
10408
10567
|
await stopConnection(accountId);
|
|
10409
10568
|
return c.json({ disconnected: true, accountId });
|
|
10410
10569
|
} catch (err) {
|
|
10411
|
-
console.error(`${
|
|
10570
|
+
console.error(`${TAG19} disconnect error: ${String(err)}`);
|
|
10412
10571
|
return c.json({ error: String(err) }, 500);
|
|
10413
10572
|
}
|
|
10414
10573
|
});
|
|
@@ -10419,7 +10578,7 @@ app3.post("/reconnect", async (c) => {
|
|
|
10419
10578
|
await startConnection(accountId);
|
|
10420
10579
|
return c.json({ reconnecting: true, accountId });
|
|
10421
10580
|
} catch (err) {
|
|
10422
|
-
console.error(`${
|
|
10581
|
+
console.error(`${TAG19} reconnect error: ${String(err)}`);
|
|
10423
10582
|
return c.json({ error: String(err) }, 500);
|
|
10424
10583
|
}
|
|
10425
10584
|
});
|
|
@@ -10438,7 +10597,7 @@ app3.post("/send", async (c) => {
|
|
|
10438
10597
|
const result = await sendTextMessage(sock, to, text, { accountId });
|
|
10439
10598
|
return c.json(result);
|
|
10440
10599
|
} catch (err) {
|
|
10441
|
-
console.error(`${
|
|
10600
|
+
console.error(`${TAG19} send error: ${String(err)}`);
|
|
10442
10601
|
return c.json({ error: String(err) }, 500);
|
|
10443
10602
|
}
|
|
10444
10603
|
});
|
|
@@ -10459,7 +10618,7 @@ app3.post("/config", async (c) => {
|
|
|
10459
10618
|
return c.json({ ok: false, error: 'Missing required field "phone" (E.164 format, e.g. +441234567890).' }, 400);
|
|
10460
10619
|
}
|
|
10461
10620
|
const result = addAdminPhone(account.accountDir, phone);
|
|
10462
|
-
console.error(`${
|
|
10621
|
+
console.error(`${TAG19} config action=add-admin-phone phone=${phone} ok=${result.ok}`);
|
|
10463
10622
|
return c.json(result, result.ok ? 200 : 400);
|
|
10464
10623
|
}
|
|
10465
10624
|
case "remove-admin-phone": {
|
|
@@ -10467,12 +10626,12 @@ app3.post("/config", async (c) => {
|
|
|
10467
10626
|
return c.json({ ok: false, error: 'Missing required field "phone".' }, 400);
|
|
10468
10627
|
}
|
|
10469
10628
|
const result = removeAdminPhone(account.accountDir, phone);
|
|
10470
|
-
console.error(`${
|
|
10629
|
+
console.error(`${TAG19} config action=remove-admin-phone phone=${phone} ok=${result.ok}`);
|
|
10471
10630
|
return c.json(result, result.ok ? 200 : 400);
|
|
10472
10631
|
}
|
|
10473
10632
|
case "list-admin-phones": {
|
|
10474
10633
|
const phones = readAdminPhones(account.accountDir);
|
|
10475
|
-
console.error(`${
|
|
10634
|
+
console.error(`${TAG19} config action=list-admin-phones count=${phones.length}`);
|
|
10476
10635
|
return c.json({ ok: true, phones });
|
|
10477
10636
|
}
|
|
10478
10637
|
case "set-public-agent": {
|
|
@@ -10480,14 +10639,14 @@ app3.post("/config", async (c) => {
|
|
|
10480
10639
|
return c.json({ ok: false, error: 'Missing required field "slug" (the agent directory name, e.g. "my-agent").' }, 400);
|
|
10481
10640
|
}
|
|
10482
10641
|
const result = setPublicAgent(account.accountDir, slug);
|
|
10483
|
-
console.error(`${
|
|
10642
|
+
console.error(`${TAG19} config action=set-public-agent slug=${slug} ok=${result.ok}`);
|
|
10484
10643
|
return c.json(result, result.ok ? 200 : 400);
|
|
10485
10644
|
}
|
|
10486
10645
|
case "get-public-agent": {
|
|
10487
10646
|
const targetAccount = typeof accountId === "string" && accountId.trim() ? accountId.trim() : "default";
|
|
10488
10647
|
const targetGroup = typeof groupJid === "string" && groupJid.trim() ? groupJid.trim() : void 0;
|
|
10489
10648
|
const resolved = resolvePublicAgent(account.accountDir, { accountId: targetAccount, groupJid: targetGroup });
|
|
10490
|
-
console.error(`${
|
|
10649
|
+
console.error(`${TAG19} config action=get-public-agent accountId=${targetAccount} groupJid=${targetGroup ?? "none"} slug=${resolved?.slug ?? "none"} source=${resolved?.source ?? "none"}`);
|
|
10491
10650
|
return c.json({ ok: true, slug: resolved?.slug ?? null, source: resolved?.source ?? null });
|
|
10492
10651
|
}
|
|
10493
10652
|
case "set-group-public-agent": {
|
|
@@ -10499,7 +10658,7 @@ app3.post("/config", async (c) => {
|
|
|
10499
10658
|
}
|
|
10500
10659
|
const targetAccount = typeof accountId === "string" && accountId.trim() ? accountId.trim() : "default";
|
|
10501
10660
|
const result = setGroupPublicAgent(account.accountDir, targetAccount, groupJid, slug);
|
|
10502
|
-
console.error(`${
|
|
10661
|
+
console.error(`${TAG19} config action=set-group-public-agent accountId=${targetAccount} groupJid=${groupJid} slug=${slug} ok=${result.ok}`);
|
|
10503
10662
|
return c.json(result, result.ok ? 200 : 400);
|
|
10504
10663
|
}
|
|
10505
10664
|
case "unset-group-public-agent": {
|
|
@@ -10508,43 +10667,43 @@ app3.post("/config", async (c) => {
|
|
|
10508
10667
|
}
|
|
10509
10668
|
const targetAccount = typeof accountId === "string" && accountId.trim() ? accountId.trim() : "default";
|
|
10510
10669
|
const result = unsetGroupPublicAgent(account.accountDir, targetAccount, groupJid);
|
|
10511
|
-
console.error(`${
|
|
10670
|
+
console.error(`${TAG19} config action=unset-group-public-agent accountId=${targetAccount} groupJid=${groupJid} ok=${result.ok}`);
|
|
10512
10671
|
return c.json(result, result.ok ? 200 : 400);
|
|
10513
10672
|
}
|
|
10514
10673
|
case "list-public-agents": {
|
|
10515
|
-
const agentsDir =
|
|
10674
|
+
const agentsDir = resolve7(account.accountDir, "agents");
|
|
10516
10675
|
const agents = [];
|
|
10517
10676
|
if (existsSync4(agentsDir)) {
|
|
10518
10677
|
try {
|
|
10519
10678
|
const entries = readdirSync2(agentsDir, { withFileTypes: true });
|
|
10520
10679
|
for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
10521
10680
|
if (!entry.isDirectory() || entry.name === "admin") continue;
|
|
10522
|
-
const configPath2 =
|
|
10681
|
+
const configPath2 = resolve7(agentsDir, entry.name, "config.json");
|
|
10523
10682
|
if (!existsSync4(configPath2)) continue;
|
|
10524
10683
|
try {
|
|
10525
10684
|
const config = JSON.parse(readFileSync6(configPath2, "utf-8"));
|
|
10526
10685
|
agents.push({ slug: entry.name, displayName: config.displayName ?? entry.name });
|
|
10527
10686
|
} catch {
|
|
10528
|
-
console.error(`${
|
|
10687
|
+
console.error(`${TAG19} config action=list-public-agents error="failed to parse config.json for agent ${entry.name}" \u2014 skipping`);
|
|
10529
10688
|
}
|
|
10530
10689
|
}
|
|
10531
10690
|
} catch (err) {
|
|
10532
|
-
console.error(`${
|
|
10691
|
+
console.error(`${TAG19} config action=list-public-agents error="failed to scan agents directory: ${String(err)}"`);
|
|
10533
10692
|
}
|
|
10534
10693
|
}
|
|
10535
|
-
console.error(`${
|
|
10694
|
+
console.error(`${TAG19} config action=list-public-agents count=${agents.length}`);
|
|
10536
10695
|
return c.json({ ok: true, agents });
|
|
10537
10696
|
}
|
|
10538
10697
|
case "schema": {
|
|
10539
10698
|
const text = serializeWhatsAppSchema();
|
|
10540
|
-
console.error(`${
|
|
10699
|
+
console.error(`${TAG19} config action=schema`);
|
|
10541
10700
|
return c.json({ ok: true, text });
|
|
10542
10701
|
}
|
|
10543
10702
|
case "list-groups": {
|
|
10544
10703
|
const groupAccountId = accountId ?? "default";
|
|
10545
10704
|
const sock = getSocket(groupAccountId);
|
|
10546
10705
|
if (!sock) {
|
|
10547
|
-
console.error(`${
|
|
10706
|
+
console.error(`${TAG19} config action=list-groups error="not connected" accountId=${groupAccountId}`);
|
|
10548
10707
|
return c.json({ ok: false, error: `WhatsApp account "${groupAccountId}" is not connected. Connect first, then retry.` });
|
|
10549
10708
|
}
|
|
10550
10709
|
try {
|
|
@@ -10554,10 +10713,10 @@ app3.post("/config", async (c) => {
|
|
|
10554
10713
|
name: g.subject ?? g.id,
|
|
10555
10714
|
participantCount: Array.isArray(g.participants) ? g.participants.length : 0
|
|
10556
10715
|
}));
|
|
10557
|
-
console.error(`${
|
|
10716
|
+
console.error(`${TAG19} config action=list-groups count=${groups.length} accountId=${groupAccountId}`);
|
|
10558
10717
|
return c.json({ ok: true, groups });
|
|
10559
10718
|
} catch (err) {
|
|
10560
|
-
console.error(`${
|
|
10719
|
+
console.error(`${TAG19} config action=list-groups error="${String(err)}" accountId=${groupAccountId}`);
|
|
10561
10720
|
return c.json({ ok: false, error: `Failed to fetch groups: ${String(err)}` });
|
|
10562
10721
|
}
|
|
10563
10722
|
}
|
|
@@ -10567,12 +10726,12 @@ app3.post("/config", async (c) => {
|
|
|
10567
10726
|
}
|
|
10568
10727
|
const result = updateConfig(account.accountDir, fields);
|
|
10569
10728
|
const fieldNames = Object.keys(fields);
|
|
10570
|
-
console.error(`${
|
|
10729
|
+
console.error(`${TAG19} config action=update-config fields=[${fieldNames.join(",")}] ok=${result.ok}`);
|
|
10571
10730
|
return c.json(result, result.ok ? 200 : 400);
|
|
10572
10731
|
}
|
|
10573
10732
|
case "get-config": {
|
|
10574
10733
|
const waConfig = getConfig(account.accountDir);
|
|
10575
|
-
console.error(`${
|
|
10734
|
+
console.error(`${TAG19} config action=get-config`);
|
|
10576
10735
|
return c.json({ ok: true, config: waConfig });
|
|
10577
10736
|
}
|
|
10578
10737
|
default:
|
|
@@ -10582,7 +10741,7 @@ app3.post("/config", async (c) => {
|
|
|
10582
10741
|
);
|
|
10583
10742
|
}
|
|
10584
10743
|
} catch (err) {
|
|
10585
|
-
console.error(`${
|
|
10744
|
+
console.error(`${TAG19} config error: ${String(err)}`);
|
|
10586
10745
|
return c.json({ ok: false, error: String(err) }, 500);
|
|
10587
10746
|
}
|
|
10588
10747
|
});
|
|
@@ -10591,55 +10750,18 @@ app3.post("/send-document", async (c) => {
|
|
|
10591
10750
|
const body = await c.req.json();
|
|
10592
10751
|
const { to, filePath, caption, maxyAccountId } = body;
|
|
10593
10752
|
const accountId = validateAccountId(body.accountId);
|
|
10594
|
-
|
|
10595
|
-
|
|
10596
|
-
|
|
10597
|
-
|
|
10598
|
-
|
|
10599
|
-
|
|
10600
|
-
|
|
10601
|
-
|
|
10602
|
-
|
|
10603
|
-
|
|
10604
|
-
const accountResolved = realpathSync2(accountDir);
|
|
10605
|
-
if (!resolvedPath.startsWith(accountResolved + "/")) {
|
|
10606
|
-
const sanitised = filePath.replace(accountDir, "<account>/");
|
|
10607
|
-
console.error(`${TAG17} send-document REJECTED path=${sanitised} reason=outside_account_directory`);
|
|
10608
|
-
return c.json({ error: "Access denied: file is outside the account directory" }, 403);
|
|
10609
|
-
}
|
|
10610
|
-
} catch (err) {
|
|
10611
|
-
const code = err.code;
|
|
10612
|
-
if (code === "ENOENT") {
|
|
10613
|
-
console.error(`${TAG17} send-document ENOENT path=${filePath}`);
|
|
10614
|
-
return c.json({ error: `File not found: ${filePath}` }, 404);
|
|
10615
|
-
}
|
|
10616
|
-
console.error(`${TAG17} send-document path error: ${String(err)}`);
|
|
10617
|
-
return c.json({ error: String(err) }, 500);
|
|
10618
|
-
}
|
|
10619
|
-
const fileStat = await stat2(resolvedPath);
|
|
10620
|
-
if (fileStat.size > MAX_FILE_SIZE_BYTES) {
|
|
10621
|
-
return c.json({ error: `File exceeds 50 MB limit (${(fileStat.size / 1024 / 1024).toFixed(1)} MB)` }, 400);
|
|
10622
|
-
}
|
|
10623
|
-
const buffer = Buffer.from(await readFile(resolvedPath));
|
|
10624
|
-
const filename = basename(resolvedPath);
|
|
10625
|
-
const mimetype = detectMimeType(resolvedPath);
|
|
10626
|
-
const sock = getSocket(accountId);
|
|
10627
|
-
if (!sock) {
|
|
10628
|
-
return c.json({ error: `WhatsApp account "${accountId}" is not connected` }, 503);
|
|
10629
|
-
}
|
|
10630
|
-
const result = await sendMediaMessage(sock, to, {
|
|
10631
|
-
type: "document",
|
|
10632
|
-
buffer,
|
|
10633
|
-
mimetype,
|
|
10634
|
-
filename,
|
|
10635
|
-
caption
|
|
10636
|
-
}, { accountId });
|
|
10637
|
-
console.error(
|
|
10638
|
-
`${TAG17} send-document to=${to} size=${fileStat.size} mime=${mimetype} ok=${result.success}` + (result.messageId ? ` id=${result.messageId}` : "")
|
|
10639
|
-
);
|
|
10640
|
-
return c.json(result);
|
|
10753
|
+
const result = await sendWhatsAppDocument({
|
|
10754
|
+
to,
|
|
10755
|
+
filePath,
|
|
10756
|
+
caption,
|
|
10757
|
+
accountId,
|
|
10758
|
+
maxyAccountId,
|
|
10759
|
+
platformRoot: PLATFORM_ROOT4
|
|
10760
|
+
});
|
|
10761
|
+
if (!result.ok) return c.json({ error: result.error }, result.status);
|
|
10762
|
+
return c.json({ success: true, messageId: result.messageId });
|
|
10641
10763
|
} catch (err) {
|
|
10642
|
-
console.error(`${
|
|
10764
|
+
console.error(`${TAG19} send-document error: ${String(err)}`);
|
|
10643
10765
|
return c.json({ error: String(err) }, 500);
|
|
10644
10766
|
}
|
|
10645
10767
|
});
|
|
@@ -10649,11 +10771,11 @@ app3.get("/activity", (c) => {
|
|
|
10649
10771
|
const result = getChannelActivity(accountId);
|
|
10650
10772
|
const total = result.accounts.reduce((sum, a) => sum + a.total, 0);
|
|
10651
10773
|
console.error(
|
|
10652
|
-
`${
|
|
10774
|
+
`${TAG19} activity accounts=${result.accounts.length} total=${total} recentEvents=${result.recentEvents.length}` + (accountId ? ` filter=${accountId}` : "")
|
|
10653
10775
|
);
|
|
10654
10776
|
return c.json(result);
|
|
10655
10777
|
} catch (err) {
|
|
10656
|
-
console.error(`${
|
|
10778
|
+
console.error(`${TAG19} activity error: ${String(err)}`);
|
|
10657
10779
|
return c.json({ error: String(err) }, 500);
|
|
10658
10780
|
}
|
|
10659
10781
|
});
|
|
@@ -10672,10 +10794,10 @@ app3.get("/conversations", (c) => {
|
|
|
10672
10794
|
};
|
|
10673
10795
|
});
|
|
10674
10796
|
conversations.sort((a, b) => (b.lastMessageTimestamp ?? 0) - (a.lastMessageTimestamp ?? 0));
|
|
10675
|
-
console.error(`${
|
|
10797
|
+
console.error(`${TAG19} conversations account=${accountId} count=${conversations.length}`);
|
|
10676
10798
|
return c.json({ conversations });
|
|
10677
10799
|
} catch (err) {
|
|
10678
|
-
console.error(`${
|
|
10800
|
+
console.error(`${TAG19} conversations error: ${String(err)}`);
|
|
10679
10801
|
return c.json({ error: String(err) }, 500);
|
|
10680
10802
|
}
|
|
10681
10803
|
});
|
|
@@ -10690,10 +10812,10 @@ app3.get("/messages", (c) => {
|
|
|
10690
10812
|
const limit = limitParam ? parseInt(limitParam, 10) : void 0;
|
|
10691
10813
|
const effectiveLimit = limit && !Number.isNaN(limit) && limit > 0 ? limit : void 0;
|
|
10692
10814
|
const messages = getMessages(accountId, jid, effectiveLimit);
|
|
10693
|
-
console.error(`${
|
|
10815
|
+
console.error(`${TAG19} messages account=${accountId} jid=${jid} limit=${effectiveLimit ?? "all"} returned=${messages.length}`);
|
|
10694
10816
|
return c.json({ messages });
|
|
10695
10817
|
} catch (err) {
|
|
10696
|
-
console.error(`${
|
|
10818
|
+
console.error(`${TAG19} messages error: ${String(err)}`);
|
|
10697
10819
|
return c.json({ error: String(err) }, 500);
|
|
10698
10820
|
}
|
|
10699
10821
|
});
|
|
@@ -10774,7 +10896,7 @@ app3.get("/conversation-graph-state", async (c) => {
|
|
|
10774
10896
|
}
|
|
10775
10897
|
} catch (err) {
|
|
10776
10898
|
const msg = err instanceof Error ? err.message : String(err);
|
|
10777
|
-
console.error(`${
|
|
10899
|
+
console.error(`${TAG19} conversation-graph-state ERR cacheKey=${cacheKey} reason=${msg}`);
|
|
10778
10900
|
return c.json({ error: `Graph query failed: ${msg}`, cacheKey, cypher: cypher.trim() }, 500);
|
|
10779
10901
|
}
|
|
10780
10902
|
const ms = Date.now() - t0;
|
|
@@ -10787,7 +10909,7 @@ app3.get("/conversation-graph-state", async (c) => {
|
|
|
10787
10909
|
ms
|
|
10788
10910
|
});
|
|
10789
10911
|
} catch (err) {
|
|
10790
|
-
console.error(`${
|
|
10912
|
+
console.error(`${TAG19} conversation-graph-state error: ${String(err)}`);
|
|
10791
10913
|
return c.json({ error: String(err) }, 500);
|
|
10792
10914
|
}
|
|
10793
10915
|
});
|
|
@@ -10799,12 +10921,12 @@ app3.get("/group-info", async (c) => {
|
|
|
10799
10921
|
return c.json({ error: "Missing required parameter: jid" }, 400);
|
|
10800
10922
|
}
|
|
10801
10923
|
if (!isGroupJid(jid)) {
|
|
10802
|
-
console.error(`${
|
|
10924
|
+
console.error(`${TAG19} group-info error="not a group JID" jid=${jid} account=${accountId}`);
|
|
10803
10925
|
return c.json({ error: `"${jid}" is not a group JID. Group JIDs end with @g.us.` }, 400);
|
|
10804
10926
|
}
|
|
10805
10927
|
const sock = getSocket(accountId);
|
|
10806
10928
|
if (!sock) {
|
|
10807
|
-
console.error(`${
|
|
10929
|
+
console.error(`${TAG19} group-info error="not connected" account=${accountId}`);
|
|
10808
10930
|
return c.json({ error: `WhatsApp account "${accountId}" is not connected. Connect first, then retry.` }, 400);
|
|
10809
10931
|
}
|
|
10810
10932
|
const meta = await sock.groupMetadata(jid);
|
|
@@ -10817,10 +10939,10 @@ app3.get("/group-info", async (c) => {
|
|
|
10817
10939
|
participantCount: meta.participants.length,
|
|
10818
10940
|
participants: meta.participants.map((p) => ({ jid: p.id, admin: p.admin ?? null }))
|
|
10819
10941
|
};
|
|
10820
|
-
console.error(`${
|
|
10942
|
+
console.error(`${TAG19} group-info jid=${jid} subject="${meta.subject}" participants=${meta.participants.length} account=${accountId}`);
|
|
10821
10943
|
return c.json(result);
|
|
10822
10944
|
} catch (err) {
|
|
10823
|
-
console.error(`${
|
|
10945
|
+
console.error(`${TAG19} group-info error="${String(err)}" jid=${jid} account=${accountId}`);
|
|
10824
10946
|
return c.json({ error: `Failed to fetch group info: ${String(err)}` }, 500);
|
|
10825
10947
|
}
|
|
10826
10948
|
});
|
|
@@ -11605,7 +11727,7 @@ var session_default = app6;
|
|
|
11605
11727
|
|
|
11606
11728
|
// server/routes/admin/logs.ts
|
|
11607
11729
|
import { existsSync as existsSync10, readdirSync as readdirSync5, readFileSync as readFileSync10, statSync as statSync5 } from "fs";
|
|
11608
|
-
import { resolve as
|
|
11730
|
+
import { resolve as resolve8, basename as basename2 } from "path";
|
|
11609
11731
|
|
|
11610
11732
|
// app/lib/logs-read-resolve.ts
|
|
11611
11733
|
import { existsSync as existsSync9 } from "fs";
|
|
@@ -11632,7 +11754,7 @@ app7.get("/", async (c) => {
|
|
|
11632
11754
|
const cacheKeyParam = c.req.query("cacheKey");
|
|
11633
11755
|
const download = c.req.query("download") === "1";
|
|
11634
11756
|
const account = resolveAccount();
|
|
11635
|
-
const accountLogDir = account ?
|
|
11757
|
+
const accountLogDir = account ? resolve8(account.accountDir, "logs") : null;
|
|
11636
11758
|
const logDirs = [];
|
|
11637
11759
|
if (accountLogDir) logDirs.push(accountLogDir);
|
|
11638
11760
|
logDirs.push(LOG_DIR);
|
|
@@ -11640,7 +11762,7 @@ app7.get("/", async (c) => {
|
|
|
11640
11762
|
const safe = basename2(fileParam);
|
|
11641
11763
|
const searched = [];
|
|
11642
11764
|
for (const dir of logDirs) {
|
|
11643
|
-
const filePath =
|
|
11765
|
+
const filePath = resolve8(dir, safe);
|
|
11644
11766
|
searched.push(filePath);
|
|
11645
11767
|
try {
|
|
11646
11768
|
const buffer = readFileSync10(filePath);
|
|
@@ -11752,10 +11874,10 @@ app7.get("/", async (c) => {
|
|
|
11752
11874
|
console.warn(`[admin/logs] readdir-fail dir=${dir} reason=${reason}`);
|
|
11753
11875
|
continue;
|
|
11754
11876
|
}
|
|
11755
|
-
files.filter((f) => !seen.has(f)).map((f) => ({ name: f, mtime: statSync5(
|
|
11877
|
+
files.filter((f) => !seen.has(f)).map((f) => ({ name: f, mtime: statSync5(resolve8(dir, f)).mtimeMs })).sort((a, b) => b.mtime - a.mtime).forEach(({ name }) => {
|
|
11756
11878
|
seen.add(name);
|
|
11757
11879
|
try {
|
|
11758
|
-
const content = readFileSync10(
|
|
11880
|
+
const content = readFileSync10(resolve8(dir, name));
|
|
11759
11881
|
const tail = content.length > TAIL_BYTES ? content.subarray(content.length - TAIL_BYTES).toString("utf-8") : content.toString("utf-8");
|
|
11760
11882
|
logs[name] = tail.trim() || "(empty)";
|
|
11761
11883
|
} catch (err) {
|
|
@@ -11795,7 +11917,7 @@ var claude_info_default = app8;
|
|
|
11795
11917
|
// server/routes/admin/attachment.ts
|
|
11796
11918
|
import { readFile as readFile2, readdir } from "fs/promises";
|
|
11797
11919
|
import { existsSync as existsSync11 } from "fs";
|
|
11798
|
-
import { resolve as
|
|
11920
|
+
import { resolve as resolve9 } from "path";
|
|
11799
11921
|
var app9 = new Hono();
|
|
11800
11922
|
app9.get("/:attachmentId", requireAdminSession, async (c) => {
|
|
11801
11923
|
const attachmentId = c.req.param("attachmentId");
|
|
@@ -11807,11 +11929,11 @@ app9.get("/:attachmentId", requireAdminSession, async (c) => {
|
|
|
11807
11929
|
if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/.test(attachmentId)) {
|
|
11808
11930
|
return new Response("Not found", { status: 404 });
|
|
11809
11931
|
}
|
|
11810
|
-
const dir =
|
|
11932
|
+
const dir = resolve9(ATTACHMENTS_ROOT, accountId, attachmentId);
|
|
11811
11933
|
if (!existsSync11(dir)) {
|
|
11812
11934
|
return new Response("Not found", { status: 404 });
|
|
11813
11935
|
}
|
|
11814
|
-
const metaPath =
|
|
11936
|
+
const metaPath = resolve9(dir, `${attachmentId}.meta.json`);
|
|
11815
11937
|
if (!existsSync11(metaPath)) {
|
|
11816
11938
|
return new Response("Not found", { status: 404 });
|
|
11817
11939
|
}
|
|
@@ -11826,7 +11948,7 @@ app9.get("/:attachmentId", requireAdminSession, async (c) => {
|
|
|
11826
11948
|
if (!dataFile) {
|
|
11827
11949
|
return new Response("Not found", { status: 404 });
|
|
11828
11950
|
}
|
|
11829
|
-
const filePath =
|
|
11951
|
+
const filePath = resolve9(dir, dataFile);
|
|
11830
11952
|
const buffer = await readFile2(filePath);
|
|
11831
11953
|
return new Response(new Uint8Array(buffer), {
|
|
11832
11954
|
headers: {
|
|
@@ -11839,13 +11961,13 @@ app9.get("/:attachmentId", requireAdminSession, async (c) => {
|
|
|
11839
11961
|
var attachment_default = app9;
|
|
11840
11962
|
|
|
11841
11963
|
// server/routes/admin/agents.ts
|
|
11842
|
-
import { resolve as
|
|
11964
|
+
import { resolve as resolve10 } from "path";
|
|
11843
11965
|
import { readdirSync as readdirSync6, readFileSync as readFileSync11, existsSync as existsSync12, rmSync } from "fs";
|
|
11844
11966
|
var app10 = new Hono();
|
|
11845
11967
|
app10.get("/", (c) => {
|
|
11846
11968
|
const account = resolveAccount();
|
|
11847
11969
|
if (!account) return c.json({ agents: [] });
|
|
11848
|
-
const agentsDir =
|
|
11970
|
+
const agentsDir = resolve10(account.accountDir, "agents");
|
|
11849
11971
|
if (!existsSync12(agentsDir)) return c.json({ agents: [] });
|
|
11850
11972
|
const agents = [];
|
|
11851
11973
|
try {
|
|
@@ -11853,7 +11975,7 @@ app10.get("/", (c) => {
|
|
|
11853
11975
|
for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
11854
11976
|
if (!entry.isDirectory()) continue;
|
|
11855
11977
|
if (entry.name === "admin") continue;
|
|
11856
|
-
const configPath2 =
|
|
11978
|
+
const configPath2 = resolve10(agentsDir, entry.name, "config.json");
|
|
11857
11979
|
if (!existsSync12(configPath2)) continue;
|
|
11858
11980
|
try {
|
|
11859
11981
|
const config = JSON.parse(readFileSync11(configPath2, "utf-8"));
|
|
@@ -11882,7 +12004,7 @@ app10.delete("/:slug", async (c) => {
|
|
|
11882
12004
|
if (slug.includes("/") || slug.includes("..") || slug.includes("\\")) {
|
|
11883
12005
|
return c.json({ error: "Invalid agent slug" }, 400);
|
|
11884
12006
|
}
|
|
11885
|
-
const agentDir =
|
|
12007
|
+
const agentDir = resolve10(account.accountDir, "agents", slug);
|
|
11886
12008
|
if (!existsSync12(agentDir)) {
|
|
11887
12009
|
return c.json({ error: "Agent not found" }, 404);
|
|
11888
12010
|
}
|
|
@@ -11912,7 +12034,7 @@ app10.post("/:slug/project", async (c) => {
|
|
|
11912
12034
|
if (slug.includes("/") || slug.includes("..") || slug.includes("\\")) {
|
|
11913
12035
|
return c.json({ error: "Invalid agent slug" }, 400);
|
|
11914
12036
|
}
|
|
11915
|
-
const agentDir =
|
|
12037
|
+
const agentDir = resolve10(account.accountDir, "agents", slug);
|
|
11916
12038
|
if (!existsSync12(agentDir)) {
|
|
11917
12039
|
return c.json({ error: "Agent not found on disk" }, 404);
|
|
11918
12040
|
}
|
|
@@ -12617,7 +12739,7 @@ var sessions_default = app11;
|
|
|
12617
12739
|
|
|
12618
12740
|
// app/lib/claude-agent/spawn-context.ts
|
|
12619
12741
|
import { existsSync as existsSync14, readFileSync as readFileSync12, readdirSync as readdirSync7, statSync as statSync6 } from "fs";
|
|
12620
|
-
import { dirname as dirname2, resolve as
|
|
12742
|
+
import { dirname as dirname2, resolve as resolve11, join as join10 } from "path";
|
|
12621
12743
|
async function resolveOwnerProfileBlock(accountId, userId) {
|
|
12622
12744
|
if (!userId) return { ok: false, reason: "missing-user-id" };
|
|
12623
12745
|
try {
|
|
@@ -12702,7 +12824,7 @@ function parseSkillPathsFromFrontmatter(fm) {
|
|
|
12702
12824
|
}
|
|
12703
12825
|
function listPluginDirs() {
|
|
12704
12826
|
const out = /* @__PURE__ */ new Map();
|
|
12705
|
-
const platformPlugins =
|
|
12827
|
+
const platformPlugins = resolve11(PLATFORM_ROOT, "plugins");
|
|
12706
12828
|
if (existsSync14(platformPlugins)) {
|
|
12707
12829
|
for (const name of readdirSync7(platformPlugins)) {
|
|
12708
12830
|
const dir = join10(platformPlugins, name);
|
|
@@ -12712,7 +12834,7 @@ function listPluginDirs() {
|
|
|
12712
12834
|
}
|
|
12713
12835
|
}
|
|
12714
12836
|
}
|
|
12715
|
-
const premiumRoot =
|
|
12837
|
+
const premiumRoot = resolve11(dirname2(PLATFORM_ROOT), "premium-plugins");
|
|
12716
12838
|
if (existsSync14(premiumRoot)) {
|
|
12717
12839
|
for (const bundle of readdirSync7(premiumRoot)) {
|
|
12718
12840
|
const bundlePlugins = join10(premiumRoot, bundle, "plugins");
|
|
@@ -12732,7 +12854,7 @@ function listPluginDirs() {
|
|
|
12732
12854
|
return out;
|
|
12733
12855
|
}
|
|
12734
12856
|
function readEnabledPlugins(accountId) {
|
|
12735
|
-
const accountFile =
|
|
12857
|
+
const accountFile = resolve11(PLATFORM_ROOT, "..", "data/accounts", accountId, "account.json");
|
|
12736
12858
|
if (!existsSync14(accountFile)) return /* @__PURE__ */ new Set();
|
|
12737
12859
|
try {
|
|
12738
12860
|
const parsed = JSON.parse(readFileSync12(accountFile, "utf-8"));
|
|
@@ -12805,7 +12927,7 @@ ${skillFm}
|
|
|
12805
12927
|
return out;
|
|
12806
12928
|
}
|
|
12807
12929
|
function computeSpecialistDomains(accountId) {
|
|
12808
|
-
const specialistsDir =
|
|
12930
|
+
const specialistsDir = resolve11(PLATFORM_ROOT, "..", "data/accounts", accountId, "specialists", "agents");
|
|
12809
12931
|
if (!existsSync14(specialistsDir)) return [];
|
|
12810
12932
|
let entries;
|
|
12811
12933
|
try {
|
|
@@ -12851,7 +12973,7 @@ ${fm}
|
|
|
12851
12973
|
return out;
|
|
12852
12974
|
}
|
|
12853
12975
|
function computeDormantPlugins(accountId) {
|
|
12854
|
-
const accountFile =
|
|
12976
|
+
const accountFile = resolve11(PLATFORM_ROOT, "..", "data/accounts", accountId, "account.json");
|
|
12855
12977
|
if (!existsSync14(accountFile)) return [];
|
|
12856
12978
|
let enabled;
|
|
12857
12979
|
try {
|
|
@@ -12866,7 +12988,7 @@ function computeDormantPlugins(accountId) {
|
|
|
12866
12988
|
);
|
|
12867
12989
|
return [];
|
|
12868
12990
|
}
|
|
12869
|
-
const pluginsDir =
|
|
12991
|
+
const pluginsDir = resolve11(PLATFORM_ROOT, "plugins");
|
|
12870
12992
|
if (!existsSync14(pluginsDir)) return [];
|
|
12871
12993
|
let entries;
|
|
12872
12994
|
try {
|
|
@@ -12877,7 +12999,7 @@ function computeDormantPlugins(accountId) {
|
|
|
12877
12999
|
const out = [];
|
|
12878
13000
|
for (const name of entries) {
|
|
12879
13001
|
if (enabled.has(name)) continue;
|
|
12880
|
-
const manifestPath =
|
|
13002
|
+
const manifestPath = resolve11(pluginsDir, name, "PLUGIN.md");
|
|
12881
13003
|
if (!existsSync14(manifestPath)) continue;
|
|
12882
13004
|
let manifest;
|
|
12883
13005
|
try {
|
|
@@ -12896,7 +13018,7 @@ function computeDormantPlugins(accountId) {
|
|
|
12896
13018
|
|
|
12897
13019
|
// server/routes/admin/claude-sessions.ts
|
|
12898
13020
|
import { existsSync as existsSync15, readFileSync as readFileSync13 } from "fs";
|
|
12899
|
-
import { resolve as
|
|
13021
|
+
import { resolve as resolve12 } from "path";
|
|
12900
13022
|
function readTunnelState(brandConfigDir) {
|
|
12901
13023
|
const statePath = `${process.env.HOME ?? ""}/${brandConfigDir}/cloudflared/tunnel.state`;
|
|
12902
13024
|
if (!existsSync15(statePath)) return null;
|
|
@@ -12912,10 +13034,10 @@ function readTunnelState(brandConfigDir) {
|
|
|
12912
13034
|
}
|
|
12913
13035
|
}
|
|
12914
13036
|
function resolveTunnelUrl() {
|
|
12915
|
-
const
|
|
13037
|
+
const platformRoot2 = process.env.MAXY_PLATFORM_ROOT ?? process.env.PLATFORM_ROOT ?? resolve12(process.cwd(), "..");
|
|
12916
13038
|
let brand;
|
|
12917
13039
|
try {
|
|
12918
|
-
brand = JSON.parse(readFileSync13(
|
|
13040
|
+
brand = JSON.parse(readFileSync13(resolve12(platformRoot2, "config", "brand.json"), "utf-8"));
|
|
12919
13041
|
} catch {
|
|
12920
13042
|
return null;
|
|
12921
13043
|
}
|
|
@@ -12926,7 +13048,7 @@ function resolveTunnelUrl() {
|
|
|
12926
13048
|
if (!state) return null;
|
|
12927
13049
|
return `https://${hostname2}.${state.domain}`;
|
|
12928
13050
|
}
|
|
12929
|
-
var
|
|
13051
|
+
var TAG20 = "[claude-session-manager:wrapper]";
|
|
12930
13052
|
async function refuseIfClaudeAuthDead(c, route, sessionId) {
|
|
12931
13053
|
const auth = await ensureAuth();
|
|
12932
13054
|
if (auth.status !== "dead" && auth.status !== "missing") return null;
|
|
@@ -12948,12 +13070,12 @@ async function performSpawnWithInitialMessage(args) {
|
|
|
12948
13070
|
const aboutOwner = await resolveOwnerProfileBlock(args.senderId, args.userId);
|
|
12949
13071
|
const ownerMs = Date.now() - ownerStart;
|
|
12950
13072
|
const aboutOwnerStatus = aboutOwner == null ? "absent" : "ok" in aboutOwner && aboutOwner.ok === false ? `unresolved:${aboutOwner.reason}` : "ok";
|
|
12951
|
-
console.log(`${
|
|
13073
|
+
console.log(`${TAG20} about-owner-resolved status=${aboutOwnerStatus} ms=${ownerMs}`);
|
|
12952
13074
|
const dormantPlugins = computeDormantPlugins(args.senderId);
|
|
12953
13075
|
const activePlugins = computeActivePlugins(args.senderId);
|
|
12954
13076
|
const specialistDomains = computeSpecialistDomains(args.senderId);
|
|
12955
13077
|
const tunnelUrl = resolveTunnelUrl();
|
|
12956
|
-
console.log(`${
|
|
13078
|
+
console.log(`${TAG20} tunnel-url-resolved value=${tunnelUrl ?? "null"}`);
|
|
12957
13079
|
const upstreamPayload = JSON.stringify({
|
|
12958
13080
|
senderId: args.senderId,
|
|
12959
13081
|
// Task 205 — pass userId through to the manager so it lands as
|
|
@@ -12980,24 +13102,24 @@ async function performSpawnWithInitialMessage(args) {
|
|
|
12980
13102
|
// unshapely values.
|
|
12981
13103
|
conversationNodeId: args.conversationNodeId
|
|
12982
13104
|
});
|
|
12983
|
-
console.log(`${
|
|
13105
|
+
console.log(`${TAG20} forward-spawn-start managerBase=${managerBase3()} bytes=${upstreamPayload.length} hidden=${args.hidden} specialist=${args.specialist ?? "none"}`);
|
|
12984
13106
|
const forwardStart = Date.now();
|
|
12985
13107
|
const upstream = await fetch(`${managerBase3()}/public-spawn`, {
|
|
12986
13108
|
method: "POST",
|
|
12987
13109
|
headers: { "content-type": "application/json" },
|
|
12988
13110
|
body: upstreamPayload
|
|
12989
13111
|
}).catch((err) => {
|
|
12990
|
-
console.error(`${
|
|
13112
|
+
console.error(`${TAG20} fetch-failed op=spawn message=${err instanceof Error ? err.message : String(err)} ms=${Date.now() - forwardStart}`);
|
|
12991
13113
|
return null;
|
|
12992
13114
|
});
|
|
12993
13115
|
if (!upstream) return {
|
|
12994
13116
|
response: new Response(JSON.stringify({ error: "manager-unreachable" }), { status: 503, headers: { "content-type": "application/json" } }),
|
|
12995
13117
|
claudeSessionId: null
|
|
12996
13118
|
};
|
|
12997
|
-
console.log(`${
|
|
13119
|
+
console.log(`${TAG20} forward-spawn-done status=${upstream.status} ms=${Date.now() - forwardStart}`);
|
|
12998
13120
|
if (args.initialMessage) {
|
|
12999
13121
|
const inputBytes = Buffer.byteLength(args.initialMessage, "utf8");
|
|
13000
|
-
console.log(`${
|
|
13122
|
+
console.log(`${TAG20} initial-message-inlined bytes=${inputBytes}`);
|
|
13001
13123
|
}
|
|
13002
13124
|
const bodyText = await upstream.text().catch(() => "");
|
|
13003
13125
|
let claudeSessionId = null;
|
|
@@ -13032,7 +13154,7 @@ app12.post("/", async (c) => {
|
|
|
13032
13154
|
if (refusal) return refusal;
|
|
13033
13155
|
const senderId = getAccountIdForSession(cacheKey) ?? "";
|
|
13034
13156
|
if (!senderId) {
|
|
13035
|
-
console.error(`${
|
|
13157
|
+
console.error(`${TAG20} reject reason=no-account-id cacheKey-prefix=${cacheKey.slice(0, 8)}`);
|
|
13036
13158
|
return c.json({ error: "admin-account-not-resolved" }, 500);
|
|
13037
13159
|
}
|
|
13038
13160
|
const userId = getUserIdForSession(cacheKey) ?? void 0;
|
|
@@ -13041,7 +13163,7 @@ app12.post("/", async (c) => {
|
|
|
13041
13163
|
const permissionMode = typeof body.permissionMode === "string" ? body.permissionMode : void 0;
|
|
13042
13164
|
const specialist = typeof body.specialist === "string" && /^[A-Za-z0-9_-]{1,64}$/.test(body.specialist) ? body.specialist : void 0;
|
|
13043
13165
|
const model = typeof body.model === "string" && /^[A-Za-z0-9._-]{1,64}$/.test(body.model) ? body.model : void 0;
|
|
13044
|
-
console.log(`${
|
|
13166
|
+
console.log(`${TAG20} 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"}`);
|
|
13045
13167
|
const conversationNodeId = cacheKey ? getSessionIdForSession(cacheKey) : void 0;
|
|
13046
13168
|
const { response, claudeSessionId } = await performSpawnWithInitialMessage({
|
|
13047
13169
|
senderId,
|
|
@@ -13060,13 +13182,13 @@ app12.post("/", async (c) => {
|
|
|
13060
13182
|
claudeSessionId,
|
|
13061
13183
|
senderId
|
|
13062
13184
|
);
|
|
13063
|
-
console.log(`${
|
|
13185
|
+
console.log(`${TAG20} route-done surface=cookie status=${response.status} route-ms=${Date.now() - routeStart}`);
|
|
13064
13186
|
return response;
|
|
13065
13187
|
});
|
|
13066
13188
|
var claude_sessions_default = app12;
|
|
13067
13189
|
|
|
13068
13190
|
// server/routes/admin/log-ingest.ts
|
|
13069
|
-
var
|
|
13191
|
+
var TAG21 = "[log-ingest]";
|
|
13070
13192
|
var TAG_PATTERN = /^[A-Za-z0-9_:-]{1,32}$/;
|
|
13071
13193
|
var LEVELS = /* @__PURE__ */ new Set(["debug", "info", "warn", "error"]);
|
|
13072
13194
|
var MAX_LINE_BYTES = 4096;
|
|
@@ -13077,7 +13199,7 @@ function isLoopbackAddr(addr) {
|
|
|
13077
13199
|
app13.post("/", async (c) => {
|
|
13078
13200
|
const remoteAddr = c.env?.incoming?.socket?.remoteAddress ?? "";
|
|
13079
13201
|
if (!isLoopbackAddr(remoteAddr)) {
|
|
13080
|
-
console.error(`${
|
|
13202
|
+
console.error(`${TAG21} reject reason=non-loopback remoteAddr=${remoteAddr}`);
|
|
13081
13203
|
return c.json({ error: "log-ingest-loopback-only" }, 403);
|
|
13082
13204
|
}
|
|
13083
13205
|
const xffHeader = c.env?.incoming?.headers?.["x-forwarded-for"];
|
|
@@ -13086,7 +13208,7 @@ app13.post("/", async (c) => {
|
|
|
13086
13208
|
const tokens = xffRaw.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
|
|
13087
13209
|
const offender = tokens.find((t) => !isLoopbackAddr(t));
|
|
13088
13210
|
if (offender !== void 0) {
|
|
13089
|
-
console.error(`${
|
|
13211
|
+
console.error(`${TAG21} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
|
|
13090
13212
|
return c.json({ error: "log-ingest-loopback-only" }, 403);
|
|
13091
13213
|
}
|
|
13092
13214
|
}
|
|
@@ -13128,18 +13250,18 @@ var ALLOWED_EVENTS = /* @__PURE__ */ new Set([
|
|
|
13128
13250
|
]);
|
|
13129
13251
|
var app14 = new Hono();
|
|
13130
13252
|
app14.post("/", async (c) => {
|
|
13131
|
-
const
|
|
13253
|
+
const TAG31 = "[admin:events]";
|
|
13132
13254
|
let body;
|
|
13133
13255
|
try {
|
|
13134
13256
|
body = await c.req.json();
|
|
13135
13257
|
} catch (err) {
|
|
13136
13258
|
const detail = err instanceof Error ? err.message : String(err);
|
|
13137
|
-
console.error(`${
|
|
13259
|
+
console.error(`${TAG31} reject reason=body-not-json detail=${detail}`);
|
|
13138
13260
|
return c.json({ ok: false, detail: "Request body was not valid JSON" }, 400);
|
|
13139
13261
|
}
|
|
13140
13262
|
const event = typeof body.event === "string" ? body.event : "";
|
|
13141
13263
|
if (!ALLOWED_EVENTS.has(event)) {
|
|
13142
|
-
console.error(`${
|
|
13264
|
+
console.error(`${TAG31} reject reason=event-not-allowed event=${JSON.stringify(event)}`);
|
|
13143
13265
|
return c.json({ ok: false, detail: `Event "${event}" is not allowed` }, 400);
|
|
13144
13266
|
}
|
|
13145
13267
|
const rawFields = body.fields && typeof body.fields === "object" ? body.fields : {};
|
|
@@ -13164,15 +13286,15 @@ var events_default = app14;
|
|
|
13164
13286
|
import { createReadStream as createReadStream2 } from "fs";
|
|
13165
13287
|
import { readdir as readdir3, readFile as readFile4, stat as stat4, mkdir as mkdir3, writeFile as writeFile4, unlink as unlink2 } from "fs/promises";
|
|
13166
13288
|
import { realpathSync as realpathSync4 } from "fs";
|
|
13167
|
-
import { basename as basename4, dirname as dirname3, join as join12, resolve as
|
|
13289
|
+
import { basename as basename4, dirname as dirname3, join as join12, resolve as resolve15, sep as sep4 } from "path";
|
|
13168
13290
|
import { Readable as Readable2 } from "stream";
|
|
13169
13291
|
|
|
13170
13292
|
// app/lib/data-path.ts
|
|
13171
13293
|
import { realpathSync as realpathSync3 } from "fs";
|
|
13172
|
-
import { resolve as
|
|
13173
|
-
var PLATFORM_ROOT5 = process.env.MAXY_PLATFORM_ROOT ??
|
|
13174
|
-
var DATA_ROOT =
|
|
13175
|
-
var CLAUDE_UPLOADS_ROOT = process.env.CLAUDE_CONFIG_DIR ?
|
|
13294
|
+
import { resolve as resolve13, normalize, sep as sep2, relative } from "path";
|
|
13295
|
+
var PLATFORM_ROOT5 = process.env.MAXY_PLATFORM_ROOT ?? resolve13(process.cwd(), "../platform");
|
|
13296
|
+
var DATA_ROOT = resolve13(PLATFORM_ROOT5, "..", "data");
|
|
13297
|
+
var CLAUDE_UPLOADS_ROOT = process.env.CLAUDE_CONFIG_DIR ? resolve13(process.env.CLAUDE_CONFIG_DIR, "uploads") : null;
|
|
13176
13298
|
var ACCOUNT_PARTITION_DIRS = /* @__PURE__ */ new Set(["uploads", "accounts"]);
|
|
13177
13299
|
var ACCOUNT_UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
13178
13300
|
function crossesForeignAccountPartition(relPath, accountId) {
|
|
@@ -13185,7 +13307,7 @@ function crossesForeignAccountPartition(relPath, accountId) {
|
|
|
13185
13307
|
}
|
|
13186
13308
|
function resolveUnderRoot(raw, rootAbs, rootLabel) {
|
|
13187
13309
|
const cleaned = normalize("/" + (raw ?? "").replace(/\\/g, "/")).replace(/^\/+/, "");
|
|
13188
|
-
const absolute =
|
|
13310
|
+
const absolute = resolve13(rootAbs, cleaned);
|
|
13189
13311
|
let rootReal;
|
|
13190
13312
|
try {
|
|
13191
13313
|
rootReal = realpathSync3(rootAbs);
|
|
@@ -13544,7 +13666,7 @@ async function cascadeDeleteDocument(params) {
|
|
|
13544
13666
|
|
|
13545
13667
|
// app/lib/file-index.ts
|
|
13546
13668
|
import * as fsp from "fs/promises";
|
|
13547
|
-
import { resolve as
|
|
13669
|
+
import { resolve as resolve14, relative as relative2, join as join11, basename as basename3, extname as extname2, sep as sep3 } from "path";
|
|
13548
13670
|
import { tmpdir as tmpdir2 } from "os";
|
|
13549
13671
|
import { execFile as execFile2 } from "child_process";
|
|
13550
13672
|
import { promisify as promisify2 } from "util";
|
|
@@ -13852,8 +13974,8 @@ async function reconcileFileIndex(accountId, opts) {
|
|
|
13852
13974
|
return withSession(opts, async (session) => {
|
|
13853
13975
|
const walked = [];
|
|
13854
13976
|
const subtreeRoots = [
|
|
13855
|
-
|
|
13856
|
-
|
|
13977
|
+
resolve14(dataRoot, "uploads", accountId),
|
|
13978
|
+
resolve14(dataRoot, "accounts", accountId)
|
|
13857
13979
|
];
|
|
13858
13980
|
let clean = true;
|
|
13859
13981
|
for (const root of subtreeRoots) {
|
|
@@ -13934,7 +14056,7 @@ async function reconcileFileIndexDebounced(accountId, opts) {
|
|
|
13934
14056
|
async function indexFile(accountId, relativePath, opts) {
|
|
13935
14057
|
const dataRoot = opts?.dataRoot ?? DATA_ROOT;
|
|
13936
14058
|
const embed2 = opts?.embed ?? embed;
|
|
13937
|
-
const absolute =
|
|
14059
|
+
const absolute = resolve14(dataRoot, relativePath);
|
|
13938
14060
|
await withSession(opts, async (session) => {
|
|
13939
14061
|
const st = await fsp.stat(absolute);
|
|
13940
14062
|
const wf = {
|
|
@@ -13979,7 +14101,7 @@ async function readMeta(absDir, baseName) {
|
|
|
13979
14101
|
}
|
|
13980
14102
|
async function readAccountNames() {
|
|
13981
14103
|
const map = /* @__PURE__ */ new Map();
|
|
13982
|
-
const accountsDir =
|
|
14104
|
+
const accountsDir = resolve15(DATA_ROOT, "accounts");
|
|
13983
14105
|
let names;
|
|
13984
14106
|
try {
|
|
13985
14107
|
names = await readdir3(accountsDir);
|
|
@@ -13988,7 +14110,7 @@ async function readAccountNames() {
|
|
|
13988
14110
|
}
|
|
13989
14111
|
for (const name of names) {
|
|
13990
14112
|
if (!UUID_RE3.test(name)) continue;
|
|
13991
|
-
const configPath2 =
|
|
14113
|
+
const configPath2 = resolve15(accountsDir, name, "account.json");
|
|
13992
14114
|
try {
|
|
13993
14115
|
const raw = await readFile4(configPath2, "utf8");
|
|
13994
14116
|
const parsed = JSON.parse(raw);
|
|
@@ -14229,8 +14351,8 @@ app15.post("/upload", requireAdminSession, async (c) => {
|
|
|
14229
14351
|
}
|
|
14230
14352
|
const safeName = basename4(file.name).replace(/[\0/\\]/g, "_");
|
|
14231
14353
|
const finalName = `${Date.now()}-${safeName}`;
|
|
14232
|
-
const destDir =
|
|
14233
|
-
const destPath =
|
|
14354
|
+
const destDir = resolve15(DATA_ROOT, "uploads", accountId);
|
|
14355
|
+
const destPath = resolve15(destDir, finalName);
|
|
14234
14356
|
try {
|
|
14235
14357
|
await mkdir3(destDir, { recursive: true });
|
|
14236
14358
|
const dataRootReal = realpathSync4(DATA_ROOT);
|
|
@@ -16337,7 +16459,7 @@ var graph_default_view_default = app21;
|
|
|
16337
16459
|
|
|
16338
16460
|
// server/routes/admin/sidebar-artefacts.ts
|
|
16339
16461
|
import { readdir as readdir4, stat as stat5 } from "fs/promises";
|
|
16340
|
-
import { resolve as
|
|
16462
|
+
import { resolve as resolve16, relative as relative3, isAbsolute, sep as sep5, basename as basename5 } from "path";
|
|
16341
16463
|
import { existsSync as existsSync16 } from "fs";
|
|
16342
16464
|
var LIMIT = 50;
|
|
16343
16465
|
var ADMIN_AGENT_FILES = ["IDENTITY.md", "SOUL.md", "KNOWLEDGE.md"];
|
|
@@ -16357,7 +16479,7 @@ app22.get("/", requireAdminSession, async (c) => {
|
|
|
16357
16479
|
if (outputs === null) {
|
|
16358
16480
|
return c.json({ error: "Failed to load artefacts" }, 500);
|
|
16359
16481
|
}
|
|
16360
|
-
const accountDir =
|
|
16482
|
+
const accountDir = resolve16(ACCOUNTS_DIR, accountId);
|
|
16361
16483
|
const agents = await fetchAgentTemplateRows(accountDir);
|
|
16362
16484
|
const artefacts = [...outputs, ...agents].sort(
|
|
16363
16485
|
(a, b) => (b.updatedAt ?? "").localeCompare(a.updatedAt ?? "")
|
|
@@ -16408,8 +16530,8 @@ async function fetchOutputArtefacts(accountId) {
|
|
|
16408
16530
|
async function fetchAgentTemplateRows(accountDir) {
|
|
16409
16531
|
const rows = [];
|
|
16410
16532
|
for (const filename of ADMIN_AGENT_FILES) {
|
|
16411
|
-
const overridePath =
|
|
16412
|
-
const bundledPath =
|
|
16533
|
+
const overridePath = resolve16(accountDir, "agents", "admin", filename);
|
|
16534
|
+
const bundledPath = resolve16(PLATFORM_ROOT, "templates", "agents", "admin", filename);
|
|
16413
16535
|
const labelStem = filename.replace(/\.md$/, "");
|
|
16414
16536
|
const row = await readAgentTemplateRow({
|
|
16415
16537
|
id: `agent-template:admin:${filename}`,
|
|
@@ -16422,12 +16544,12 @@ async function fetchAgentTemplateRows(accountDir) {
|
|
|
16422
16544
|
});
|
|
16423
16545
|
if (row) rows.push(row);
|
|
16424
16546
|
}
|
|
16425
|
-
const overrideDir =
|
|
16426
|
-
const bundledDir =
|
|
16547
|
+
const overrideDir = resolve16(accountDir, "specialists", "agents");
|
|
16548
|
+
const bundledDir = resolve16(PLATFORM_ROOT, "templates", "specialists", "agents");
|
|
16427
16549
|
const specialistNames = await unionSpecialistFilenames(overrideDir, bundledDir);
|
|
16428
16550
|
for (const filename of specialistNames) {
|
|
16429
|
-
const overridePath =
|
|
16430
|
-
const bundledPath =
|
|
16551
|
+
const overridePath = resolve16(overrideDir, filename);
|
|
16552
|
+
const bundledPath = resolve16(bundledDir, filename);
|
|
16431
16553
|
const row = await readAgentTemplateRow({
|
|
16432
16554
|
id: `agent-template:specialist:${filename}`,
|
|
16433
16555
|
displayName: filename.replace(/\.md$/, ""),
|
|
@@ -16513,7 +16635,7 @@ var sidebar_artefacts_default = app22;
|
|
|
16513
16635
|
|
|
16514
16636
|
// server/routes/admin/sidebar-sessions.ts
|
|
16515
16637
|
import { readdirSync as readdirSync8, readFileSync as readFileSync14, statSync as statSync7 } from "fs";
|
|
16516
|
-
import { join as join13, resolve as
|
|
16638
|
+
import { join as join13, resolve as resolve17 } from "path";
|
|
16517
16639
|
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;
|
|
16518
16640
|
var PID_FILE_RE = /^([0-9]+)\.json$/;
|
|
16519
16641
|
var TITLE_MAX = 80;
|
|
@@ -16694,7 +16816,7 @@ app23.get("/", requireAdminSession, async (c) => {
|
|
|
16694
16816
|
const projectsRoot = join13(configDir2, "projects");
|
|
16695
16817
|
const jsonls = enumerateJsonls(projectsRoot);
|
|
16696
16818
|
const pidsBySession = sessionIdToPidMap(configDir2);
|
|
16697
|
-
const accountDir = accountId ?
|
|
16819
|
+
const accountDir = accountId ? resolve17(ACCOUNTS_DIR, accountId) : null;
|
|
16698
16820
|
const userTitles = loadUserTitles(accountDir);
|
|
16699
16821
|
const rows = [];
|
|
16700
16822
|
const seenIds = /* @__PURE__ */ new Set();
|
|
@@ -16748,7 +16870,7 @@ var sidebar_sessions_default = app23;
|
|
|
16748
16870
|
|
|
16749
16871
|
// server/routes/admin/session-delete.ts
|
|
16750
16872
|
import { existsSync as existsSync17, readdirSync as readdirSync9, readFileSync as readFileSync15, unlinkSync as unlinkSync2 } from "fs";
|
|
16751
|
-
import { join as join14, resolve as
|
|
16873
|
+
import { join as join14, resolve as resolve18 } from "path";
|
|
16752
16874
|
var app24 = new Hono();
|
|
16753
16875
|
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;
|
|
16754
16876
|
var PID_FILE_RE2 = /^([0-9]+)\.json$/;
|
|
@@ -16797,7 +16919,7 @@ function isPidAlive(pid) {
|
|
|
16797
16919
|
}
|
|
16798
16920
|
}
|
|
16799
16921
|
function sleep2(ms) {
|
|
16800
|
-
return new Promise((
|
|
16922
|
+
return new Promise((resolve27) => setTimeout(resolve27, ms));
|
|
16801
16923
|
}
|
|
16802
16924
|
function sendSignal(pid, signal) {
|
|
16803
16925
|
try {
|
|
@@ -16865,8 +16987,8 @@ app24.post("/", requireAdminSession, async (c) => {
|
|
|
16865
16987
|
if (!configDir2) {
|
|
16866
16988
|
return c.json({ error: "CLAUDE_CONFIG_DIR not set" }, 500);
|
|
16867
16989
|
}
|
|
16868
|
-
const projectsRoot =
|
|
16869
|
-
const resolvedProject =
|
|
16990
|
+
const projectsRoot = resolve18(join14(configDir2, "projects")) + "/";
|
|
16991
|
+
const resolvedProject = resolve18(projectDir) + "/";
|
|
16870
16992
|
if (!resolvedProject.startsWith(projectsRoot)) {
|
|
16871
16993
|
console.error(`[sessions-delete] reject reason=projectDir-outside-tree projectDir=${projectDir}`);
|
|
16872
16994
|
return c.json({ error: "projectDir is not under CLAUDE_CONFIG_DIR/projects/" }, 400);
|
|
@@ -17097,8 +17219,8 @@ var system_stats_default = app26;
|
|
|
17097
17219
|
|
|
17098
17220
|
// server/routes/admin/health.ts
|
|
17099
17221
|
import { existsSync as existsSync18, readFileSync as readFileSync16 } from "fs";
|
|
17100
|
-
import { resolve as
|
|
17101
|
-
var PLATFORM_ROOT6 = process.env.MAXY_PLATFORM_ROOT ??
|
|
17222
|
+
import { resolve as resolve19, join as join15 } from "path";
|
|
17223
|
+
var PLATFORM_ROOT6 = process.env.MAXY_PLATFORM_ROOT ?? resolve19(process.cwd(), "..");
|
|
17102
17224
|
var brandHostname = "maxy";
|
|
17103
17225
|
var brandJsonPath = join15(PLATFORM_ROOT6, "config", "brand.json");
|
|
17104
17226
|
if (existsSync18(brandJsonPath)) {
|
|
@@ -17108,7 +17230,7 @@ if (existsSync18(brandJsonPath)) {
|
|
|
17108
17230
|
} catch {
|
|
17109
17231
|
}
|
|
17110
17232
|
}
|
|
17111
|
-
var VERSION_FILE =
|
|
17233
|
+
var VERSION_FILE = resolve19(PLATFORM_ROOT6, `config/.${brandHostname}-version`);
|
|
17112
17234
|
var PROCESS_STARTED_AT = (/* @__PURE__ */ new Date()).toISOString();
|
|
17113
17235
|
var PROBE_TIMEOUT_MS = 1e3;
|
|
17114
17236
|
function readVersion() {
|
|
@@ -17165,7 +17287,7 @@ var health_default2 = app27;
|
|
|
17165
17287
|
|
|
17166
17288
|
// server/routes/admin/linkedin-ingest.ts
|
|
17167
17289
|
import { randomUUID as randomUUID9 } from "crypto";
|
|
17168
|
-
var
|
|
17290
|
+
var TAG22 = "[linkedin-ingest-route]";
|
|
17169
17291
|
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}$/;
|
|
17170
17292
|
var ISO = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,3})?(?:Z|[+-]\d{2}:\d{2})$/;
|
|
17171
17293
|
var LINKEDIN_URL = /^https:\/\/www\.linkedin\.com\//;
|
|
@@ -17269,29 +17391,29 @@ app28.post("/", requireAdminSession, async (c) => {
|
|
|
17269
17391
|
try {
|
|
17270
17392
|
body = await c.req.json();
|
|
17271
17393
|
} catch {
|
|
17272
|
-
console.error(
|
|
17394
|
+
console.error(TAG22 + " rejected status=400 reason=schema:body-not-json");
|
|
17273
17395
|
return c.json({ ok: false, error: "schema", reason: "body-not-json" }, 400);
|
|
17274
17396
|
}
|
|
17275
17397
|
const v = validate(body);
|
|
17276
17398
|
if (!v.ok) {
|
|
17277
|
-
console.error(
|
|
17399
|
+
console.error(TAG22 + " rejected status=" + v.status + " reason=" + v.reason + " missing=" + v.missing.join(","));
|
|
17278
17400
|
return c.json({ ok: false, error: v.error, reason: v.reason, missing: v.missing }, v.status);
|
|
17279
17401
|
}
|
|
17280
17402
|
const envelope = v.envelope;
|
|
17281
17403
|
const cacheKey = c.var.cacheKey ?? "";
|
|
17282
17404
|
const senderId = getAccountIdForSession(cacheKey) ?? "";
|
|
17283
17405
|
if (!senderId) {
|
|
17284
|
-
console.error(
|
|
17406
|
+
console.error(TAG22 + " rejected status=500 reason=admin-account-not-resolved");
|
|
17285
17407
|
return c.json({ ok: false, error: "admin-account-not-resolved" }, 500);
|
|
17286
17408
|
}
|
|
17287
17409
|
const payloadBytes = JSON.stringify(envelope).length;
|
|
17288
17410
|
console.log(
|
|
17289
|
-
|
|
17411
|
+
TAG22 + " received kind=" + envelope.kind + " account=" + senderId.slice(0, 8) + " pageUrl=" + envelope.pageUrl + " dispatchId=" + envelope.dispatchId + " payloadBytes=" + payloadBytes
|
|
17290
17412
|
);
|
|
17291
17413
|
const initialMessage = buildInitialMessage(envelope);
|
|
17292
17414
|
const spawnStart = Date.now();
|
|
17293
17415
|
const sessionId = randomUUID9();
|
|
17294
|
-
console.log(
|
|
17416
|
+
console.log(TAG22 + " route target=rc-spawn dispatchId=" + envelope.dispatchId + " sessionId=" + sessionId.slice(0, 8));
|
|
17295
17417
|
const spawned = await managerRcSpawn({
|
|
17296
17418
|
sessionId,
|
|
17297
17419
|
initialMessage,
|
|
@@ -17300,12 +17422,12 @@ app28.post("/", requireAdminSession, async (c) => {
|
|
|
17300
17422
|
});
|
|
17301
17423
|
if ("error" in spawned) {
|
|
17302
17424
|
console.error(
|
|
17303
|
-
|
|
17425
|
+
TAG22 + " dispatch-failed dispatchId=" + envelope.dispatchId + " status=" + spawned.status + " ms=" + (Date.now() - spawnStart) + " message=" + spawned.error
|
|
17304
17426
|
);
|
|
17305
17427
|
return c.json({ ok: false, error: "dispatch-failed", upstreamStatus: spawned.status, detail: spawned.error }, 502);
|
|
17306
17428
|
}
|
|
17307
17429
|
console.log(
|
|
17308
|
-
|
|
17430
|
+
TAG22 + " dispatched dispatchId=" + envelope.dispatchId + " taskId=" + spawned.sessionId + " ms=" + (Date.now() - spawnStart)
|
|
17309
17431
|
);
|
|
17310
17432
|
return c.json({ ok: true, dispatchId: envelope.dispatchId, taskId: spawned.sessionId }, 202);
|
|
17311
17433
|
});
|
|
@@ -17313,7 +17435,7 @@ var linkedin_ingest_default = app28;
|
|
|
17313
17435
|
|
|
17314
17436
|
// server/routes/admin/post-turn-context.ts
|
|
17315
17437
|
import neo4j2 from "neo4j-driver";
|
|
17316
|
-
var
|
|
17438
|
+
var TAG23 = "[post-turn-context]";
|
|
17317
17439
|
var STRIPPED_PROPERTIES2 = /* @__PURE__ */ new Set([
|
|
17318
17440
|
"embedding",
|
|
17319
17441
|
"passwordHash",
|
|
@@ -17355,7 +17477,7 @@ var app29 = new Hono();
|
|
|
17355
17477
|
app29.get("/", async (c) => {
|
|
17356
17478
|
const remoteAddr = c.env?.incoming?.socket?.remoteAddress ?? "";
|
|
17357
17479
|
if (!isLoopbackAddr2(remoteAddr)) {
|
|
17358
|
-
console.error(`${
|
|
17480
|
+
console.error(`${TAG23} reject reason=non-loopback remoteAddr=${remoteAddr}`);
|
|
17359
17481
|
return c.json({ error: "post-turn-context-loopback-only" }, 403);
|
|
17360
17482
|
}
|
|
17361
17483
|
const xffHeader = c.env?.incoming?.headers?.["x-forwarded-for"];
|
|
@@ -17364,7 +17486,7 @@ app29.get("/", async (c) => {
|
|
|
17364
17486
|
const tokens = xffRaw.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
|
|
17365
17487
|
const offender = tokens.find((t) => !isLoopbackAddr2(t));
|
|
17366
17488
|
if (offender !== void 0) {
|
|
17367
|
-
console.error(`${
|
|
17489
|
+
console.error(`${TAG23} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
|
|
17368
17490
|
return c.json({ error: "post-turn-context-loopback-only" }, 403);
|
|
17369
17491
|
}
|
|
17370
17492
|
}
|
|
@@ -17396,7 +17518,7 @@ app29.get("/", async (c) => {
|
|
|
17396
17518
|
writes.sort((a, b) => a.sortKey.localeCompare(b.sortKey));
|
|
17397
17519
|
const total = Date.now() - started;
|
|
17398
17520
|
console.log(
|
|
17399
|
-
`${
|
|
17521
|
+
`${TAG23} sessionId=${sessionId} accountId=${accountId} writes=${writes.length} ms=${total}`
|
|
17400
17522
|
);
|
|
17401
17523
|
return c.json({
|
|
17402
17524
|
writes: writes.map(({ elementId, labels, properties }) => ({ elementId, labels, properties }))
|
|
@@ -17405,7 +17527,7 @@ app29.get("/", async (c) => {
|
|
|
17405
17527
|
const elapsed = Date.now() - started;
|
|
17406
17528
|
const message = err instanceof Error ? err.message : String(err);
|
|
17407
17529
|
console.error(
|
|
17408
|
-
`${
|
|
17530
|
+
`${TAG23} neo4j-unreachable sessionId=${sessionId} ms=${elapsed} err="${message}"`
|
|
17409
17531
|
);
|
|
17410
17532
|
return c.json({ error: `post-turn-context unavailable: ${message}` }, 503);
|
|
17411
17533
|
} finally {
|
|
@@ -17416,7 +17538,7 @@ var post_turn_context_default = app29;
|
|
|
17416
17538
|
|
|
17417
17539
|
// server/routes/admin/public-session-context.ts
|
|
17418
17540
|
import neo4j3 from "neo4j-driver";
|
|
17419
|
-
var
|
|
17541
|
+
var TAG24 = "[public-session-context]";
|
|
17420
17542
|
var STRIPPED_PROPERTIES3 = /* @__PURE__ */ new Set([
|
|
17421
17543
|
"embedding",
|
|
17422
17544
|
"passwordHash",
|
|
@@ -17458,7 +17580,7 @@ var app30 = new Hono();
|
|
|
17458
17580
|
app30.get("/", async (c) => {
|
|
17459
17581
|
const remoteAddr = c.env?.incoming?.socket?.remoteAddress ?? "";
|
|
17460
17582
|
if (!isLoopbackAddr3(remoteAddr)) {
|
|
17461
|
-
console.error(`${
|
|
17583
|
+
console.error(`${TAG24} reject reason=non-loopback remoteAddr=${remoteAddr}`);
|
|
17462
17584
|
return c.json({ error: "public-session-context-loopback-only" }, 403);
|
|
17463
17585
|
}
|
|
17464
17586
|
const xffHeader = c.env?.incoming?.headers?.["x-forwarded-for"];
|
|
@@ -17467,7 +17589,7 @@ app30.get("/", async (c) => {
|
|
|
17467
17589
|
const tokens = xffRaw.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
|
|
17468
17590
|
const offender = tokens.find((t) => !isLoopbackAddr3(t));
|
|
17469
17591
|
if (offender !== void 0) {
|
|
17470
|
-
console.error(`${
|
|
17592
|
+
console.error(`${TAG24} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
|
|
17471
17593
|
return c.json({ error: "public-session-context-loopback-only" }, 403);
|
|
17472
17594
|
}
|
|
17473
17595
|
}
|
|
@@ -17498,7 +17620,7 @@ app30.get("/", async (c) => {
|
|
|
17498
17620
|
writes.sort((a, b) => a.sortKey.localeCompare(b.sortKey));
|
|
17499
17621
|
const total = Date.now() - started;
|
|
17500
17622
|
console.log(
|
|
17501
|
-
`${
|
|
17623
|
+
`${TAG24} sliceToken=${sliceToken.slice(0, 8)} writes=${writes.length} ms=${total}`
|
|
17502
17624
|
);
|
|
17503
17625
|
return c.json({
|
|
17504
17626
|
writes: writes.map(({ elementId, labels, properties }) => ({ elementId, labels, properties }))
|
|
@@ -17507,7 +17629,7 @@ app30.get("/", async (c) => {
|
|
|
17507
17629
|
const elapsed = Date.now() - started;
|
|
17508
17630
|
const message = err instanceof Error ? err.message : String(err);
|
|
17509
17631
|
console.error(
|
|
17510
|
-
`${
|
|
17632
|
+
`${TAG24} neo4j-unreachable sliceToken=${sliceToken.slice(0, 8)} ms=${elapsed} err="${message}"`
|
|
17511
17633
|
);
|
|
17512
17634
|
return c.json({ error: `public-session-context unavailable: ${message}` }, 503);
|
|
17513
17635
|
} finally {
|
|
@@ -17517,7 +17639,7 @@ app30.get("/", async (c) => {
|
|
|
17517
17639
|
var public_session_context_default = app30;
|
|
17518
17640
|
|
|
17519
17641
|
// server/routes/admin/public-session-exit.ts
|
|
17520
|
-
var
|
|
17642
|
+
var TAG25 = "[public-session-exit-route]";
|
|
17521
17643
|
function isLoopbackAddr4(addr) {
|
|
17522
17644
|
return addr === "127.0.0.1" || addr === "::1" || addr === "::ffff:127.0.0.1";
|
|
17523
17645
|
}
|
|
@@ -17525,7 +17647,7 @@ var app31 = new Hono();
|
|
|
17525
17647
|
app31.post("/", async (c) => {
|
|
17526
17648
|
const remoteAddr = c.env?.incoming?.socket?.remoteAddress ?? "";
|
|
17527
17649
|
if (!isLoopbackAddr4(remoteAddr)) {
|
|
17528
|
-
console.error(`${
|
|
17650
|
+
console.error(`${TAG25} reject reason=non-loopback remoteAddr=${remoteAddr}`);
|
|
17529
17651
|
return c.json({ error: "public-session-exit-loopback-only" }, 403);
|
|
17530
17652
|
}
|
|
17531
17653
|
const xffHeader = c.env?.incoming?.headers?.["x-forwarded-for"];
|
|
@@ -17534,7 +17656,7 @@ app31.post("/", async (c) => {
|
|
|
17534
17656
|
const tokens = xffRaw.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
|
|
17535
17657
|
const offender = tokens.find((t) => !isLoopbackAddr4(t));
|
|
17536
17658
|
if (offender !== void 0) {
|
|
17537
|
-
console.error(`${
|
|
17659
|
+
console.error(`${TAG25} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
|
|
17538
17660
|
return c.json({ error: "public-session-exit-loopback-only" }, 403);
|
|
17539
17661
|
}
|
|
17540
17662
|
}
|
|
@@ -17552,7 +17674,7 @@ app31.post("/", async (c) => {
|
|
|
17552
17674
|
var public_session_exit_default = app31;
|
|
17553
17675
|
|
|
17554
17676
|
// server/routes/admin/access-session-evict.ts
|
|
17555
|
-
var
|
|
17677
|
+
var TAG26 = "[access-session-evict]";
|
|
17556
17678
|
function isLoopbackAddr5(addr) {
|
|
17557
17679
|
return addr === "127.0.0.1" || addr === "::1" || addr === "::ffff:127.0.0.1";
|
|
17558
17680
|
}
|
|
@@ -17560,7 +17682,7 @@ var app32 = new Hono();
|
|
|
17560
17682
|
app32.post("/", async (c) => {
|
|
17561
17683
|
const remoteAddr = c.env?.incoming?.socket?.remoteAddress ?? "";
|
|
17562
17684
|
if (!isLoopbackAddr5(remoteAddr)) {
|
|
17563
|
-
console.error(`${
|
|
17685
|
+
console.error(`${TAG26} reject reason=non-loopback remoteAddr=${remoteAddr}`);
|
|
17564
17686
|
return c.json({ error: "access-session-evict-loopback-only" }, 403);
|
|
17565
17687
|
}
|
|
17566
17688
|
const xffHeader = c.env?.incoming?.headers?.["x-forwarded-for"];
|
|
@@ -17569,7 +17691,7 @@ app32.post("/", async (c) => {
|
|
|
17569
17691
|
const tokens = xffRaw.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
|
|
17570
17692
|
const offender = tokens.find((t) => !isLoopbackAddr5(t));
|
|
17571
17693
|
if (offender !== void 0) {
|
|
17572
|
-
console.error(`${
|
|
17694
|
+
console.error(`${TAG26} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
|
|
17573
17695
|
return c.json({ error: "access-session-evict-loopback-only" }, 403);
|
|
17574
17696
|
}
|
|
17575
17697
|
}
|
|
@@ -17582,7 +17704,7 @@ app32.post("/", async (c) => {
|
|
|
17582
17704
|
const grantId = typeof body.grantId === "string" ? body.grantId.trim() : "";
|
|
17583
17705
|
if (!grantId) return c.json({ error: "grantId required" }, 400);
|
|
17584
17706
|
const dropped = evictAccessSessionsByGrant(grantId);
|
|
17585
|
-
console.log(`${
|
|
17707
|
+
console.log(`${TAG26} grantId=${grantId} dropped=${dropped}`);
|
|
17586
17708
|
return c.json({ ok: true, dropped });
|
|
17587
17709
|
});
|
|
17588
17710
|
var access_session_evict_default = app32;
|
|
@@ -17590,7 +17712,7 @@ var access_session_evict_default = app32;
|
|
|
17590
17712
|
// server/routes/admin/identity.ts
|
|
17591
17713
|
var import_dist4 = __toESM(require_dist3(), 1);
|
|
17592
17714
|
var app33 = new Hono();
|
|
17593
|
-
var
|
|
17715
|
+
var TAG27 = "[admin-identity]";
|
|
17594
17716
|
function managerBase5() {
|
|
17595
17717
|
const port2 = (0, import_dist4.requirePortEnv)("CLAUDE_SESSION_MANAGER_PORT", { tag: "admin-identity" });
|
|
17596
17718
|
return `http://127.0.0.1:${port2}`;
|
|
@@ -17621,20 +17743,20 @@ app33.post("/validate", async (c) => {
|
|
|
17621
17743
|
const verdict = validatePin(pin, USERS_FILE);
|
|
17622
17744
|
if (verdict.kind !== "match") {
|
|
17623
17745
|
if (verdict.kind === "miss") fireStop();
|
|
17624
|
-
console.log(`${
|
|
17746
|
+
console.log(`${TAG27} op=endpoint-validate result=${verdict.kind}${sessionId ? ` sessionId=${sessionId.slice(0, 8)}` : ""}`);
|
|
17625
17747
|
return c.json({ kind: verdict.kind });
|
|
17626
17748
|
}
|
|
17627
17749
|
const { userId } = verdict;
|
|
17628
17750
|
const accounts = resolveUserAccounts(userId);
|
|
17629
17751
|
if (accounts.length === 0) {
|
|
17630
17752
|
fireStop();
|
|
17631
|
-
console.log(`${
|
|
17753
|
+
console.log(`${TAG27} op=endpoint-validate result=no-account userId=${userId.slice(0, 8)}`);
|
|
17632
17754
|
return c.json({ kind: "miss" });
|
|
17633
17755
|
}
|
|
17634
17756
|
const accountId = accounts[0].accountId;
|
|
17635
17757
|
const { userName } = await resolveUserIdentity(accountId, userId);
|
|
17636
17758
|
const aboutOwner = await resolveOwnerProfileBlock(accountId, userId);
|
|
17637
|
-
console.log(`${
|
|
17759
|
+
console.log(`${TAG27} op=endpoint-validate result=match userId=${userId.slice(0, 8)} aboutOwner=${aboutOwner.ok ? "ok" : `unresolved:${aboutOwner.reason}`}`);
|
|
17638
17760
|
return c.json({ kind: "match", userId, userName, aboutOwner });
|
|
17639
17761
|
});
|
|
17640
17762
|
var identity_default = app33;
|
|
@@ -17674,13 +17796,13 @@ var admin_default = app34;
|
|
|
17674
17796
|
// app/lib/access-gate.ts
|
|
17675
17797
|
import neo4j4 from "neo4j-driver";
|
|
17676
17798
|
import { readFileSync as readFileSync17 } from "fs";
|
|
17677
|
-
import { resolve as
|
|
17799
|
+
import { resolve as resolve20 } from "path";
|
|
17678
17800
|
import { randomUUID as randomUUID10 } from "crypto";
|
|
17679
|
-
var PLATFORM_ROOT7 = process.env.MAXY_PLATFORM_ROOT ??
|
|
17801
|
+
var PLATFORM_ROOT7 = process.env.MAXY_PLATFORM_ROOT ?? resolve20(process.cwd(), "..");
|
|
17680
17802
|
var driver = null;
|
|
17681
17803
|
function readPassword() {
|
|
17682
17804
|
if (process.env.NEO4J_PASSWORD) return process.env.NEO4J_PASSWORD;
|
|
17683
|
-
const passwordFile =
|
|
17805
|
+
const passwordFile = resolve20(PLATFORM_ROOT7, "config/.neo4j-password");
|
|
17684
17806
|
try {
|
|
17685
17807
|
return readFileSync17(passwordFile, "utf-8").trim();
|
|
17686
17808
|
} catch {
|
|
@@ -17880,7 +18002,7 @@ async function generateNewMagicToken(grantId) {
|
|
|
17880
18002
|
}
|
|
17881
18003
|
|
|
17882
18004
|
// server/routes/access/verify-token.ts
|
|
17883
|
-
var
|
|
18005
|
+
var TAG28 = "[access-verify]";
|
|
17884
18006
|
var MINT_TAG = "[access-session-mint]";
|
|
17885
18007
|
var COOKIE_NAME = "__access_session";
|
|
17886
18008
|
var app35 = new Hono();
|
|
@@ -17899,39 +18021,39 @@ app35.post("/", async (c) => {
|
|
|
17899
18021
|
}
|
|
17900
18022
|
const rateMsg = checkAccessRateLimit(ip, agentSlug);
|
|
17901
18023
|
if (rateMsg) {
|
|
17902
|
-
console.error(`${
|
|
18024
|
+
console.error(`${TAG28} grantId=- agentSlug=${agentSlug} result=rate-limited ip=${ip}`);
|
|
17903
18025
|
return c.json({ error: rateMsg }, 429);
|
|
17904
18026
|
}
|
|
17905
18027
|
const grant = await findGrantByMagicToken(token);
|
|
17906
18028
|
if (!grant) {
|
|
17907
18029
|
recordAccessFailedAttempt(ip, agentSlug);
|
|
17908
|
-
console.error(`${
|
|
18030
|
+
console.error(`${TAG28} grantId=- agentSlug=${agentSlug} result=notfound ip=${ip}`);
|
|
17909
18031
|
return c.json({ error: "invalid-or-expired-link" }, 401);
|
|
17910
18032
|
}
|
|
17911
18033
|
if (grant.agentSlug !== agentSlug) {
|
|
17912
18034
|
recordAccessFailedAttempt(ip, agentSlug);
|
|
17913
18035
|
console.error(
|
|
17914
|
-
`${
|
|
18036
|
+
`${TAG28} grantId=${grant.grantId} agentSlug=${agentSlug} result=agent-mismatch grantAgent=${grant.agentSlug} ip=${ip}`
|
|
17915
18037
|
);
|
|
17916
18038
|
return c.json({ error: "invalid-or-expired-link" }, 401);
|
|
17917
18039
|
}
|
|
17918
18040
|
if (grant.status === "expired" || grant.status === "revoked") {
|
|
17919
18041
|
recordAccessFailedAttempt(ip, agentSlug);
|
|
17920
18042
|
console.error(
|
|
17921
|
-
`${
|
|
18043
|
+
`${TAG28} grantId=${grant.grantId} agentSlug=${agentSlug} result=expired status=${grant.status} ip=${ip}`
|
|
17922
18044
|
);
|
|
17923
18045
|
return c.json({ error: "access-no-longer-valid" }, 401);
|
|
17924
18046
|
}
|
|
17925
18047
|
if (grant.expiresAt !== null && grant.expiresAt < Date.now()) {
|
|
17926
18048
|
recordAccessFailedAttempt(ip, agentSlug);
|
|
17927
18049
|
console.error(
|
|
17928
|
-
`${
|
|
18050
|
+
`${TAG28} grantId=${grant.grantId} agentSlug=${agentSlug} result=expired reason=expiresAt-past ip=${ip}`
|
|
17929
18051
|
);
|
|
17930
18052
|
return c.json({ error: "access-no-longer-valid" }, 401);
|
|
17931
18053
|
}
|
|
17932
18054
|
if (!grant.sliceToken) {
|
|
17933
18055
|
console.error(
|
|
17934
|
-
`${
|
|
18056
|
+
`${TAG28} grantId=${grant.grantId} agentSlug=${agentSlug} result=no-slice-token reason=schema-violation`
|
|
17935
18057
|
);
|
|
17936
18058
|
return c.json({ error: "grant-misconfigured" }, 500);
|
|
17937
18059
|
}
|
|
@@ -17947,12 +18069,12 @@ app35.post("/", async (c) => {
|
|
|
17947
18069
|
await consumeMagicTokenAndActivate(grant.grantId);
|
|
17948
18070
|
} catch (err) {
|
|
17949
18071
|
console.error(
|
|
17950
|
-
`${
|
|
18072
|
+
`${TAG28} grantId=${grant.grantId} agentSlug=${agentSlug} result=consume-failed err="${err instanceof Error ? err.message : String(err)}"`
|
|
17951
18073
|
);
|
|
17952
18074
|
return c.json({ error: "verification-failed" }, 500);
|
|
17953
18075
|
}
|
|
17954
18076
|
clearAccessRateLimit(ip, agentSlug);
|
|
17955
|
-
console.log(`${
|
|
18077
|
+
console.log(`${TAG28} grantId=${grant.grantId} agentSlug=${agentSlug} result=ok ip=${ip}`);
|
|
17956
18078
|
console.log(
|
|
17957
18079
|
`${MINT_TAG} grantId=${grant.grantId} sliceToken=${grant.sliceToken.slice(0, 8)} agentSlug=${agentSlug} personId=${grant.personId ?? "none"}`
|
|
17958
18080
|
);
|
|
@@ -17970,9 +18092,9 @@ var verify_token_default = app35;
|
|
|
17970
18092
|
|
|
17971
18093
|
// app/lib/access-email.ts
|
|
17972
18094
|
import { spawn as spawn2 } from "child_process";
|
|
17973
|
-
import { resolve as
|
|
17974
|
-
var PLATFORM_ROOT8 = process.env.MAXY_PLATFORM_ROOT ??
|
|
17975
|
-
var SEND_SCRIPT =
|
|
18095
|
+
import { resolve as resolve21 } from "path";
|
|
18096
|
+
var PLATFORM_ROOT8 = process.env.MAXY_PLATFORM_ROOT ?? resolve21(process.cwd(), "..");
|
|
18097
|
+
var SEND_SCRIPT = resolve21(
|
|
17976
18098
|
PLATFORM_ROOT8,
|
|
17977
18099
|
"plugins",
|
|
17978
18100
|
"email",
|
|
@@ -18028,7 +18150,7 @@ async function sendMagicLinkEmail(payload) {
|
|
|
18028
18150
|
}
|
|
18029
18151
|
|
|
18030
18152
|
// server/routes/access/request-magic-link.ts
|
|
18031
|
-
var
|
|
18153
|
+
var TAG29 = "[access-request-link]";
|
|
18032
18154
|
var app36 = new Hono();
|
|
18033
18155
|
var VISITOR_MESSAGE = "If that email is on the invite list, a fresh link is on the way.";
|
|
18034
18156
|
app36.post("/", async (c) => {
|
|
@@ -18045,18 +18167,18 @@ app36.post("/", async (c) => {
|
|
|
18045
18167
|
}
|
|
18046
18168
|
const rateMsg = checkRequestLinkRateLimit(contactValue);
|
|
18047
18169
|
if (rateMsg) {
|
|
18048
|
-
console.error(`${
|
|
18170
|
+
console.error(`${TAG29} contactValue=${maskContact(contactValue)} result=rate-limited`);
|
|
18049
18171
|
return c.json({ error: rateMsg }, 429);
|
|
18050
18172
|
}
|
|
18051
18173
|
recordRequestLinkAttempt(contactValue);
|
|
18052
18174
|
const accountId = process.env.ACCOUNT_ID ?? "";
|
|
18053
18175
|
if (!accountId) {
|
|
18054
|
-
console.error(`${
|
|
18176
|
+
console.error(`${TAG29} contactValue=${maskContact(contactValue)} result=no-account-id`);
|
|
18055
18177
|
return c.json({ message: VISITOR_MESSAGE }, 200);
|
|
18056
18178
|
}
|
|
18057
18179
|
const grant = await findActiveGrantByContact(contactValue, agentSlug, accountId);
|
|
18058
18180
|
if (!grant) {
|
|
18059
|
-
console.log(`${
|
|
18181
|
+
console.log(`${TAG29} contactValue=${maskContact(contactValue)} result=notfound`);
|
|
18060
18182
|
return c.json({ message: VISITOR_MESSAGE }, 200);
|
|
18061
18183
|
}
|
|
18062
18184
|
let token;
|
|
@@ -18064,7 +18186,7 @@ app36.post("/", async (c) => {
|
|
|
18064
18186
|
token = await generateNewMagicToken(grant.grantId);
|
|
18065
18187
|
} catch (err) {
|
|
18066
18188
|
console.error(
|
|
18067
|
-
`${
|
|
18189
|
+
`${TAG29} contactValue=${maskContact(contactValue)} result=mint-failed err="${err instanceof Error ? err.message : String(err)}"`
|
|
18068
18190
|
);
|
|
18069
18191
|
return c.json({ message: VISITOR_MESSAGE }, 200);
|
|
18070
18192
|
}
|
|
@@ -18096,12 +18218,12 @@ app36.post("/", async (c) => {
|
|
|
18096
18218
|
});
|
|
18097
18219
|
if (!sendResult.ok) {
|
|
18098
18220
|
console.error(
|
|
18099
|
-
`${
|
|
18221
|
+
`${TAG29} contactValue=${maskContact(contactValue)} result=send-failed err="${sendResult.error}"`
|
|
18100
18222
|
);
|
|
18101
18223
|
return c.json({ message: VISITOR_MESSAGE }, 200);
|
|
18102
18224
|
}
|
|
18103
18225
|
console.log(
|
|
18104
|
-
`${
|
|
18226
|
+
`${TAG29} contactValue=${maskContact(contactValue)} result=ok messageId=${sendResult.messageId}`
|
|
18105
18227
|
);
|
|
18106
18228
|
return c.json({ message: VISITOR_MESSAGE }, 200);
|
|
18107
18229
|
});
|
|
@@ -18115,7 +18237,7 @@ var access_default = app37;
|
|
|
18115
18237
|
|
|
18116
18238
|
// server/routes/sites.ts
|
|
18117
18239
|
import { existsSync as existsSync19, readFileSync as readFileSync18, realpathSync as realpathSync5, statSync as statSync8 } from "fs";
|
|
18118
|
-
import { resolve as
|
|
18240
|
+
import { resolve as resolve22 } from "path";
|
|
18119
18241
|
var SAFE_SEG_RE = /^[a-z0-9_][a-z0-9_.-]{0,99}$/i;
|
|
18120
18242
|
var MIME = {
|
|
18121
18243
|
".html": "text/html; charset=utf-8",
|
|
@@ -18172,8 +18294,8 @@ app38.get("/:rel{.*}", (c) => {
|
|
|
18172
18294
|
}
|
|
18173
18295
|
segments.push(seg);
|
|
18174
18296
|
}
|
|
18175
|
-
const rootDir =
|
|
18176
|
-
let filePath = segments.length === 0 ? rootDir :
|
|
18297
|
+
const rootDir = resolve22(account.accountDir, "sites");
|
|
18298
|
+
let filePath = segments.length === 0 ? rootDir : resolve22(rootDir, ...segments);
|
|
18177
18299
|
if (filePath !== rootDir && !filePath.startsWith(rootDir + "/")) {
|
|
18178
18300
|
console.error(`[sites] path-traversal-rejected path=${reqPath} reason=escape status=403`);
|
|
18179
18301
|
return c.text("Forbidden", 403);
|
|
@@ -18193,7 +18315,7 @@ app38.get("/:rel{.*}", (c) => {
|
|
|
18193
18315
|
return c.redirect(target, 301);
|
|
18194
18316
|
}
|
|
18195
18317
|
if (stat7?.isDirectory()) {
|
|
18196
|
-
filePath =
|
|
18318
|
+
filePath = resolve22(filePath, "index.html");
|
|
18197
18319
|
}
|
|
18198
18320
|
if (!filePath.startsWith(rootDir + "/")) {
|
|
18199
18321
|
console.error(`[sites] path-traversal-rejected path=${reqPath} reason=escape status=403`);
|
|
@@ -18337,9 +18459,9 @@ var cachedAttempted = false;
|
|
|
18337
18459
|
function readBrandConfig() {
|
|
18338
18460
|
if (cachedAttempted) return cached2;
|
|
18339
18461
|
cachedAttempted = true;
|
|
18340
|
-
const
|
|
18341
|
-
if (!
|
|
18342
|
-
const brandPath = join16(
|
|
18462
|
+
const platformRoot2 = process.env.MAXY_PLATFORM_ROOT;
|
|
18463
|
+
if (!platformRoot2) return null;
|
|
18464
|
+
const brandPath = join16(platformRoot2, "config", "brand.json");
|
|
18343
18465
|
if (!existsSync20(brandPath)) return null;
|
|
18344
18466
|
try {
|
|
18345
18467
|
cached2 = JSON.parse(readFileSync20(brandPath, "utf-8"));
|
|
@@ -18830,14 +18952,14 @@ async function writeEvent(opts) {
|
|
|
18830
18952
|
var visitor_event_default = app41;
|
|
18831
18953
|
|
|
18832
18954
|
// server/routes/session.ts
|
|
18833
|
-
import { resolve as
|
|
18955
|
+
import { resolve as resolve23 } from "path";
|
|
18834
18956
|
import { existsSync as existsSync21, writeFileSync as writeFileSync7, mkdirSync as mkdirSync5 } from "fs";
|
|
18835
18957
|
var UUID_RE4 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
18836
18958
|
function writeBrandingCache(accountId, agentSlug, branding) {
|
|
18837
18959
|
try {
|
|
18838
|
-
const cacheDir =
|
|
18960
|
+
const cacheDir = resolve23(MAXY_DIR, "branding-cache", accountId);
|
|
18839
18961
|
mkdirSync5(cacheDir, { recursive: true });
|
|
18840
|
-
writeFileSync7(
|
|
18962
|
+
writeFileSync7(resolve23(cacheDir, `${agentSlug}.json`), JSON.stringify(branding), "utf-8");
|
|
18841
18963
|
} catch (err) {
|
|
18842
18964
|
console.error(`[branding] cache write failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
18843
18965
|
}
|
|
@@ -18916,8 +19038,8 @@ app42.post("/", async (c) => {
|
|
|
18916
19038
|
}
|
|
18917
19039
|
let agentConfig = null;
|
|
18918
19040
|
if (account) {
|
|
18919
|
-
const agentDir =
|
|
18920
|
-
if (!existsSync21(agentDir) || !existsSync21(
|
|
19041
|
+
const agentDir = resolve23(account.accountDir, "agents", agentSlug);
|
|
19042
|
+
if (!existsSync21(agentDir) || !existsSync21(resolve23(agentDir, "config.json"))) {
|
|
18921
19043
|
return c.json({ error: "Agent not found" }, 404);
|
|
18922
19044
|
}
|
|
18923
19045
|
agentConfig = resolveAgentConfig(account.accountDir, agentSlug);
|
|
@@ -19154,7 +19276,7 @@ function startGraphHealthTimer() {
|
|
|
19154
19276
|
|
|
19155
19277
|
// app/lib/file-watcher.ts
|
|
19156
19278
|
import * as fsp2 from "fs/promises";
|
|
19157
|
-
import { resolve as
|
|
19279
|
+
import { resolve as resolve24, sep as sep6 } from "path";
|
|
19158
19280
|
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;
|
|
19159
19281
|
var DEFAULT_COALESCE_MS = 500;
|
|
19160
19282
|
var ROOTS = ["uploads", "accounts"];
|
|
@@ -19173,7 +19295,7 @@ async function startFileWatcher(opts = {}) {
|
|
|
19173
19295
|
const dropFn = opts.drop ?? dropFileIndex;
|
|
19174
19296
|
for (const r of ROOTS) {
|
|
19175
19297
|
try {
|
|
19176
|
-
await fsp2.mkdir(
|
|
19298
|
+
await fsp2.mkdir(resolve24(dataRoot, r), { recursive: true });
|
|
19177
19299
|
} catch (err) {
|
|
19178
19300
|
console.error(
|
|
19179
19301
|
`[file-watcher] start-failed root="${r}" err="${err.message}" \u2014 index will be maintained by the 5-min reconcile backstop only`
|
|
@@ -19194,7 +19316,7 @@ async function startFileWatcher(opts = {}) {
|
|
|
19194
19316
|
timers.set(relativePath, t);
|
|
19195
19317
|
}
|
|
19196
19318
|
async function runHook(relativePath, accountId) {
|
|
19197
|
-
const absolute =
|
|
19319
|
+
const absolute = resolve24(dataRoot, relativePath);
|
|
19198
19320
|
let exists = false;
|
|
19199
19321
|
try {
|
|
19200
19322
|
const st = await fsp2.stat(absolute);
|
|
@@ -19218,7 +19340,7 @@ async function startFileWatcher(opts = {}) {
|
|
|
19218
19340
|
}
|
|
19219
19341
|
}
|
|
19220
19342
|
async function watchRoot(rootName) {
|
|
19221
|
-
const absRoot =
|
|
19343
|
+
const absRoot = resolve24(dataRoot, rootName);
|
|
19222
19344
|
try {
|
|
19223
19345
|
const iter = fsp2.watch(absRoot, { recursive: true, signal: controller.signal });
|
|
19224
19346
|
for await (const event of iter) {
|
|
@@ -19256,7 +19378,7 @@ async function startFileWatcher(opts = {}) {
|
|
|
19256
19378
|
}
|
|
19257
19379
|
|
|
19258
19380
|
// app/lib/whatsapp/inbound/claude-bridge.ts
|
|
19259
|
-
var
|
|
19381
|
+
var TAG30 = "[whatsapp-adaptor]";
|
|
19260
19382
|
function whatsappTurnTimeoutMs() {
|
|
19261
19383
|
return Number(process.env.WHATSAPP_PTY_TURN_TIMEOUT_MS ?? String(5 * 6e4));
|
|
19262
19384
|
}
|
|
@@ -19278,7 +19400,7 @@ async function dispatchToClaude(input) {
|
|
|
19278
19400
|
await input.reply(result.turnText);
|
|
19279
19401
|
} catch (err) {
|
|
19280
19402
|
const m = err instanceof Error ? err.message : String(err);
|
|
19281
|
-
console.error(`${
|
|
19403
|
+
console.error(`${TAG30} reject reason=reply-failed senderId=${input.senderId} message=${m}`);
|
|
19282
19404
|
}
|
|
19283
19405
|
}
|
|
19284
19406
|
function startReaper2() {
|
|
@@ -19316,7 +19438,7 @@ function broadcastAdminShutdown(reason) {
|
|
|
19316
19438
|
// ../lib/entitlement/src/index.ts
|
|
19317
19439
|
import { createPublicKey, createHash as createHash5, verify as cryptoVerify } from "crypto";
|
|
19318
19440
|
import { existsSync as existsSync22, readFileSync as readFileSync21, statSync as statSync9 } from "fs";
|
|
19319
|
-
import { resolve as
|
|
19441
|
+
import { resolve as resolve25 } from "path";
|
|
19320
19442
|
|
|
19321
19443
|
// ../lib/entitlement/src/canonicalize.ts
|
|
19322
19444
|
function canonicalize(value) {
|
|
@@ -19351,7 +19473,7 @@ var PUBKEY_SHA256 = "8eee6bcb33545fd13b16d3199a5735ca5db5062834c7b49dfe4f23801d9
|
|
|
19351
19473
|
var GRACE_DAYS = 7;
|
|
19352
19474
|
var GRACE_MS = GRACE_DAYS * 24 * 60 * 60 * 1e3;
|
|
19353
19475
|
function pubkeyPath(brand) {
|
|
19354
|
-
return
|
|
19476
|
+
return resolve25(brand.platformRoot, "lib", "entitlement", "rubytech-pubkey.pem");
|
|
19355
19477
|
}
|
|
19356
19478
|
var memo = null;
|
|
19357
19479
|
function memoKey(mtimeMs, account) {
|
|
@@ -19363,7 +19485,7 @@ function resolveEntitlement(brand, account) {
|
|
|
19363
19485
|
if (brand.commercialMode !== true) {
|
|
19364
19486
|
return logResolved(implicitTrust(account), null);
|
|
19365
19487
|
}
|
|
19366
|
-
const entitlementPath =
|
|
19488
|
+
const entitlementPath = resolve25(brand.configDir, "entitlement.json");
|
|
19367
19489
|
if (!existsSync22(entitlementPath)) {
|
|
19368
19490
|
return logResolved(anonymousFallback("missing"), { reason: "missing" });
|
|
19369
19491
|
}
|
|
@@ -19925,8 +20047,8 @@ app43.get("/agent-assets/:slug/:filename", (c) => {
|
|
|
19925
20047
|
console.error(`[agent-assets] no-account slug=${slug} file=${filename}`);
|
|
19926
20048
|
return c.text("Not found", 404);
|
|
19927
20049
|
}
|
|
19928
|
-
const filePath =
|
|
19929
|
-
const expectedDir =
|
|
20050
|
+
const filePath = resolve26(account.accountDir, "agents", slug, "assets", filename);
|
|
20051
|
+
const expectedDir = resolve26(account.accountDir, "agents", slug, "assets");
|
|
19930
20052
|
if (!filePath.startsWith(expectedDir + "/")) {
|
|
19931
20053
|
console.error(`[agent-assets] path-traversal-rejected slug=${slug} file=${filename}`);
|
|
19932
20054
|
return c.text("Forbidden", 403);
|
|
@@ -19955,8 +20077,8 @@ app43.get("/generated/:filename", (c) => {
|
|
|
19955
20077
|
console.error(`[generated] serve file=${filename} status=404`);
|
|
19956
20078
|
return c.text("Not found", 404);
|
|
19957
20079
|
}
|
|
19958
|
-
const filePath =
|
|
19959
|
-
const expectedDir =
|
|
20080
|
+
const filePath = resolve26(account.accountDir, "generated", filename);
|
|
20081
|
+
const expectedDir = resolve26(account.accountDir, "generated");
|
|
19960
20082
|
if (!filePath.startsWith(expectedDir + "/")) {
|
|
19961
20083
|
console.error(`[generated] serve file=${filename} status=403`);
|
|
19962
20084
|
return c.text("Forbidden", 403);
|
|
@@ -20043,7 +20165,7 @@ var clientErrorReporterScript = `<script>
|
|
|
20043
20165
|
function cachedHtml(file) {
|
|
20044
20166
|
let html = htmlCache.get(file);
|
|
20045
20167
|
if (!html) {
|
|
20046
|
-
html = readFileSync22(
|
|
20168
|
+
html = readFileSync22(resolve26(process.cwd(), "public", file), "utf-8");
|
|
20047
20169
|
const productNameEsc = escapeHtml(BRAND.productName);
|
|
20048
20170
|
html = html.replace(/<title>([^<]*)<\/title>/, (_match, inner) => `<title>${escapeHtml(inner).replace(/Maxy/g, productNameEsc)}</title>`);
|
|
20049
20171
|
html = html.replace('href="/favicon.ico"', `href="${escapeHtml(brandFaviconPath)}"`);
|
|
@@ -20161,7 +20283,7 @@ app43.use("/vnc-popout.html", logViewerFetch);
|
|
|
20161
20283
|
app43.get("/vnc-popout.html", (c) => {
|
|
20162
20284
|
let html = htmlCache.get("vnc-popout.html");
|
|
20163
20285
|
if (!html) {
|
|
20164
|
-
html = readFileSync22(
|
|
20286
|
+
html = readFileSync22(resolve26(process.cwd(), "public", "vnc-popout.html"), "utf-8");
|
|
20165
20287
|
const name = escapeHtml(BRAND.productName);
|
|
20166
20288
|
html = html.replace("<title>Browser \u2014 Maxy</title>", `<title>${name}</title>`);
|
|
20167
20289
|
html = html.replace("</head>", ` ${brandScript}
|
|
@@ -20405,7 +20527,7 @@ if (bootAccountConfig?.whatsapp) {
|
|
|
20405
20527
|
}
|
|
20406
20528
|
init({
|
|
20407
20529
|
configDir: configDirForWhatsApp,
|
|
20408
|
-
platformRoot:
|
|
20530
|
+
platformRoot: resolve26(process.env.MAXY_PLATFORM_ROOT ?? join17(__dirname, "..")),
|
|
20409
20531
|
accountConfig: bootAccountConfig,
|
|
20410
20532
|
onMessage: async (msg) => {
|
|
20411
20533
|
if (msg.text && !msg.isOwnerMirror) {
|