@rubytech/create-realagent-code 0.1.252 → 0.1.254
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/http-server.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/http-server.js +14 -0
- package/payload/platform/services/claude-session-manager/dist/http-server.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 +498 -322
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() {
|
|
@@ -8857,8 +8857,9 @@ async function managerDelete(sessionId) {
|
|
|
8857
8857
|
await fetch(`${managerBase()}/${sessionId}`, { method: "DELETE" }).catch(() => {
|
|
8858
8858
|
});
|
|
8859
8859
|
}
|
|
8860
|
-
function managerLogFollowUrl(sessionId) {
|
|
8861
|
-
|
|
8860
|
+
function managerLogFollowUrl(sessionId, opts) {
|
|
8861
|
+
const base = `${managerBase()}/${sessionId}/log?follow=1`;
|
|
8862
|
+
return opts?.boundary ? `${base}&boundary=1` : base;
|
|
8862
8863
|
}
|
|
8863
8864
|
|
|
8864
8865
|
// app/lib/channel-pty-bridge/admin-session-id.ts
|
|
@@ -9044,9 +9045,10 @@ function startFollower(opts) {
|
|
|
9044
9045
|
let res;
|
|
9045
9046
|
let attempt = 0;
|
|
9046
9047
|
for (; ; ) {
|
|
9047
|
-
res = await fetch(
|
|
9048
|
-
|
|
9049
|
-
|
|
9048
|
+
res = await fetch(
|
|
9049
|
+
managerLogFollowUrl(entry.sessionId, { boundary: opts.suppressResumeReplay === true }),
|
|
9050
|
+
{ signal: abort.signal }
|
|
9051
|
+
);
|
|
9050
9052
|
console.error(`${tag} follower-connect sessionId=${sid} status=${res.status}`);
|
|
9051
9053
|
if (res.status === 202) {
|
|
9052
9054
|
attempt += 1;
|
|
@@ -9073,6 +9075,11 @@ function startFollower(opts) {
|
|
|
9073
9075
|
const reader = res.body.getReader();
|
|
9074
9076
|
const decoder = new TextDecoder("utf8");
|
|
9075
9077
|
let buffered = "";
|
|
9078
|
+
const fileDelivery = opts.fileDelivery ?? null;
|
|
9079
|
+
let firedFileTools = [];
|
|
9080
|
+
let suppressing = opts.suppressResumeReplay === true;
|
|
9081
|
+
let discardedTurns = 0;
|
|
9082
|
+
let discardedFileTools = 0;
|
|
9076
9083
|
while (!abort.signal.aborted) {
|
|
9077
9084
|
const { value, done } = await reader.read();
|
|
9078
9085
|
if (done) break;
|
|
@@ -9092,8 +9099,18 @@ function startFollower(opts) {
|
|
|
9092
9099
|
);
|
|
9093
9100
|
continue;
|
|
9094
9101
|
}
|
|
9102
|
+
if (event.type === "__channel_resume_boundary__") {
|
|
9103
|
+
if (suppressing) {
|
|
9104
|
+
suppressing = false;
|
|
9105
|
+
console.error(
|
|
9106
|
+
`${tag} follower-resume-boundary sessionId=${sid} discarded-head-turns=${discardedTurns} discarded-head-filetools=${discardedFileTools}`
|
|
9107
|
+
);
|
|
9108
|
+
}
|
|
9109
|
+
continue;
|
|
9110
|
+
}
|
|
9095
9111
|
if (event.type === "user") {
|
|
9096
9112
|
entry.pendingTurnText = "";
|
|
9113
|
+
firedFileTools = [];
|
|
9097
9114
|
continue;
|
|
9098
9115
|
}
|
|
9099
9116
|
if (event.type !== "assistant") continue;
|
|
@@ -9103,15 +9120,45 @@ function startFollower(opts) {
|
|
|
9103
9120
|
for (const block of msg.content) {
|
|
9104
9121
|
if (block?.type === "text" && typeof block.text === "string") {
|
|
9105
9122
|
entry.pendingTurnText += block.text;
|
|
9123
|
+
} else if (fileDelivery && block?.type === "tool_use" && typeof block.name === "string" && fileDelivery.isFileDeliveryTool(block.name)) {
|
|
9124
|
+
if (suppressing) {
|
|
9125
|
+
discardedFileTools += 1;
|
|
9126
|
+
continue;
|
|
9127
|
+
}
|
|
9128
|
+
firedFileTools.push(block.name);
|
|
9129
|
+
try {
|
|
9130
|
+
await fileDelivery.onFileToolUse({ toolName: block.name, input: block.input });
|
|
9131
|
+
} catch (err) {
|
|
9132
|
+
console.error(
|
|
9133
|
+
`${tag} file-delivery-error sessionId=${sid} tool=${block.name} message=${err instanceof Error ? err.message : String(err)}`
|
|
9134
|
+
);
|
|
9135
|
+
}
|
|
9106
9136
|
}
|
|
9107
9137
|
}
|
|
9108
9138
|
}
|
|
9109
9139
|
if (msg.stop_reason === "end_turn") {
|
|
9140
|
+
if (suppressing) {
|
|
9141
|
+
entry.pendingTurnText = "";
|
|
9142
|
+
firedFileTools = [];
|
|
9143
|
+
discardedTurns += 1;
|
|
9144
|
+
continue;
|
|
9145
|
+
}
|
|
9110
9146
|
const flush = entry.pendingTurnText;
|
|
9111
9147
|
entry.pendingTurnText = "";
|
|
9112
9148
|
if (flush.trim()) {
|
|
9113
9149
|
await fanOut(entry.subscribers, flush, opts.onError, tag);
|
|
9114
9150
|
}
|
|
9151
|
+
if (fileDelivery && firedFileTools.length > 0) {
|
|
9152
|
+
const fired = firedFileTools;
|
|
9153
|
+
firedFileTools = [];
|
|
9154
|
+
try {
|
|
9155
|
+
fileDelivery.onTurnEnd(fired);
|
|
9156
|
+
} catch (err) {
|
|
9157
|
+
console.error(
|
|
9158
|
+
`${tag} file-delivery-error sessionId=${sid} phase=turn-end message=${err instanceof Error ? err.message : String(err)}`
|
|
9159
|
+
);
|
|
9160
|
+
}
|
|
9161
|
+
}
|
|
9115
9162
|
}
|
|
9116
9163
|
}
|
|
9117
9164
|
}
|
|
@@ -9138,10 +9185,164 @@ async function fanOut(subscribers, text, onError, tag) {
|
|
|
9138
9185
|
);
|
|
9139
9186
|
}
|
|
9140
9187
|
|
|
9188
|
+
// app/lib/whatsapp/outbound/send-document.ts
|
|
9189
|
+
import { realpathSync as realpathSync2 } from "fs";
|
|
9190
|
+
import { readFile, stat as stat2 } from "fs/promises";
|
|
9191
|
+
import { resolve as resolve4, basename } from "path";
|
|
9192
|
+
var TAG15 = "[whatsapp:outbound]";
|
|
9193
|
+
var WHATSAPP_DOCUMENT_MAX_BYTES = 100 * 1024 * 1024;
|
|
9194
|
+
var lastDocumentOutboundAt = /* @__PURE__ */ new Map();
|
|
9195
|
+
function normalizeJid(to) {
|
|
9196
|
+
return to.includes("@") ? to : toWhatsappJid(to);
|
|
9197
|
+
}
|
|
9198
|
+
function documentOutboundAt(to) {
|
|
9199
|
+
return lastDocumentOutboundAt.get(normalizeJid(to));
|
|
9200
|
+
}
|
|
9201
|
+
async function sendWhatsAppDocument(input) {
|
|
9202
|
+
const { to, filePath, caption, accountId, maxyAccountId, platformRoot: platformRoot2 } = input;
|
|
9203
|
+
if (!to || !filePath) {
|
|
9204
|
+
return { ok: false, status: 400, error: "Missing required fields: to, filePath" };
|
|
9205
|
+
}
|
|
9206
|
+
if (!maxyAccountId || !platformRoot2) {
|
|
9207
|
+
return { ok: false, status: 400, error: "Cannot validate file path: missing account or platform context" };
|
|
9208
|
+
}
|
|
9209
|
+
const accountDir = resolve4(platformRoot2, "..", "data/accounts", maxyAccountId);
|
|
9210
|
+
let resolvedPath;
|
|
9211
|
+
try {
|
|
9212
|
+
resolvedPath = realpathSync2(filePath);
|
|
9213
|
+
const accountResolved = realpathSync2(accountDir);
|
|
9214
|
+
if (!resolvedPath.startsWith(accountResolved + "/")) {
|
|
9215
|
+
const sanitised = filePath.replace(accountDir, "<account>/");
|
|
9216
|
+
console.error(`${TAG15} document REJECTED path=${sanitised} reason=outside_account_directory`);
|
|
9217
|
+
return { ok: false, status: 403, error: "Access denied: file is outside the account directory" };
|
|
9218
|
+
}
|
|
9219
|
+
} catch (err) {
|
|
9220
|
+
const code = err.code;
|
|
9221
|
+
if (code === "ENOENT") {
|
|
9222
|
+
console.error(`${TAG15} document ENOENT path=${filePath}`);
|
|
9223
|
+
return { ok: false, status: 404, error: `File not found: ${filePath}` };
|
|
9224
|
+
}
|
|
9225
|
+
console.error(`${TAG15} document path error: ${String(err)}`);
|
|
9226
|
+
return { ok: false, status: 500, error: String(err) };
|
|
9227
|
+
}
|
|
9228
|
+
const fileStat = await stat2(resolvedPath);
|
|
9229
|
+
if (fileStat.size > WHATSAPP_DOCUMENT_MAX_BYTES) {
|
|
9230
|
+
return {
|
|
9231
|
+
ok: false,
|
|
9232
|
+
status: 400,
|
|
9233
|
+
error: `File exceeds 100 MB limit (${(fileStat.size / 1024 / 1024).toFixed(1)} MB)`
|
|
9234
|
+
};
|
|
9235
|
+
}
|
|
9236
|
+
const filename = basename(resolvedPath);
|
|
9237
|
+
const jid = normalizeJid(to);
|
|
9238
|
+
const sock = getSocket(accountId);
|
|
9239
|
+
if (!sock) {
|
|
9240
|
+
console.error(`${TAG15} sent document to=${jid} file=${filename} bytes=${fileStat.size} ok=false reason=not-connected`);
|
|
9241
|
+
return { ok: false, status: 503, error: `WhatsApp account "${accountId}" is not connected` };
|
|
9242
|
+
}
|
|
9243
|
+
const buffer = Buffer.from(await readFile(resolvedPath));
|
|
9244
|
+
const mimetype = detectMimeType(resolvedPath);
|
|
9245
|
+
const result = await sendMediaMessage(
|
|
9246
|
+
sock,
|
|
9247
|
+
to,
|
|
9248
|
+
{ type: "document", buffer, mimetype, filename, caption },
|
|
9249
|
+
{ accountId }
|
|
9250
|
+
);
|
|
9251
|
+
console.error(
|
|
9252
|
+
`${TAG15} sent document to=${jid} file=${filename} bytes=${fileStat.size} ok=${result.success}` + (result.messageId ? ` id=${result.messageId}` : "")
|
|
9253
|
+
);
|
|
9254
|
+
if (result.success) {
|
|
9255
|
+
lastDocumentOutboundAt.set(jid, Date.now());
|
|
9256
|
+
return { ok: true, messageId: result.messageId };
|
|
9257
|
+
}
|
|
9258
|
+
return { ok: false, status: 500, error: result.error ?? "send failed" };
|
|
9259
|
+
}
|
|
9260
|
+
|
|
9261
|
+
// app/lib/whatsapp/inbound/file-delivery-bridge.ts
|
|
9262
|
+
var TAG16 = "[whatsapp-adaptor]";
|
|
9263
|
+
var SEND_USER_FILE = "SendUserFile";
|
|
9264
|
+
var WHATSAPP_SEND_DOCUMENT = "whatsapp-send-document";
|
|
9265
|
+
function platformRoot() {
|
|
9266
|
+
return process.env.MAXY_PLATFORM_ROOT || "";
|
|
9267
|
+
}
|
|
9268
|
+
function makeWhatsAppFileDelivery(entry) {
|
|
9269
|
+
let turnStartedAt = null;
|
|
9270
|
+
let failedFiles = [];
|
|
9271
|
+
let sendUserFileAttempts = 0;
|
|
9272
|
+
return {
|
|
9273
|
+
isFileDeliveryTool(toolName) {
|
|
9274
|
+
return toolName === SEND_USER_FILE || toolName === WHATSAPP_SEND_DOCUMENT;
|
|
9275
|
+
},
|
|
9276
|
+
async onFileToolUse(use) {
|
|
9277
|
+
if (turnStartedAt === null) turnStartedAt = Date.now();
|
|
9278
|
+
if (use.toolName !== SEND_USER_FILE) return;
|
|
9279
|
+
const input = use.input ?? {};
|
|
9280
|
+
const files = Array.isArray(input.files) ? input.files.filter((f) => typeof f === "string") : [];
|
|
9281
|
+
const caption = typeof input.caption === "string" ? input.caption : void 0;
|
|
9282
|
+
if (files.length === 0) return;
|
|
9283
|
+
let maxyAccountId;
|
|
9284
|
+
try {
|
|
9285
|
+
maxyAccountId = resolvePlatformAccountId();
|
|
9286
|
+
} catch (err) {
|
|
9287
|
+
console.error(
|
|
9288
|
+
`${TAG16} file-delivery reject reason=account-unresolved sender=${entry.senderId} message=${err instanceof Error ? err.message : String(err)}`
|
|
9289
|
+
);
|
|
9290
|
+
return;
|
|
9291
|
+
}
|
|
9292
|
+
for (let i = 0; i < files.length; i++) {
|
|
9293
|
+
sendUserFileAttempts++;
|
|
9294
|
+
const result = await sendWhatsAppDocument({
|
|
9295
|
+
to: entry.senderId,
|
|
9296
|
+
filePath: files[i],
|
|
9297
|
+
caption: i === 0 ? caption : void 0,
|
|
9298
|
+
accountId: entry.accountId,
|
|
9299
|
+
maxyAccountId,
|
|
9300
|
+
platformRoot: platformRoot()
|
|
9301
|
+
});
|
|
9302
|
+
if (!result.ok) {
|
|
9303
|
+
failedFiles.push(files[i]);
|
|
9304
|
+
console.error(
|
|
9305
|
+
`${TAG16} file-delivery reject reason=send-failed sender=${entry.senderId} status=${result.status} message=${result.error}`
|
|
9306
|
+
);
|
|
9307
|
+
}
|
|
9308
|
+
}
|
|
9309
|
+
},
|
|
9310
|
+
onTurnEnd(firedTools) {
|
|
9311
|
+
const startedAt = turnStartedAt ?? 0;
|
|
9312
|
+
const failed = failedFiles;
|
|
9313
|
+
const attempts = sendUserFileAttempts;
|
|
9314
|
+
turnStartedAt = null;
|
|
9315
|
+
failedFiles = [];
|
|
9316
|
+
sendUserFileAttempts = 0;
|
|
9317
|
+
const sid = entry.sessionId.slice(0, 8);
|
|
9318
|
+
for (const file of failed) {
|
|
9319
|
+
console.error(
|
|
9320
|
+
`${TAG16} file-delivery-unreconciled sender=${entry.senderId} sessionId=${sid} tool=${SEND_USER_FILE} file=${file}`
|
|
9321
|
+
);
|
|
9322
|
+
}
|
|
9323
|
+
const okAt = documentOutboundAt(entry.senderId);
|
|
9324
|
+
const documentWentOut = okAt !== void 0 && okAt >= startedAt;
|
|
9325
|
+
if (firedTools.includes(WHATSAPP_SEND_DOCUMENT) && !documentWentOut) {
|
|
9326
|
+
console.error(
|
|
9327
|
+
`${TAG16} file-delivery-unreconciled sender=${entry.senderId} sessionId=${sid} tool=${WHATSAPP_SEND_DOCUMENT}`
|
|
9328
|
+
);
|
|
9329
|
+
}
|
|
9330
|
+
if (firedTools.includes(SEND_USER_FILE) && attempts === 0 && !documentWentOut) {
|
|
9331
|
+
console.error(
|
|
9332
|
+
`${TAG16} file-delivery-unreconciled sender=${entry.senderId} sessionId=${sid} tool=${SEND_USER_FILE}`
|
|
9333
|
+
);
|
|
9334
|
+
}
|
|
9335
|
+
}
|
|
9336
|
+
};
|
|
9337
|
+
}
|
|
9338
|
+
|
|
9141
9339
|
// app/lib/channel-pty-bridge/bridge.ts
|
|
9142
9340
|
function tagFor(channel) {
|
|
9143
9341
|
return `[${channel}-adaptor]`;
|
|
9144
9342
|
}
|
|
9343
|
+
function fileDeliveryFor(entry) {
|
|
9344
|
+
return entry.channel === "whatsapp" ? makeWhatsAppFileDelivery(entry) : null;
|
|
9345
|
+
}
|
|
9145
9346
|
function publicIdleMs() {
|
|
9146
9347
|
return Number(process.env.CHANNEL_PTY_IDLE_MS ?? String(5 * 6e4));
|
|
9147
9348
|
}
|
|
@@ -9163,6 +9364,13 @@ async function ensureEntry(input) {
|
|
|
9163
9364
|
existing.followerAbort = startFollower({
|
|
9164
9365
|
entry: existing,
|
|
9165
9366
|
tag,
|
|
9367
|
+
fileDelivery: fileDeliveryFor(existing),
|
|
9368
|
+
// Task 631 — every channel attach resumes an existing session after a
|
|
9369
|
+
// restart (the in-memory index is process memory and empty post-restart).
|
|
9370
|
+
// Suppress replay of the pre-attach JSONL so the first inbound is answered
|
|
9371
|
+
// by a fresh turn, not a historical end_turn, and historical SendUserFile
|
|
9372
|
+
// is not re-sent.
|
|
9373
|
+
suppressResumeReplay: true,
|
|
9166
9374
|
onClose: () => {
|
|
9167
9375
|
existing.followerRunning = false;
|
|
9168
9376
|
}
|
|
@@ -9182,7 +9390,7 @@ async function ensureEntry(input) {
|
|
|
9182
9390
|
});
|
|
9183
9391
|
} else {
|
|
9184
9392
|
console.error(`${tag} route role=${input.role} target=public-spawn senderId=${input.senderId}`);
|
|
9185
|
-
const attachmentDir =
|
|
9393
|
+
const attachmentDir = resolve5(ATTACHMENTS_ROOT, "public", input.senderId);
|
|
9186
9394
|
spawned = await managerSpawn({
|
|
9187
9395
|
senderId: input.senderId,
|
|
9188
9396
|
role: input.role,
|
|
@@ -9223,6 +9431,12 @@ async function ensureEntry(input) {
|
|
|
9223
9431
|
entry.followerAbort = startFollower({
|
|
9224
9432
|
entry,
|
|
9225
9433
|
tag,
|
|
9434
|
+
fileDelivery: fileDeliveryFor(entry),
|
|
9435
|
+
// Task 631 — see the existing-entry branch above. A fresh spawn has an
|
|
9436
|
+
// empty JSONL, so the manager emits the boundary sentinel right after an
|
|
9437
|
+
// empty head and the follower arms immediately; suppression is a no-op for
|
|
9438
|
+
// the cold-spawn first turn and correct for any resume.
|
|
9439
|
+
suppressResumeReplay: true,
|
|
9226
9440
|
onClose: () => {
|
|
9227
9441
|
entry.followerRunning = false;
|
|
9228
9442
|
}
|
|
@@ -9308,12 +9522,12 @@ async function dispatchOnce(input) {
|
|
|
9308
9522
|
});
|
|
9309
9523
|
if (!entry) return { error: "spawn-failed" };
|
|
9310
9524
|
entry.lastInboundAt = Date.now();
|
|
9311
|
-
let
|
|
9525
|
+
let resolve27;
|
|
9312
9526
|
const turnPromise = new Promise((r) => {
|
|
9313
|
-
|
|
9527
|
+
resolve27 = r;
|
|
9314
9528
|
});
|
|
9315
9529
|
const listener = (text) => {
|
|
9316
|
-
|
|
9530
|
+
resolve27(text);
|
|
9317
9531
|
};
|
|
9318
9532
|
entry.subscribers.add(listener);
|
|
9319
9533
|
const writeOk = await writeInput(entry, input.text);
|
|
@@ -9672,19 +9886,18 @@ app2.post("/", async (c) => {
|
|
|
9672
9886
|
var chat_default = app2;
|
|
9673
9887
|
|
|
9674
9888
|
// 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";
|
|
9889
|
+
import { join as join5, resolve as resolve7 } from "path";
|
|
9890
|
+
import { readdirSync as readdirSync2, readFileSync as readFileSync6, existsSync as existsSync4 } from "fs";
|
|
9678
9891
|
|
|
9679
9892
|
// app/lib/whatsapp/login.ts
|
|
9680
9893
|
var import_qrcode = __toESM(require_lib(), 1);
|
|
9681
9894
|
import { randomUUID as randomUUID6 } from "crypto";
|
|
9682
|
-
var
|
|
9895
|
+
var TAG17 = "[whatsapp:login]";
|
|
9683
9896
|
async function renderQrTerminal(qr) {
|
|
9684
9897
|
try {
|
|
9685
9898
|
return await import_qrcode.default.toString(qr, { type: "utf8" });
|
|
9686
9899
|
} catch (err) {
|
|
9687
|
-
console.error(`${
|
|
9900
|
+
console.error(`${TAG17} terminal QR render failed: ${String(err)}`);
|
|
9688
9901
|
return void 0;
|
|
9689
9902
|
}
|
|
9690
9903
|
}
|
|
@@ -9694,7 +9907,7 @@ function closeSocket(sock) {
|
|
|
9694
9907
|
try {
|
|
9695
9908
|
sock.ws?.close?.();
|
|
9696
9909
|
} catch (err) {
|
|
9697
|
-
console.warn(`${
|
|
9910
|
+
console.warn(`${TAG17} socket close error during cleanup: ${String(err)}`);
|
|
9698
9911
|
}
|
|
9699
9912
|
}
|
|
9700
9913
|
function resetActiveLogin(accountId) {
|
|
@@ -9717,7 +9930,7 @@ async function loginConnectionLoop(accountId, login) {
|
|
|
9717
9930
|
const current = activeLogins.get(accountId);
|
|
9718
9931
|
if (current?.id === login.id) {
|
|
9719
9932
|
current.connected = true;
|
|
9720
|
-
console.error(`${
|
|
9933
|
+
console.error(`${TAG17} loginConnectionLoop: connected account=${accountId} attempt=${attempt}`);
|
|
9721
9934
|
}
|
|
9722
9935
|
return;
|
|
9723
9936
|
} catch (err) {
|
|
@@ -9727,7 +9940,7 @@ async function loginConnectionLoop(accountId, login) {
|
|
|
9727
9940
|
if (!classification.shouldRetry || attempt >= LOGIN_MAX_RECONNECTS) {
|
|
9728
9941
|
if (attempt >= LOGIN_MAX_RECONNECTS) {
|
|
9729
9942
|
console.error(
|
|
9730
|
-
`${
|
|
9943
|
+
`${TAG17} login reconnect attempts exhausted (${attempt}/${LOGIN_MAX_RECONNECTS}) \u2014 surfacing error to agent`
|
|
9731
9944
|
);
|
|
9732
9945
|
current.error = `Login failed after ${attempt} reconnect attempts: ${formatError(err)}`;
|
|
9733
9946
|
} else {
|
|
@@ -9739,7 +9952,7 @@ async function loginConnectionLoop(accountId, login) {
|
|
|
9739
9952
|
attempt++;
|
|
9740
9953
|
const delay = LOGIN_RECONNECT_DELAYS[attempt - 1] ?? 8e3;
|
|
9741
9954
|
console.error(
|
|
9742
|
-
`${
|
|
9955
|
+
`${TAG17} status=${classification.statusCode ?? "unknown"} restart required \u2014 reconnecting with saved creds (attempt ${attempt}/${LOGIN_MAX_RECONNECTS}) delay=${delay}ms`
|
|
9743
9956
|
);
|
|
9744
9957
|
closeSocket(current.sock);
|
|
9745
9958
|
await new Promise((r) => setTimeout(r, delay));
|
|
@@ -9750,7 +9963,7 @@ async function loginConnectionLoop(accountId, login) {
|
|
|
9750
9963
|
current.sock = newSock;
|
|
9751
9964
|
} catch (sockErr) {
|
|
9752
9965
|
console.error(
|
|
9753
|
-
`${
|
|
9966
|
+
`${TAG17} reconnect socket creation failed (attempt ${attempt}/${LOGIN_MAX_RECONNECTS}): ${String(sockErr)}`
|
|
9754
9967
|
);
|
|
9755
9968
|
current.error = `Reconnection failed: ${String(sockErr)}`;
|
|
9756
9969
|
return;
|
|
@@ -9764,7 +9977,7 @@ async function startLogin(opts) {
|
|
|
9764
9977
|
const hasAuth = await authExists(authDir);
|
|
9765
9978
|
const selfId = readSelfId(authDir);
|
|
9766
9979
|
console.error(
|
|
9767
|
-
`${
|
|
9980
|
+
`${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
9981
|
);
|
|
9769
9982
|
if (hasAuth && !force) {
|
|
9770
9983
|
const who = selfId.e164 ?? selfId.jid ?? "unknown";
|
|
@@ -9776,7 +9989,7 @@ async function startLogin(opts) {
|
|
|
9776
9989
|
await clearAuth(authDir);
|
|
9777
9990
|
const existing = activeLogins.get(accountId);
|
|
9778
9991
|
if (existing && isLoginFresh(existing) && existing.qrDataUrl && !force) {
|
|
9779
|
-
console.error(`${
|
|
9992
|
+
console.error(`${TAG17} startLogin account=${accountId} guard: returning existing QR (age=${Math.round((Date.now() - existing.startedAt) / 1e3)}s)`);
|
|
9780
9993
|
return {
|
|
9781
9994
|
qrDataUrl: existing.qrDataUrl,
|
|
9782
9995
|
qrRaw: existing.qr,
|
|
@@ -9785,13 +9998,13 @@ async function startLogin(opts) {
|
|
|
9785
9998
|
};
|
|
9786
9999
|
}
|
|
9787
10000
|
if (existing) {
|
|
9788
|
-
console.error(`${
|
|
10001
|
+
console.error(`${TAG17} startLogin account=${accountId} ${force ? "force override" : "stale/no-QR"}, resetting active login`);
|
|
9789
10002
|
}
|
|
9790
10003
|
resetActiveLogin(accountId);
|
|
9791
10004
|
let resolveQr = null;
|
|
9792
10005
|
let rejectQr = null;
|
|
9793
|
-
const qrPromise = new Promise((
|
|
9794
|
-
resolveQr =
|
|
10006
|
+
const qrPromise = new Promise((resolve27, reject) => {
|
|
10007
|
+
resolveQr = resolve27;
|
|
9795
10008
|
rejectQr = reject;
|
|
9796
10009
|
});
|
|
9797
10010
|
const qrTimer = setTimeout(
|
|
@@ -9807,14 +10020,14 @@ async function startLogin(opts) {
|
|
|
9807
10020
|
onQr: (qr2) => {
|
|
9808
10021
|
loginQrCount++;
|
|
9809
10022
|
if (pendingQr) {
|
|
9810
|
-
console.error(`${
|
|
10023
|
+
console.error(`${TAG17} QR rotation #${loginQrCount} received for account=${accountId} \u2014 not forwarded (initial QR already captured)`);
|
|
9811
10024
|
return;
|
|
9812
10025
|
}
|
|
9813
10026
|
pendingQr = qr2;
|
|
9814
10027
|
const current = activeLogins.get(accountId);
|
|
9815
10028
|
if (current && !current.qr) current.qr = qr2;
|
|
9816
10029
|
clearTimeout(qrTimer);
|
|
9817
|
-
console.error(`${
|
|
10030
|
+
console.error(`${TAG17} QR #${loginQrCount} received for account=${accountId} \u2014 forwarding to caller`);
|
|
9818
10031
|
resolveQr?.(qr2);
|
|
9819
10032
|
}
|
|
9820
10033
|
});
|
|
@@ -9834,7 +10047,7 @@ async function startLogin(opts) {
|
|
|
9834
10047
|
activeLogins.set(accountId, login);
|
|
9835
10048
|
if (pendingQr && !login.qr) login.qr = pendingQr;
|
|
9836
10049
|
loginConnectionLoop(accountId, login).catch((err) => {
|
|
9837
|
-
console.error(`${
|
|
10050
|
+
console.error(`${TAG17} loginConnectionLoop unexpected error: ${String(err)}`);
|
|
9838
10051
|
const current = activeLogins.get(accountId);
|
|
9839
10052
|
if (current?.id === login.id) {
|
|
9840
10053
|
current.error = `Unexpected login error: ${String(err)}`;
|
|
@@ -9860,7 +10073,7 @@ async function waitForLogin(opts) {
|
|
|
9860
10073
|
const { accountId, timeoutMs = 6e4 } = opts;
|
|
9861
10074
|
const login = activeLogins.get(accountId);
|
|
9862
10075
|
console.error(
|
|
9863
|
-
`${
|
|
10076
|
+
`${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
10077
|
);
|
|
9865
10078
|
if (!login) {
|
|
9866
10079
|
return { connected: false, message: "No active WhatsApp login in progress." };
|
|
@@ -9873,7 +10086,7 @@ async function waitForLogin(opts) {
|
|
|
9873
10086
|
while (Date.now() < deadline) {
|
|
9874
10087
|
if (login.connected) {
|
|
9875
10088
|
const selfId = readSelfId(login.authDir);
|
|
9876
|
-
console.error(`${
|
|
10089
|
+
console.error(`${TAG17} login complete for account=${accountId} phone=${selfId.e164 ?? "unknown"}`);
|
|
9877
10090
|
const sock = login.sock;
|
|
9878
10091
|
const authDir = login.authDir;
|
|
9879
10092
|
activeLogins.delete(accountId);
|
|
@@ -9893,17 +10106,17 @@ async function waitForLogin(opts) {
|
|
|
9893
10106
|
await new Promise((r) => setTimeout(r, 1e3));
|
|
9894
10107
|
}
|
|
9895
10108
|
const elapsed = Math.round((Date.now() - (deadline - timeoutMs)) / 1e3);
|
|
9896
|
-
console.error(`${
|
|
10109
|
+
console.error(`${TAG17} waitForLogin timeout account=${accountId} elapsed=${elapsed}s \u2014 cleaning up active login`);
|
|
9897
10110
|
resetActiveLogin(accountId);
|
|
9898
10111
|
return { connected: false, message: "Login timed out. Try generating a new QR." };
|
|
9899
10112
|
}
|
|
9900
10113
|
|
|
9901
10114
|
// app/lib/whatsapp/config-persist.ts
|
|
9902
10115
|
import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, existsSync as existsSync3 } from "fs";
|
|
9903
|
-
import { resolve as
|
|
9904
|
-
var
|
|
10116
|
+
import { resolve as resolve6, join as join4 } from "path";
|
|
10117
|
+
var TAG18 = "[whatsapp:config]";
|
|
9905
10118
|
function configPath(accountDir) {
|
|
9906
|
-
return
|
|
10119
|
+
return resolve6(accountDir, "account.json");
|
|
9907
10120
|
}
|
|
9908
10121
|
function readConfig(accountDir) {
|
|
9909
10122
|
const path2 = configPath(accountDir);
|
|
@@ -9918,9 +10131,9 @@ function reloadManagerConfig(accountDir) {
|
|
|
9918
10131
|
try {
|
|
9919
10132
|
const config = readConfig(accountDir);
|
|
9920
10133
|
reloadConfig(config);
|
|
9921
|
-
console.error(`${
|
|
10134
|
+
console.error(`${TAG18} reloaded manager config`);
|
|
9922
10135
|
} catch (err) {
|
|
9923
|
-
console.error(`${
|
|
10136
|
+
console.error(`${TAG18} manager config reload failed: ${String(err)}`);
|
|
9924
10137
|
}
|
|
9925
10138
|
}
|
|
9926
10139
|
var E164_PATTERN = /^\+\d{7,15}$/;
|
|
@@ -9946,25 +10159,25 @@ function persistAfterPairing(accountDir, accountId, selfPhone) {
|
|
|
9946
10159
|
const adminPhones = wa.adminPhones;
|
|
9947
10160
|
if (!adminPhones.includes(normalized)) {
|
|
9948
10161
|
adminPhones.push(normalized);
|
|
9949
|
-
console.error(`${
|
|
10162
|
+
console.error(`${TAG18} added selfPhone=${normalized} to adminPhones`);
|
|
9950
10163
|
}
|
|
9951
10164
|
} else {
|
|
9952
|
-
console.error(`${
|
|
10165
|
+
console.error(`${TAG18} skipping adminPhones \u2014 selfPhone is null account=${accountId}`);
|
|
9953
10166
|
}
|
|
9954
10167
|
const parsed = WhatsAppConfigSchema.safeParse(wa);
|
|
9955
10168
|
if (!parsed.success) {
|
|
9956
10169
|
const msg = parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ");
|
|
9957
|
-
console.error(`${
|
|
10170
|
+
console.error(`${TAG18} validation failed after pairing: ${msg}`);
|
|
9958
10171
|
return { ok: false, error: `Validation failed: ${msg}` };
|
|
9959
10172
|
}
|
|
9960
10173
|
config.whatsapp = parsed.data;
|
|
9961
10174
|
writeConfig(accountDir, config);
|
|
9962
|
-
console.error(`${
|
|
10175
|
+
console.error(`${TAG18} persisted after pairing account=${accountId} phone=${selfPhone ?? "null"}`);
|
|
9963
10176
|
reloadManagerConfig(accountDir);
|
|
9964
10177
|
return { ok: true };
|
|
9965
10178
|
} catch (err) {
|
|
9966
10179
|
const msg = err instanceof Error ? err.message : String(err);
|
|
9967
|
-
console.error(`${
|
|
10180
|
+
console.error(`${TAG18} persist failed account=${accountId}: ${msg}`);
|
|
9968
10181
|
return { ok: false, error: msg };
|
|
9969
10182
|
}
|
|
9970
10183
|
}
|
|
@@ -9994,12 +10207,12 @@ function addAdminPhone(accountDir, phone) {
|
|
|
9994
10207
|
}
|
|
9995
10208
|
config.whatsapp = parsed.data;
|
|
9996
10209
|
writeConfig(accountDir, config);
|
|
9997
|
-
console.error(`${
|
|
10210
|
+
console.error(`${TAG18} added admin phone=${normalized}`);
|
|
9998
10211
|
reloadManagerConfig(accountDir);
|
|
9999
10212
|
return { ok: true, message: `Added ${normalized} as admin phone. Messages from this number will route to the admin agent.` };
|
|
10000
10213
|
} catch (err) {
|
|
10001
10214
|
const msg = err instanceof Error ? err.message : String(err);
|
|
10002
|
-
console.error(`${
|
|
10215
|
+
console.error(`${TAG18} addAdminPhone failed: ${msg}`);
|
|
10003
10216
|
return { ok: false, error: msg };
|
|
10004
10217
|
}
|
|
10005
10218
|
}
|
|
@@ -10027,12 +10240,12 @@ function removeAdminPhone(accountDir, phone) {
|
|
|
10027
10240
|
}
|
|
10028
10241
|
config.whatsapp = parsed.data;
|
|
10029
10242
|
writeConfig(accountDir, config);
|
|
10030
|
-
console.error(`${
|
|
10243
|
+
console.error(`${TAG18} removed admin phone=${normalized}`);
|
|
10031
10244
|
reloadManagerConfig(accountDir);
|
|
10032
10245
|
return { ok: true, message: `Removed ${normalized} from admin phones. Messages from this number will now route to the public agent.` };
|
|
10033
10246
|
} catch (err) {
|
|
10034
10247
|
const msg = err instanceof Error ? err.message : String(err);
|
|
10035
|
-
console.error(`${
|
|
10248
|
+
console.error(`${TAG18} removeAdminPhone failed: ${msg}`);
|
|
10036
10249
|
return { ok: false, error: msg };
|
|
10037
10250
|
}
|
|
10038
10251
|
}
|
|
@@ -10070,12 +10283,12 @@ function setPublicAgent(accountDir, slug) {
|
|
|
10070
10283
|
}
|
|
10071
10284
|
config.whatsapp = parsed.data;
|
|
10072
10285
|
writeConfig(accountDir, config);
|
|
10073
|
-
console.error(`${
|
|
10286
|
+
console.error(`${TAG18} publicAgent set to ${trimmed}`);
|
|
10074
10287
|
reloadManagerConfig(accountDir);
|
|
10075
10288
|
return { ok: true, message: `Public agent set to "${trimmed}". WhatsApp messages from non-admin phones will be handled by this agent.` };
|
|
10076
10289
|
} catch (err) {
|
|
10077
10290
|
const msg = err instanceof Error ? err.message : String(err);
|
|
10078
|
-
console.error(`${
|
|
10291
|
+
console.error(`${TAG18} setPublicAgent failed: ${msg}`);
|
|
10079
10292
|
return { ok: false, error: msg };
|
|
10080
10293
|
}
|
|
10081
10294
|
}
|
|
@@ -10139,12 +10352,12 @@ function setGroupPublicAgent(accountDir, accountId, groupJid, slug) {
|
|
|
10139
10352
|
}
|
|
10140
10353
|
config.whatsapp = parsed.data;
|
|
10141
10354
|
writeConfig(accountDir, config);
|
|
10142
|
-
console.error(`${
|
|
10355
|
+
console.error(`${TAG18} setGroupPublicAgent account=${trimmedAccount} groupJid=${trimmedGroup} slug=${trimmedSlug}`);
|
|
10143
10356
|
reloadManagerConfig(accountDir);
|
|
10144
10357
|
return { ok: true, message: `Per-group public agent set to "${trimmedSlug}" for group ${trimmedGroup}.` };
|
|
10145
10358
|
} catch (err) {
|
|
10146
10359
|
const msg = err instanceof Error ? err.message : String(err);
|
|
10147
|
-
console.error(`${
|
|
10360
|
+
console.error(`${TAG18} setGroupPublicAgent failed: ${msg}`);
|
|
10148
10361
|
return { ok: false, error: msg };
|
|
10149
10362
|
}
|
|
10150
10363
|
}
|
|
@@ -10171,12 +10384,12 @@ function unsetGroupPublicAgent(accountDir, accountId, groupJid) {
|
|
|
10171
10384
|
}
|
|
10172
10385
|
config.whatsapp = parsed.data;
|
|
10173
10386
|
writeConfig(accountDir, config);
|
|
10174
|
-
console.error(`${
|
|
10387
|
+
console.error(`${TAG18} unsetGroupPublicAgent account=${trimmedAccount} groupJid=${trimmedGroup}`);
|
|
10175
10388
|
reloadManagerConfig(accountDir);
|
|
10176
10389
|
return { ok: true, message: `Per-group public agent override removed for group ${trimmedGroup}.` };
|
|
10177
10390
|
} catch (err) {
|
|
10178
10391
|
const msg = err instanceof Error ? err.message : String(err);
|
|
10179
|
-
console.error(`${
|
|
10392
|
+
console.error(`${TAG18} unsetGroupPublicAgent failed: ${msg}`);
|
|
10180
10393
|
return { ok: false, error: msg };
|
|
10181
10394
|
}
|
|
10182
10395
|
}
|
|
@@ -10197,17 +10410,17 @@ function updateConfig(accountDir, fields) {
|
|
|
10197
10410
|
const parsed = WhatsAppConfigSchema.safeParse(wa);
|
|
10198
10411
|
if (!parsed.success) {
|
|
10199
10412
|
const msg = parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ");
|
|
10200
|
-
console.error(`${
|
|
10413
|
+
console.error(`${TAG18} update validation failed: ${msg}`);
|
|
10201
10414
|
return { ok: false, error: `Validation failed: ${msg}` };
|
|
10202
10415
|
}
|
|
10203
10416
|
config.whatsapp = parsed.data;
|
|
10204
10417
|
writeConfig(accountDir, config);
|
|
10205
|
-
console.error(`${
|
|
10418
|
+
console.error(`${TAG18} updated fields=[${fieldNames.join(",")}]`);
|
|
10206
10419
|
reloadManagerConfig(accountDir);
|
|
10207
10420
|
return { ok: true, message: `Updated WhatsApp config: ${fieldNames.join(", ")}.` };
|
|
10208
10421
|
} catch (err) {
|
|
10209
10422
|
const msg = err instanceof Error ? err.message : String(err);
|
|
10210
|
-
console.error(`${
|
|
10423
|
+
console.error(`${TAG18} updateConfig failed: ${msg}`);
|
|
10211
10424
|
return { ok: false, error: msg };
|
|
10212
10425
|
}
|
|
10213
10426
|
}
|
|
@@ -10333,17 +10546,17 @@ function serializeWhatsAppSchema() {
|
|
|
10333
10546
|
}
|
|
10334
10547
|
|
|
10335
10548
|
// server/routes/whatsapp.ts
|
|
10336
|
-
var
|
|
10549
|
+
var TAG19 = "[whatsapp:api]";
|
|
10337
10550
|
var PLATFORM_ROOT4 = process.env.MAXY_PLATFORM_ROOT || "";
|
|
10338
10551
|
var app3 = new Hono();
|
|
10339
10552
|
app3.get("/status", (c) => {
|
|
10340
10553
|
try {
|
|
10341
10554
|
const status = getStatus();
|
|
10342
10555
|
const summary = status.map((a) => `${a.accountId}:${a.connected ? "up" : "down"}`).join(", ");
|
|
10343
|
-
console.error(`${
|
|
10556
|
+
console.error(`${TAG19} status accounts=${status.length} [${summary}]`);
|
|
10344
10557
|
return c.json({ accounts: status });
|
|
10345
10558
|
} catch (err) {
|
|
10346
|
-
console.error(`${
|
|
10559
|
+
console.error(`${TAG19} status error: ${String(err)}`);
|
|
10347
10560
|
return c.json({ error: String(err) }, 500);
|
|
10348
10561
|
}
|
|
10349
10562
|
});
|
|
@@ -10354,10 +10567,10 @@ app3.post("/login/start", async (c) => {
|
|
|
10354
10567
|
const force = body.force ?? false;
|
|
10355
10568
|
const authDir = join5(MAXY_DIR, "credentials", "whatsapp", accountId);
|
|
10356
10569
|
const result = await startLogin({ accountId, authDir, force });
|
|
10357
|
-
console.error(`${
|
|
10570
|
+
console.error(`${TAG19} login/start result account=${accountId} hasQr=${!!result.qrRaw}${result.selfPhone ? ` phone=${result.selfPhone}` : ""}`);
|
|
10358
10571
|
return c.json(result);
|
|
10359
10572
|
} catch (err) {
|
|
10360
|
-
console.error(`${
|
|
10573
|
+
console.error(`${TAG19} login/start error: ${String(err)}`);
|
|
10361
10574
|
return c.json({ error: String(err) }, 500);
|
|
10362
10575
|
}
|
|
10363
10576
|
});
|
|
@@ -10372,7 +10585,7 @@ app3.post("/login/wait", async (c) => {
|
|
|
10372
10585
|
try {
|
|
10373
10586
|
await registerLoginSocket(accountId, result.sock, result.authDir);
|
|
10374
10587
|
} catch (regErr) {
|
|
10375
|
-
console.error(`${
|
|
10588
|
+
console.error(`${TAG19} registerLoginSocket failed account=${accountId}: ${String(regErr)}`);
|
|
10376
10589
|
}
|
|
10377
10590
|
try {
|
|
10378
10591
|
const account = resolveAccount();
|
|
@@ -10380,16 +10593,16 @@ app3.post("/login/wait", async (c) => {
|
|
|
10380
10593
|
const persistResult = persistAfterPairing(account.accountDir, accountId, result.selfPhone ?? null);
|
|
10381
10594
|
configPersisted = persistResult.ok;
|
|
10382
10595
|
if (!persistResult.ok) {
|
|
10383
|
-
console.error(`${
|
|
10596
|
+
console.error(`${TAG19} config persist failed account=${accountId}: ${persistResult.error}`);
|
|
10384
10597
|
}
|
|
10385
10598
|
} else {
|
|
10386
|
-
console.error(`${
|
|
10599
|
+
console.error(`${TAG19} config persist skipped \u2014 no account resolved`);
|
|
10387
10600
|
}
|
|
10388
10601
|
} catch (persistErr) {
|
|
10389
|
-
console.error(`${
|
|
10602
|
+
console.error(`${TAG19} config persist error account=${accountId}: ${String(persistErr)}`);
|
|
10390
10603
|
}
|
|
10391
10604
|
}
|
|
10392
|
-
console.error(`${
|
|
10605
|
+
console.error(`${TAG19} login/wait result account=${accountId} connected=${result.connected}${result.selfPhone ? ` phone=${result.selfPhone}` : ""} configPersisted=${configPersisted}`);
|
|
10393
10606
|
return c.json({
|
|
10394
10607
|
connected: result.connected,
|
|
10395
10608
|
message: result.message,
|
|
@@ -10397,7 +10610,7 @@ app3.post("/login/wait", async (c) => {
|
|
|
10397
10610
|
configPersisted
|
|
10398
10611
|
});
|
|
10399
10612
|
} catch (err) {
|
|
10400
|
-
console.error(`${
|
|
10613
|
+
console.error(`${TAG19} login/wait error: ${String(err)}`);
|
|
10401
10614
|
return c.json({ error: String(err) }, 500);
|
|
10402
10615
|
}
|
|
10403
10616
|
});
|
|
@@ -10408,7 +10621,7 @@ app3.post("/disconnect", async (c) => {
|
|
|
10408
10621
|
await stopConnection(accountId);
|
|
10409
10622
|
return c.json({ disconnected: true, accountId });
|
|
10410
10623
|
} catch (err) {
|
|
10411
|
-
console.error(`${
|
|
10624
|
+
console.error(`${TAG19} disconnect error: ${String(err)}`);
|
|
10412
10625
|
return c.json({ error: String(err) }, 500);
|
|
10413
10626
|
}
|
|
10414
10627
|
});
|
|
@@ -10419,7 +10632,7 @@ app3.post("/reconnect", async (c) => {
|
|
|
10419
10632
|
await startConnection(accountId);
|
|
10420
10633
|
return c.json({ reconnecting: true, accountId });
|
|
10421
10634
|
} catch (err) {
|
|
10422
|
-
console.error(`${
|
|
10635
|
+
console.error(`${TAG19} reconnect error: ${String(err)}`);
|
|
10423
10636
|
return c.json({ error: String(err) }, 500);
|
|
10424
10637
|
}
|
|
10425
10638
|
});
|
|
@@ -10438,7 +10651,7 @@ app3.post("/send", async (c) => {
|
|
|
10438
10651
|
const result = await sendTextMessage(sock, to, text, { accountId });
|
|
10439
10652
|
return c.json(result);
|
|
10440
10653
|
} catch (err) {
|
|
10441
|
-
console.error(`${
|
|
10654
|
+
console.error(`${TAG19} send error: ${String(err)}`);
|
|
10442
10655
|
return c.json({ error: String(err) }, 500);
|
|
10443
10656
|
}
|
|
10444
10657
|
});
|
|
@@ -10459,7 +10672,7 @@ app3.post("/config", async (c) => {
|
|
|
10459
10672
|
return c.json({ ok: false, error: 'Missing required field "phone" (E.164 format, e.g. +441234567890).' }, 400);
|
|
10460
10673
|
}
|
|
10461
10674
|
const result = addAdminPhone(account.accountDir, phone);
|
|
10462
|
-
console.error(`${
|
|
10675
|
+
console.error(`${TAG19} config action=add-admin-phone phone=${phone} ok=${result.ok}`);
|
|
10463
10676
|
return c.json(result, result.ok ? 200 : 400);
|
|
10464
10677
|
}
|
|
10465
10678
|
case "remove-admin-phone": {
|
|
@@ -10467,12 +10680,12 @@ app3.post("/config", async (c) => {
|
|
|
10467
10680
|
return c.json({ ok: false, error: 'Missing required field "phone".' }, 400);
|
|
10468
10681
|
}
|
|
10469
10682
|
const result = removeAdminPhone(account.accountDir, phone);
|
|
10470
|
-
console.error(`${
|
|
10683
|
+
console.error(`${TAG19} config action=remove-admin-phone phone=${phone} ok=${result.ok}`);
|
|
10471
10684
|
return c.json(result, result.ok ? 200 : 400);
|
|
10472
10685
|
}
|
|
10473
10686
|
case "list-admin-phones": {
|
|
10474
10687
|
const phones = readAdminPhones(account.accountDir);
|
|
10475
|
-
console.error(`${
|
|
10688
|
+
console.error(`${TAG19} config action=list-admin-phones count=${phones.length}`);
|
|
10476
10689
|
return c.json({ ok: true, phones });
|
|
10477
10690
|
}
|
|
10478
10691
|
case "set-public-agent": {
|
|
@@ -10480,14 +10693,14 @@ app3.post("/config", async (c) => {
|
|
|
10480
10693
|
return c.json({ ok: false, error: 'Missing required field "slug" (the agent directory name, e.g. "my-agent").' }, 400);
|
|
10481
10694
|
}
|
|
10482
10695
|
const result = setPublicAgent(account.accountDir, slug);
|
|
10483
|
-
console.error(`${
|
|
10696
|
+
console.error(`${TAG19} config action=set-public-agent slug=${slug} ok=${result.ok}`);
|
|
10484
10697
|
return c.json(result, result.ok ? 200 : 400);
|
|
10485
10698
|
}
|
|
10486
10699
|
case "get-public-agent": {
|
|
10487
10700
|
const targetAccount = typeof accountId === "string" && accountId.trim() ? accountId.trim() : "default";
|
|
10488
10701
|
const targetGroup = typeof groupJid === "string" && groupJid.trim() ? groupJid.trim() : void 0;
|
|
10489
10702
|
const resolved = resolvePublicAgent(account.accountDir, { accountId: targetAccount, groupJid: targetGroup });
|
|
10490
|
-
console.error(`${
|
|
10703
|
+
console.error(`${TAG19} config action=get-public-agent accountId=${targetAccount} groupJid=${targetGroup ?? "none"} slug=${resolved?.slug ?? "none"} source=${resolved?.source ?? "none"}`);
|
|
10491
10704
|
return c.json({ ok: true, slug: resolved?.slug ?? null, source: resolved?.source ?? null });
|
|
10492
10705
|
}
|
|
10493
10706
|
case "set-group-public-agent": {
|
|
@@ -10499,7 +10712,7 @@ app3.post("/config", async (c) => {
|
|
|
10499
10712
|
}
|
|
10500
10713
|
const targetAccount = typeof accountId === "string" && accountId.trim() ? accountId.trim() : "default";
|
|
10501
10714
|
const result = setGroupPublicAgent(account.accountDir, targetAccount, groupJid, slug);
|
|
10502
|
-
console.error(`${
|
|
10715
|
+
console.error(`${TAG19} config action=set-group-public-agent accountId=${targetAccount} groupJid=${groupJid} slug=${slug} ok=${result.ok}`);
|
|
10503
10716
|
return c.json(result, result.ok ? 200 : 400);
|
|
10504
10717
|
}
|
|
10505
10718
|
case "unset-group-public-agent": {
|
|
@@ -10508,43 +10721,43 @@ app3.post("/config", async (c) => {
|
|
|
10508
10721
|
}
|
|
10509
10722
|
const targetAccount = typeof accountId === "string" && accountId.trim() ? accountId.trim() : "default";
|
|
10510
10723
|
const result = unsetGroupPublicAgent(account.accountDir, targetAccount, groupJid);
|
|
10511
|
-
console.error(`${
|
|
10724
|
+
console.error(`${TAG19} config action=unset-group-public-agent accountId=${targetAccount} groupJid=${groupJid} ok=${result.ok}`);
|
|
10512
10725
|
return c.json(result, result.ok ? 200 : 400);
|
|
10513
10726
|
}
|
|
10514
10727
|
case "list-public-agents": {
|
|
10515
|
-
const agentsDir =
|
|
10728
|
+
const agentsDir = resolve7(account.accountDir, "agents");
|
|
10516
10729
|
const agents = [];
|
|
10517
10730
|
if (existsSync4(agentsDir)) {
|
|
10518
10731
|
try {
|
|
10519
10732
|
const entries = readdirSync2(agentsDir, { withFileTypes: true });
|
|
10520
10733
|
for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
10521
10734
|
if (!entry.isDirectory() || entry.name === "admin") continue;
|
|
10522
|
-
const configPath2 =
|
|
10735
|
+
const configPath2 = resolve7(agentsDir, entry.name, "config.json");
|
|
10523
10736
|
if (!existsSync4(configPath2)) continue;
|
|
10524
10737
|
try {
|
|
10525
10738
|
const config = JSON.parse(readFileSync6(configPath2, "utf-8"));
|
|
10526
10739
|
agents.push({ slug: entry.name, displayName: config.displayName ?? entry.name });
|
|
10527
10740
|
} catch {
|
|
10528
|
-
console.error(`${
|
|
10741
|
+
console.error(`${TAG19} config action=list-public-agents error="failed to parse config.json for agent ${entry.name}" \u2014 skipping`);
|
|
10529
10742
|
}
|
|
10530
10743
|
}
|
|
10531
10744
|
} catch (err) {
|
|
10532
|
-
console.error(`${
|
|
10745
|
+
console.error(`${TAG19} config action=list-public-agents error="failed to scan agents directory: ${String(err)}"`);
|
|
10533
10746
|
}
|
|
10534
10747
|
}
|
|
10535
|
-
console.error(`${
|
|
10748
|
+
console.error(`${TAG19} config action=list-public-agents count=${agents.length}`);
|
|
10536
10749
|
return c.json({ ok: true, agents });
|
|
10537
10750
|
}
|
|
10538
10751
|
case "schema": {
|
|
10539
10752
|
const text = serializeWhatsAppSchema();
|
|
10540
|
-
console.error(`${
|
|
10753
|
+
console.error(`${TAG19} config action=schema`);
|
|
10541
10754
|
return c.json({ ok: true, text });
|
|
10542
10755
|
}
|
|
10543
10756
|
case "list-groups": {
|
|
10544
10757
|
const groupAccountId = accountId ?? "default";
|
|
10545
10758
|
const sock = getSocket(groupAccountId);
|
|
10546
10759
|
if (!sock) {
|
|
10547
|
-
console.error(`${
|
|
10760
|
+
console.error(`${TAG19} config action=list-groups error="not connected" accountId=${groupAccountId}`);
|
|
10548
10761
|
return c.json({ ok: false, error: `WhatsApp account "${groupAccountId}" is not connected. Connect first, then retry.` });
|
|
10549
10762
|
}
|
|
10550
10763
|
try {
|
|
@@ -10554,10 +10767,10 @@ app3.post("/config", async (c) => {
|
|
|
10554
10767
|
name: g.subject ?? g.id,
|
|
10555
10768
|
participantCount: Array.isArray(g.participants) ? g.participants.length : 0
|
|
10556
10769
|
}));
|
|
10557
|
-
console.error(`${
|
|
10770
|
+
console.error(`${TAG19} config action=list-groups count=${groups.length} accountId=${groupAccountId}`);
|
|
10558
10771
|
return c.json({ ok: true, groups });
|
|
10559
10772
|
} catch (err) {
|
|
10560
|
-
console.error(`${
|
|
10773
|
+
console.error(`${TAG19} config action=list-groups error="${String(err)}" accountId=${groupAccountId}`);
|
|
10561
10774
|
return c.json({ ok: false, error: `Failed to fetch groups: ${String(err)}` });
|
|
10562
10775
|
}
|
|
10563
10776
|
}
|
|
@@ -10567,12 +10780,12 @@ app3.post("/config", async (c) => {
|
|
|
10567
10780
|
}
|
|
10568
10781
|
const result = updateConfig(account.accountDir, fields);
|
|
10569
10782
|
const fieldNames = Object.keys(fields);
|
|
10570
|
-
console.error(`${
|
|
10783
|
+
console.error(`${TAG19} config action=update-config fields=[${fieldNames.join(",")}] ok=${result.ok}`);
|
|
10571
10784
|
return c.json(result, result.ok ? 200 : 400);
|
|
10572
10785
|
}
|
|
10573
10786
|
case "get-config": {
|
|
10574
10787
|
const waConfig = getConfig(account.accountDir);
|
|
10575
|
-
console.error(`${
|
|
10788
|
+
console.error(`${TAG19} config action=get-config`);
|
|
10576
10789
|
return c.json({ ok: true, config: waConfig });
|
|
10577
10790
|
}
|
|
10578
10791
|
default:
|
|
@@ -10582,7 +10795,7 @@ app3.post("/config", async (c) => {
|
|
|
10582
10795
|
);
|
|
10583
10796
|
}
|
|
10584
10797
|
} catch (err) {
|
|
10585
|
-
console.error(`${
|
|
10798
|
+
console.error(`${TAG19} config error: ${String(err)}`);
|
|
10586
10799
|
return c.json({ ok: false, error: String(err) }, 500);
|
|
10587
10800
|
}
|
|
10588
10801
|
});
|
|
@@ -10591,55 +10804,18 @@ app3.post("/send-document", async (c) => {
|
|
|
10591
10804
|
const body = await c.req.json();
|
|
10592
10805
|
const { to, filePath, caption, maxyAccountId } = body;
|
|
10593
10806
|
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);
|
|
10807
|
+
const result = await sendWhatsAppDocument({
|
|
10808
|
+
to,
|
|
10809
|
+
filePath,
|
|
10810
|
+
caption,
|
|
10811
|
+
accountId,
|
|
10812
|
+
maxyAccountId,
|
|
10813
|
+
platformRoot: PLATFORM_ROOT4
|
|
10814
|
+
});
|
|
10815
|
+
if (!result.ok) return c.json({ error: result.error }, result.status);
|
|
10816
|
+
return c.json({ success: true, messageId: result.messageId });
|
|
10641
10817
|
} catch (err) {
|
|
10642
|
-
console.error(`${
|
|
10818
|
+
console.error(`${TAG19} send-document error: ${String(err)}`);
|
|
10643
10819
|
return c.json({ error: String(err) }, 500);
|
|
10644
10820
|
}
|
|
10645
10821
|
});
|
|
@@ -10649,11 +10825,11 @@ app3.get("/activity", (c) => {
|
|
|
10649
10825
|
const result = getChannelActivity(accountId);
|
|
10650
10826
|
const total = result.accounts.reduce((sum, a) => sum + a.total, 0);
|
|
10651
10827
|
console.error(
|
|
10652
|
-
`${
|
|
10828
|
+
`${TAG19} activity accounts=${result.accounts.length} total=${total} recentEvents=${result.recentEvents.length}` + (accountId ? ` filter=${accountId}` : "")
|
|
10653
10829
|
);
|
|
10654
10830
|
return c.json(result);
|
|
10655
10831
|
} catch (err) {
|
|
10656
|
-
console.error(`${
|
|
10832
|
+
console.error(`${TAG19} activity error: ${String(err)}`);
|
|
10657
10833
|
return c.json({ error: String(err) }, 500);
|
|
10658
10834
|
}
|
|
10659
10835
|
});
|
|
@@ -10672,10 +10848,10 @@ app3.get("/conversations", (c) => {
|
|
|
10672
10848
|
};
|
|
10673
10849
|
});
|
|
10674
10850
|
conversations.sort((a, b) => (b.lastMessageTimestamp ?? 0) - (a.lastMessageTimestamp ?? 0));
|
|
10675
|
-
console.error(`${
|
|
10851
|
+
console.error(`${TAG19} conversations account=${accountId} count=${conversations.length}`);
|
|
10676
10852
|
return c.json({ conversations });
|
|
10677
10853
|
} catch (err) {
|
|
10678
|
-
console.error(`${
|
|
10854
|
+
console.error(`${TAG19} conversations error: ${String(err)}`);
|
|
10679
10855
|
return c.json({ error: String(err) }, 500);
|
|
10680
10856
|
}
|
|
10681
10857
|
});
|
|
@@ -10690,10 +10866,10 @@ app3.get("/messages", (c) => {
|
|
|
10690
10866
|
const limit = limitParam ? parseInt(limitParam, 10) : void 0;
|
|
10691
10867
|
const effectiveLimit = limit && !Number.isNaN(limit) && limit > 0 ? limit : void 0;
|
|
10692
10868
|
const messages = getMessages(accountId, jid, effectiveLimit);
|
|
10693
|
-
console.error(`${
|
|
10869
|
+
console.error(`${TAG19} messages account=${accountId} jid=${jid} limit=${effectiveLimit ?? "all"} returned=${messages.length}`);
|
|
10694
10870
|
return c.json({ messages });
|
|
10695
10871
|
} catch (err) {
|
|
10696
|
-
console.error(`${
|
|
10872
|
+
console.error(`${TAG19} messages error: ${String(err)}`);
|
|
10697
10873
|
return c.json({ error: String(err) }, 500);
|
|
10698
10874
|
}
|
|
10699
10875
|
});
|
|
@@ -10774,7 +10950,7 @@ app3.get("/conversation-graph-state", async (c) => {
|
|
|
10774
10950
|
}
|
|
10775
10951
|
} catch (err) {
|
|
10776
10952
|
const msg = err instanceof Error ? err.message : String(err);
|
|
10777
|
-
console.error(`${
|
|
10953
|
+
console.error(`${TAG19} conversation-graph-state ERR cacheKey=${cacheKey} reason=${msg}`);
|
|
10778
10954
|
return c.json({ error: `Graph query failed: ${msg}`, cacheKey, cypher: cypher.trim() }, 500);
|
|
10779
10955
|
}
|
|
10780
10956
|
const ms = Date.now() - t0;
|
|
@@ -10787,7 +10963,7 @@ app3.get("/conversation-graph-state", async (c) => {
|
|
|
10787
10963
|
ms
|
|
10788
10964
|
});
|
|
10789
10965
|
} catch (err) {
|
|
10790
|
-
console.error(`${
|
|
10966
|
+
console.error(`${TAG19} conversation-graph-state error: ${String(err)}`);
|
|
10791
10967
|
return c.json({ error: String(err) }, 500);
|
|
10792
10968
|
}
|
|
10793
10969
|
});
|
|
@@ -10799,12 +10975,12 @@ app3.get("/group-info", async (c) => {
|
|
|
10799
10975
|
return c.json({ error: "Missing required parameter: jid" }, 400);
|
|
10800
10976
|
}
|
|
10801
10977
|
if (!isGroupJid(jid)) {
|
|
10802
|
-
console.error(`${
|
|
10978
|
+
console.error(`${TAG19} group-info error="not a group JID" jid=${jid} account=${accountId}`);
|
|
10803
10979
|
return c.json({ error: `"${jid}" is not a group JID. Group JIDs end with @g.us.` }, 400);
|
|
10804
10980
|
}
|
|
10805
10981
|
const sock = getSocket(accountId);
|
|
10806
10982
|
if (!sock) {
|
|
10807
|
-
console.error(`${
|
|
10983
|
+
console.error(`${TAG19} group-info error="not connected" account=${accountId}`);
|
|
10808
10984
|
return c.json({ error: `WhatsApp account "${accountId}" is not connected. Connect first, then retry.` }, 400);
|
|
10809
10985
|
}
|
|
10810
10986
|
const meta = await sock.groupMetadata(jid);
|
|
@@ -10817,10 +10993,10 @@ app3.get("/group-info", async (c) => {
|
|
|
10817
10993
|
participantCount: meta.participants.length,
|
|
10818
10994
|
participants: meta.participants.map((p) => ({ jid: p.id, admin: p.admin ?? null }))
|
|
10819
10995
|
};
|
|
10820
|
-
console.error(`${
|
|
10996
|
+
console.error(`${TAG19} group-info jid=${jid} subject="${meta.subject}" participants=${meta.participants.length} account=${accountId}`);
|
|
10821
10997
|
return c.json(result);
|
|
10822
10998
|
} catch (err) {
|
|
10823
|
-
console.error(`${
|
|
10999
|
+
console.error(`${TAG19} group-info error="${String(err)}" jid=${jid} account=${accountId}`);
|
|
10824
11000
|
return c.json({ error: `Failed to fetch group info: ${String(err)}` }, 500);
|
|
10825
11001
|
}
|
|
10826
11002
|
});
|
|
@@ -11605,7 +11781,7 @@ var session_default = app6;
|
|
|
11605
11781
|
|
|
11606
11782
|
// server/routes/admin/logs.ts
|
|
11607
11783
|
import { existsSync as existsSync10, readdirSync as readdirSync5, readFileSync as readFileSync10, statSync as statSync5 } from "fs";
|
|
11608
|
-
import { resolve as
|
|
11784
|
+
import { resolve as resolve8, basename as basename2 } from "path";
|
|
11609
11785
|
|
|
11610
11786
|
// app/lib/logs-read-resolve.ts
|
|
11611
11787
|
import { existsSync as existsSync9 } from "fs";
|
|
@@ -11632,7 +11808,7 @@ app7.get("/", async (c) => {
|
|
|
11632
11808
|
const cacheKeyParam = c.req.query("cacheKey");
|
|
11633
11809
|
const download = c.req.query("download") === "1";
|
|
11634
11810
|
const account = resolveAccount();
|
|
11635
|
-
const accountLogDir = account ?
|
|
11811
|
+
const accountLogDir = account ? resolve8(account.accountDir, "logs") : null;
|
|
11636
11812
|
const logDirs = [];
|
|
11637
11813
|
if (accountLogDir) logDirs.push(accountLogDir);
|
|
11638
11814
|
logDirs.push(LOG_DIR);
|
|
@@ -11640,7 +11816,7 @@ app7.get("/", async (c) => {
|
|
|
11640
11816
|
const safe = basename2(fileParam);
|
|
11641
11817
|
const searched = [];
|
|
11642
11818
|
for (const dir of logDirs) {
|
|
11643
|
-
const filePath =
|
|
11819
|
+
const filePath = resolve8(dir, safe);
|
|
11644
11820
|
searched.push(filePath);
|
|
11645
11821
|
try {
|
|
11646
11822
|
const buffer = readFileSync10(filePath);
|
|
@@ -11752,10 +11928,10 @@ app7.get("/", async (c) => {
|
|
|
11752
11928
|
console.warn(`[admin/logs] readdir-fail dir=${dir} reason=${reason}`);
|
|
11753
11929
|
continue;
|
|
11754
11930
|
}
|
|
11755
|
-
files.filter((f) => !seen.has(f)).map((f) => ({ name: f, mtime: statSync5(
|
|
11931
|
+
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
11932
|
seen.add(name);
|
|
11757
11933
|
try {
|
|
11758
|
-
const content = readFileSync10(
|
|
11934
|
+
const content = readFileSync10(resolve8(dir, name));
|
|
11759
11935
|
const tail = content.length > TAIL_BYTES ? content.subarray(content.length - TAIL_BYTES).toString("utf-8") : content.toString("utf-8");
|
|
11760
11936
|
logs[name] = tail.trim() || "(empty)";
|
|
11761
11937
|
} catch (err) {
|
|
@@ -11795,7 +11971,7 @@ var claude_info_default = app8;
|
|
|
11795
11971
|
// server/routes/admin/attachment.ts
|
|
11796
11972
|
import { readFile as readFile2, readdir } from "fs/promises";
|
|
11797
11973
|
import { existsSync as existsSync11 } from "fs";
|
|
11798
|
-
import { resolve as
|
|
11974
|
+
import { resolve as resolve9 } from "path";
|
|
11799
11975
|
var app9 = new Hono();
|
|
11800
11976
|
app9.get("/:attachmentId", requireAdminSession, async (c) => {
|
|
11801
11977
|
const attachmentId = c.req.param("attachmentId");
|
|
@@ -11807,11 +11983,11 @@ app9.get("/:attachmentId", requireAdminSession, async (c) => {
|
|
|
11807
11983
|
if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/.test(attachmentId)) {
|
|
11808
11984
|
return new Response("Not found", { status: 404 });
|
|
11809
11985
|
}
|
|
11810
|
-
const dir =
|
|
11986
|
+
const dir = resolve9(ATTACHMENTS_ROOT, accountId, attachmentId);
|
|
11811
11987
|
if (!existsSync11(dir)) {
|
|
11812
11988
|
return new Response("Not found", { status: 404 });
|
|
11813
11989
|
}
|
|
11814
|
-
const metaPath =
|
|
11990
|
+
const metaPath = resolve9(dir, `${attachmentId}.meta.json`);
|
|
11815
11991
|
if (!existsSync11(metaPath)) {
|
|
11816
11992
|
return new Response("Not found", { status: 404 });
|
|
11817
11993
|
}
|
|
@@ -11826,7 +12002,7 @@ app9.get("/:attachmentId", requireAdminSession, async (c) => {
|
|
|
11826
12002
|
if (!dataFile) {
|
|
11827
12003
|
return new Response("Not found", { status: 404 });
|
|
11828
12004
|
}
|
|
11829
|
-
const filePath =
|
|
12005
|
+
const filePath = resolve9(dir, dataFile);
|
|
11830
12006
|
const buffer = await readFile2(filePath);
|
|
11831
12007
|
return new Response(new Uint8Array(buffer), {
|
|
11832
12008
|
headers: {
|
|
@@ -11839,13 +12015,13 @@ app9.get("/:attachmentId", requireAdminSession, async (c) => {
|
|
|
11839
12015
|
var attachment_default = app9;
|
|
11840
12016
|
|
|
11841
12017
|
// server/routes/admin/agents.ts
|
|
11842
|
-
import { resolve as
|
|
12018
|
+
import { resolve as resolve10 } from "path";
|
|
11843
12019
|
import { readdirSync as readdirSync6, readFileSync as readFileSync11, existsSync as existsSync12, rmSync } from "fs";
|
|
11844
12020
|
var app10 = new Hono();
|
|
11845
12021
|
app10.get("/", (c) => {
|
|
11846
12022
|
const account = resolveAccount();
|
|
11847
12023
|
if (!account) return c.json({ agents: [] });
|
|
11848
|
-
const agentsDir =
|
|
12024
|
+
const agentsDir = resolve10(account.accountDir, "agents");
|
|
11849
12025
|
if (!existsSync12(agentsDir)) return c.json({ agents: [] });
|
|
11850
12026
|
const agents = [];
|
|
11851
12027
|
try {
|
|
@@ -11853,7 +12029,7 @@ app10.get("/", (c) => {
|
|
|
11853
12029
|
for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
11854
12030
|
if (!entry.isDirectory()) continue;
|
|
11855
12031
|
if (entry.name === "admin") continue;
|
|
11856
|
-
const configPath2 =
|
|
12032
|
+
const configPath2 = resolve10(agentsDir, entry.name, "config.json");
|
|
11857
12033
|
if (!existsSync12(configPath2)) continue;
|
|
11858
12034
|
try {
|
|
11859
12035
|
const config = JSON.parse(readFileSync11(configPath2, "utf-8"));
|
|
@@ -11882,7 +12058,7 @@ app10.delete("/:slug", async (c) => {
|
|
|
11882
12058
|
if (slug.includes("/") || slug.includes("..") || slug.includes("\\")) {
|
|
11883
12059
|
return c.json({ error: "Invalid agent slug" }, 400);
|
|
11884
12060
|
}
|
|
11885
|
-
const agentDir =
|
|
12061
|
+
const agentDir = resolve10(account.accountDir, "agents", slug);
|
|
11886
12062
|
if (!existsSync12(agentDir)) {
|
|
11887
12063
|
return c.json({ error: "Agent not found" }, 404);
|
|
11888
12064
|
}
|
|
@@ -11912,7 +12088,7 @@ app10.post("/:slug/project", async (c) => {
|
|
|
11912
12088
|
if (slug.includes("/") || slug.includes("..") || slug.includes("\\")) {
|
|
11913
12089
|
return c.json({ error: "Invalid agent slug" }, 400);
|
|
11914
12090
|
}
|
|
11915
|
-
const agentDir =
|
|
12091
|
+
const agentDir = resolve10(account.accountDir, "agents", slug);
|
|
11916
12092
|
if (!existsSync12(agentDir)) {
|
|
11917
12093
|
return c.json({ error: "Agent not found on disk" }, 404);
|
|
11918
12094
|
}
|
|
@@ -12617,7 +12793,7 @@ var sessions_default = app11;
|
|
|
12617
12793
|
|
|
12618
12794
|
// app/lib/claude-agent/spawn-context.ts
|
|
12619
12795
|
import { existsSync as existsSync14, readFileSync as readFileSync12, readdirSync as readdirSync7, statSync as statSync6 } from "fs";
|
|
12620
|
-
import { dirname as dirname2, resolve as
|
|
12796
|
+
import { dirname as dirname2, resolve as resolve11, join as join10 } from "path";
|
|
12621
12797
|
async function resolveOwnerProfileBlock(accountId, userId) {
|
|
12622
12798
|
if (!userId) return { ok: false, reason: "missing-user-id" };
|
|
12623
12799
|
try {
|
|
@@ -12702,7 +12878,7 @@ function parseSkillPathsFromFrontmatter(fm) {
|
|
|
12702
12878
|
}
|
|
12703
12879
|
function listPluginDirs() {
|
|
12704
12880
|
const out = /* @__PURE__ */ new Map();
|
|
12705
|
-
const platformPlugins =
|
|
12881
|
+
const platformPlugins = resolve11(PLATFORM_ROOT, "plugins");
|
|
12706
12882
|
if (existsSync14(platformPlugins)) {
|
|
12707
12883
|
for (const name of readdirSync7(platformPlugins)) {
|
|
12708
12884
|
const dir = join10(platformPlugins, name);
|
|
@@ -12712,7 +12888,7 @@ function listPluginDirs() {
|
|
|
12712
12888
|
}
|
|
12713
12889
|
}
|
|
12714
12890
|
}
|
|
12715
|
-
const premiumRoot =
|
|
12891
|
+
const premiumRoot = resolve11(dirname2(PLATFORM_ROOT), "premium-plugins");
|
|
12716
12892
|
if (existsSync14(premiumRoot)) {
|
|
12717
12893
|
for (const bundle of readdirSync7(premiumRoot)) {
|
|
12718
12894
|
const bundlePlugins = join10(premiumRoot, bundle, "plugins");
|
|
@@ -12732,7 +12908,7 @@ function listPluginDirs() {
|
|
|
12732
12908
|
return out;
|
|
12733
12909
|
}
|
|
12734
12910
|
function readEnabledPlugins(accountId) {
|
|
12735
|
-
const accountFile =
|
|
12911
|
+
const accountFile = resolve11(PLATFORM_ROOT, "..", "data/accounts", accountId, "account.json");
|
|
12736
12912
|
if (!existsSync14(accountFile)) return /* @__PURE__ */ new Set();
|
|
12737
12913
|
try {
|
|
12738
12914
|
const parsed = JSON.parse(readFileSync12(accountFile, "utf-8"));
|
|
@@ -12805,7 +12981,7 @@ ${skillFm}
|
|
|
12805
12981
|
return out;
|
|
12806
12982
|
}
|
|
12807
12983
|
function computeSpecialistDomains(accountId) {
|
|
12808
|
-
const specialistsDir =
|
|
12984
|
+
const specialistsDir = resolve11(PLATFORM_ROOT, "..", "data/accounts", accountId, "specialists", "agents");
|
|
12809
12985
|
if (!existsSync14(specialistsDir)) return [];
|
|
12810
12986
|
let entries;
|
|
12811
12987
|
try {
|
|
@@ -12851,7 +13027,7 @@ ${fm}
|
|
|
12851
13027
|
return out;
|
|
12852
13028
|
}
|
|
12853
13029
|
function computeDormantPlugins(accountId) {
|
|
12854
|
-
const accountFile =
|
|
13030
|
+
const accountFile = resolve11(PLATFORM_ROOT, "..", "data/accounts", accountId, "account.json");
|
|
12855
13031
|
if (!existsSync14(accountFile)) return [];
|
|
12856
13032
|
let enabled;
|
|
12857
13033
|
try {
|
|
@@ -12866,7 +13042,7 @@ function computeDormantPlugins(accountId) {
|
|
|
12866
13042
|
);
|
|
12867
13043
|
return [];
|
|
12868
13044
|
}
|
|
12869
|
-
const pluginsDir =
|
|
13045
|
+
const pluginsDir = resolve11(PLATFORM_ROOT, "plugins");
|
|
12870
13046
|
if (!existsSync14(pluginsDir)) return [];
|
|
12871
13047
|
let entries;
|
|
12872
13048
|
try {
|
|
@@ -12877,7 +13053,7 @@ function computeDormantPlugins(accountId) {
|
|
|
12877
13053
|
const out = [];
|
|
12878
13054
|
for (const name of entries) {
|
|
12879
13055
|
if (enabled.has(name)) continue;
|
|
12880
|
-
const manifestPath =
|
|
13056
|
+
const manifestPath = resolve11(pluginsDir, name, "PLUGIN.md");
|
|
12881
13057
|
if (!existsSync14(manifestPath)) continue;
|
|
12882
13058
|
let manifest;
|
|
12883
13059
|
try {
|
|
@@ -12896,7 +13072,7 @@ function computeDormantPlugins(accountId) {
|
|
|
12896
13072
|
|
|
12897
13073
|
// server/routes/admin/claude-sessions.ts
|
|
12898
13074
|
import { existsSync as existsSync15, readFileSync as readFileSync13 } from "fs";
|
|
12899
|
-
import { resolve as
|
|
13075
|
+
import { resolve as resolve12 } from "path";
|
|
12900
13076
|
function readTunnelState(brandConfigDir) {
|
|
12901
13077
|
const statePath = `${process.env.HOME ?? ""}/${brandConfigDir}/cloudflared/tunnel.state`;
|
|
12902
13078
|
if (!existsSync15(statePath)) return null;
|
|
@@ -12912,10 +13088,10 @@ function readTunnelState(brandConfigDir) {
|
|
|
12912
13088
|
}
|
|
12913
13089
|
}
|
|
12914
13090
|
function resolveTunnelUrl() {
|
|
12915
|
-
const
|
|
13091
|
+
const platformRoot2 = process.env.MAXY_PLATFORM_ROOT ?? process.env.PLATFORM_ROOT ?? resolve12(process.cwd(), "..");
|
|
12916
13092
|
let brand;
|
|
12917
13093
|
try {
|
|
12918
|
-
brand = JSON.parse(readFileSync13(
|
|
13094
|
+
brand = JSON.parse(readFileSync13(resolve12(platformRoot2, "config", "brand.json"), "utf-8"));
|
|
12919
13095
|
} catch {
|
|
12920
13096
|
return null;
|
|
12921
13097
|
}
|
|
@@ -12926,7 +13102,7 @@ function resolveTunnelUrl() {
|
|
|
12926
13102
|
if (!state) return null;
|
|
12927
13103
|
return `https://${hostname2}.${state.domain}`;
|
|
12928
13104
|
}
|
|
12929
|
-
var
|
|
13105
|
+
var TAG20 = "[claude-session-manager:wrapper]";
|
|
12930
13106
|
async function refuseIfClaudeAuthDead(c, route, sessionId) {
|
|
12931
13107
|
const auth = await ensureAuth();
|
|
12932
13108
|
if (auth.status !== "dead" && auth.status !== "missing") return null;
|
|
@@ -12948,12 +13124,12 @@ async function performSpawnWithInitialMessage(args) {
|
|
|
12948
13124
|
const aboutOwner = await resolveOwnerProfileBlock(args.senderId, args.userId);
|
|
12949
13125
|
const ownerMs = Date.now() - ownerStart;
|
|
12950
13126
|
const aboutOwnerStatus = aboutOwner == null ? "absent" : "ok" in aboutOwner && aboutOwner.ok === false ? `unresolved:${aboutOwner.reason}` : "ok";
|
|
12951
|
-
console.log(`${
|
|
13127
|
+
console.log(`${TAG20} about-owner-resolved status=${aboutOwnerStatus} ms=${ownerMs}`);
|
|
12952
13128
|
const dormantPlugins = computeDormantPlugins(args.senderId);
|
|
12953
13129
|
const activePlugins = computeActivePlugins(args.senderId);
|
|
12954
13130
|
const specialistDomains = computeSpecialistDomains(args.senderId);
|
|
12955
13131
|
const tunnelUrl = resolveTunnelUrl();
|
|
12956
|
-
console.log(`${
|
|
13132
|
+
console.log(`${TAG20} tunnel-url-resolved value=${tunnelUrl ?? "null"}`);
|
|
12957
13133
|
const upstreamPayload = JSON.stringify({
|
|
12958
13134
|
senderId: args.senderId,
|
|
12959
13135
|
// Task 205 — pass userId through to the manager so it lands as
|
|
@@ -12980,24 +13156,24 @@ async function performSpawnWithInitialMessage(args) {
|
|
|
12980
13156
|
// unshapely values.
|
|
12981
13157
|
conversationNodeId: args.conversationNodeId
|
|
12982
13158
|
});
|
|
12983
|
-
console.log(`${
|
|
13159
|
+
console.log(`${TAG20} forward-spawn-start managerBase=${managerBase3()} bytes=${upstreamPayload.length} hidden=${args.hidden} specialist=${args.specialist ?? "none"}`);
|
|
12984
13160
|
const forwardStart = Date.now();
|
|
12985
13161
|
const upstream = await fetch(`${managerBase3()}/public-spawn`, {
|
|
12986
13162
|
method: "POST",
|
|
12987
13163
|
headers: { "content-type": "application/json" },
|
|
12988
13164
|
body: upstreamPayload
|
|
12989
13165
|
}).catch((err) => {
|
|
12990
|
-
console.error(`${
|
|
13166
|
+
console.error(`${TAG20} fetch-failed op=spawn message=${err instanceof Error ? err.message : String(err)} ms=${Date.now() - forwardStart}`);
|
|
12991
13167
|
return null;
|
|
12992
13168
|
});
|
|
12993
13169
|
if (!upstream) return {
|
|
12994
13170
|
response: new Response(JSON.stringify({ error: "manager-unreachable" }), { status: 503, headers: { "content-type": "application/json" } }),
|
|
12995
13171
|
claudeSessionId: null
|
|
12996
13172
|
};
|
|
12997
|
-
console.log(`${
|
|
13173
|
+
console.log(`${TAG20} forward-spawn-done status=${upstream.status} ms=${Date.now() - forwardStart}`);
|
|
12998
13174
|
if (args.initialMessage) {
|
|
12999
13175
|
const inputBytes = Buffer.byteLength(args.initialMessage, "utf8");
|
|
13000
|
-
console.log(`${
|
|
13176
|
+
console.log(`${TAG20} initial-message-inlined bytes=${inputBytes}`);
|
|
13001
13177
|
}
|
|
13002
13178
|
const bodyText = await upstream.text().catch(() => "");
|
|
13003
13179
|
let claudeSessionId = null;
|
|
@@ -13032,7 +13208,7 @@ app12.post("/", async (c) => {
|
|
|
13032
13208
|
if (refusal) return refusal;
|
|
13033
13209
|
const senderId = getAccountIdForSession(cacheKey) ?? "";
|
|
13034
13210
|
if (!senderId) {
|
|
13035
|
-
console.error(`${
|
|
13211
|
+
console.error(`${TAG20} reject reason=no-account-id cacheKey-prefix=${cacheKey.slice(0, 8)}`);
|
|
13036
13212
|
return c.json({ error: "admin-account-not-resolved" }, 500);
|
|
13037
13213
|
}
|
|
13038
13214
|
const userId = getUserIdForSession(cacheKey) ?? void 0;
|
|
@@ -13041,7 +13217,7 @@ app12.post("/", async (c) => {
|
|
|
13041
13217
|
const permissionMode = typeof body.permissionMode === "string" ? body.permissionMode : void 0;
|
|
13042
13218
|
const specialist = typeof body.specialist === "string" && /^[A-Za-z0-9_-]{1,64}$/.test(body.specialist) ? body.specialist : void 0;
|
|
13043
13219
|
const model = typeof body.model === "string" && /^[A-Za-z0-9._-]{1,64}$/.test(body.model) ? body.model : void 0;
|
|
13044
|
-
console.log(`${
|
|
13220
|
+
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
13221
|
const conversationNodeId = cacheKey ? getSessionIdForSession(cacheKey) : void 0;
|
|
13046
13222
|
const { response, claudeSessionId } = await performSpawnWithInitialMessage({
|
|
13047
13223
|
senderId,
|
|
@@ -13060,13 +13236,13 @@ app12.post("/", async (c) => {
|
|
|
13060
13236
|
claudeSessionId,
|
|
13061
13237
|
senderId
|
|
13062
13238
|
);
|
|
13063
|
-
console.log(`${
|
|
13239
|
+
console.log(`${TAG20} route-done surface=cookie status=${response.status} route-ms=${Date.now() - routeStart}`);
|
|
13064
13240
|
return response;
|
|
13065
13241
|
});
|
|
13066
13242
|
var claude_sessions_default = app12;
|
|
13067
13243
|
|
|
13068
13244
|
// server/routes/admin/log-ingest.ts
|
|
13069
|
-
var
|
|
13245
|
+
var TAG21 = "[log-ingest]";
|
|
13070
13246
|
var TAG_PATTERN = /^[A-Za-z0-9_:-]{1,32}$/;
|
|
13071
13247
|
var LEVELS = /* @__PURE__ */ new Set(["debug", "info", "warn", "error"]);
|
|
13072
13248
|
var MAX_LINE_BYTES = 4096;
|
|
@@ -13077,7 +13253,7 @@ function isLoopbackAddr(addr) {
|
|
|
13077
13253
|
app13.post("/", async (c) => {
|
|
13078
13254
|
const remoteAddr = c.env?.incoming?.socket?.remoteAddress ?? "";
|
|
13079
13255
|
if (!isLoopbackAddr(remoteAddr)) {
|
|
13080
|
-
console.error(`${
|
|
13256
|
+
console.error(`${TAG21} reject reason=non-loopback remoteAddr=${remoteAddr}`);
|
|
13081
13257
|
return c.json({ error: "log-ingest-loopback-only" }, 403);
|
|
13082
13258
|
}
|
|
13083
13259
|
const xffHeader = c.env?.incoming?.headers?.["x-forwarded-for"];
|
|
@@ -13086,7 +13262,7 @@ app13.post("/", async (c) => {
|
|
|
13086
13262
|
const tokens = xffRaw.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
|
|
13087
13263
|
const offender = tokens.find((t) => !isLoopbackAddr(t));
|
|
13088
13264
|
if (offender !== void 0) {
|
|
13089
|
-
console.error(`${
|
|
13265
|
+
console.error(`${TAG21} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
|
|
13090
13266
|
return c.json({ error: "log-ingest-loopback-only" }, 403);
|
|
13091
13267
|
}
|
|
13092
13268
|
}
|
|
@@ -13128,18 +13304,18 @@ var ALLOWED_EVENTS = /* @__PURE__ */ new Set([
|
|
|
13128
13304
|
]);
|
|
13129
13305
|
var app14 = new Hono();
|
|
13130
13306
|
app14.post("/", async (c) => {
|
|
13131
|
-
const
|
|
13307
|
+
const TAG31 = "[admin:events]";
|
|
13132
13308
|
let body;
|
|
13133
13309
|
try {
|
|
13134
13310
|
body = await c.req.json();
|
|
13135
13311
|
} catch (err) {
|
|
13136
13312
|
const detail = err instanceof Error ? err.message : String(err);
|
|
13137
|
-
console.error(`${
|
|
13313
|
+
console.error(`${TAG31} reject reason=body-not-json detail=${detail}`);
|
|
13138
13314
|
return c.json({ ok: false, detail: "Request body was not valid JSON" }, 400);
|
|
13139
13315
|
}
|
|
13140
13316
|
const event = typeof body.event === "string" ? body.event : "";
|
|
13141
13317
|
if (!ALLOWED_EVENTS.has(event)) {
|
|
13142
|
-
console.error(`${
|
|
13318
|
+
console.error(`${TAG31} reject reason=event-not-allowed event=${JSON.stringify(event)}`);
|
|
13143
13319
|
return c.json({ ok: false, detail: `Event "${event}" is not allowed` }, 400);
|
|
13144
13320
|
}
|
|
13145
13321
|
const rawFields = body.fields && typeof body.fields === "object" ? body.fields : {};
|
|
@@ -13164,15 +13340,15 @@ var events_default = app14;
|
|
|
13164
13340
|
import { createReadStream as createReadStream2 } from "fs";
|
|
13165
13341
|
import { readdir as readdir3, readFile as readFile4, stat as stat4, mkdir as mkdir3, writeFile as writeFile4, unlink as unlink2 } from "fs/promises";
|
|
13166
13342
|
import { realpathSync as realpathSync4 } from "fs";
|
|
13167
|
-
import { basename as basename4, dirname as dirname3, join as join12, resolve as
|
|
13343
|
+
import { basename as basename4, dirname as dirname3, join as join12, resolve as resolve15, sep as sep4 } from "path";
|
|
13168
13344
|
import { Readable as Readable2 } from "stream";
|
|
13169
13345
|
|
|
13170
13346
|
// app/lib/data-path.ts
|
|
13171
13347
|
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 ?
|
|
13348
|
+
import { resolve as resolve13, normalize, sep as sep2, relative } from "path";
|
|
13349
|
+
var PLATFORM_ROOT5 = process.env.MAXY_PLATFORM_ROOT ?? resolve13(process.cwd(), "../platform");
|
|
13350
|
+
var DATA_ROOT = resolve13(PLATFORM_ROOT5, "..", "data");
|
|
13351
|
+
var CLAUDE_UPLOADS_ROOT = process.env.CLAUDE_CONFIG_DIR ? resolve13(process.env.CLAUDE_CONFIG_DIR, "uploads") : null;
|
|
13176
13352
|
var ACCOUNT_PARTITION_DIRS = /* @__PURE__ */ new Set(["uploads", "accounts"]);
|
|
13177
13353
|
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
13354
|
function crossesForeignAccountPartition(relPath, accountId) {
|
|
@@ -13185,7 +13361,7 @@ function crossesForeignAccountPartition(relPath, accountId) {
|
|
|
13185
13361
|
}
|
|
13186
13362
|
function resolveUnderRoot(raw, rootAbs, rootLabel) {
|
|
13187
13363
|
const cleaned = normalize("/" + (raw ?? "").replace(/\\/g, "/")).replace(/^\/+/, "");
|
|
13188
|
-
const absolute =
|
|
13364
|
+
const absolute = resolve13(rootAbs, cleaned);
|
|
13189
13365
|
let rootReal;
|
|
13190
13366
|
try {
|
|
13191
13367
|
rootReal = realpathSync3(rootAbs);
|
|
@@ -13544,7 +13720,7 @@ async function cascadeDeleteDocument(params) {
|
|
|
13544
13720
|
|
|
13545
13721
|
// app/lib/file-index.ts
|
|
13546
13722
|
import * as fsp from "fs/promises";
|
|
13547
|
-
import { resolve as
|
|
13723
|
+
import { resolve as resolve14, relative as relative2, join as join11, basename as basename3, extname as extname2, sep as sep3 } from "path";
|
|
13548
13724
|
import { tmpdir as tmpdir2 } from "os";
|
|
13549
13725
|
import { execFile as execFile2 } from "child_process";
|
|
13550
13726
|
import { promisify as promisify2 } from "util";
|
|
@@ -13852,8 +14028,8 @@ async function reconcileFileIndex(accountId, opts) {
|
|
|
13852
14028
|
return withSession(opts, async (session) => {
|
|
13853
14029
|
const walked = [];
|
|
13854
14030
|
const subtreeRoots = [
|
|
13855
|
-
|
|
13856
|
-
|
|
14031
|
+
resolve14(dataRoot, "uploads", accountId),
|
|
14032
|
+
resolve14(dataRoot, "accounts", accountId)
|
|
13857
14033
|
];
|
|
13858
14034
|
let clean = true;
|
|
13859
14035
|
for (const root of subtreeRoots) {
|
|
@@ -13934,7 +14110,7 @@ async function reconcileFileIndexDebounced(accountId, opts) {
|
|
|
13934
14110
|
async function indexFile(accountId, relativePath, opts) {
|
|
13935
14111
|
const dataRoot = opts?.dataRoot ?? DATA_ROOT;
|
|
13936
14112
|
const embed2 = opts?.embed ?? embed;
|
|
13937
|
-
const absolute =
|
|
14113
|
+
const absolute = resolve14(dataRoot, relativePath);
|
|
13938
14114
|
await withSession(opts, async (session) => {
|
|
13939
14115
|
const st = await fsp.stat(absolute);
|
|
13940
14116
|
const wf = {
|
|
@@ -13979,7 +14155,7 @@ async function readMeta(absDir, baseName) {
|
|
|
13979
14155
|
}
|
|
13980
14156
|
async function readAccountNames() {
|
|
13981
14157
|
const map = /* @__PURE__ */ new Map();
|
|
13982
|
-
const accountsDir =
|
|
14158
|
+
const accountsDir = resolve15(DATA_ROOT, "accounts");
|
|
13983
14159
|
let names;
|
|
13984
14160
|
try {
|
|
13985
14161
|
names = await readdir3(accountsDir);
|
|
@@ -13988,7 +14164,7 @@ async function readAccountNames() {
|
|
|
13988
14164
|
}
|
|
13989
14165
|
for (const name of names) {
|
|
13990
14166
|
if (!UUID_RE3.test(name)) continue;
|
|
13991
|
-
const configPath2 =
|
|
14167
|
+
const configPath2 = resolve15(accountsDir, name, "account.json");
|
|
13992
14168
|
try {
|
|
13993
14169
|
const raw = await readFile4(configPath2, "utf8");
|
|
13994
14170
|
const parsed = JSON.parse(raw);
|
|
@@ -14229,8 +14405,8 @@ app15.post("/upload", requireAdminSession, async (c) => {
|
|
|
14229
14405
|
}
|
|
14230
14406
|
const safeName = basename4(file.name).replace(/[\0/\\]/g, "_");
|
|
14231
14407
|
const finalName = `${Date.now()}-${safeName}`;
|
|
14232
|
-
const destDir =
|
|
14233
|
-
const destPath =
|
|
14408
|
+
const destDir = resolve15(DATA_ROOT, "uploads", accountId);
|
|
14409
|
+
const destPath = resolve15(destDir, finalName);
|
|
14234
14410
|
try {
|
|
14235
14411
|
await mkdir3(destDir, { recursive: true });
|
|
14236
14412
|
const dataRootReal = realpathSync4(DATA_ROOT);
|
|
@@ -16337,7 +16513,7 @@ var graph_default_view_default = app21;
|
|
|
16337
16513
|
|
|
16338
16514
|
// server/routes/admin/sidebar-artefacts.ts
|
|
16339
16515
|
import { readdir as readdir4, stat as stat5 } from "fs/promises";
|
|
16340
|
-
import { resolve as
|
|
16516
|
+
import { resolve as resolve16, relative as relative3, isAbsolute, sep as sep5, basename as basename5 } from "path";
|
|
16341
16517
|
import { existsSync as existsSync16 } from "fs";
|
|
16342
16518
|
var LIMIT = 50;
|
|
16343
16519
|
var ADMIN_AGENT_FILES = ["IDENTITY.md", "SOUL.md", "KNOWLEDGE.md"];
|
|
@@ -16357,7 +16533,7 @@ app22.get("/", requireAdminSession, async (c) => {
|
|
|
16357
16533
|
if (outputs === null) {
|
|
16358
16534
|
return c.json({ error: "Failed to load artefacts" }, 500);
|
|
16359
16535
|
}
|
|
16360
|
-
const accountDir =
|
|
16536
|
+
const accountDir = resolve16(ACCOUNTS_DIR, accountId);
|
|
16361
16537
|
const agents = await fetchAgentTemplateRows(accountDir);
|
|
16362
16538
|
const artefacts = [...outputs, ...agents].sort(
|
|
16363
16539
|
(a, b) => (b.updatedAt ?? "").localeCompare(a.updatedAt ?? "")
|
|
@@ -16408,8 +16584,8 @@ async function fetchOutputArtefacts(accountId) {
|
|
|
16408
16584
|
async function fetchAgentTemplateRows(accountDir) {
|
|
16409
16585
|
const rows = [];
|
|
16410
16586
|
for (const filename of ADMIN_AGENT_FILES) {
|
|
16411
|
-
const overridePath =
|
|
16412
|
-
const bundledPath =
|
|
16587
|
+
const overridePath = resolve16(accountDir, "agents", "admin", filename);
|
|
16588
|
+
const bundledPath = resolve16(PLATFORM_ROOT, "templates", "agents", "admin", filename);
|
|
16413
16589
|
const labelStem = filename.replace(/\.md$/, "");
|
|
16414
16590
|
const row = await readAgentTemplateRow({
|
|
16415
16591
|
id: `agent-template:admin:${filename}`,
|
|
@@ -16422,12 +16598,12 @@ async function fetchAgentTemplateRows(accountDir) {
|
|
|
16422
16598
|
});
|
|
16423
16599
|
if (row) rows.push(row);
|
|
16424
16600
|
}
|
|
16425
|
-
const overrideDir =
|
|
16426
|
-
const bundledDir =
|
|
16601
|
+
const overrideDir = resolve16(accountDir, "specialists", "agents");
|
|
16602
|
+
const bundledDir = resolve16(PLATFORM_ROOT, "templates", "specialists", "agents");
|
|
16427
16603
|
const specialistNames = await unionSpecialistFilenames(overrideDir, bundledDir);
|
|
16428
16604
|
for (const filename of specialistNames) {
|
|
16429
|
-
const overridePath =
|
|
16430
|
-
const bundledPath =
|
|
16605
|
+
const overridePath = resolve16(overrideDir, filename);
|
|
16606
|
+
const bundledPath = resolve16(bundledDir, filename);
|
|
16431
16607
|
const row = await readAgentTemplateRow({
|
|
16432
16608
|
id: `agent-template:specialist:${filename}`,
|
|
16433
16609
|
displayName: filename.replace(/\.md$/, ""),
|
|
@@ -16513,7 +16689,7 @@ var sidebar_artefacts_default = app22;
|
|
|
16513
16689
|
|
|
16514
16690
|
// server/routes/admin/sidebar-sessions.ts
|
|
16515
16691
|
import { readdirSync as readdirSync8, readFileSync as readFileSync14, statSync as statSync7 } from "fs";
|
|
16516
|
-
import { join as join13, resolve as
|
|
16692
|
+
import { join as join13, resolve as resolve17 } from "path";
|
|
16517
16693
|
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
16694
|
var PID_FILE_RE = /^([0-9]+)\.json$/;
|
|
16519
16695
|
var TITLE_MAX = 80;
|
|
@@ -16694,7 +16870,7 @@ app23.get("/", requireAdminSession, async (c) => {
|
|
|
16694
16870
|
const projectsRoot = join13(configDir2, "projects");
|
|
16695
16871
|
const jsonls = enumerateJsonls(projectsRoot);
|
|
16696
16872
|
const pidsBySession = sessionIdToPidMap(configDir2);
|
|
16697
|
-
const accountDir = accountId ?
|
|
16873
|
+
const accountDir = accountId ? resolve17(ACCOUNTS_DIR, accountId) : null;
|
|
16698
16874
|
const userTitles = loadUserTitles(accountDir);
|
|
16699
16875
|
const rows = [];
|
|
16700
16876
|
const seenIds = /* @__PURE__ */ new Set();
|
|
@@ -16748,7 +16924,7 @@ var sidebar_sessions_default = app23;
|
|
|
16748
16924
|
|
|
16749
16925
|
// server/routes/admin/session-delete.ts
|
|
16750
16926
|
import { existsSync as existsSync17, readdirSync as readdirSync9, readFileSync as readFileSync15, unlinkSync as unlinkSync2 } from "fs";
|
|
16751
|
-
import { join as join14, resolve as
|
|
16927
|
+
import { join as join14, resolve as resolve18 } from "path";
|
|
16752
16928
|
var app24 = new Hono();
|
|
16753
16929
|
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
16930
|
var PID_FILE_RE2 = /^([0-9]+)\.json$/;
|
|
@@ -16797,7 +16973,7 @@ function isPidAlive(pid) {
|
|
|
16797
16973
|
}
|
|
16798
16974
|
}
|
|
16799
16975
|
function sleep2(ms) {
|
|
16800
|
-
return new Promise((
|
|
16976
|
+
return new Promise((resolve27) => setTimeout(resolve27, ms));
|
|
16801
16977
|
}
|
|
16802
16978
|
function sendSignal(pid, signal) {
|
|
16803
16979
|
try {
|
|
@@ -16865,8 +17041,8 @@ app24.post("/", requireAdminSession, async (c) => {
|
|
|
16865
17041
|
if (!configDir2) {
|
|
16866
17042
|
return c.json({ error: "CLAUDE_CONFIG_DIR not set" }, 500);
|
|
16867
17043
|
}
|
|
16868
|
-
const projectsRoot =
|
|
16869
|
-
const resolvedProject =
|
|
17044
|
+
const projectsRoot = resolve18(join14(configDir2, "projects")) + "/";
|
|
17045
|
+
const resolvedProject = resolve18(projectDir) + "/";
|
|
16870
17046
|
if (!resolvedProject.startsWith(projectsRoot)) {
|
|
16871
17047
|
console.error(`[sessions-delete] reject reason=projectDir-outside-tree projectDir=${projectDir}`);
|
|
16872
17048
|
return c.json({ error: "projectDir is not under CLAUDE_CONFIG_DIR/projects/" }, 400);
|
|
@@ -17097,8 +17273,8 @@ var system_stats_default = app26;
|
|
|
17097
17273
|
|
|
17098
17274
|
// server/routes/admin/health.ts
|
|
17099
17275
|
import { existsSync as existsSync18, readFileSync as readFileSync16 } from "fs";
|
|
17100
|
-
import { resolve as
|
|
17101
|
-
var PLATFORM_ROOT6 = process.env.MAXY_PLATFORM_ROOT ??
|
|
17276
|
+
import { resolve as resolve19, join as join15 } from "path";
|
|
17277
|
+
var PLATFORM_ROOT6 = process.env.MAXY_PLATFORM_ROOT ?? resolve19(process.cwd(), "..");
|
|
17102
17278
|
var brandHostname = "maxy";
|
|
17103
17279
|
var brandJsonPath = join15(PLATFORM_ROOT6, "config", "brand.json");
|
|
17104
17280
|
if (existsSync18(brandJsonPath)) {
|
|
@@ -17108,7 +17284,7 @@ if (existsSync18(brandJsonPath)) {
|
|
|
17108
17284
|
} catch {
|
|
17109
17285
|
}
|
|
17110
17286
|
}
|
|
17111
|
-
var VERSION_FILE =
|
|
17287
|
+
var VERSION_FILE = resolve19(PLATFORM_ROOT6, `config/.${brandHostname}-version`);
|
|
17112
17288
|
var PROCESS_STARTED_AT = (/* @__PURE__ */ new Date()).toISOString();
|
|
17113
17289
|
var PROBE_TIMEOUT_MS = 1e3;
|
|
17114
17290
|
function readVersion() {
|
|
@@ -17165,7 +17341,7 @@ var health_default2 = app27;
|
|
|
17165
17341
|
|
|
17166
17342
|
// server/routes/admin/linkedin-ingest.ts
|
|
17167
17343
|
import { randomUUID as randomUUID9 } from "crypto";
|
|
17168
|
-
var
|
|
17344
|
+
var TAG22 = "[linkedin-ingest-route]";
|
|
17169
17345
|
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
17346
|
var ISO = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,3})?(?:Z|[+-]\d{2}:\d{2})$/;
|
|
17171
17347
|
var LINKEDIN_URL = /^https:\/\/www\.linkedin\.com\//;
|
|
@@ -17269,29 +17445,29 @@ app28.post("/", requireAdminSession, async (c) => {
|
|
|
17269
17445
|
try {
|
|
17270
17446
|
body = await c.req.json();
|
|
17271
17447
|
} catch {
|
|
17272
|
-
console.error(
|
|
17448
|
+
console.error(TAG22 + " rejected status=400 reason=schema:body-not-json");
|
|
17273
17449
|
return c.json({ ok: false, error: "schema", reason: "body-not-json" }, 400);
|
|
17274
17450
|
}
|
|
17275
17451
|
const v = validate(body);
|
|
17276
17452
|
if (!v.ok) {
|
|
17277
|
-
console.error(
|
|
17453
|
+
console.error(TAG22 + " rejected status=" + v.status + " reason=" + v.reason + " missing=" + v.missing.join(","));
|
|
17278
17454
|
return c.json({ ok: false, error: v.error, reason: v.reason, missing: v.missing }, v.status);
|
|
17279
17455
|
}
|
|
17280
17456
|
const envelope = v.envelope;
|
|
17281
17457
|
const cacheKey = c.var.cacheKey ?? "";
|
|
17282
17458
|
const senderId = getAccountIdForSession(cacheKey) ?? "";
|
|
17283
17459
|
if (!senderId) {
|
|
17284
|
-
console.error(
|
|
17460
|
+
console.error(TAG22 + " rejected status=500 reason=admin-account-not-resolved");
|
|
17285
17461
|
return c.json({ ok: false, error: "admin-account-not-resolved" }, 500);
|
|
17286
17462
|
}
|
|
17287
17463
|
const payloadBytes = JSON.stringify(envelope).length;
|
|
17288
17464
|
console.log(
|
|
17289
|
-
|
|
17465
|
+
TAG22 + " received kind=" + envelope.kind + " account=" + senderId.slice(0, 8) + " pageUrl=" + envelope.pageUrl + " dispatchId=" + envelope.dispatchId + " payloadBytes=" + payloadBytes
|
|
17290
17466
|
);
|
|
17291
17467
|
const initialMessage = buildInitialMessage(envelope);
|
|
17292
17468
|
const spawnStart = Date.now();
|
|
17293
17469
|
const sessionId = randomUUID9();
|
|
17294
|
-
console.log(
|
|
17470
|
+
console.log(TAG22 + " route target=rc-spawn dispatchId=" + envelope.dispatchId + " sessionId=" + sessionId.slice(0, 8));
|
|
17295
17471
|
const spawned = await managerRcSpawn({
|
|
17296
17472
|
sessionId,
|
|
17297
17473
|
initialMessage,
|
|
@@ -17300,12 +17476,12 @@ app28.post("/", requireAdminSession, async (c) => {
|
|
|
17300
17476
|
});
|
|
17301
17477
|
if ("error" in spawned) {
|
|
17302
17478
|
console.error(
|
|
17303
|
-
|
|
17479
|
+
TAG22 + " dispatch-failed dispatchId=" + envelope.dispatchId + " status=" + spawned.status + " ms=" + (Date.now() - spawnStart) + " message=" + spawned.error
|
|
17304
17480
|
);
|
|
17305
17481
|
return c.json({ ok: false, error: "dispatch-failed", upstreamStatus: spawned.status, detail: spawned.error }, 502);
|
|
17306
17482
|
}
|
|
17307
17483
|
console.log(
|
|
17308
|
-
|
|
17484
|
+
TAG22 + " dispatched dispatchId=" + envelope.dispatchId + " taskId=" + spawned.sessionId + " ms=" + (Date.now() - spawnStart)
|
|
17309
17485
|
);
|
|
17310
17486
|
return c.json({ ok: true, dispatchId: envelope.dispatchId, taskId: spawned.sessionId }, 202);
|
|
17311
17487
|
});
|
|
@@ -17313,7 +17489,7 @@ var linkedin_ingest_default = app28;
|
|
|
17313
17489
|
|
|
17314
17490
|
// server/routes/admin/post-turn-context.ts
|
|
17315
17491
|
import neo4j2 from "neo4j-driver";
|
|
17316
|
-
var
|
|
17492
|
+
var TAG23 = "[post-turn-context]";
|
|
17317
17493
|
var STRIPPED_PROPERTIES2 = /* @__PURE__ */ new Set([
|
|
17318
17494
|
"embedding",
|
|
17319
17495
|
"passwordHash",
|
|
@@ -17355,7 +17531,7 @@ var app29 = new Hono();
|
|
|
17355
17531
|
app29.get("/", async (c) => {
|
|
17356
17532
|
const remoteAddr = c.env?.incoming?.socket?.remoteAddress ?? "";
|
|
17357
17533
|
if (!isLoopbackAddr2(remoteAddr)) {
|
|
17358
|
-
console.error(`${
|
|
17534
|
+
console.error(`${TAG23} reject reason=non-loopback remoteAddr=${remoteAddr}`);
|
|
17359
17535
|
return c.json({ error: "post-turn-context-loopback-only" }, 403);
|
|
17360
17536
|
}
|
|
17361
17537
|
const xffHeader = c.env?.incoming?.headers?.["x-forwarded-for"];
|
|
@@ -17364,7 +17540,7 @@ app29.get("/", async (c) => {
|
|
|
17364
17540
|
const tokens = xffRaw.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
|
|
17365
17541
|
const offender = tokens.find((t) => !isLoopbackAddr2(t));
|
|
17366
17542
|
if (offender !== void 0) {
|
|
17367
|
-
console.error(`${
|
|
17543
|
+
console.error(`${TAG23} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
|
|
17368
17544
|
return c.json({ error: "post-turn-context-loopback-only" }, 403);
|
|
17369
17545
|
}
|
|
17370
17546
|
}
|
|
@@ -17396,7 +17572,7 @@ app29.get("/", async (c) => {
|
|
|
17396
17572
|
writes.sort((a, b) => a.sortKey.localeCompare(b.sortKey));
|
|
17397
17573
|
const total = Date.now() - started;
|
|
17398
17574
|
console.log(
|
|
17399
|
-
`${
|
|
17575
|
+
`${TAG23} sessionId=${sessionId} accountId=${accountId} writes=${writes.length} ms=${total}`
|
|
17400
17576
|
);
|
|
17401
17577
|
return c.json({
|
|
17402
17578
|
writes: writes.map(({ elementId, labels, properties }) => ({ elementId, labels, properties }))
|
|
@@ -17405,7 +17581,7 @@ app29.get("/", async (c) => {
|
|
|
17405
17581
|
const elapsed = Date.now() - started;
|
|
17406
17582
|
const message = err instanceof Error ? err.message : String(err);
|
|
17407
17583
|
console.error(
|
|
17408
|
-
`${
|
|
17584
|
+
`${TAG23} neo4j-unreachable sessionId=${sessionId} ms=${elapsed} err="${message}"`
|
|
17409
17585
|
);
|
|
17410
17586
|
return c.json({ error: `post-turn-context unavailable: ${message}` }, 503);
|
|
17411
17587
|
} finally {
|
|
@@ -17416,7 +17592,7 @@ var post_turn_context_default = app29;
|
|
|
17416
17592
|
|
|
17417
17593
|
// server/routes/admin/public-session-context.ts
|
|
17418
17594
|
import neo4j3 from "neo4j-driver";
|
|
17419
|
-
var
|
|
17595
|
+
var TAG24 = "[public-session-context]";
|
|
17420
17596
|
var STRIPPED_PROPERTIES3 = /* @__PURE__ */ new Set([
|
|
17421
17597
|
"embedding",
|
|
17422
17598
|
"passwordHash",
|
|
@@ -17458,7 +17634,7 @@ var app30 = new Hono();
|
|
|
17458
17634
|
app30.get("/", async (c) => {
|
|
17459
17635
|
const remoteAddr = c.env?.incoming?.socket?.remoteAddress ?? "";
|
|
17460
17636
|
if (!isLoopbackAddr3(remoteAddr)) {
|
|
17461
|
-
console.error(`${
|
|
17637
|
+
console.error(`${TAG24} reject reason=non-loopback remoteAddr=${remoteAddr}`);
|
|
17462
17638
|
return c.json({ error: "public-session-context-loopback-only" }, 403);
|
|
17463
17639
|
}
|
|
17464
17640
|
const xffHeader = c.env?.incoming?.headers?.["x-forwarded-for"];
|
|
@@ -17467,7 +17643,7 @@ app30.get("/", async (c) => {
|
|
|
17467
17643
|
const tokens = xffRaw.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
|
|
17468
17644
|
const offender = tokens.find((t) => !isLoopbackAddr3(t));
|
|
17469
17645
|
if (offender !== void 0) {
|
|
17470
|
-
console.error(`${
|
|
17646
|
+
console.error(`${TAG24} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
|
|
17471
17647
|
return c.json({ error: "public-session-context-loopback-only" }, 403);
|
|
17472
17648
|
}
|
|
17473
17649
|
}
|
|
@@ -17498,7 +17674,7 @@ app30.get("/", async (c) => {
|
|
|
17498
17674
|
writes.sort((a, b) => a.sortKey.localeCompare(b.sortKey));
|
|
17499
17675
|
const total = Date.now() - started;
|
|
17500
17676
|
console.log(
|
|
17501
|
-
`${
|
|
17677
|
+
`${TAG24} sliceToken=${sliceToken.slice(0, 8)} writes=${writes.length} ms=${total}`
|
|
17502
17678
|
);
|
|
17503
17679
|
return c.json({
|
|
17504
17680
|
writes: writes.map(({ elementId, labels, properties }) => ({ elementId, labels, properties }))
|
|
@@ -17507,7 +17683,7 @@ app30.get("/", async (c) => {
|
|
|
17507
17683
|
const elapsed = Date.now() - started;
|
|
17508
17684
|
const message = err instanceof Error ? err.message : String(err);
|
|
17509
17685
|
console.error(
|
|
17510
|
-
`${
|
|
17686
|
+
`${TAG24} neo4j-unreachable sliceToken=${sliceToken.slice(0, 8)} ms=${elapsed} err="${message}"`
|
|
17511
17687
|
);
|
|
17512
17688
|
return c.json({ error: `public-session-context unavailable: ${message}` }, 503);
|
|
17513
17689
|
} finally {
|
|
@@ -17517,7 +17693,7 @@ app30.get("/", async (c) => {
|
|
|
17517
17693
|
var public_session_context_default = app30;
|
|
17518
17694
|
|
|
17519
17695
|
// server/routes/admin/public-session-exit.ts
|
|
17520
|
-
var
|
|
17696
|
+
var TAG25 = "[public-session-exit-route]";
|
|
17521
17697
|
function isLoopbackAddr4(addr) {
|
|
17522
17698
|
return addr === "127.0.0.1" || addr === "::1" || addr === "::ffff:127.0.0.1";
|
|
17523
17699
|
}
|
|
@@ -17525,7 +17701,7 @@ var app31 = new Hono();
|
|
|
17525
17701
|
app31.post("/", async (c) => {
|
|
17526
17702
|
const remoteAddr = c.env?.incoming?.socket?.remoteAddress ?? "";
|
|
17527
17703
|
if (!isLoopbackAddr4(remoteAddr)) {
|
|
17528
|
-
console.error(`${
|
|
17704
|
+
console.error(`${TAG25} reject reason=non-loopback remoteAddr=${remoteAddr}`);
|
|
17529
17705
|
return c.json({ error: "public-session-exit-loopback-only" }, 403);
|
|
17530
17706
|
}
|
|
17531
17707
|
const xffHeader = c.env?.incoming?.headers?.["x-forwarded-for"];
|
|
@@ -17534,7 +17710,7 @@ app31.post("/", async (c) => {
|
|
|
17534
17710
|
const tokens = xffRaw.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
|
|
17535
17711
|
const offender = tokens.find((t) => !isLoopbackAddr4(t));
|
|
17536
17712
|
if (offender !== void 0) {
|
|
17537
|
-
console.error(`${
|
|
17713
|
+
console.error(`${TAG25} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
|
|
17538
17714
|
return c.json({ error: "public-session-exit-loopback-only" }, 403);
|
|
17539
17715
|
}
|
|
17540
17716
|
}
|
|
@@ -17552,7 +17728,7 @@ app31.post("/", async (c) => {
|
|
|
17552
17728
|
var public_session_exit_default = app31;
|
|
17553
17729
|
|
|
17554
17730
|
// server/routes/admin/access-session-evict.ts
|
|
17555
|
-
var
|
|
17731
|
+
var TAG26 = "[access-session-evict]";
|
|
17556
17732
|
function isLoopbackAddr5(addr) {
|
|
17557
17733
|
return addr === "127.0.0.1" || addr === "::1" || addr === "::ffff:127.0.0.1";
|
|
17558
17734
|
}
|
|
@@ -17560,7 +17736,7 @@ var app32 = new Hono();
|
|
|
17560
17736
|
app32.post("/", async (c) => {
|
|
17561
17737
|
const remoteAddr = c.env?.incoming?.socket?.remoteAddress ?? "";
|
|
17562
17738
|
if (!isLoopbackAddr5(remoteAddr)) {
|
|
17563
|
-
console.error(`${
|
|
17739
|
+
console.error(`${TAG26} reject reason=non-loopback remoteAddr=${remoteAddr}`);
|
|
17564
17740
|
return c.json({ error: "access-session-evict-loopback-only" }, 403);
|
|
17565
17741
|
}
|
|
17566
17742
|
const xffHeader = c.env?.incoming?.headers?.["x-forwarded-for"];
|
|
@@ -17569,7 +17745,7 @@ app32.post("/", async (c) => {
|
|
|
17569
17745
|
const tokens = xffRaw.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
|
|
17570
17746
|
const offender = tokens.find((t) => !isLoopbackAddr5(t));
|
|
17571
17747
|
if (offender !== void 0) {
|
|
17572
|
-
console.error(`${
|
|
17748
|
+
console.error(`${TAG26} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
|
|
17573
17749
|
return c.json({ error: "access-session-evict-loopback-only" }, 403);
|
|
17574
17750
|
}
|
|
17575
17751
|
}
|
|
@@ -17582,7 +17758,7 @@ app32.post("/", async (c) => {
|
|
|
17582
17758
|
const grantId = typeof body.grantId === "string" ? body.grantId.trim() : "";
|
|
17583
17759
|
if (!grantId) return c.json({ error: "grantId required" }, 400);
|
|
17584
17760
|
const dropped = evictAccessSessionsByGrant(grantId);
|
|
17585
|
-
console.log(`${
|
|
17761
|
+
console.log(`${TAG26} grantId=${grantId} dropped=${dropped}`);
|
|
17586
17762
|
return c.json({ ok: true, dropped });
|
|
17587
17763
|
});
|
|
17588
17764
|
var access_session_evict_default = app32;
|
|
@@ -17590,7 +17766,7 @@ var access_session_evict_default = app32;
|
|
|
17590
17766
|
// server/routes/admin/identity.ts
|
|
17591
17767
|
var import_dist4 = __toESM(require_dist3(), 1);
|
|
17592
17768
|
var app33 = new Hono();
|
|
17593
|
-
var
|
|
17769
|
+
var TAG27 = "[admin-identity]";
|
|
17594
17770
|
function managerBase5() {
|
|
17595
17771
|
const port2 = (0, import_dist4.requirePortEnv)("CLAUDE_SESSION_MANAGER_PORT", { tag: "admin-identity" });
|
|
17596
17772
|
return `http://127.0.0.1:${port2}`;
|
|
@@ -17621,20 +17797,20 @@ app33.post("/validate", async (c) => {
|
|
|
17621
17797
|
const verdict = validatePin(pin, USERS_FILE);
|
|
17622
17798
|
if (verdict.kind !== "match") {
|
|
17623
17799
|
if (verdict.kind === "miss") fireStop();
|
|
17624
|
-
console.log(`${
|
|
17800
|
+
console.log(`${TAG27} op=endpoint-validate result=${verdict.kind}${sessionId ? ` sessionId=${sessionId.slice(0, 8)}` : ""}`);
|
|
17625
17801
|
return c.json({ kind: verdict.kind });
|
|
17626
17802
|
}
|
|
17627
17803
|
const { userId } = verdict;
|
|
17628
17804
|
const accounts = resolveUserAccounts(userId);
|
|
17629
17805
|
if (accounts.length === 0) {
|
|
17630
17806
|
fireStop();
|
|
17631
|
-
console.log(`${
|
|
17807
|
+
console.log(`${TAG27} op=endpoint-validate result=no-account userId=${userId.slice(0, 8)}`);
|
|
17632
17808
|
return c.json({ kind: "miss" });
|
|
17633
17809
|
}
|
|
17634
17810
|
const accountId = accounts[0].accountId;
|
|
17635
17811
|
const { userName } = await resolveUserIdentity(accountId, userId);
|
|
17636
17812
|
const aboutOwner = await resolveOwnerProfileBlock(accountId, userId);
|
|
17637
|
-
console.log(`${
|
|
17813
|
+
console.log(`${TAG27} op=endpoint-validate result=match userId=${userId.slice(0, 8)} aboutOwner=${aboutOwner.ok ? "ok" : `unresolved:${aboutOwner.reason}`}`);
|
|
17638
17814
|
return c.json({ kind: "match", userId, userName, aboutOwner });
|
|
17639
17815
|
});
|
|
17640
17816
|
var identity_default = app33;
|
|
@@ -17674,13 +17850,13 @@ var admin_default = app34;
|
|
|
17674
17850
|
// app/lib/access-gate.ts
|
|
17675
17851
|
import neo4j4 from "neo4j-driver";
|
|
17676
17852
|
import { readFileSync as readFileSync17 } from "fs";
|
|
17677
|
-
import { resolve as
|
|
17853
|
+
import { resolve as resolve20 } from "path";
|
|
17678
17854
|
import { randomUUID as randomUUID10 } from "crypto";
|
|
17679
|
-
var PLATFORM_ROOT7 = process.env.MAXY_PLATFORM_ROOT ??
|
|
17855
|
+
var PLATFORM_ROOT7 = process.env.MAXY_PLATFORM_ROOT ?? resolve20(process.cwd(), "..");
|
|
17680
17856
|
var driver = null;
|
|
17681
17857
|
function readPassword() {
|
|
17682
17858
|
if (process.env.NEO4J_PASSWORD) return process.env.NEO4J_PASSWORD;
|
|
17683
|
-
const passwordFile =
|
|
17859
|
+
const passwordFile = resolve20(PLATFORM_ROOT7, "config/.neo4j-password");
|
|
17684
17860
|
try {
|
|
17685
17861
|
return readFileSync17(passwordFile, "utf-8").trim();
|
|
17686
17862
|
} catch {
|
|
@@ -17880,7 +18056,7 @@ async function generateNewMagicToken(grantId) {
|
|
|
17880
18056
|
}
|
|
17881
18057
|
|
|
17882
18058
|
// server/routes/access/verify-token.ts
|
|
17883
|
-
var
|
|
18059
|
+
var TAG28 = "[access-verify]";
|
|
17884
18060
|
var MINT_TAG = "[access-session-mint]";
|
|
17885
18061
|
var COOKIE_NAME = "__access_session";
|
|
17886
18062
|
var app35 = new Hono();
|
|
@@ -17899,39 +18075,39 @@ app35.post("/", async (c) => {
|
|
|
17899
18075
|
}
|
|
17900
18076
|
const rateMsg = checkAccessRateLimit(ip, agentSlug);
|
|
17901
18077
|
if (rateMsg) {
|
|
17902
|
-
console.error(`${
|
|
18078
|
+
console.error(`${TAG28} grantId=- agentSlug=${agentSlug} result=rate-limited ip=${ip}`);
|
|
17903
18079
|
return c.json({ error: rateMsg }, 429);
|
|
17904
18080
|
}
|
|
17905
18081
|
const grant = await findGrantByMagicToken(token);
|
|
17906
18082
|
if (!grant) {
|
|
17907
18083
|
recordAccessFailedAttempt(ip, agentSlug);
|
|
17908
|
-
console.error(`${
|
|
18084
|
+
console.error(`${TAG28} grantId=- agentSlug=${agentSlug} result=notfound ip=${ip}`);
|
|
17909
18085
|
return c.json({ error: "invalid-or-expired-link" }, 401);
|
|
17910
18086
|
}
|
|
17911
18087
|
if (grant.agentSlug !== agentSlug) {
|
|
17912
18088
|
recordAccessFailedAttempt(ip, agentSlug);
|
|
17913
18089
|
console.error(
|
|
17914
|
-
`${
|
|
18090
|
+
`${TAG28} grantId=${grant.grantId} agentSlug=${agentSlug} result=agent-mismatch grantAgent=${grant.agentSlug} ip=${ip}`
|
|
17915
18091
|
);
|
|
17916
18092
|
return c.json({ error: "invalid-or-expired-link" }, 401);
|
|
17917
18093
|
}
|
|
17918
18094
|
if (grant.status === "expired" || grant.status === "revoked") {
|
|
17919
18095
|
recordAccessFailedAttempt(ip, agentSlug);
|
|
17920
18096
|
console.error(
|
|
17921
|
-
`${
|
|
18097
|
+
`${TAG28} grantId=${grant.grantId} agentSlug=${agentSlug} result=expired status=${grant.status} ip=${ip}`
|
|
17922
18098
|
);
|
|
17923
18099
|
return c.json({ error: "access-no-longer-valid" }, 401);
|
|
17924
18100
|
}
|
|
17925
18101
|
if (grant.expiresAt !== null && grant.expiresAt < Date.now()) {
|
|
17926
18102
|
recordAccessFailedAttempt(ip, agentSlug);
|
|
17927
18103
|
console.error(
|
|
17928
|
-
`${
|
|
18104
|
+
`${TAG28} grantId=${grant.grantId} agentSlug=${agentSlug} result=expired reason=expiresAt-past ip=${ip}`
|
|
17929
18105
|
);
|
|
17930
18106
|
return c.json({ error: "access-no-longer-valid" }, 401);
|
|
17931
18107
|
}
|
|
17932
18108
|
if (!grant.sliceToken) {
|
|
17933
18109
|
console.error(
|
|
17934
|
-
`${
|
|
18110
|
+
`${TAG28} grantId=${grant.grantId} agentSlug=${agentSlug} result=no-slice-token reason=schema-violation`
|
|
17935
18111
|
);
|
|
17936
18112
|
return c.json({ error: "grant-misconfigured" }, 500);
|
|
17937
18113
|
}
|
|
@@ -17947,12 +18123,12 @@ app35.post("/", async (c) => {
|
|
|
17947
18123
|
await consumeMagicTokenAndActivate(grant.grantId);
|
|
17948
18124
|
} catch (err) {
|
|
17949
18125
|
console.error(
|
|
17950
|
-
`${
|
|
18126
|
+
`${TAG28} grantId=${grant.grantId} agentSlug=${agentSlug} result=consume-failed err="${err instanceof Error ? err.message : String(err)}"`
|
|
17951
18127
|
);
|
|
17952
18128
|
return c.json({ error: "verification-failed" }, 500);
|
|
17953
18129
|
}
|
|
17954
18130
|
clearAccessRateLimit(ip, agentSlug);
|
|
17955
|
-
console.log(`${
|
|
18131
|
+
console.log(`${TAG28} grantId=${grant.grantId} agentSlug=${agentSlug} result=ok ip=${ip}`);
|
|
17956
18132
|
console.log(
|
|
17957
18133
|
`${MINT_TAG} grantId=${grant.grantId} sliceToken=${grant.sliceToken.slice(0, 8)} agentSlug=${agentSlug} personId=${grant.personId ?? "none"}`
|
|
17958
18134
|
);
|
|
@@ -17970,9 +18146,9 @@ var verify_token_default = app35;
|
|
|
17970
18146
|
|
|
17971
18147
|
// app/lib/access-email.ts
|
|
17972
18148
|
import { spawn as spawn2 } from "child_process";
|
|
17973
|
-
import { resolve as
|
|
17974
|
-
var PLATFORM_ROOT8 = process.env.MAXY_PLATFORM_ROOT ??
|
|
17975
|
-
var SEND_SCRIPT =
|
|
18149
|
+
import { resolve as resolve21 } from "path";
|
|
18150
|
+
var PLATFORM_ROOT8 = process.env.MAXY_PLATFORM_ROOT ?? resolve21(process.cwd(), "..");
|
|
18151
|
+
var SEND_SCRIPT = resolve21(
|
|
17976
18152
|
PLATFORM_ROOT8,
|
|
17977
18153
|
"plugins",
|
|
17978
18154
|
"email",
|
|
@@ -18028,7 +18204,7 @@ async function sendMagicLinkEmail(payload) {
|
|
|
18028
18204
|
}
|
|
18029
18205
|
|
|
18030
18206
|
// server/routes/access/request-magic-link.ts
|
|
18031
|
-
var
|
|
18207
|
+
var TAG29 = "[access-request-link]";
|
|
18032
18208
|
var app36 = new Hono();
|
|
18033
18209
|
var VISITOR_MESSAGE = "If that email is on the invite list, a fresh link is on the way.";
|
|
18034
18210
|
app36.post("/", async (c) => {
|
|
@@ -18045,18 +18221,18 @@ app36.post("/", async (c) => {
|
|
|
18045
18221
|
}
|
|
18046
18222
|
const rateMsg = checkRequestLinkRateLimit(contactValue);
|
|
18047
18223
|
if (rateMsg) {
|
|
18048
|
-
console.error(`${
|
|
18224
|
+
console.error(`${TAG29} contactValue=${maskContact(contactValue)} result=rate-limited`);
|
|
18049
18225
|
return c.json({ error: rateMsg }, 429);
|
|
18050
18226
|
}
|
|
18051
18227
|
recordRequestLinkAttempt(contactValue);
|
|
18052
18228
|
const accountId = process.env.ACCOUNT_ID ?? "";
|
|
18053
18229
|
if (!accountId) {
|
|
18054
|
-
console.error(`${
|
|
18230
|
+
console.error(`${TAG29} contactValue=${maskContact(contactValue)} result=no-account-id`);
|
|
18055
18231
|
return c.json({ message: VISITOR_MESSAGE }, 200);
|
|
18056
18232
|
}
|
|
18057
18233
|
const grant = await findActiveGrantByContact(contactValue, agentSlug, accountId);
|
|
18058
18234
|
if (!grant) {
|
|
18059
|
-
console.log(`${
|
|
18235
|
+
console.log(`${TAG29} contactValue=${maskContact(contactValue)} result=notfound`);
|
|
18060
18236
|
return c.json({ message: VISITOR_MESSAGE }, 200);
|
|
18061
18237
|
}
|
|
18062
18238
|
let token;
|
|
@@ -18064,7 +18240,7 @@ app36.post("/", async (c) => {
|
|
|
18064
18240
|
token = await generateNewMagicToken(grant.grantId);
|
|
18065
18241
|
} catch (err) {
|
|
18066
18242
|
console.error(
|
|
18067
|
-
`${
|
|
18243
|
+
`${TAG29} contactValue=${maskContact(contactValue)} result=mint-failed err="${err instanceof Error ? err.message : String(err)}"`
|
|
18068
18244
|
);
|
|
18069
18245
|
return c.json({ message: VISITOR_MESSAGE }, 200);
|
|
18070
18246
|
}
|
|
@@ -18096,12 +18272,12 @@ app36.post("/", async (c) => {
|
|
|
18096
18272
|
});
|
|
18097
18273
|
if (!sendResult.ok) {
|
|
18098
18274
|
console.error(
|
|
18099
|
-
`${
|
|
18275
|
+
`${TAG29} contactValue=${maskContact(contactValue)} result=send-failed err="${sendResult.error}"`
|
|
18100
18276
|
);
|
|
18101
18277
|
return c.json({ message: VISITOR_MESSAGE }, 200);
|
|
18102
18278
|
}
|
|
18103
18279
|
console.log(
|
|
18104
|
-
`${
|
|
18280
|
+
`${TAG29} contactValue=${maskContact(contactValue)} result=ok messageId=${sendResult.messageId}`
|
|
18105
18281
|
);
|
|
18106
18282
|
return c.json({ message: VISITOR_MESSAGE }, 200);
|
|
18107
18283
|
});
|
|
@@ -18115,7 +18291,7 @@ var access_default = app37;
|
|
|
18115
18291
|
|
|
18116
18292
|
// server/routes/sites.ts
|
|
18117
18293
|
import { existsSync as existsSync19, readFileSync as readFileSync18, realpathSync as realpathSync5, statSync as statSync8 } from "fs";
|
|
18118
|
-
import { resolve as
|
|
18294
|
+
import { resolve as resolve22 } from "path";
|
|
18119
18295
|
var SAFE_SEG_RE = /^[a-z0-9_][a-z0-9_.-]{0,99}$/i;
|
|
18120
18296
|
var MIME = {
|
|
18121
18297
|
".html": "text/html; charset=utf-8",
|
|
@@ -18172,8 +18348,8 @@ app38.get("/:rel{.*}", (c) => {
|
|
|
18172
18348
|
}
|
|
18173
18349
|
segments.push(seg);
|
|
18174
18350
|
}
|
|
18175
|
-
const rootDir =
|
|
18176
|
-
let filePath = segments.length === 0 ? rootDir :
|
|
18351
|
+
const rootDir = resolve22(account.accountDir, "sites");
|
|
18352
|
+
let filePath = segments.length === 0 ? rootDir : resolve22(rootDir, ...segments);
|
|
18177
18353
|
if (filePath !== rootDir && !filePath.startsWith(rootDir + "/")) {
|
|
18178
18354
|
console.error(`[sites] path-traversal-rejected path=${reqPath} reason=escape status=403`);
|
|
18179
18355
|
return c.text("Forbidden", 403);
|
|
@@ -18193,7 +18369,7 @@ app38.get("/:rel{.*}", (c) => {
|
|
|
18193
18369
|
return c.redirect(target, 301);
|
|
18194
18370
|
}
|
|
18195
18371
|
if (stat7?.isDirectory()) {
|
|
18196
|
-
filePath =
|
|
18372
|
+
filePath = resolve22(filePath, "index.html");
|
|
18197
18373
|
}
|
|
18198
18374
|
if (!filePath.startsWith(rootDir + "/")) {
|
|
18199
18375
|
console.error(`[sites] path-traversal-rejected path=${reqPath} reason=escape status=403`);
|
|
@@ -18337,9 +18513,9 @@ var cachedAttempted = false;
|
|
|
18337
18513
|
function readBrandConfig() {
|
|
18338
18514
|
if (cachedAttempted) return cached2;
|
|
18339
18515
|
cachedAttempted = true;
|
|
18340
|
-
const
|
|
18341
|
-
if (!
|
|
18342
|
-
const brandPath = join16(
|
|
18516
|
+
const platformRoot2 = process.env.MAXY_PLATFORM_ROOT;
|
|
18517
|
+
if (!platformRoot2) return null;
|
|
18518
|
+
const brandPath = join16(platformRoot2, "config", "brand.json");
|
|
18343
18519
|
if (!existsSync20(brandPath)) return null;
|
|
18344
18520
|
try {
|
|
18345
18521
|
cached2 = JSON.parse(readFileSync20(brandPath, "utf-8"));
|
|
@@ -18830,14 +19006,14 @@ async function writeEvent(opts) {
|
|
|
18830
19006
|
var visitor_event_default = app41;
|
|
18831
19007
|
|
|
18832
19008
|
// server/routes/session.ts
|
|
18833
|
-
import { resolve as
|
|
19009
|
+
import { resolve as resolve23 } from "path";
|
|
18834
19010
|
import { existsSync as existsSync21, writeFileSync as writeFileSync7, mkdirSync as mkdirSync5 } from "fs";
|
|
18835
19011
|
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
19012
|
function writeBrandingCache(accountId, agentSlug, branding) {
|
|
18837
19013
|
try {
|
|
18838
|
-
const cacheDir =
|
|
19014
|
+
const cacheDir = resolve23(MAXY_DIR, "branding-cache", accountId);
|
|
18839
19015
|
mkdirSync5(cacheDir, { recursive: true });
|
|
18840
|
-
writeFileSync7(
|
|
19016
|
+
writeFileSync7(resolve23(cacheDir, `${agentSlug}.json`), JSON.stringify(branding), "utf-8");
|
|
18841
19017
|
} catch (err) {
|
|
18842
19018
|
console.error(`[branding] cache write failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
18843
19019
|
}
|
|
@@ -18916,8 +19092,8 @@ app42.post("/", async (c) => {
|
|
|
18916
19092
|
}
|
|
18917
19093
|
let agentConfig = null;
|
|
18918
19094
|
if (account) {
|
|
18919
|
-
const agentDir =
|
|
18920
|
-
if (!existsSync21(agentDir) || !existsSync21(
|
|
19095
|
+
const agentDir = resolve23(account.accountDir, "agents", agentSlug);
|
|
19096
|
+
if (!existsSync21(agentDir) || !existsSync21(resolve23(agentDir, "config.json"))) {
|
|
18921
19097
|
return c.json({ error: "Agent not found" }, 404);
|
|
18922
19098
|
}
|
|
18923
19099
|
agentConfig = resolveAgentConfig(account.accountDir, agentSlug);
|
|
@@ -19154,7 +19330,7 @@ function startGraphHealthTimer() {
|
|
|
19154
19330
|
|
|
19155
19331
|
// app/lib/file-watcher.ts
|
|
19156
19332
|
import * as fsp2 from "fs/promises";
|
|
19157
|
-
import { resolve as
|
|
19333
|
+
import { resolve as resolve24, sep as sep6 } from "path";
|
|
19158
19334
|
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
19335
|
var DEFAULT_COALESCE_MS = 500;
|
|
19160
19336
|
var ROOTS = ["uploads", "accounts"];
|
|
@@ -19173,7 +19349,7 @@ async function startFileWatcher(opts = {}) {
|
|
|
19173
19349
|
const dropFn = opts.drop ?? dropFileIndex;
|
|
19174
19350
|
for (const r of ROOTS) {
|
|
19175
19351
|
try {
|
|
19176
|
-
await fsp2.mkdir(
|
|
19352
|
+
await fsp2.mkdir(resolve24(dataRoot, r), { recursive: true });
|
|
19177
19353
|
} catch (err) {
|
|
19178
19354
|
console.error(
|
|
19179
19355
|
`[file-watcher] start-failed root="${r}" err="${err.message}" \u2014 index will be maintained by the 5-min reconcile backstop only`
|
|
@@ -19194,7 +19370,7 @@ async function startFileWatcher(opts = {}) {
|
|
|
19194
19370
|
timers.set(relativePath, t);
|
|
19195
19371
|
}
|
|
19196
19372
|
async function runHook(relativePath, accountId) {
|
|
19197
|
-
const absolute =
|
|
19373
|
+
const absolute = resolve24(dataRoot, relativePath);
|
|
19198
19374
|
let exists = false;
|
|
19199
19375
|
try {
|
|
19200
19376
|
const st = await fsp2.stat(absolute);
|
|
@@ -19218,7 +19394,7 @@ async function startFileWatcher(opts = {}) {
|
|
|
19218
19394
|
}
|
|
19219
19395
|
}
|
|
19220
19396
|
async function watchRoot(rootName) {
|
|
19221
|
-
const absRoot =
|
|
19397
|
+
const absRoot = resolve24(dataRoot, rootName);
|
|
19222
19398
|
try {
|
|
19223
19399
|
const iter = fsp2.watch(absRoot, { recursive: true, signal: controller.signal });
|
|
19224
19400
|
for await (const event of iter) {
|
|
@@ -19256,7 +19432,7 @@ async function startFileWatcher(opts = {}) {
|
|
|
19256
19432
|
}
|
|
19257
19433
|
|
|
19258
19434
|
// app/lib/whatsapp/inbound/claude-bridge.ts
|
|
19259
|
-
var
|
|
19435
|
+
var TAG30 = "[whatsapp-adaptor]";
|
|
19260
19436
|
function whatsappTurnTimeoutMs() {
|
|
19261
19437
|
return Number(process.env.WHATSAPP_PTY_TURN_TIMEOUT_MS ?? String(5 * 6e4));
|
|
19262
19438
|
}
|
|
@@ -19278,7 +19454,7 @@ async function dispatchToClaude(input) {
|
|
|
19278
19454
|
await input.reply(result.turnText);
|
|
19279
19455
|
} catch (err) {
|
|
19280
19456
|
const m = err instanceof Error ? err.message : String(err);
|
|
19281
|
-
console.error(`${
|
|
19457
|
+
console.error(`${TAG30} reject reason=reply-failed senderId=${input.senderId} message=${m}`);
|
|
19282
19458
|
}
|
|
19283
19459
|
}
|
|
19284
19460
|
function startReaper2() {
|
|
@@ -19316,7 +19492,7 @@ function broadcastAdminShutdown(reason) {
|
|
|
19316
19492
|
// ../lib/entitlement/src/index.ts
|
|
19317
19493
|
import { createPublicKey, createHash as createHash5, verify as cryptoVerify } from "crypto";
|
|
19318
19494
|
import { existsSync as existsSync22, readFileSync as readFileSync21, statSync as statSync9 } from "fs";
|
|
19319
|
-
import { resolve as
|
|
19495
|
+
import { resolve as resolve25 } from "path";
|
|
19320
19496
|
|
|
19321
19497
|
// ../lib/entitlement/src/canonicalize.ts
|
|
19322
19498
|
function canonicalize(value) {
|
|
@@ -19351,7 +19527,7 @@ var PUBKEY_SHA256 = "8eee6bcb33545fd13b16d3199a5735ca5db5062834c7b49dfe4f23801d9
|
|
|
19351
19527
|
var GRACE_DAYS = 7;
|
|
19352
19528
|
var GRACE_MS = GRACE_DAYS * 24 * 60 * 60 * 1e3;
|
|
19353
19529
|
function pubkeyPath(brand) {
|
|
19354
|
-
return
|
|
19530
|
+
return resolve25(brand.platformRoot, "lib", "entitlement", "rubytech-pubkey.pem");
|
|
19355
19531
|
}
|
|
19356
19532
|
var memo = null;
|
|
19357
19533
|
function memoKey(mtimeMs, account) {
|
|
@@ -19363,7 +19539,7 @@ function resolveEntitlement(brand, account) {
|
|
|
19363
19539
|
if (brand.commercialMode !== true) {
|
|
19364
19540
|
return logResolved(implicitTrust(account), null);
|
|
19365
19541
|
}
|
|
19366
|
-
const entitlementPath =
|
|
19542
|
+
const entitlementPath = resolve25(brand.configDir, "entitlement.json");
|
|
19367
19543
|
if (!existsSync22(entitlementPath)) {
|
|
19368
19544
|
return logResolved(anonymousFallback("missing"), { reason: "missing" });
|
|
19369
19545
|
}
|
|
@@ -19925,8 +20101,8 @@ app43.get("/agent-assets/:slug/:filename", (c) => {
|
|
|
19925
20101
|
console.error(`[agent-assets] no-account slug=${slug} file=${filename}`);
|
|
19926
20102
|
return c.text("Not found", 404);
|
|
19927
20103
|
}
|
|
19928
|
-
const filePath =
|
|
19929
|
-
const expectedDir =
|
|
20104
|
+
const filePath = resolve26(account.accountDir, "agents", slug, "assets", filename);
|
|
20105
|
+
const expectedDir = resolve26(account.accountDir, "agents", slug, "assets");
|
|
19930
20106
|
if (!filePath.startsWith(expectedDir + "/")) {
|
|
19931
20107
|
console.error(`[agent-assets] path-traversal-rejected slug=${slug} file=${filename}`);
|
|
19932
20108
|
return c.text("Forbidden", 403);
|
|
@@ -19955,8 +20131,8 @@ app43.get("/generated/:filename", (c) => {
|
|
|
19955
20131
|
console.error(`[generated] serve file=${filename} status=404`);
|
|
19956
20132
|
return c.text("Not found", 404);
|
|
19957
20133
|
}
|
|
19958
|
-
const filePath =
|
|
19959
|
-
const expectedDir =
|
|
20134
|
+
const filePath = resolve26(account.accountDir, "generated", filename);
|
|
20135
|
+
const expectedDir = resolve26(account.accountDir, "generated");
|
|
19960
20136
|
if (!filePath.startsWith(expectedDir + "/")) {
|
|
19961
20137
|
console.error(`[generated] serve file=${filename} status=403`);
|
|
19962
20138
|
return c.text("Forbidden", 403);
|
|
@@ -20043,7 +20219,7 @@ var clientErrorReporterScript = `<script>
|
|
|
20043
20219
|
function cachedHtml(file) {
|
|
20044
20220
|
let html = htmlCache.get(file);
|
|
20045
20221
|
if (!html) {
|
|
20046
|
-
html = readFileSync22(
|
|
20222
|
+
html = readFileSync22(resolve26(process.cwd(), "public", file), "utf-8");
|
|
20047
20223
|
const productNameEsc = escapeHtml(BRAND.productName);
|
|
20048
20224
|
html = html.replace(/<title>([^<]*)<\/title>/, (_match, inner) => `<title>${escapeHtml(inner).replace(/Maxy/g, productNameEsc)}</title>`);
|
|
20049
20225
|
html = html.replace('href="/favicon.ico"', `href="${escapeHtml(brandFaviconPath)}"`);
|
|
@@ -20161,7 +20337,7 @@ app43.use("/vnc-popout.html", logViewerFetch);
|
|
|
20161
20337
|
app43.get("/vnc-popout.html", (c) => {
|
|
20162
20338
|
let html = htmlCache.get("vnc-popout.html");
|
|
20163
20339
|
if (!html) {
|
|
20164
|
-
html = readFileSync22(
|
|
20340
|
+
html = readFileSync22(resolve26(process.cwd(), "public", "vnc-popout.html"), "utf-8");
|
|
20165
20341
|
const name = escapeHtml(BRAND.productName);
|
|
20166
20342
|
html = html.replace("<title>Browser \u2014 Maxy</title>", `<title>${name}</title>`);
|
|
20167
20343
|
html = html.replace("</head>", ` ${brandScript}
|
|
@@ -20405,7 +20581,7 @@ if (bootAccountConfig?.whatsapp) {
|
|
|
20405
20581
|
}
|
|
20406
20582
|
init({
|
|
20407
20583
|
configDir: configDirForWhatsApp,
|
|
20408
|
-
platformRoot:
|
|
20584
|
+
platformRoot: resolve26(process.env.MAXY_PLATFORM_ROOT ?? join17(__dirname, "..")),
|
|
20409
20585
|
accountConfig: bootAccountConfig,
|
|
20410
20586
|
onMessage: async (msg) => {
|
|
20411
20587
|
if (msg.text && !msg.isOwnerMirror) {
|