@rubytech/create-maxy-code 0.1.464 → 0.1.465
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/payload/platform/lib/models/dist/index.d.ts.map +1 -1
- package/payload/platform/lib/models/dist/index.js +7 -2
- package/payload/platform/lib/models/dist/index.js.map +1 -1
- package/payload/platform/lib/models/src/index.ts +7 -2
- package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +17 -8
- package/payload/platform/plugins/cloudflare/skills/data-portal/SKILL.md +10 -1
- package/payload/platform/plugins/docs/references/claudeai-connectors.md +16 -7
- package/payload/platform/plugins/scheduling/PLUGIN.md +21 -9
- package/payload/server/server.js +626 -550
package/payload/server/server.js
CHANGED
|
@@ -1422,10 +1422,10 @@ var createStreamBody = (stream2) => {
|
|
|
1422
1422
|
});
|
|
1423
1423
|
return body;
|
|
1424
1424
|
};
|
|
1425
|
-
var getStats = (
|
|
1425
|
+
var getStats = (path3) => {
|
|
1426
1426
|
let stats;
|
|
1427
1427
|
try {
|
|
1428
|
-
stats = statSync(
|
|
1428
|
+
stats = statSync(path3);
|
|
1429
1429
|
} catch {
|
|
1430
1430
|
}
|
|
1431
1431
|
return stats;
|
|
@@ -1468,21 +1468,21 @@ var serveStatic = (options = { root: "" }) => {
|
|
|
1468
1468
|
return next();
|
|
1469
1469
|
}
|
|
1470
1470
|
}
|
|
1471
|
-
let
|
|
1471
|
+
let path3 = join(
|
|
1472
1472
|
root,
|
|
1473
1473
|
!optionPath && options.rewriteRequestPath ? options.rewriteRequestPath(filename, c) : filename
|
|
1474
1474
|
);
|
|
1475
|
-
let stats = getStats(
|
|
1475
|
+
let stats = getStats(path3);
|
|
1476
1476
|
if (stats && stats.isDirectory()) {
|
|
1477
1477
|
const indexFile2 = options.index ?? "index.html";
|
|
1478
|
-
|
|
1479
|
-
stats = getStats(
|
|
1478
|
+
path3 = join(path3, indexFile2);
|
|
1479
|
+
stats = getStats(path3);
|
|
1480
1480
|
}
|
|
1481
1481
|
if (!stats) {
|
|
1482
|
-
await options.onNotFound?.(
|
|
1482
|
+
await options.onNotFound?.(path3, c);
|
|
1483
1483
|
return next();
|
|
1484
1484
|
}
|
|
1485
|
-
const mimeType = getMimeType(
|
|
1485
|
+
const mimeType = getMimeType(path3);
|
|
1486
1486
|
c.header("Content-Type", mimeType || "application/octet-stream");
|
|
1487
1487
|
if (options.precompressed && (!mimeType || COMPRESSIBLE_CONTENT_TYPE_REGEX.test(mimeType))) {
|
|
1488
1488
|
const acceptEncodingSet = new Set(
|
|
@@ -1492,12 +1492,12 @@ var serveStatic = (options = { root: "" }) => {
|
|
|
1492
1492
|
if (!acceptEncodingSet.has(encoding)) {
|
|
1493
1493
|
continue;
|
|
1494
1494
|
}
|
|
1495
|
-
const precompressedStats = getStats(
|
|
1495
|
+
const precompressedStats = getStats(path3 + ENCODINGS[encoding]);
|
|
1496
1496
|
if (precompressedStats) {
|
|
1497
1497
|
c.header("Content-Encoding", encoding);
|
|
1498
1498
|
c.header("Vary", "Accept-Encoding", { append: true });
|
|
1499
1499
|
stats = precompressedStats;
|
|
1500
|
-
|
|
1500
|
+
path3 = path3 + ENCODINGS[encoding];
|
|
1501
1501
|
break;
|
|
1502
1502
|
}
|
|
1503
1503
|
}
|
|
@@ -1511,7 +1511,7 @@ var serveStatic = (options = { root: "" }) => {
|
|
|
1511
1511
|
result = c.body(null);
|
|
1512
1512
|
} else if (!range) {
|
|
1513
1513
|
c.header("Content-Length", size.toString());
|
|
1514
|
-
result = c.body(createStreamBody(createReadStream(
|
|
1514
|
+
result = c.body(createStreamBody(createReadStream(path3)), 200);
|
|
1515
1515
|
} else {
|
|
1516
1516
|
c.header("Accept-Ranges", "bytes");
|
|
1517
1517
|
c.header("Date", stats.birthtime.toUTCString());
|
|
@@ -1522,12 +1522,12 @@ var serveStatic = (options = { root: "" }) => {
|
|
|
1522
1522
|
end = size - 1;
|
|
1523
1523
|
}
|
|
1524
1524
|
const chunksize = end - start + 1;
|
|
1525
|
-
const stream2 = createReadStream(
|
|
1525
|
+
const stream2 = createReadStream(path3, { start, end });
|
|
1526
1526
|
c.header("Content-Length", chunksize.toString());
|
|
1527
1527
|
c.header("Content-Range", `bytes ${start}-${end}/${stats.size}`);
|
|
1528
1528
|
result = c.body(createStreamBody(stream2), 206);
|
|
1529
1529
|
}
|
|
1530
|
-
await options.onFound?.(
|
|
1530
|
+
await options.onFound?.(path3, c);
|
|
1531
1531
|
return result;
|
|
1532
1532
|
};
|
|
1533
1533
|
};
|
|
@@ -2176,7 +2176,7 @@ function listCredentialAccountIds(configDir2) {
|
|
|
2176
2176
|
// app/lib/whatsapp/session.ts
|
|
2177
2177
|
import { randomUUID } from "crypto";
|
|
2178
2178
|
import fsSync2 from "fs";
|
|
2179
|
-
import
|
|
2179
|
+
import fs3 from "fs/promises";
|
|
2180
2180
|
import { inspect } from "util";
|
|
2181
2181
|
import {
|
|
2182
2182
|
Browsers,
|
|
@@ -2187,7 +2187,63 @@ import {
|
|
|
2187
2187
|
useMultiFileAuthState
|
|
2188
2188
|
} from "@whiskeysockets/baileys";
|
|
2189
2189
|
import { createRequire } from "module";
|
|
2190
|
+
|
|
2191
|
+
// app/lib/whatsapp/baileys-debug-window.ts
|
|
2192
|
+
import fs2 from "fs";
|
|
2193
|
+
import path2 from "path";
|
|
2190
2194
|
var TAG2 = "[whatsapp:session]";
|
|
2195
|
+
var SENTINEL_BASENAME = "baileys-debug-until";
|
|
2196
|
+
var STAT_INTERVAL_MS = 1e3;
|
|
2197
|
+
var VALID_LEVELS = /* @__PURE__ */ new Set(["trace", "debug", "info"]);
|
|
2198
|
+
var INACTIVE = { active: false, level: "warn" };
|
|
2199
|
+
var cache = /* @__PURE__ */ new Map();
|
|
2200
|
+
function sentinelPath(authDir) {
|
|
2201
|
+
return path2.join(authDir, SENTINEL_BASENAME);
|
|
2202
|
+
}
|
|
2203
|
+
function readSentinel(authDir, nowMs) {
|
|
2204
|
+
let raw;
|
|
2205
|
+
try {
|
|
2206
|
+
raw = fs2.readFileSync(sentinelPath(authDir), "utf-8");
|
|
2207
|
+
} catch {
|
|
2208
|
+
return { state: INACTIVE, deadlineMs: null };
|
|
2209
|
+
}
|
|
2210
|
+
const line = raw.split("\n", 1)[0].trim();
|
|
2211
|
+
if (!line) return { state: INACTIVE, deadlineMs: null };
|
|
2212
|
+
const tokens = line.split(/\s+/);
|
|
2213
|
+
const deadlineMs = Date.parse(tokens[0]);
|
|
2214
|
+
if (Number.isNaN(deadlineMs) || deadlineMs <= nowMs) {
|
|
2215
|
+
return { state: INACTIVE, deadlineMs: null };
|
|
2216
|
+
}
|
|
2217
|
+
let level = "trace";
|
|
2218
|
+
if (tokens.length > 1) {
|
|
2219
|
+
if (!VALID_LEVELS.has(tokens[1])) return { state: INACTIVE, deadlineMs: null };
|
|
2220
|
+
level = tokens[1];
|
|
2221
|
+
}
|
|
2222
|
+
return { state: { active: true, level }, deadlineMs };
|
|
2223
|
+
}
|
|
2224
|
+
function resolveDebugWindow(authDir, nowMs = Date.now()) {
|
|
2225
|
+
if (!authDir) return INACTIVE;
|
|
2226
|
+
const prev = cache.get(authDir);
|
|
2227
|
+
if (prev && nowMs - prev.checkedAtMs < STAT_INTERVAL_MS) {
|
|
2228
|
+
return prev.state;
|
|
2229
|
+
}
|
|
2230
|
+
const { state, deadlineMs } = readSentinel(authDir, nowMs);
|
|
2231
|
+
const wasActive = prev?.state.active ?? false;
|
|
2232
|
+
if (!wasActive && state.active) {
|
|
2233
|
+
console.error(
|
|
2234
|
+
`${TAG2} op=baileys-debug-window-open account=${path2.basename(authDir)} level=${state.level} until=${new Date(deadlineMs).toISOString()}`
|
|
2235
|
+
);
|
|
2236
|
+
} else if (wasActive && !state.active) {
|
|
2237
|
+
console.error(
|
|
2238
|
+
`${TAG2} op=baileys-debug-window-close account=${path2.basename(authDir)}`
|
|
2239
|
+
);
|
|
2240
|
+
}
|
|
2241
|
+
cache.set(authDir, { checkedAtMs: nowMs, state });
|
|
2242
|
+
return state;
|
|
2243
|
+
}
|
|
2244
|
+
|
|
2245
|
+
// app/lib/whatsapp/session.ts
|
|
2246
|
+
var TAG3 = "[whatsapp:session]";
|
|
2191
2247
|
var WIRE_TAG = "[whatsapp:wire]";
|
|
2192
2248
|
var baileysPkgVersion = (() => {
|
|
2193
2249
|
try {
|
|
@@ -2197,7 +2253,7 @@ var baileysPkgVersion = (() => {
|
|
|
2197
2253
|
return "unknown";
|
|
2198
2254
|
}
|
|
2199
2255
|
})();
|
|
2200
|
-
console.error(`${
|
|
2256
|
+
console.error(`${TAG3} baileys package loaded version=${baileysPkgVersion}`);
|
|
2201
2257
|
var __BAILEYS_TAG = "[whatsapp:baileys]";
|
|
2202
2258
|
var INSPECT_OPTS = {
|
|
2203
2259
|
depth: null,
|
|
@@ -2222,7 +2278,8 @@ function bindingsToPrefix(bindings) {
|
|
|
2222
2278
|
}
|
|
2223
2279
|
return parts.join(" ");
|
|
2224
2280
|
}
|
|
2225
|
-
|
|
2281
|
+
var LEVEL_RANK = { trace: 10, debug: 20, info: 30 };
|
|
2282
|
+
function createBaileysLogger(bindings = {}, authDir) {
|
|
2226
2283
|
const prefix = Object.keys(bindings).length > 0 ? bindingsToPrefix(bindings) : "";
|
|
2227
2284
|
const emit = (level, args) => {
|
|
2228
2285
|
const serialized = args.map(serializeArg).join(" ");
|
|
@@ -2232,23 +2289,32 @@ function createBaileysLogger(bindings = {}) {
|
|
|
2232
2289
|
console.error(__BAILEYS_TAG, level, serialized);
|
|
2233
2290
|
}
|
|
2234
2291
|
};
|
|
2292
|
+
const emitIfWindowed = (rank, level, args) => {
|
|
2293
|
+
const w = resolveDebugWindow(authDir);
|
|
2294
|
+
if (w.active && rank >= LEVEL_RANK[w.level]) {
|
|
2295
|
+
emit(level, args);
|
|
2296
|
+
}
|
|
2297
|
+
};
|
|
2235
2298
|
return {
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2299
|
+
// Live getter: Baileys reads config.logger.level per-frame and (verified on
|
|
2300
|
+
// the pinned version) never shallow-copies the logger, so the window's level
|
|
2301
|
+
// reaches socket.js:449/465 as it changes. A Baileys that spread the logger
|
|
2302
|
+
// would freeze this to one value — revisit on upgrade.
|
|
2303
|
+
get level() {
|
|
2304
|
+
return resolveDebugWindow(authDir).level;
|
|
2242
2305
|
},
|
|
2306
|
+
trace: (...args) => emitIfWindowed(LEVEL_RANK.trace, "TRACE", args),
|
|
2307
|
+
debug: (...args) => emitIfWindowed(LEVEL_RANK.debug, "DEBUG", args),
|
|
2308
|
+
info: (...args) => emitIfWindowed(LEVEL_RANK.info, "INFO", args),
|
|
2243
2309
|
warn: (...args) => emit("WARN", args),
|
|
2244
2310
|
error: (...args) => emit("ERROR", args),
|
|
2245
2311
|
fatal: (...args) => emit("FATAL", args),
|
|
2246
|
-
child: (childBindings) => createBaileysLogger({ ...bindings, ...childBindings ?? {} })
|
|
2312
|
+
child: (childBindings) => createBaileysLogger({ ...bindings, ...childBindings ?? {} }, authDir)
|
|
2247
2313
|
};
|
|
2248
2314
|
}
|
|
2249
2315
|
var credsSaveQueue = Promise.resolve();
|
|
2250
2316
|
async function drainCredsSaveQueue(timeoutMs = 5e3) {
|
|
2251
|
-
console.error(`${
|
|
2317
|
+
console.error(`${TAG3} draining credential save queue\u2026`);
|
|
2252
2318
|
const timer2 = new Promise(
|
|
2253
2319
|
(resolve37) => setTimeout(() => resolve37("timeout"), timeoutMs)
|
|
2254
2320
|
);
|
|
@@ -2257,9 +2323,9 @@ async function drainCredsSaveQueue(timeoutMs = 5e3) {
|
|
|
2257
2323
|
timer2
|
|
2258
2324
|
]);
|
|
2259
2325
|
if (result === "timeout") {
|
|
2260
|
-
console.error(`${
|
|
2326
|
+
console.error(`${TAG3} credential save queue drain timed out after ${timeoutMs}ms`);
|
|
2261
2327
|
} else {
|
|
2262
|
-
console.error(`${
|
|
2328
|
+
console.error(`${TAG3} credential save queue drained`);
|
|
2263
2329
|
}
|
|
2264
2330
|
}
|
|
2265
2331
|
function readCredsJsonRaw2(filePath) {
|
|
@@ -2282,53 +2348,53 @@ async function safeSaveCreds(authDir, saveCreds) {
|
|
|
2282
2348
|
JSON.parse(raw);
|
|
2283
2349
|
fsSync2.copyFileSync(credsPath, backupPath);
|
|
2284
2350
|
} catch (err) {
|
|
2285
|
-
console.warn(`${
|
|
2351
|
+
console.warn(`${TAG3} backup pre-copy failed (parse or write) authDir=${authDir}: ${String(err)}`);
|
|
2286
2352
|
}
|
|
2287
2353
|
}
|
|
2288
2354
|
} catch (err) {
|
|
2289
|
-
console.warn(`${
|
|
2355
|
+
console.warn(`${TAG3} backup preparation failed authDir=${authDir}: ${String(err)}`);
|
|
2290
2356
|
}
|
|
2291
2357
|
try {
|
|
2292
2358
|
await Promise.resolve(saveCreds());
|
|
2293
2359
|
const credsPath = resolveCredsPath(authDir);
|
|
2294
2360
|
try {
|
|
2295
|
-
const stats = await
|
|
2361
|
+
const stats = await fs3.stat(credsPath);
|
|
2296
2362
|
if (stats.size <= 1) {
|
|
2297
|
-
console.error(`${
|
|
2363
|
+
console.error(`${TAG3} WARNING: creds file empty/truncated after save path=${credsPath} size=${stats.size}`);
|
|
2298
2364
|
} else {
|
|
2299
|
-
console.error(`${
|
|
2365
|
+
console.error(`${TAG3} creds saved and verified path=${credsPath} size=${stats.size}`);
|
|
2300
2366
|
}
|
|
2301
2367
|
} catch (statErr) {
|
|
2302
|
-
console.error(`${
|
|
2368
|
+
console.error(`${TAG3} creds NOT found on disk after save path=${credsPath}: ${String(statErr)}`);
|
|
2303
2369
|
}
|
|
2304
2370
|
} catch (err) {
|
|
2305
|
-
console.error(`${
|
|
2371
|
+
console.error(`${TAG3} failed saving creds to ${authDir}: ${String(err)}`);
|
|
2306
2372
|
}
|
|
2307
2373
|
}
|
|
2308
2374
|
function enqueueSaveCreds(authDir, saveCreds) {
|
|
2309
2375
|
credsSaveQueue = credsSaveQueue.then(() => safeSaveCreds(authDir, saveCreds)).catch((err) => {
|
|
2310
|
-
console.error(`${
|
|
2376
|
+
console.error(`${TAG3} creds save queue error: ${String(err)}`);
|
|
2311
2377
|
});
|
|
2312
2378
|
}
|
|
2313
2379
|
async function createWaSocket(opts) {
|
|
2314
2380
|
const { authDir, onQr, onConnectionUpdate, silent, correlationId, account, getCodeIssuedTs } = opts;
|
|
2315
2381
|
const cid = correlationId ?? "none";
|
|
2316
2382
|
const acct = account ?? "unknown";
|
|
2317
|
-
await
|
|
2383
|
+
await fs3.mkdir(authDir, { recursive: true });
|
|
2318
2384
|
maybeRestoreCredsFromBackup(authDir);
|
|
2319
2385
|
const { state, saveCreds } = await useMultiFileAuthState(authDir);
|
|
2320
2386
|
const { version } = await fetchLatestBaileysVersion();
|
|
2321
2387
|
if (!silent) {
|
|
2322
2388
|
const preCensus = credsCensus(authDir);
|
|
2323
2389
|
console.error(
|
|
2324
|
-
`${
|
|
2390
|
+
`${TAG3} op=creds-pre cid=${cid} account=${acct} credsPresent=${preCensus.credsPresent} registered=${preCensus.registered} hasAccount=${preCensus.hasAccountSignature}`
|
|
2325
2391
|
);
|
|
2326
2392
|
}
|
|
2327
2393
|
const openTs = Date.now();
|
|
2328
2394
|
console.error(
|
|
2329
|
-
`${
|
|
2395
|
+
`${TAG3} op=socket-open cid=${cid} account=${acct} baileysVersion=${baileysPkgVersion} waProtocolVersion=${version.join(".")} openTs=${openTs}`
|
|
2330
2396
|
);
|
|
2331
|
-
const baileysLogger = createBaileysLogger();
|
|
2397
|
+
const baileysLogger = createBaileysLogger({}, authDir);
|
|
2332
2398
|
const sock = makeWASocket({
|
|
2333
2399
|
auth: {
|
|
2334
2400
|
creds: state.creds,
|
|
@@ -2343,7 +2409,7 @@ async function createWaSocket(opts) {
|
|
|
2343
2409
|
fireInitQueries: false
|
|
2344
2410
|
});
|
|
2345
2411
|
sock.ev.on("creds.update", () => {
|
|
2346
|
-
console.error(`${
|
|
2412
|
+
console.error(`${TAG3} creds.update received \u2014 saving to ${authDir}`);
|
|
2347
2413
|
enqueueSaveCreds(authDir, saveCreds);
|
|
2348
2414
|
});
|
|
2349
2415
|
let qrSequence = 0;
|
|
@@ -2363,14 +2429,14 @@ async function createWaSocket(opts) {
|
|
|
2363
2429
|
parts.push(`disconnectReason=${formatError(update.lastDisconnect.error)}`);
|
|
2364
2430
|
}
|
|
2365
2431
|
if (!silent && parts.length > 0) {
|
|
2366
|
-
console.error(`${
|
|
2432
|
+
console.error(`${TAG3} connection.update ${parts.join(" ")}`);
|
|
2367
2433
|
}
|
|
2368
2434
|
if (update.qr && !silent) {
|
|
2369
2435
|
const now = Date.now();
|
|
2370
2436
|
const sinceLastRefMs = lastQrTs ? now - lastQrTs : null;
|
|
2371
2437
|
lastQrTs = now;
|
|
2372
2438
|
console.error(
|
|
2373
|
-
`${
|
|
2439
|
+
`${TAG3} op=qr cid=${cid} qr=#${qrSequence} sinceOpenMs=${now - openTs} sinceLastRefMs=${sinceLastRefMs ?? "n/a"}`
|
|
2374
2440
|
);
|
|
2375
2441
|
}
|
|
2376
2442
|
if (update.isNewLogin === true && !pairSuccessLogged && !silent) {
|
|
@@ -2379,7 +2445,7 @@ async function createWaSocket(opts) {
|
|
|
2379
2445
|
const sinceCodeIssuedMs = codeIssuedTs ? Date.now() - codeIssuedTs : null;
|
|
2380
2446
|
const afterCensus = credsCensus(authDir);
|
|
2381
2447
|
console.error(
|
|
2382
|
-
`${
|
|
2448
|
+
`${TAG3} op=pair-success cid=${cid} account=${acct} sinceCodeIssuedMs=${sinceCodeIssuedMs ?? "n/a"} hasAccountAfter=${afterCensus.hasAccountSignature}`
|
|
2383
2449
|
);
|
|
2384
2450
|
}
|
|
2385
2451
|
if (update.qr && onQr) {
|
|
@@ -2389,25 +2455,25 @@ async function createWaSocket(opts) {
|
|
|
2389
2455
|
const status = getStatusCode(update.lastDisconnect?.error);
|
|
2390
2456
|
if (!silent) {
|
|
2391
2457
|
console.error(
|
|
2392
|
-
`${
|
|
2458
|
+
`${TAG3} op=close cid=${cid} account=${acct} status=${status ?? "unknown"} reason="${formatError(update.lastDisconnect?.error)}" socketLifeMs=${Date.now() - openTs} kind=${closeKind(status, everOpen)}`
|
|
2393
2459
|
);
|
|
2394
2460
|
}
|
|
2395
2461
|
if (status === DisconnectReason.loggedOut && !silent) {
|
|
2396
|
-
console.error(`${
|
|
2462
|
+
console.error(`${TAG3} session logged out (401) \u2014 re-link required`);
|
|
2397
2463
|
}
|
|
2398
2464
|
}
|
|
2399
2465
|
if (update.connection === "open") {
|
|
2400
2466
|
everOpen = true;
|
|
2401
|
-
if (!silent) console.error(`${
|
|
2467
|
+
if (!silent) console.error(`${TAG3} connected`);
|
|
2402
2468
|
}
|
|
2403
2469
|
onConnectionUpdate?.(update);
|
|
2404
2470
|
} catch (err) {
|
|
2405
|
-
console.error(`${
|
|
2471
|
+
console.error(`${TAG3} connection.update handler error: ${String(err)}`);
|
|
2406
2472
|
}
|
|
2407
2473
|
});
|
|
2408
2474
|
if (sock.ws && typeof sock.ws.on === "function") {
|
|
2409
2475
|
sock.ws.on("error", (err) => {
|
|
2410
|
-
console.error(`${
|
|
2476
|
+
console.error(`${TAG3} WebSocket error: ${String(err)}`);
|
|
2411
2477
|
});
|
|
2412
2478
|
sock.ws.on("CB:message", (node) => {
|
|
2413
2479
|
const attrs = node?.attrs ?? {};
|
|
@@ -2547,7 +2613,7 @@ function withTimeout(label, promise, timeoutMs) {
|
|
|
2547
2613
|
}
|
|
2548
2614
|
|
|
2549
2615
|
// app/lib/whatsapp/init-queries.ts
|
|
2550
|
-
var
|
|
2616
|
+
var TAG4 = "[whatsapp:init]";
|
|
2551
2617
|
var DEFAULT_QUERY_TIMEOUT_MS = 15e3;
|
|
2552
2618
|
var INSPECT_OPTS2 = {
|
|
2553
2619
|
depth: null,
|
|
@@ -2575,20 +2641,20 @@ async function runInitQueries(sock, ctx) {
|
|
|
2575
2641
|
elapsedMs: 0
|
|
2576
2642
|
};
|
|
2577
2643
|
console.error(
|
|
2578
|
-
`${
|
|
2644
|
+
`${TAG4} runInitQueries start account=${ctx.accountId} attempt=${ctx.attempt} selfPhone=${ctx.selfPhone ?? "unknown"} authDir=${ctx.authDir} queryTimeoutMs=${queryTimeoutMs}`
|
|
2579
2645
|
);
|
|
2580
2646
|
console.error(
|
|
2581
|
-
`${
|
|
2647
|
+
`${TAG4} fetchProps SKIPPED account=${ctx.accountId} reason=no-platform-consumer`
|
|
2582
2648
|
);
|
|
2583
2649
|
if (ctx.signal?.aborted) {
|
|
2584
2650
|
result.aborted = true;
|
|
2585
2651
|
result.elapsedMs = Date.now() - startedAt;
|
|
2586
2652
|
console.error(
|
|
2587
|
-
`${
|
|
2653
|
+
`${TAG4} runInitQueries aborted before start account=${ctx.accountId}`
|
|
2588
2654
|
);
|
|
2589
2655
|
return result;
|
|
2590
2656
|
}
|
|
2591
|
-
console.error(`${
|
|
2657
|
+
console.error(`${TAG4} fetchBlocklist start account=${ctx.accountId}`);
|
|
2592
2658
|
try {
|
|
2593
2659
|
const blocklist = await withTimeout(
|
|
2594
2660
|
"fetchBlocklist",
|
|
@@ -2598,24 +2664,24 @@ async function runInitQueries(sock, ctx) {
|
|
|
2598
2664
|
const count = Array.isArray(blocklist) ? blocklist.length : 0;
|
|
2599
2665
|
result.blocklistOk = true;
|
|
2600
2666
|
console.error(
|
|
2601
|
-
`${
|
|
2667
|
+
`${TAG4} fetchBlocklist ok account=${ctx.accountId} count=${count}`
|
|
2602
2668
|
);
|
|
2603
2669
|
} catch (err) {
|
|
2604
2670
|
const formatted = formatErr(err);
|
|
2605
2671
|
result.errors.blocklist = formatted;
|
|
2606
2672
|
console.error(
|
|
2607
|
-
`${
|
|
2673
|
+
`${TAG4} fetchBlocklist FAILED account=${ctx.accountId}: ${formatted}`
|
|
2608
2674
|
);
|
|
2609
2675
|
}
|
|
2610
2676
|
if (ctx.signal?.aborted) {
|
|
2611
2677
|
result.aborted = true;
|
|
2612
2678
|
result.elapsedMs = Date.now() - startedAt;
|
|
2613
2679
|
console.error(
|
|
2614
|
-
`${
|
|
2680
|
+
`${TAG4} runInitQueries aborted mid-sequence account=${ctx.accountId} after=fetchBlocklist`
|
|
2615
2681
|
);
|
|
2616
2682
|
return result;
|
|
2617
2683
|
}
|
|
2618
|
-
console.error(`${
|
|
2684
|
+
console.error(`${TAG4} fetchPrivacySettings start account=${ctx.accountId}`);
|
|
2619
2685
|
try {
|
|
2620
2686
|
const settings = await withTimeout(
|
|
2621
2687
|
"fetchPrivacySettings",
|
|
@@ -2625,18 +2691,18 @@ async function runInitQueries(sock, ctx) {
|
|
|
2625
2691
|
);
|
|
2626
2692
|
result.privacySettingsOk = true;
|
|
2627
2693
|
console.error(
|
|
2628
|
-
`${
|
|
2694
|
+
`${TAG4} fetchPrivacySettings ok account=${ctx.accountId} keys=${settings ? Object.keys(settings).join(",") : ""}`
|
|
2629
2695
|
);
|
|
2630
2696
|
} catch (err) {
|
|
2631
2697
|
const formatted = formatErr(err);
|
|
2632
2698
|
result.errors.privacySettings = formatted;
|
|
2633
2699
|
console.error(
|
|
2634
|
-
`${
|
|
2700
|
+
`${TAG4} fetchPrivacySettings FAILED account=${ctx.accountId}: ${formatted}`
|
|
2635
2701
|
);
|
|
2636
2702
|
}
|
|
2637
2703
|
result.elapsedMs = Date.now() - startedAt;
|
|
2638
2704
|
console.error(
|
|
2639
|
-
`${
|
|
2705
|
+
`${TAG4} runInitQueries done account=${ctx.accountId} blocklistOk=${result.blocklistOk} privacySettingsOk=${result.privacySettingsOk} elapsedMs=${result.elapsedMs}`
|
|
2640
2706
|
);
|
|
2641
2707
|
return result;
|
|
2642
2708
|
}
|
|
@@ -3533,17 +3599,17 @@ var INBOUND_TTL_MS = 20 * 6e4;
|
|
|
3533
3599
|
var INBOUND_MAX = 5e3;
|
|
3534
3600
|
var agentSentCache = /* @__PURE__ */ new Map();
|
|
3535
3601
|
var inboundCache = /* @__PURE__ */ new Map();
|
|
3536
|
-
function pruneCache(
|
|
3602
|
+
function pruneCache(cache3, ttl, max) {
|
|
3537
3603
|
const now = Date.now();
|
|
3538
|
-
for (const [key, entry] of
|
|
3539
|
-
if (now - entry.ts > ttl)
|
|
3604
|
+
for (const [key, entry] of cache3) {
|
|
3605
|
+
if (now - entry.ts > ttl) cache3.delete(key);
|
|
3540
3606
|
}
|
|
3541
|
-
if (
|
|
3542
|
-
const excess =
|
|
3607
|
+
if (cache3.size > max) {
|
|
3608
|
+
const excess = cache3.size - max;
|
|
3543
3609
|
let count = 0;
|
|
3544
|
-
for (const key of
|
|
3610
|
+
for (const key of cache3.keys()) {
|
|
3545
3611
|
if (count >= excess) break;
|
|
3546
|
-
|
|
3612
|
+
cache3.delete(key);
|
|
3547
3613
|
count++;
|
|
3548
3614
|
}
|
|
3549
3615
|
}
|
|
@@ -3577,7 +3643,7 @@ function isDuplicateInbound(key) {
|
|
|
3577
3643
|
}
|
|
3578
3644
|
|
|
3579
3645
|
// app/lib/whatsapp/inbound/self-chat.ts
|
|
3580
|
-
var
|
|
3646
|
+
var TAG5 = "[whatsapp:self-chat]";
|
|
3581
3647
|
function normalizeJid(jid) {
|
|
3582
3648
|
const m = jid.match(/^(\d+)(?::\d+)?@(s\.whatsapp\.net|lid)$/i);
|
|
3583
3649
|
return m ? `${m[1]}@${m[2].toLowerCase()}` : jid;
|
|
@@ -3597,10 +3663,10 @@ function evaluateSelfChatCommand(input) {
|
|
|
3597
3663
|
const connAtMs = input.lastConnectedAtMs ?? 0;
|
|
3598
3664
|
const msgId = input.msgId ?? "?";
|
|
3599
3665
|
console.error(
|
|
3600
|
-
`${
|
|
3666
|
+
`${TAG5} op=seen account=${input.accountId} msgId=${msgId} tsMs=${tsMs} connAtMs=${connAtMs} fromMe=true`
|
|
3601
3667
|
);
|
|
3602
3668
|
const skip = (reason) => {
|
|
3603
|
-
console.error(`${
|
|
3669
|
+
console.error(`${TAG5} op=skip account=${input.accountId} msgId=${msgId} reason=${reason}`);
|
|
3604
3670
|
return { kind: "skip", reason };
|
|
3605
3671
|
};
|
|
3606
3672
|
if (input.msgId && isAgentSentMessage(input.msgId)) return skip("echo");
|
|
@@ -3608,13 +3674,13 @@ function evaluateSelfChatCommand(input) {
|
|
|
3608
3674
|
if (isDuplicateInbound(`${input.accountId}:${input.remoteJid}:${input.msgId}`)) return skip("duplicate");
|
|
3609
3675
|
if (!input.text) return skip("no-text");
|
|
3610
3676
|
console.error(
|
|
3611
|
-
`${
|
|
3677
|
+
`${TAG5} op=dispatch account=${input.accountId} msgId=${msgId} bytes=${Buffer.byteLength(input.text, "utf8")} agentType=admin`
|
|
3612
3678
|
);
|
|
3613
3679
|
return { kind: "dispatch", text: input.text };
|
|
3614
3680
|
}
|
|
3615
3681
|
|
|
3616
3682
|
// app/lib/whatsapp/activity.ts
|
|
3617
|
-
var
|
|
3683
|
+
var TAG6 = "[whatsapp:activity]";
|
|
3618
3684
|
var RECENT_EVENTS_MAX = 200;
|
|
3619
3685
|
var counters = /* @__PURE__ */ new Map();
|
|
3620
3686
|
var recentEvents = [];
|
|
@@ -3642,10 +3708,10 @@ function recordActivity(event) {
|
|
|
3642
3708
|
recentEvents.splice(0, recentEvents.length - RECENT_EVENTS_MAX);
|
|
3643
3709
|
}
|
|
3644
3710
|
console.error(
|
|
3645
|
-
`${
|
|
3711
|
+
`${TAG6} channel-activity: direction=${direction} account=${accountId} jid=${jid}` + (messageType ? ` type=${messageType}` : "")
|
|
3646
3712
|
);
|
|
3647
3713
|
} catch (err) {
|
|
3648
|
-
console.error(`${
|
|
3714
|
+
console.error(`${TAG6} recording failed: ${String(err)}`);
|
|
3649
3715
|
}
|
|
3650
3716
|
}
|
|
3651
3717
|
function getChannelActivity(accountId) {
|
|
@@ -3666,7 +3732,7 @@ function getChannelActivity(accountId) {
|
|
|
3666
3732
|
}
|
|
3667
3733
|
|
|
3668
3734
|
// app/lib/whatsapp/outbound/send.ts
|
|
3669
|
-
var
|
|
3735
|
+
var TAG7 = "[whatsapp:outbound]";
|
|
3670
3736
|
function buildQuoted(q) {
|
|
3671
3737
|
return {
|
|
3672
3738
|
key: {
|
|
@@ -3687,14 +3753,14 @@ async function sendTextMessage(sock, to, text, opts) {
|
|
|
3687
3753
|
const messageId = result?.key?.id;
|
|
3688
3754
|
if (messageId) {
|
|
3689
3755
|
trackAgentSentMessage(messageId);
|
|
3690
|
-
console.error(`${
|
|
3756
|
+
console.error(`${TAG7} sent text to=${jid} id=${messageId}`);
|
|
3691
3757
|
}
|
|
3692
3758
|
if (opts?.accountId) {
|
|
3693
3759
|
recordActivity({ accountId: opts.accountId, direction: "outbound", jid, messageType: "text" });
|
|
3694
3760
|
}
|
|
3695
3761
|
return { success: true, messageId: messageId ?? void 0 };
|
|
3696
3762
|
} catch (err) {
|
|
3697
|
-
console.error(`${
|
|
3763
|
+
console.error(`${TAG7} send failed to=${to}: ${String(err)}`);
|
|
3698
3764
|
return { success: false, error: String(err) };
|
|
3699
3765
|
}
|
|
3700
3766
|
}
|
|
@@ -3764,20 +3830,20 @@ async function sendMediaMessage(sock, to, media, opts) {
|
|
|
3764
3830
|
const messageId = result?.key?.id;
|
|
3765
3831
|
if (messageId) {
|
|
3766
3832
|
trackAgentSentMessage(messageId);
|
|
3767
|
-
console.error(`${
|
|
3833
|
+
console.error(`${TAG7} sent ${media.type} to=${jid} id=${messageId}`);
|
|
3768
3834
|
}
|
|
3769
3835
|
if (opts?.accountId) {
|
|
3770
3836
|
recordActivity({ accountId: opts.accountId, direction: "outbound", jid, messageType: media.type });
|
|
3771
3837
|
}
|
|
3772
3838
|
return { success: true, messageId: messageId ?? void 0 };
|
|
3773
3839
|
} catch (err) {
|
|
3774
|
-
console.error(`${
|
|
3840
|
+
console.error(`${TAG7} send media failed to=${to}: ${String(err)}`);
|
|
3775
3841
|
return { success: false, error: String(err) };
|
|
3776
3842
|
}
|
|
3777
3843
|
}
|
|
3778
3844
|
|
|
3779
3845
|
// app/lib/whatsapp/persist-message.ts
|
|
3780
|
-
var
|
|
3846
|
+
var TAG8 = "[whatsapp-persist]";
|
|
3781
3847
|
var sessionWriteLocks = /* @__PURE__ */ new Map();
|
|
3782
3848
|
async function persistWhatsAppMessage(input) {
|
|
3783
3849
|
if (!input.body && !input.attachmentId || !input.msgKeyId) return null;
|
|
@@ -3828,14 +3894,14 @@ async function persistWhatsAppMessage(input) {
|
|
|
3828
3894
|
};
|
|
3829
3895
|
const result = await session.run(cypher, params);
|
|
3830
3896
|
if (result.records.length === 0) {
|
|
3831
|
-
console.error(`${
|
|
3897
|
+
console.error(`${TAG8} skip reason=conversation-not-found accountId=${input.platformAccountId} cacheKey=${input.cacheKey} messageId=${messageId}`);
|
|
3832
3898
|
return null;
|
|
3833
3899
|
}
|
|
3834
3900
|
const rec = result.records[0];
|
|
3835
3901
|
const senderElementId = rec.get("senderElementId");
|
|
3836
3902
|
const senderReused = rec.get("senderReused") === true;
|
|
3837
3903
|
console.error(
|
|
3838
|
-
`${
|
|
3904
|
+
`${TAG8} sender-resolved telephone=${senderTelephone} nodeKind=Person elementId=${senderElementId} reused=${senderReused}`
|
|
3839
3905
|
);
|
|
3840
3906
|
const diskRecord = {
|
|
3841
3907
|
messageId,
|
|
@@ -3857,18 +3923,18 @@ async function persistWhatsAppMessage(input) {
|
|
|
3857
3923
|
const { created } = appendMessage(input.platformAccountId, diskRecord, altDedupeRemoteJids);
|
|
3858
3924
|
const ms = Date.now() - t0;
|
|
3859
3925
|
if (!created) {
|
|
3860
|
-
console.error(`${
|
|
3926
|
+
console.error(`${TAG8} noop reason=existing-message messageId=${messageId}`);
|
|
3861
3927
|
return { messageId, created: false, senderElementId };
|
|
3862
3928
|
}
|
|
3863
3929
|
console.error(
|
|
3864
|
-
`${
|
|
3930
|
+
`${TAG8} write messageId=${messageId} accountId=${input.platformAccountId} cacheKey=${input.cacheKey} fromMe=${input.fromMe} dateSent=${dateSentIso} bodyBytes=${Buffer.byteLength(input.body, "utf8")} attachment=${input.attachmentId ?? "none"} ms=${ms}`
|
|
3865
3931
|
);
|
|
3866
3932
|
return { messageId, created: true, senderElementId };
|
|
3867
3933
|
} catch (err) {
|
|
3868
3934
|
const ms = Date.now() - t0;
|
|
3869
3935
|
const reason = sanitizeReason(err);
|
|
3870
3936
|
console.error(
|
|
3871
|
-
`${
|
|
3937
|
+
`${TAG8} FAIL accountId=${input.platformAccountId} waname=${input.accountId} remoteJid=${input.remoteJid} msgKey=${input.msgKeyId} reason=${reason} ms=${ms}`
|
|
3872
3938
|
);
|
|
3873
3939
|
return null;
|
|
3874
3940
|
} finally {
|
|
@@ -3901,7 +3967,7 @@ function sanitizeReason(err) {
|
|
|
3901
3967
|
}
|
|
3902
3968
|
|
|
3903
3969
|
// app/lib/whatsapp/ensure-conversation.ts
|
|
3904
|
-
var
|
|
3970
|
+
var TAG9 = "[whatsapp-persist]";
|
|
3905
3971
|
async function ensureWhatsAppConversation(input) {
|
|
3906
3972
|
const t0 = Date.now();
|
|
3907
3973
|
try {
|
|
@@ -3913,19 +3979,19 @@ async function ensureWhatsAppConversation(input) {
|
|
|
3913
3979
|
const ms = Date.now() - t0;
|
|
3914
3980
|
if (!result.sessionId) {
|
|
3915
3981
|
console.error(
|
|
3916
|
-
`${
|
|
3982
|
+
`${TAG9} conversation-merged FAIL cacheKey=${input.cacheKey} reason=null-sessionId ms=${ms}`
|
|
3917
3983
|
);
|
|
3918
3984
|
return null;
|
|
3919
3985
|
}
|
|
3920
3986
|
console.error(
|
|
3921
|
-
`${
|
|
3987
|
+
`${TAG9} conversation-merged cacheKey=${input.cacheKey} agentType=${input.agentType} channel=whatsapp created=${result.created} ms=${ms}`
|
|
3922
3988
|
);
|
|
3923
3989
|
return { sessionId: result.sessionId, created: result.created };
|
|
3924
3990
|
} catch (err) {
|
|
3925
3991
|
const ms = Date.now() - t0;
|
|
3926
3992
|
const reason = err instanceof Error ? `${err.name}:${err.message.slice(0, 200)}` : String(err).slice(0, 200);
|
|
3927
3993
|
console.error(
|
|
3928
|
-
`${
|
|
3994
|
+
`${TAG9} conversation-merged FAIL cacheKey=${input.cacheKey} reason=${reason} ms=${ms}`
|
|
3929
3995
|
);
|
|
3930
3996
|
return null;
|
|
3931
3997
|
}
|
|
@@ -3935,16 +4001,16 @@ async function ensureWhatsAppConversation(input) {
|
|
|
3935
4001
|
import { readdirSync as readdirSync2, readFileSync as readFileSync5 } from "fs";
|
|
3936
4002
|
import { resolve as resolve3 } from "path";
|
|
3937
4003
|
var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
3938
|
-
var
|
|
4004
|
+
var cache2 = /* @__PURE__ */ new Map();
|
|
3939
4005
|
function enumerateValidAccountIds(accountsDir) {
|
|
3940
|
-
const cached3 =
|
|
4006
|
+
const cached3 = cache2.get(accountsDir);
|
|
3941
4007
|
if (cached3 !== void 0) return cached3;
|
|
3942
4008
|
let names;
|
|
3943
4009
|
try {
|
|
3944
4010
|
names = readdirSync2(accountsDir);
|
|
3945
4011
|
} catch (err) {
|
|
3946
4012
|
if (err.code === "ENOENT") {
|
|
3947
|
-
|
|
4013
|
+
cache2.set(accountsDir, []);
|
|
3948
4014
|
return [];
|
|
3949
4015
|
}
|
|
3950
4016
|
throw err;
|
|
@@ -3961,7 +4027,7 @@ function enumerateValidAccountIds(accountsDir) {
|
|
|
3961
4027
|
if (code === "ENOENT") continue;
|
|
3962
4028
|
}
|
|
3963
4029
|
}
|
|
3964
|
-
|
|
4030
|
+
cache2.set(accountsDir, valid);
|
|
3965
4031
|
return valid;
|
|
3966
4032
|
}
|
|
3967
4033
|
function resolveHouseOrSoleAccountId(accountsDir) {
|
|
@@ -4559,7 +4625,7 @@ async function storeComponentArtefact(accountId, attachmentId, mimeType, content
|
|
|
4559
4625
|
}
|
|
4560
4626
|
|
|
4561
4627
|
// app/lib/whatsapp/inbound/media.ts
|
|
4562
|
-
var
|
|
4628
|
+
var TAG10 = "[whatsapp:media]";
|
|
4563
4629
|
var MEDIA_DIR = "/tmp/maxy-media";
|
|
4564
4630
|
var DOWNLOAD_TIMEOUT_MS = 45e3;
|
|
4565
4631
|
function deterministicAttachmentId(seed) {
|
|
@@ -4647,25 +4713,25 @@ async function downloadInboundMediaBuffer(msg, sock, opts) {
|
|
|
4647
4713
|
}
|
|
4648
4714
|
);
|
|
4649
4715
|
if (!buffer || buffer.length === 0) {
|
|
4650
|
-
console.error(`${
|
|
4716
|
+
console.error(`${TAG10} primary download returned empty, trying direct fallback`);
|
|
4651
4717
|
const downloadable = getDownloadableContent(content);
|
|
4652
4718
|
if (downloadable) {
|
|
4653
4719
|
try {
|
|
4654
4720
|
const stream2 = await downloadContentFromMessage(downloadable.downloadable, downloadable.mediaType);
|
|
4655
4721
|
buffer = await streamToBuffer(stream2);
|
|
4656
4722
|
} catch (fallbackErr) {
|
|
4657
|
-
console.error(`${
|
|
4723
|
+
console.error(`${TAG10} direct download fallback failed: ${String(fallbackErr)}`);
|
|
4658
4724
|
}
|
|
4659
4725
|
}
|
|
4660
4726
|
}
|
|
4661
4727
|
if (!buffer || buffer.length === 0) {
|
|
4662
|
-
console.error(`${
|
|
4728
|
+
console.error(`${TAG10} download failed: empty buffer for ${mimetype ?? "unknown"}`);
|
|
4663
4729
|
return void 0;
|
|
4664
4730
|
}
|
|
4665
4731
|
if (buffer.length > maxBytes) {
|
|
4666
4732
|
const sizeMB = (buffer.length / (1024 * 1024)).toFixed(1);
|
|
4667
4733
|
const limitMB = (maxBytes / (1024 * 1024)).toFixed(0);
|
|
4668
|
-
console.error(`${
|
|
4734
|
+
console.error(`${TAG10} media too large type=${mimetype ?? "unknown"} size=${sizeMB}MB limit=${limitMB}MB`);
|
|
4669
4735
|
return void 0;
|
|
4670
4736
|
}
|
|
4671
4737
|
return {
|
|
@@ -4677,7 +4743,7 @@ async function downloadInboundMediaBuffer(msg, sock, opts) {
|
|
|
4677
4743
|
timeoutMs
|
|
4678
4744
|
);
|
|
4679
4745
|
} catch (err) {
|
|
4680
|
-
console.error(`${
|
|
4746
|
+
console.error(`${TAG10} media download failed type=${mimetype ?? "unknown"} error=${String(err)}`);
|
|
4681
4747
|
return void 0;
|
|
4682
4748
|
}
|
|
4683
4749
|
}
|
|
@@ -4689,7 +4755,7 @@ async function downloadInboundMedia(msg, sock, opts) {
|
|
|
4689
4755
|
const filePath = join6(MEDIA_DIR, `${randomUUID4()}.${ext}`);
|
|
4690
4756
|
await writeFile2(filePath, res.buffer);
|
|
4691
4757
|
const sizeKB = (res.buffer.length / 1024).toFixed(0);
|
|
4692
|
-
console.error(`${
|
|
4758
|
+
console.error(`${TAG10} media downloaded type=${res.mimetype} size=${sizeKB}KB path=${filePath}`);
|
|
4693
4759
|
return { path: filePath, mimetype: res.mimetype, size: res.buffer.length };
|
|
4694
4760
|
}
|
|
4695
4761
|
async function storeInboundMediaServable(msg, sock, accountId, seed, opts) {
|
|
@@ -4698,19 +4764,27 @@ async function storeInboundMediaServable(msg, sock, accountId, seed, opts) {
|
|
|
4698
4764
|
if (attachmentExists(accountId, attachmentId)) return attachmentId;
|
|
4699
4765
|
const res = await downloadInboundMediaBufferCached(msg, sock, opts);
|
|
4700
4766
|
if (!res) {
|
|
4701
|
-
console.error(`${
|
|
4767
|
+
console.error(`${TAG10} servable-store skip reason=no-buffer id=${attachmentId}`);
|
|
4702
4768
|
return null;
|
|
4703
4769
|
}
|
|
4704
4770
|
const filename = res.filename ?? `${attachmentId}.${mimeToExt(res.mimetype)}`;
|
|
4705
4771
|
await storeMediaBuffer(accountId, attachmentId, filename, res.mimetype, res.buffer);
|
|
4706
|
-
console.error(`${
|
|
4772
|
+
console.error(`${TAG10} servable-store ok id=${attachmentId} mime=${res.mimetype} bytes=${res.buffer.length}`);
|
|
4707
4773
|
return attachmentId;
|
|
4708
4774
|
} catch (err) {
|
|
4709
|
-
console.error(`${
|
|
4775
|
+
console.error(`${TAG10} servable-store FAIL id=${attachmentId} reason=${String(err).slice(0, 120)}`);
|
|
4710
4776
|
return null;
|
|
4711
4777
|
}
|
|
4712
4778
|
}
|
|
4713
4779
|
|
|
4780
|
+
// app/lib/whatsapp/outbound/document-limits.ts
|
|
4781
|
+
var WHATSAPP_DOCUMENT_MAX_BYTES = 100 * 1024 * 1024;
|
|
4782
|
+
|
|
4783
|
+
// app/lib/whatsapp/echo-store-max-bytes.ts
|
|
4784
|
+
function echoStoreMaxBytes(args) {
|
|
4785
|
+
return args.fromMe ? WHATSAPP_DOCUMENT_MAX_BYTES : (args.mediaMaxMb ?? 50) * 1024 * 1024;
|
|
4786
|
+
}
|
|
4787
|
+
|
|
4714
4788
|
// app/lib/whatsapp/inbound/bounded-queue.ts
|
|
4715
4789
|
function createBoundedQueue(limit, opts) {
|
|
4716
4790
|
if (!Number.isInteger(limit) || limit < 1) {
|
|
@@ -4746,7 +4820,7 @@ function createBoundedQueue(limit, opts) {
|
|
|
4746
4820
|
}
|
|
4747
4821
|
|
|
4748
4822
|
// app/lib/whatsapp/inbound/debounce.ts
|
|
4749
|
-
var
|
|
4823
|
+
var TAG11 = "[whatsapp:debounce]";
|
|
4750
4824
|
var STT_TAG = "[whatsapp:stt-await]";
|
|
4751
4825
|
function createInboundDebouncer(opts) {
|
|
4752
4826
|
const { debounceMs, buildKey, onFlush, onError } = opts;
|
|
@@ -4777,7 +4851,7 @@ function createInboundDebouncer(opts) {
|
|
|
4777
4851
|
pending.delete(key);
|
|
4778
4852
|
const batchSize = batch.entries.length;
|
|
4779
4853
|
try {
|
|
4780
|
-
console.error(`${
|
|
4854
|
+
console.error(`${TAG11} debounce flush key=${key} batchSize=${batchSize}`);
|
|
4781
4855
|
const result = onFlush(batch.entries);
|
|
4782
4856
|
if (result && typeof result.catch === "function") {
|
|
4783
4857
|
result.catch(onError);
|
|
@@ -4857,7 +4931,7 @@ function combineInboundBatch(entries) {
|
|
|
4857
4931
|
}
|
|
4858
4932
|
|
|
4859
4933
|
// app/lib/whatsapp/opening-hours.ts
|
|
4860
|
-
var
|
|
4934
|
+
var TAG12 = "[whatsapp:hours]";
|
|
4861
4935
|
async function isBusinessOpen(accountId) {
|
|
4862
4936
|
try {
|
|
4863
4937
|
const timezone = await resolveTimezone(accountId);
|
|
@@ -4875,7 +4949,7 @@ async function isBusinessOpen(accountId) {
|
|
|
4875
4949
|
{ accountId, dayOfWeek, previousDayOfWeek }
|
|
4876
4950
|
);
|
|
4877
4951
|
if (result.records.length === 0) {
|
|
4878
|
-
console.error(`${
|
|
4952
|
+
console.error(`${TAG12} [${accountId}] business hours check: no opening hours configured, treating as open`);
|
|
4879
4953
|
return { open: true, reason: "no opening hours configured" };
|
|
4880
4954
|
}
|
|
4881
4955
|
const specs = result.records.filter((r) => r.get("opens") != null && r.get("closes") != null).map((r) => ({
|
|
@@ -4884,7 +4958,7 @@ async function isBusinessOpen(accountId) {
|
|
|
4884
4958
|
closes: String(r.get("closes")).trim()
|
|
4885
4959
|
}));
|
|
4886
4960
|
if (specs.length === 0) {
|
|
4887
|
-
console.error(`${
|
|
4961
|
+
console.error(`${TAG12} [${accountId}] business hours check: no opening hours configured, treating as open`);
|
|
4888
4962
|
return { open: true, reason: "no opening hours configured" };
|
|
4889
4963
|
}
|
|
4890
4964
|
for (const spec of specs) {
|
|
@@ -4894,7 +4968,7 @@ async function isBusinessOpen(accountId) {
|
|
|
4894
4968
|
if (spec.opens > spec.closes && currentTime < spec.closes) {
|
|
4895
4969
|
const hoursStr = `${spec.opens}-${spec.closes} (${spec.day})`;
|
|
4896
4970
|
console.error(
|
|
4897
|
-
`${
|
|
4971
|
+
`${TAG12} [${accountId}] business hours check: open (day=${dayOfWeek}, time=${currentTime}, hours=${hoursStr})`
|
|
4898
4972
|
);
|
|
4899
4973
|
return {
|
|
4900
4974
|
open: true,
|
|
@@ -4907,7 +4981,7 @@ async function isBusinessOpen(accountId) {
|
|
|
4907
4981
|
} else if (isTimeInRange(currentTime, spec.opens, spec.closes)) {
|
|
4908
4982
|
const hoursStr = `${spec.opens}-${spec.closes}`;
|
|
4909
4983
|
console.error(
|
|
4910
|
-
`${
|
|
4984
|
+
`${TAG12} [${accountId}] business hours check: open (day=${dayOfWeek}, time=${currentTime}, hours=${hoursStr})`
|
|
4911
4985
|
);
|
|
4912
4986
|
return {
|
|
4913
4987
|
open: true,
|
|
@@ -4921,7 +4995,7 @@ async function isBusinessOpen(accountId) {
|
|
|
4921
4995
|
const todaySpecs = specs.filter((s) => s.day === dayOfWeek);
|
|
4922
4996
|
const allHours = todaySpecs.map((s) => `${s.opens}-${s.closes}`).join(", ") || "none today";
|
|
4923
4997
|
console.error(
|
|
4924
|
-
`${
|
|
4998
|
+
`${TAG12} [${accountId}] business hours check: closed (day=${dayOfWeek}, time=${currentTime}, hours=${allHours})`
|
|
4925
4999
|
);
|
|
4926
5000
|
return {
|
|
4927
5001
|
open: false,
|
|
@@ -4935,7 +5009,7 @@ async function isBusinessOpen(accountId) {
|
|
|
4935
5009
|
}
|
|
4936
5010
|
} catch (err) {
|
|
4937
5011
|
console.error(
|
|
4938
|
-
`${
|
|
5012
|
+
`${TAG12} [${accountId}] business hours check failed, treating as open: ${err instanceof Error ? err.message : String(err)}`
|
|
4939
5013
|
);
|
|
4940
5014
|
return { open: true, reason: "hours check failed (treating as open)" };
|
|
4941
5015
|
}
|
|
@@ -4981,7 +5055,7 @@ import { execFile } from "child_process";
|
|
|
4981
5055
|
import { unlink, stat } from "fs/promises";
|
|
4982
5056
|
import { promisify } from "util";
|
|
4983
5057
|
var execFileAsync = promisify(execFile);
|
|
4984
|
-
var
|
|
5058
|
+
var TAG13 = "[stt]";
|
|
4985
5059
|
var WHISPER_BINARY = process.env.WHISPER_BINARY ?? "/opt/whisper.cpp/build/bin/whisper-cli";
|
|
4986
5060
|
var WHISPER_MODEL = process.env.WHISPER_MODEL ?? "/opt/whisper.cpp/models/ggml-base.bin";
|
|
4987
5061
|
var WHISPER_TIMEOUT_MS = 20 * 60 * 1e3;
|
|
@@ -5000,11 +5074,11 @@ async function transcribe(audioPath, mimetype) {
|
|
|
5000
5074
|
const s = await stat(audioPath);
|
|
5001
5075
|
audioBytes = s.size;
|
|
5002
5076
|
} catch {
|
|
5003
|
-
console.error(`${
|
|
5077
|
+
console.error(`${TAG13} failed: file not readable path=${audioPath}`);
|
|
5004
5078
|
return void 0;
|
|
5005
5079
|
}
|
|
5006
5080
|
console.error(
|
|
5007
|
-
`${
|
|
5081
|
+
`${TAG13} start provider=whisper-local audio_bytes=${audioBytes} mimetype=${mimetype} path=${audioPath}`
|
|
5008
5082
|
);
|
|
5009
5083
|
const wavPath = audioPath.replace(/\.[^.]+$/, "") + ".wav";
|
|
5010
5084
|
try {
|
|
@@ -5023,7 +5097,7 @@ async function transcribe(audioPath, mimetype) {
|
|
|
5023
5097
|
], { timeout: 3e4 });
|
|
5024
5098
|
} catch (err) {
|
|
5025
5099
|
const reason = err instanceof Error ? err.message : String(err);
|
|
5026
|
-
console.error(`${
|
|
5100
|
+
console.error(`${TAG13} failed: ffmpeg conversion error=${reason}`);
|
|
5027
5101
|
return void 0;
|
|
5028
5102
|
}
|
|
5029
5103
|
try {
|
|
@@ -5039,20 +5113,20 @@ async function transcribe(audioPath, mimetype) {
|
|
|
5039
5113
|
const durationMs = Date.now() - startMs;
|
|
5040
5114
|
const { text, count: stripped } = stripSpecialTokens(stdout.trim());
|
|
5041
5115
|
if (!text) {
|
|
5042
|
-
console.error(`${
|
|
5116
|
+
console.error(`${TAG13} failed: whisper returned empty output duration_ms=${durationMs}`);
|
|
5043
5117
|
return void 0;
|
|
5044
5118
|
}
|
|
5045
5119
|
const langMatch = stderr.match(/auto-detected language:\s*(\w+)/);
|
|
5046
5120
|
const language = langMatch?.[1] ?? "unknown";
|
|
5047
5121
|
const words = text.split(/\s+/).filter(Boolean).length;
|
|
5048
5122
|
console.error(
|
|
5049
|
-
`${
|
|
5123
|
+
`${TAG13} done provider=whisper-local duration_ms=${durationMs} words=${words} stripped=${stripped} lang=${language}`
|
|
5050
5124
|
);
|
|
5051
5125
|
return { text, language, durationMs };
|
|
5052
5126
|
} catch (err) {
|
|
5053
5127
|
const durationMs = Date.now() - startMs;
|
|
5054
5128
|
const reason = err instanceof Error ? err.message : String(err);
|
|
5055
|
-
console.error(`${
|
|
5129
|
+
console.error(`${TAG13} failed provider=whisper-local duration_ms=${durationMs} error=${reason}`);
|
|
5056
5130
|
return void 0;
|
|
5057
5131
|
} finally {
|
|
5058
5132
|
unlink(wavPath).catch(() => {
|
|
@@ -5071,7 +5145,7 @@ var WaReplyError = class extends Error {
|
|
|
5071
5145
|
};
|
|
5072
5146
|
|
|
5073
5147
|
// app/lib/whatsapp/manager.ts
|
|
5074
|
-
var
|
|
5148
|
+
var TAG14 = "[whatsapp:manager]";
|
|
5075
5149
|
var MAX_RECONNECT_ATTEMPTS = 10;
|
|
5076
5150
|
var MESSAGE_STORE_MAX = 500;
|
|
5077
5151
|
var APPEND_MEDIA_CONCURRENCY = 3;
|
|
@@ -5098,7 +5172,7 @@ function storeMessage(storeKey, entry) {
|
|
|
5098
5172
|
if (entries.length > MESSAGE_STORE_MAX) {
|
|
5099
5173
|
const trimmed = entries.length - MESSAGE_STORE_MAX;
|
|
5100
5174
|
entries.splice(0, trimmed);
|
|
5101
|
-
console.error(`${
|
|
5175
|
+
console.error(`${TAG14} message store trimmed for ${storeKey}: ${trimmed} oldest messages removed`);
|
|
5102
5176
|
}
|
|
5103
5177
|
}
|
|
5104
5178
|
function deriveCacheKey(input) {
|
|
@@ -5141,7 +5215,7 @@ function buildSelfChatDispatch(input) {
|
|
|
5141
5215
|
}
|
|
5142
5216
|
async function init(opts) {
|
|
5143
5217
|
if (initialized) {
|
|
5144
|
-
console.error(`${
|
|
5218
|
+
console.error(`${TAG14} already initialized`);
|
|
5145
5219
|
return;
|
|
5146
5220
|
}
|
|
5147
5221
|
configDir = opts.configDir;
|
|
@@ -5149,20 +5223,20 @@ async function init(opts) {
|
|
|
5149
5223
|
loadConfig(opts.accountConfig);
|
|
5150
5224
|
const accountIds = listCredentialAccountIds(configDir);
|
|
5151
5225
|
if (accountIds.length === 0) {
|
|
5152
|
-
console.error(`${
|
|
5226
|
+
console.error(`${TAG14} init: no stored WhatsApp credentials found`);
|
|
5153
5227
|
initialized = true;
|
|
5154
5228
|
return;
|
|
5155
5229
|
}
|
|
5156
|
-
console.error(`${
|
|
5230
|
+
console.error(`${TAG14} init: found ${accountIds.length} credentialed account(s): ${accountIds.join(", ")}`);
|
|
5157
5231
|
initialized = true;
|
|
5158
5232
|
for (const accountId of accountIds) {
|
|
5159
5233
|
const accountCfg = whatsAppConfig.accounts?.[accountId];
|
|
5160
5234
|
if (accountCfg?.enabled === false) {
|
|
5161
|
-
console.error(`${
|
|
5235
|
+
console.error(`${TAG14} skipping disabled account=${accountId}`);
|
|
5162
5236
|
continue;
|
|
5163
5237
|
}
|
|
5164
5238
|
startConnection(accountId).catch((err) => {
|
|
5165
|
-
console.error(`${
|
|
5239
|
+
console.error(`${TAG14} failed to auto-start account=${accountId}: ${formatError(err)}`);
|
|
5166
5240
|
});
|
|
5167
5241
|
}
|
|
5168
5242
|
}
|
|
@@ -5171,7 +5245,7 @@ async function startConnection(accountId) {
|
|
|
5171
5245
|
const authDir = resolveAuthDir(configDir, accountId);
|
|
5172
5246
|
const hasAuth = await authExists(authDir);
|
|
5173
5247
|
if (!hasAuth) {
|
|
5174
|
-
console.error(`${
|
|
5248
|
+
console.error(`${TAG14} no credentials for account=${accountId}`);
|
|
5175
5249
|
return;
|
|
5176
5250
|
}
|
|
5177
5251
|
let platformAccountId;
|
|
@@ -5222,11 +5296,11 @@ async function stopConnection(accountId) {
|
|
|
5222
5296
|
conn.sock.ev.removeAllListeners("creds.update");
|
|
5223
5297
|
conn.sock.ws?.close?.();
|
|
5224
5298
|
} catch (err) {
|
|
5225
|
-
console.warn(`${
|
|
5299
|
+
console.warn(`${TAG14} socket cleanup error during stop account=${accountId}: ${String(err)}`);
|
|
5226
5300
|
}
|
|
5227
5301
|
}
|
|
5228
5302
|
connections.delete(accountId);
|
|
5229
|
-
console.error(`${
|
|
5303
|
+
console.error(`${TAG14} stopped account=${accountId}`);
|
|
5230
5304
|
}
|
|
5231
5305
|
function getStatus() {
|
|
5232
5306
|
return Array.from(connections.values()).map((conn) => ({
|
|
@@ -5305,9 +5379,9 @@ async function registerLoginSocket(accountId, sock, authDir) {
|
|
|
5305
5379
|
monitorInbound(conn);
|
|
5306
5380
|
try {
|
|
5307
5381
|
await sock.sendPresenceUpdate("available");
|
|
5308
|
-
console.error(`${
|
|
5382
|
+
console.error(`${TAG14} presence set to available account=${accountId}`);
|
|
5309
5383
|
} catch (err) {
|
|
5310
|
-
console.error(`${
|
|
5384
|
+
console.error(`${TAG14} presence update failed account=${accountId}: ${String(err)}`);
|
|
5311
5385
|
}
|
|
5312
5386
|
await runInitQueries(sock, {
|
|
5313
5387
|
accountId,
|
|
@@ -5318,7 +5392,7 @@ async function registerLoginSocket(accountId, sock, authDir) {
|
|
|
5318
5392
|
});
|
|
5319
5393
|
watchForDisconnect(conn);
|
|
5320
5394
|
attachSelfIdRefreshOnCredsUpdate(conn);
|
|
5321
|
-
console.error(`${
|
|
5395
|
+
console.error(`${TAG14} registered login socket for account=${accountId} phone=${conn.selfPhone ?? "unknown"}`);
|
|
5322
5396
|
}
|
|
5323
5397
|
function attachSelfIdRefreshOnCredsUpdate(conn) {
|
|
5324
5398
|
if (!conn.sock) return;
|
|
@@ -5363,7 +5437,7 @@ async function shutdown() {
|
|
|
5363
5437
|
const ids = Array.from(connections.keys());
|
|
5364
5438
|
await Promise.all(ids.map((id) => stopConnection(id)));
|
|
5365
5439
|
initialized = false;
|
|
5366
|
-
console.error(`${
|
|
5440
|
+
console.error(`${TAG14} shutdown complete`);
|
|
5367
5441
|
}
|
|
5368
5442
|
function loadConfig(accountConfig) {
|
|
5369
5443
|
try {
|
|
@@ -5374,19 +5448,19 @@ function loadConfig(accountConfig) {
|
|
|
5374
5448
|
if (result.ok) {
|
|
5375
5449
|
whatsAppConfig = result.data;
|
|
5376
5450
|
if (result.droppedKeys.length > 0) {
|
|
5377
|
-
console.error(`${
|
|
5451
|
+
console.error(`${TAG14} config: dropped unknown keys=[${result.droppedKeys.join(",")}] preserved known config`);
|
|
5378
5452
|
}
|
|
5379
5453
|
} else {
|
|
5380
|
-
console.error(`${
|
|
5454
|
+
console.error(`${TAG14} config validation failed: ${result.error.message}`);
|
|
5381
5455
|
whatsAppConfig = {};
|
|
5382
5456
|
}
|
|
5383
5457
|
const diskAdmin = Array.isArray(wa.adminPhones) ? wa.adminPhones : [];
|
|
5384
5458
|
if (diskAdmin.length > 0 && (whatsAppConfig.adminPhones?.length ?? 0) === 0) {
|
|
5385
|
-
console.error(`${
|
|
5459
|
+
console.error(`${TAG14} WARN adminPhones present on disk but empty after load`);
|
|
5386
5460
|
}
|
|
5387
5461
|
}
|
|
5388
5462
|
} catch (err) {
|
|
5389
|
-
console.error(`${
|
|
5463
|
+
console.error(`${TAG14} config load error: ${String(err)}`);
|
|
5390
5464
|
whatsAppConfig = {};
|
|
5391
5465
|
}
|
|
5392
5466
|
}
|
|
@@ -5397,7 +5471,7 @@ async function connectWithReconnect(conn) {
|
|
|
5397
5471
|
let cycleError = null;
|
|
5398
5472
|
let connectedAt;
|
|
5399
5473
|
try {
|
|
5400
|
-
console.error(`${
|
|
5474
|
+
console.error(`${TAG14} connecting account=${conn.accountId} attempt=${conn.reconnectAttempts}`);
|
|
5401
5475
|
if (conn.debouncer) {
|
|
5402
5476
|
await conn.debouncer.destroy();
|
|
5403
5477
|
conn.debouncer = null;
|
|
@@ -5410,7 +5484,7 @@ async function connectWithReconnect(conn) {
|
|
|
5410
5484
|
conn.sock = sock;
|
|
5411
5485
|
applySelfIdentity(conn, sock, "pre-listener");
|
|
5412
5486
|
monitorInbound(conn);
|
|
5413
|
-
console.error(`${
|
|
5487
|
+
console.error(`${TAG14} socket created account=${conn.accountId} \u2014 waiting for connection`);
|
|
5414
5488
|
await waitForConnection(sock);
|
|
5415
5489
|
connectedAt = Date.now();
|
|
5416
5490
|
conn.connected = true;
|
|
@@ -5418,12 +5492,12 @@ async function connectWithReconnect(conn) {
|
|
|
5418
5492
|
conn.lastError = void 0;
|
|
5419
5493
|
conn.terminalReason = void 0;
|
|
5420
5494
|
applySelfIdentity(conn, sock, "post-connect");
|
|
5421
|
-
console.error(`${
|
|
5495
|
+
console.error(`${TAG14} connected account=${conn.accountId} phone=${conn.selfPhone ?? "unknown"}`);
|
|
5422
5496
|
try {
|
|
5423
5497
|
await sock.sendPresenceUpdate("available");
|
|
5424
|
-
console.error(`${
|
|
5498
|
+
console.error(`${TAG14} presence set to available account=${conn.accountId}`);
|
|
5425
5499
|
} catch (err) {
|
|
5426
|
-
console.error(`${
|
|
5500
|
+
console.error(`${TAG14} presence update failed account=${conn.accountId}: ${String(err)}`);
|
|
5427
5501
|
}
|
|
5428
5502
|
await runInitQueries(sock, {
|
|
5429
5503
|
accountId: conn.accountId,
|
|
@@ -5446,10 +5520,10 @@ async function connectWithReconnect(conn) {
|
|
|
5446
5520
|
}
|
|
5447
5521
|
const classification = classifyDisconnect(err);
|
|
5448
5522
|
conn.lastError = classification.message;
|
|
5449
|
-
console.error(`${
|
|
5523
|
+
console.error(`${TAG14} disconnect account=${conn.accountId}: ${classification.kind} \u2014 ${classification.message}`);
|
|
5450
5524
|
if (!classification.shouldRetry) {
|
|
5451
5525
|
conn.terminalReason = classification.message;
|
|
5452
|
-
console.error(`${
|
|
5526
|
+
console.error(`${TAG14} terminal disconnect for account=${conn.accountId}, stopping reconnection`);
|
|
5453
5527
|
return;
|
|
5454
5528
|
}
|
|
5455
5529
|
}
|
|
@@ -5462,7 +5536,7 @@ async function connectWithReconnect(conn) {
|
|
|
5462
5536
|
if (decision.action === "abort") {
|
|
5463
5537
|
const stuckReason = `GIVING UP account=${conn.accountId} attempts=${decision.finalAttempts}/${maxAttempts} uptimeMsLast=${uptimeMs} stableThresholdMs=${STABLE_UPTIME_MS} lastError=${conn.lastError ?? "(none)"}`;
|
|
5464
5538
|
console.error(
|
|
5465
|
-
`${
|
|
5539
|
+
`${TAG14} ${stuckReason} \u2014 re-pair via QR or restart required; WhatsApp will not reconnect automatically`
|
|
5466
5540
|
);
|
|
5467
5541
|
conn.sessionStuckReason = stuckReason;
|
|
5468
5542
|
conn.lastError = stuckReason;
|
|
@@ -5471,17 +5545,17 @@ async function connectWithReconnect(conn) {
|
|
|
5471
5545
|
conn.reconnectAttempts = decision.nextAttempts;
|
|
5472
5546
|
if (decision.reason === "short-lived") {
|
|
5473
5547
|
console.error(
|
|
5474
|
-
`${
|
|
5548
|
+
`${TAG14} short-lived session account=${conn.accountId} uptimeMs=${uptimeMs} attempt=${decision.nextAttempts}/${maxAttempts} lastError=${conn.lastError ?? "(clean disconnect)"}`
|
|
5475
5549
|
);
|
|
5476
5550
|
} else {
|
|
5477
5551
|
console.error(
|
|
5478
|
-
`${
|
|
5552
|
+
`${TAG14} session stable account=${conn.accountId} uptimeMs=${uptimeMs} \u2014 reconnect counter reset`
|
|
5479
5553
|
);
|
|
5480
5554
|
}
|
|
5481
5555
|
if (decision.reason === "short-lived" || cycleError) {
|
|
5482
5556
|
const delay = computeBackoff(Math.max(1, decision.nextAttempts));
|
|
5483
5557
|
console.error(
|
|
5484
|
-
`${
|
|
5558
|
+
`${TAG14} reconnecting account=${conn.accountId} in ${delay}ms (attempt ${decision.nextAttempts}/${maxAttempts})`
|
|
5485
5559
|
);
|
|
5486
5560
|
await new Promise((resolve37) => {
|
|
5487
5561
|
const timer2 = setTimeout(resolve37, delay);
|
|
@@ -5515,11 +5589,11 @@ function watchForDisconnect(conn) {
|
|
|
5515
5589
|
conn.sock.ev.on("connection.update", (update) => {
|
|
5516
5590
|
if (update.connection === "close") {
|
|
5517
5591
|
if (connections.get(conn.accountId) !== conn) return;
|
|
5518
|
-
console.error(`${
|
|
5592
|
+
console.error(`${TAG14} socket disconnected for account=${conn.accountId}`);
|
|
5519
5593
|
conn.connected = false;
|
|
5520
5594
|
conn.sock = null;
|
|
5521
5595
|
connectWithReconnect(conn).catch((err) => {
|
|
5522
|
-
console.error(`${
|
|
5596
|
+
console.error(`${TAG14} reconnection failed for account=${conn.accountId}: ${formatError(err)}`);
|
|
5523
5597
|
});
|
|
5524
5598
|
}
|
|
5525
5599
|
});
|
|
@@ -5545,7 +5619,7 @@ function monitorInbound(conn) {
|
|
|
5545
5619
|
if (!conn.sock || !onInboundMessage) return;
|
|
5546
5620
|
const sock = conn.sock;
|
|
5547
5621
|
const debounceMs = whatsAppConfig.accounts?.[conn.accountId]?.debounceMs ?? whatsAppConfig.debounceMs ?? 0;
|
|
5548
|
-
console.error(`${
|
|
5622
|
+
console.error(`${TAG14} monitorInbound started account=${conn.accountId} debounceMs=${debounceMs}`);
|
|
5549
5623
|
conn.debouncer = createInboundDebouncer({
|
|
5550
5624
|
debounceMs,
|
|
5551
5625
|
buildKey: (payload) => {
|
|
@@ -5555,12 +5629,12 @@ function monitorInbound(conn) {
|
|
|
5555
5629
|
onFlush: (entries) => {
|
|
5556
5630
|
if (!onInboundMessage) return;
|
|
5557
5631
|
if (entries.length > 1) {
|
|
5558
|
-
console.error(`${
|
|
5632
|
+
console.error(`${TAG14} debounce: combining ${entries.length} messages account=${conn.accountId} from=${entries[0].senderPhone}`);
|
|
5559
5633
|
}
|
|
5560
5634
|
onInboundMessage(combineInboundBatch(entries));
|
|
5561
5635
|
},
|
|
5562
5636
|
onError: (err) => {
|
|
5563
|
-
console.error(`${
|
|
5637
|
+
console.error(`${TAG14} debounce flush error account=${conn.accountId}: ${String(err)}`);
|
|
5564
5638
|
}
|
|
5565
5639
|
});
|
|
5566
5640
|
sock.ev.on("messages.upsert", async (upsert) => {
|
|
@@ -5592,7 +5666,7 @@ function monitorInbound(conn) {
|
|
|
5592
5666
|
}
|
|
5593
5667
|
const entries = messageStore.get(storeKey);
|
|
5594
5668
|
console.error(
|
|
5595
|
-
`${
|
|
5669
|
+
`${TAG14} stored message ${msg.key.id ?? "?"} for ${remoteJid} (type: ${upsert.type}, store size: ${entries?.length ?? 0}/${MESSAGE_STORE_MAX}) account=${conn.platformAccountId}`
|
|
5596
5670
|
);
|
|
5597
5671
|
recordActivity({
|
|
5598
5672
|
accountId: conn.accountId,
|
|
@@ -5638,11 +5712,14 @@ function monitorInbound(conn) {
|
|
|
5638
5712
|
const wantsServableMedia = mediaServable && !!conn.sock && (!fromMe || !alreadyRecorded);
|
|
5639
5713
|
if (fromMe && mediaServable && !!conn.sock) {
|
|
5640
5714
|
console.error(
|
|
5641
|
-
`${
|
|
5715
|
+
`${TAG14} fromMe-media-echo msgKey=${msg.key.id} decision=${alreadyRecorded ? "skip" : "store"} reason=${alreadyRecorded ? "already-recorded" : "no-prior-record"}`
|
|
5642
5716
|
);
|
|
5643
5717
|
}
|
|
5644
5718
|
const mediaSeed = `whatsapp-live:${conn.accountId}:${canonicalRemoteJid}:${msg.key.id}`;
|
|
5645
|
-
const maxBytes = (
|
|
5719
|
+
const maxBytes = echoStoreMaxBytes({
|
|
5720
|
+
fromMe,
|
|
5721
|
+
mediaMaxMb: whatsAppConfig.mediaMaxMb
|
|
5722
|
+
});
|
|
5646
5723
|
const basePersist = {
|
|
5647
5724
|
accountId: conn.accountId,
|
|
5648
5725
|
platformAccountId: conn.platformAccountId,
|
|
@@ -5687,7 +5764,7 @@ function monitorInbound(conn) {
|
|
|
5687
5764
|
receiptDeferredToWorker = true;
|
|
5688
5765
|
if (!accepted) {
|
|
5689
5766
|
console.error(
|
|
5690
|
-
`${
|
|
5767
|
+
`${TAG14} append-media overflow account=${conn.platformAccountId} msgKey=${msgKeyId ?? "?"} \u2014 pending cap ${APPEND_MEDIA_MAX_PENDING} reached, text-fallback persist, inline media shed`
|
|
5691
5768
|
);
|
|
5692
5769
|
const persisted = await persistWhatsAppMessage({
|
|
5693
5770
|
...basePersist,
|
|
@@ -5761,13 +5838,13 @@ function monitorInbound(conn) {
|
|
|
5761
5838
|
const skippedSenderJid = isGroupJid(remoteJid) ? msg.key.participant ?? remoteJid : remoteJid;
|
|
5762
5839
|
const skippedSenderPhone = await resolveJidToE164(skippedSenderJid, conn.lidMapping) ?? skippedSenderJid;
|
|
5763
5840
|
console.error(
|
|
5764
|
-
`${
|
|
5841
|
+
`${TAG14} append-type message ${msg.key.id ?? "?"} dispatch skipped from=${skippedSenderPhone}${msg.pushName ? ` name=${msg.pushName}` : ""} account=${conn.accountId} (persistence not confirmed here)`
|
|
5765
5842
|
);
|
|
5766
5843
|
continue;
|
|
5767
5844
|
}
|
|
5768
5845
|
await handleInboundMessage(conn, msg);
|
|
5769
5846
|
} catch (err) {
|
|
5770
|
-
console.error(`${
|
|
5847
|
+
console.error(`${TAG14} inbound handler error account=${conn.accountId}: ${String(err)}`);
|
|
5771
5848
|
}
|
|
5772
5849
|
}
|
|
5773
5850
|
});
|
|
@@ -5823,31 +5900,31 @@ async function handleInboundMessage(conn, msg) {
|
|
|
5823
5900
|
const remoteJid = msg.key.remoteJid;
|
|
5824
5901
|
if (!remoteJid) return;
|
|
5825
5902
|
if (remoteJid === "status@broadcast") {
|
|
5826
|
-
console.error(`${
|
|
5903
|
+
console.error(`${TAG14} drop: status broadcast account=${conn.accountId}`);
|
|
5827
5904
|
return;
|
|
5828
5905
|
}
|
|
5829
5906
|
if (!msg.message) {
|
|
5830
|
-
console.error(`${
|
|
5907
|
+
console.error(`${TAG14} drop: empty message account=${conn.accountId} from=${remoteJid}`);
|
|
5831
5908
|
return;
|
|
5832
5909
|
}
|
|
5833
5910
|
const dedupKey = `${conn.accountId}:${remoteJid}:${msg.key.id}`;
|
|
5834
5911
|
if (isDuplicateInbound(dedupKey)) {
|
|
5835
|
-
console.error(`${
|
|
5912
|
+
console.error(`${TAG14} drop: duplicate account=${conn.accountId} key=${dedupKey}`);
|
|
5836
5913
|
return;
|
|
5837
5914
|
}
|
|
5838
5915
|
if (msg.key.fromMe) {
|
|
5839
5916
|
if (msg.key.id && isAgentSentMessage(msg.key.id)) {
|
|
5840
|
-
console.error(`${
|
|
5917
|
+
console.error(`${TAG14} drop: echo suppression account=${conn.accountId} msgId=${msg.key.id}`);
|
|
5841
5918
|
return;
|
|
5842
5919
|
}
|
|
5843
5920
|
const extracted2 = extractMessage(msg);
|
|
5844
5921
|
if (!extracted2.text) {
|
|
5845
|
-
console.error(`${
|
|
5922
|
+
console.error(`${TAG14} owner reply skipped \u2014 no text content account=${conn.accountId}`);
|
|
5846
5923
|
return;
|
|
5847
5924
|
}
|
|
5848
5925
|
const isGroup2 = isGroupJid(remoteJid);
|
|
5849
5926
|
const senderPhone2 = conn.selfPhone ?? "owner";
|
|
5850
|
-
console.error(`${
|
|
5927
|
+
console.error(`${TAG14} owner reply mirrored to session from=${senderPhone2} account=${conn.accountId}`);
|
|
5851
5928
|
const reply2 = async (text) => {
|
|
5852
5929
|
const currentSock = conn.sock;
|
|
5853
5930
|
if (!currentSock) throw new Error("WhatsApp disconnected \u2014 cannot reply");
|
|
@@ -5881,7 +5958,7 @@ async function handleInboundMessage(conn, msg) {
|
|
|
5881
5958
|
}
|
|
5882
5959
|
const extracted = extractMessage(msg);
|
|
5883
5960
|
if (!extracted.text && !extracted.mediaType) {
|
|
5884
|
-
console.error(`${
|
|
5961
|
+
console.error(`${TAG14} drop: no text or media account=${conn.accountId} from=${remoteJid}`);
|
|
5885
5962
|
return;
|
|
5886
5963
|
}
|
|
5887
5964
|
let mediaResult;
|
|
@@ -5891,7 +5968,7 @@ async function handleInboundMessage(conn, msg) {
|
|
|
5891
5968
|
maxBytes: maxMb * 1024 * 1024
|
|
5892
5969
|
});
|
|
5893
5970
|
if (!mediaResult) {
|
|
5894
|
-
console.error(`${
|
|
5971
|
+
console.error(`${TAG14} media download returned undefined account=${conn.accountId} type=${extracted.mediaType} from=${remoteJid}`);
|
|
5895
5972
|
}
|
|
5896
5973
|
}
|
|
5897
5974
|
const isGroup = isGroupJid(remoteJid);
|
|
@@ -5932,7 +6009,7 @@ async function handleInboundMessage(conn, msg) {
|
|
|
5932
6009
|
});
|
|
5933
6010
|
}
|
|
5934
6011
|
console.error(
|
|
5935
|
-
`${
|
|
6012
|
+
`${TAG14} inbound account=${conn.accountId} from=${senderPhone} group=${isGroup} access=${accessResult.allowed ? "allowed" : "blocked"}(${accessResult.reason}) agent=${accessResult.agentType}` + (extracted.mediaType ? ` media=${extracted.mediaType}` : "") + (mediaResult ? ` mediaPath=${mediaResult.path}` : "") + (extracted.quotedMessage ? ` replyTo=${extracted.quotedMessage.id}` : "")
|
|
5936
6013
|
);
|
|
5937
6014
|
if (!accessResult.allowed && accessResult.reason === "account-manager-unresolved") {
|
|
5938
6015
|
console.error(
|
|
@@ -5958,7 +6035,7 @@ async function handleInboundMessage(conn, msg) {
|
|
|
5958
6035
|
}
|
|
5959
6036
|
const reply = async (text) => {
|
|
5960
6037
|
if (isGroupJid(remoteJid)) {
|
|
5961
|
-
console.error(`${
|
|
6038
|
+
console.error(`${TAG14} op=group-reply-blocked jid=${remoteJid}`);
|
|
5962
6039
|
throw new WaReplyError("group reply blocked \u2014 observe-only", false);
|
|
5963
6040
|
}
|
|
5964
6041
|
const currentSock = conn.sock;
|
|
@@ -6005,15 +6082,15 @@ async function handleInboundMessage(conn, msg) {
|
|
|
6005
6082
|
if (accessResult.agentType === "public") {
|
|
6006
6083
|
const hoursResult = await isBusinessOpen(conn.accountId);
|
|
6007
6084
|
if (!hoursResult.open) {
|
|
6008
|
-
console.error(`${
|
|
6085
|
+
console.error(`${TAG14} [${conn.accountId}] dispatch skipped: business closed`);
|
|
6009
6086
|
const afterHoursMessage = whatsAppConfig.accounts?.[conn.accountId]?.afterHoursMessage ?? whatsAppConfig.afterHoursMessage;
|
|
6010
6087
|
if (afterHoursMessage) {
|
|
6011
6088
|
try {
|
|
6012
6089
|
await reply(afterHoursMessage);
|
|
6013
|
-
console.error(`${
|
|
6090
|
+
console.error(`${TAG14} [${conn.accountId}] after-hours auto-reply sent to ${remoteJid}`);
|
|
6014
6091
|
} catch (err) {
|
|
6015
6092
|
console.error(
|
|
6016
|
-
`${
|
|
6093
|
+
`${TAG14} [${conn.accountId}] after-hours auto-reply failed: ${err instanceof Error ? err.message : String(err)}`
|
|
6017
6094
|
);
|
|
6018
6095
|
}
|
|
6019
6096
|
}
|
|
@@ -6458,13 +6535,13 @@ function audioExtension(mimeType) {
|
|
|
6458
6535
|
}
|
|
6459
6536
|
|
|
6460
6537
|
// app/lib/stt/voice-note.ts
|
|
6461
|
-
var
|
|
6538
|
+
var TAG15 = "[voice]";
|
|
6462
6539
|
async function transcribeVoiceNote(file, source) {
|
|
6463
6540
|
const startMs = Date.now();
|
|
6464
6541
|
const bytes = file.size;
|
|
6465
6542
|
const mimeType = file.type;
|
|
6466
6543
|
console.error(
|
|
6467
|
-
`${
|
|
6544
|
+
`${TAG15} recording send source=${source} duration_ms=unknown bytes=${bytes} format=${mimeType}`
|
|
6468
6545
|
);
|
|
6469
6546
|
let tempDir;
|
|
6470
6547
|
let tempPath;
|
|
@@ -6476,7 +6553,7 @@ async function transcribeVoiceNote(file, source) {
|
|
|
6476
6553
|
await writeFile3(tempPath, buffer);
|
|
6477
6554
|
} catch (err) {
|
|
6478
6555
|
const reason = err instanceof Error ? err.message : String(err);
|
|
6479
|
-
console.error(`${
|
|
6556
|
+
console.error(`${TAG15} failed source=${source} error=temp-file-write: ${reason}`);
|
|
6480
6557
|
return { ok: false, error: "Could not process voice note" };
|
|
6481
6558
|
}
|
|
6482
6559
|
try {
|
|
@@ -6484,7 +6561,7 @@ async function transcribeVoiceNote(file, source) {
|
|
|
6484
6561
|
if (!sttResult) {
|
|
6485
6562
|
const elapsed2 = Date.now() - startMs;
|
|
6486
6563
|
console.error(
|
|
6487
|
-
`${
|
|
6564
|
+
`${TAG15} failed source=${source} error=transcription-failed duration_ms=${elapsed2}`
|
|
6488
6565
|
);
|
|
6489
6566
|
return { ok: false, error: "Could not transcribe voice note. Please try again or type your message." };
|
|
6490
6567
|
}
|
|
@@ -6492,7 +6569,7 @@ async function transcribeVoiceNote(file, source) {
|
|
|
6492
6569
|
const elapsed = Date.now() - startMs;
|
|
6493
6570
|
const words = rawText.split(/\s+/).filter(Boolean).length;
|
|
6494
6571
|
console.error(
|
|
6495
|
-
`${
|
|
6572
|
+
`${TAG15} transcribed source=${source} duration_ms=${elapsed} stt_ms=${sttResult.durationMs} words=${words}`
|
|
6496
6573
|
);
|
|
6497
6574
|
return {
|
|
6498
6575
|
ok: true,
|
|
@@ -6502,7 +6579,7 @@ async function transcribeVoiceNote(file, source) {
|
|
|
6502
6579
|
const elapsed = Date.now() - startMs;
|
|
6503
6580
|
const reason = err instanceof Error ? err.message : String(err);
|
|
6504
6581
|
console.error(
|
|
6505
|
-
`${
|
|
6582
|
+
`${TAG15} failed source=${source} error=${reason} duration_ms=${elapsed}`
|
|
6506
6583
|
);
|
|
6507
6584
|
return { ok: false, error: "Voice note transcription failed. Please try again or type your message." };
|
|
6508
6585
|
} finally {
|
|
@@ -6549,7 +6626,7 @@ function composePublicSessionTitle(input) {
|
|
|
6549
6626
|
}
|
|
6550
6627
|
|
|
6551
6628
|
// app/lib/webchat/persist-customer-inbound.ts
|
|
6552
|
-
var
|
|
6629
|
+
var TAG16 = "[channel-house]";
|
|
6553
6630
|
async function persistCustomerWebchatInbound(input) {
|
|
6554
6631
|
if (!input.text) return null;
|
|
6555
6632
|
try {
|
|
@@ -6565,7 +6642,7 @@ async function persistCustomerWebchatInbound(input) {
|
|
|
6565
6642
|
);
|
|
6566
6643
|
if (!conv.sessionId) {
|
|
6567
6644
|
console.error(
|
|
6568
|
-
`${
|
|
6645
|
+
`${TAG16} op=persist-skip reason=conversation-merge-failed accountId=${input.houseAccountId} key=${input.sessionKey}`
|
|
6569
6646
|
);
|
|
6570
6647
|
return null;
|
|
6571
6648
|
}
|
|
@@ -6577,17 +6654,17 @@ async function persistCustomerWebchatInbound(input) {
|
|
|
6577
6654
|
);
|
|
6578
6655
|
if (!messageId) {
|
|
6579
6656
|
console.error(
|
|
6580
|
-
`${
|
|
6657
|
+
`${TAG16} op=persist-skip reason=message-write-failed accountId=${input.houseAccountId} sessionId=${conv.sessionId.slice(0, 8)}`
|
|
6581
6658
|
);
|
|
6582
6659
|
return null;
|
|
6583
6660
|
}
|
|
6584
6661
|
console.error(
|
|
6585
|
-
`${
|
|
6662
|
+
`${TAG16} op=persist accountId=${input.houseAccountId} sessionId=${conv.sessionId.slice(0, 8)} messageId=${messageId.slice(0, 8)} bytes=${Buffer.byteLength(input.text, "utf8")}`
|
|
6586
6663
|
);
|
|
6587
6664
|
return { sessionId: conv.sessionId, messageId };
|
|
6588
6665
|
} catch (err) {
|
|
6589
6666
|
console.error(
|
|
6590
|
-
`${
|
|
6667
|
+
`${TAG16} op=persist-fail accountId=${input.houseAccountId} key=${input.sessionKey} reason=${err instanceof Error ? err.message : String(err)}`
|
|
6591
6668
|
);
|
|
6592
6669
|
return null;
|
|
6593
6670
|
}
|
|
@@ -6895,7 +6972,7 @@ function parseScheduleProvenance(v) {
|
|
|
6895
6972
|
}
|
|
6896
6973
|
|
|
6897
6974
|
// server/routes/channel/schedule-inject.ts
|
|
6898
|
-
var
|
|
6975
|
+
var TAG17 = "[schedule-inject]";
|
|
6899
6976
|
function isLoopbackAddr(addr) {
|
|
6900
6977
|
return addr === "127.0.0.1" || addr === "::1" || addr === "::ffff:127.0.0.1";
|
|
6901
6978
|
}
|
|
@@ -6905,7 +6982,7 @@ function createScheduleInjectRoutes(deps) {
|
|
|
6905
6982
|
const env = c.env;
|
|
6906
6983
|
const remoteAddr = env?.incoming?.socket?.remoteAddress ?? "";
|
|
6907
6984
|
if (!isLoopbackAddr(remoteAddr)) {
|
|
6908
|
-
console.error(`${
|
|
6985
|
+
console.error(`${TAG17} reject reason=non-loopback remoteAddr=${remoteAddr}`);
|
|
6909
6986
|
return c.json({ ok: false, error: "schedule-inject-loopback-only" }, 403);
|
|
6910
6987
|
}
|
|
6911
6988
|
const xffHeader = env?.incoming?.headers?.["x-forwarded-for"];
|
|
@@ -6913,7 +6990,7 @@ function createScheduleInjectRoutes(deps) {
|
|
|
6913
6990
|
if (xffRaw.length > 0) {
|
|
6914
6991
|
const offender = xffRaw.split(",").map((t) => t.trim()).filter(Boolean).find((t) => !isLoopbackAddr(t));
|
|
6915
6992
|
if (offender !== void 0) {
|
|
6916
|
-
console.error(`${
|
|
6993
|
+
console.error(`${TAG17} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
|
|
6917
6994
|
return c.json({ ok: false, error: "schedule-inject-loopback-only" }, 403);
|
|
6918
6995
|
}
|
|
6919
6996
|
}
|
|
@@ -6929,23 +7006,23 @@ function createScheduleInjectRoutes(deps) {
|
|
|
6929
7006
|
const eventId = typeof body.eventId === "string" ? body.eventId : "";
|
|
6930
7007
|
const scheduleProvenance = parseScheduleProvenance(body.scheduleProvenance);
|
|
6931
7008
|
if (channel !== "whatsapp" && channel !== "telegram" || !destination || !prompt) {
|
|
6932
|
-
console.error(`${
|
|
7009
|
+
console.error(`${TAG17} reject reason=bad-request channel=${String(channel)} hasDestination=${Boolean(destination)} hasPrompt=${Boolean(prompt)}`);
|
|
6933
7010
|
return c.json({ ok: false, error: "schedule-inject-bad-request" }, 400);
|
|
6934
7011
|
}
|
|
6935
7012
|
const account = deps.resolveAccount();
|
|
6936
7013
|
if (!account) {
|
|
6937
|
-
console.error(`${
|
|
7014
|
+
console.error(`${TAG17} reject reason=no-account eventId=${eventId}`);
|
|
6938
7015
|
return c.json({ ok: false, error: "no-account" }, 500);
|
|
6939
7016
|
}
|
|
6940
7017
|
const houseAccountId = account.accountId;
|
|
6941
|
-
console.error(`${
|
|
7018
|
+
console.error(`${TAG17} op=received eventId=${eventId} channel=${channel} destination=${destination} account=${houseAccountId}`);
|
|
6942
7019
|
if (channel === "whatsapp") {
|
|
6943
7020
|
const effectiveAccount2 = deps.effectiveAccountFor(houseAccountId, account.accountDir, destination);
|
|
6944
7021
|
if (effectiveAccount2 === null) {
|
|
6945
|
-
console.error(`${
|
|
7022
|
+
console.error(`${TAG17} op=schedule-account-manager-reject eventId=${eventId} destination=${destination} reason=unresolved-effective-account`);
|
|
6946
7023
|
return c.json({ ok: false, error: "account-manager-unresolved" }, 403);
|
|
6947
7024
|
}
|
|
6948
|
-
console.error(`${
|
|
7025
|
+
console.error(`${TAG17} op=effective-account eventId=${eventId} effectiveAccount=${effectiveAccount2}`);
|
|
6949
7026
|
const sessionId2 = adminSessionIdFor(effectiveAccount2, normalizeTarget(destination) ?? destination);
|
|
6950
7027
|
const reply2 = (text) => deps.sendWhatsAppText(houseAccountId, destination, text);
|
|
6951
7028
|
try {
|
|
@@ -6967,17 +7044,17 @@ function createScheduleInjectRoutes(deps) {
|
|
|
6967
7044
|
reply: reply2
|
|
6968
7045
|
});
|
|
6969
7046
|
} catch (err) {
|
|
6970
|
-
console.error(`${
|
|
7047
|
+
console.error(`${TAG17} op=inject-spawn eventId=${eventId} sessionId=${sessionId2} result=inject-error status=${err instanceof Error ? err.message : String(err)}`);
|
|
6971
7048
|
return c.json({ ok: false, error: "inject-error" }, 502);
|
|
6972
7049
|
}
|
|
6973
|
-
console.error(`${
|
|
7050
|
+
console.error(`${TAG17} op=inject-spawn eventId=${eventId} sessionId=${sessionId2} result=ok status=-`);
|
|
6974
7051
|
return c.json({ ok: true }, 200);
|
|
6975
7052
|
}
|
|
6976
7053
|
const effectiveAccount = houseAccountId;
|
|
6977
|
-
console.error(`${
|
|
7054
|
+
console.error(`${TAG17} op=effective-account eventId=${eventId} effectiveAccount=${effectiveAccount}`);
|
|
6978
7055
|
const botToken = account.config.telegram?.adminBotToken;
|
|
6979
7056
|
if (!botToken) {
|
|
6980
|
-
console.error(`${
|
|
7057
|
+
console.error(`${TAG17} reject reason=no-admin-bot-token eventId=${eventId}`);
|
|
6981
7058
|
return c.json({ ok: false, error: "no-admin-bot-token" }, 400);
|
|
6982
7059
|
}
|
|
6983
7060
|
const chatId = Number(destination);
|
|
@@ -6996,10 +7073,10 @@ function createScheduleInjectRoutes(deps) {
|
|
|
6996
7073
|
reply
|
|
6997
7074
|
});
|
|
6998
7075
|
} catch (err) {
|
|
6999
|
-
console.error(`${
|
|
7076
|
+
console.error(`${TAG17} op=inject-spawn eventId=${eventId} sessionId=${sessionId} result=inject-error status=${err instanceof Error ? err.message : String(err)}`);
|
|
7000
7077
|
return c.json({ ok: false, error: "inject-error" }, 502);
|
|
7001
7078
|
}
|
|
7002
|
-
console.error(`${
|
|
7079
|
+
console.error(`${TAG17} op=inject-spawn eventId=${eventId} sessionId=${sessionId} result=ok status=-`);
|
|
7003
7080
|
return c.json({ ok: true }, 200);
|
|
7004
7081
|
});
|
|
7005
7082
|
return app63;
|
|
@@ -7033,26 +7110,26 @@ import { randomUUID as randomUUID6 } from "crypto";
|
|
|
7033
7110
|
// app/lib/whatsapp/config-persist.ts
|
|
7034
7111
|
import { readFileSync as readFileSync11, writeFileSync as writeFileSync4, existsSync as existsSync6 } from "fs";
|
|
7035
7112
|
import { resolve as resolve8, join as join8, dirname as dirname2, basename as basename2 } from "path";
|
|
7036
|
-
var
|
|
7113
|
+
var TAG18 = "[whatsapp:config]";
|
|
7037
7114
|
function configPath(accountDir) {
|
|
7038
7115
|
return resolve8(accountDir, "account.json");
|
|
7039
7116
|
}
|
|
7040
7117
|
function readConfig(accountDir) {
|
|
7041
|
-
const
|
|
7042
|
-
if (!existsSync6(
|
|
7043
|
-
return JSON.parse(readFileSync11(
|
|
7118
|
+
const path3 = configPath(accountDir);
|
|
7119
|
+
if (!existsSync6(path3)) throw new Error(`account.json not found at ${path3}`);
|
|
7120
|
+
return JSON.parse(readFileSync11(path3, "utf-8"));
|
|
7044
7121
|
}
|
|
7045
7122
|
function writeConfig(accountDir, config) {
|
|
7046
|
-
const
|
|
7047
|
-
writeFileSync4(
|
|
7123
|
+
const path3 = configPath(accountDir);
|
|
7124
|
+
writeFileSync4(path3, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
7048
7125
|
}
|
|
7049
7126
|
function reloadManagerConfig(accountDir) {
|
|
7050
7127
|
try {
|
|
7051
7128
|
const config = readConfig(accountDir);
|
|
7052
7129
|
reloadConfig(config);
|
|
7053
|
-
console.error(`${
|
|
7130
|
+
console.error(`${TAG18} reloaded manager config`);
|
|
7054
7131
|
} catch (err) {
|
|
7055
|
-
console.error(`${
|
|
7132
|
+
console.error(`${TAG18} manager config reload failed: ${String(err)}`);
|
|
7056
7133
|
}
|
|
7057
7134
|
}
|
|
7058
7135
|
var E164_PATTERN = /^\+\d{7,15}$/;
|
|
@@ -7078,25 +7155,25 @@ function persistAfterPairing(accountDir, accountId, selfPhone) {
|
|
|
7078
7155
|
const adminPhones = wa.adminPhones;
|
|
7079
7156
|
if (!adminPhones.includes(normalized)) {
|
|
7080
7157
|
adminPhones.push(normalized);
|
|
7081
|
-
console.error(`${
|
|
7158
|
+
console.error(`${TAG18} added selfPhone=${normalized} to adminPhones`);
|
|
7082
7159
|
}
|
|
7083
7160
|
} else {
|
|
7084
|
-
console.error(`${
|
|
7161
|
+
console.error(`${TAG18} skipping adminPhones \u2014 selfPhone is null account=${accountId}`);
|
|
7085
7162
|
}
|
|
7086
7163
|
const parsed = WhatsAppConfigSchema.safeParse(wa);
|
|
7087
7164
|
if (!parsed.success) {
|
|
7088
7165
|
const msg = parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ");
|
|
7089
|
-
console.error(`${
|
|
7166
|
+
console.error(`${TAG18} validation failed after pairing: ${msg}`);
|
|
7090
7167
|
return { ok: false, error: `Validation failed: ${msg}` };
|
|
7091
7168
|
}
|
|
7092
7169
|
config.whatsapp = parsed.data;
|
|
7093
7170
|
writeConfig(accountDir, config);
|
|
7094
|
-
console.error(`${
|
|
7171
|
+
console.error(`${TAG18} persisted after pairing account=${accountId} phone=${selfPhone ?? "null"}`);
|
|
7095
7172
|
reloadManagerConfig(accountDir);
|
|
7096
7173
|
return { ok: true };
|
|
7097
7174
|
} catch (err) {
|
|
7098
7175
|
const msg = err instanceof Error ? err.message : String(err);
|
|
7099
|
-
console.error(`${
|
|
7176
|
+
console.error(`${TAG18} persist failed account=${accountId}: ${msg}`);
|
|
7100
7177
|
return { ok: false, error: msg };
|
|
7101
7178
|
}
|
|
7102
7179
|
}
|
|
@@ -7110,7 +7187,7 @@ function addAdminPhone(accountDir, phone) {
|
|
|
7110
7187
|
const targetId = typeof config.accountId === "string" && config.accountId ? config.accountId : basename2(resolve8(accountDir));
|
|
7111
7188
|
const socketOwner = resolveHouseOrSoleAccountId(dirname2(resolve8(accountDir)));
|
|
7112
7189
|
if (targetId !== socketOwner) {
|
|
7113
|
-
console.error(`${
|
|
7190
|
+
console.error(`${TAG18} add-admin-phone rejected accountId=${targetId} reason=not-socket-account`);
|
|
7114
7191
|
return {
|
|
7115
7192
|
ok: false,
|
|
7116
7193
|
reason: "not-socket-account",
|
|
@@ -7139,12 +7216,12 @@ function addAdminPhone(accountDir, phone) {
|
|
|
7139
7216
|
}
|
|
7140
7217
|
config.whatsapp = parsed.data;
|
|
7141
7218
|
writeConfig(accountDir, config);
|
|
7142
|
-
console.error(`${
|
|
7219
|
+
console.error(`${TAG18} added admin phone=${normalized}`);
|
|
7143
7220
|
reloadManagerConfig(accountDir);
|
|
7144
7221
|
return { ok: true, message: `Added ${normalized} as admin phone. Messages from this number will route to the admin agent.` };
|
|
7145
7222
|
} catch (err) {
|
|
7146
7223
|
const msg = err instanceof Error ? err.message : String(err);
|
|
7147
|
-
console.error(`${
|
|
7224
|
+
console.error(`${TAG18} addAdminPhone failed: ${msg}`);
|
|
7148
7225
|
return { ok: false, error: msg };
|
|
7149
7226
|
}
|
|
7150
7227
|
}
|
|
@@ -7172,12 +7249,12 @@ function removeAdminPhone(accountDir, phone) {
|
|
|
7172
7249
|
}
|
|
7173
7250
|
config.whatsapp = parsed.data;
|
|
7174
7251
|
writeConfig(accountDir, config);
|
|
7175
|
-
console.error(`${
|
|
7252
|
+
console.error(`${TAG18} removed admin phone=${normalized}`);
|
|
7176
7253
|
reloadManagerConfig(accountDir);
|
|
7177
7254
|
return { ok: true, message: `Removed ${normalized} from admin phones. Messages from this number will now route to the public agent.` };
|
|
7178
7255
|
} catch (err) {
|
|
7179
7256
|
const msg = err instanceof Error ? err.message : String(err);
|
|
7180
|
-
console.error(`${
|
|
7257
|
+
console.error(`${TAG18} removeAdminPhone failed: ${msg}`);
|
|
7181
7258
|
return { ok: false, error: msg };
|
|
7182
7259
|
}
|
|
7183
7260
|
}
|
|
@@ -7272,13 +7349,13 @@ function setAccountManager(accountDir, phone, subAccountId, mode = "active") {
|
|
|
7272
7349
|
}
|
|
7273
7350
|
config.whatsapp = parsed.data;
|
|
7274
7351
|
writeConfig(accountDir, config);
|
|
7275
|
-
console.error(`${
|
|
7352
|
+
console.error(`${TAG18} bound account manager phone=${normalized} managesAccount=${sub} mode=${mode}`);
|
|
7276
7353
|
reloadManagerConfig(accountDir);
|
|
7277
7354
|
const behaviour = mode === "passive" ? `Their WhatsApp messages will each file one task scoped to that sub-account; the agent sends nothing back.` : `Their WhatsApp messages will reach that sub-account's admin agent; replies still go out over this account's WhatsApp device.`;
|
|
7278
7355
|
return { ok: true, message: `Bound ${normalized} as ${mode} account manager for sub-account ${sub}. ${behaviour}` };
|
|
7279
7356
|
} catch (err) {
|
|
7280
7357
|
const msg = err instanceof Error ? err.message : String(err);
|
|
7281
|
-
console.error(`${
|
|
7358
|
+
console.error(`${TAG18} setAccountManager failed: ${msg}`);
|
|
7282
7359
|
return { ok: false, error: msg };
|
|
7283
7360
|
}
|
|
7284
7361
|
}
|
|
@@ -7303,12 +7380,12 @@ function clearAccountManager(accountDir, phone) {
|
|
|
7303
7380
|
}
|
|
7304
7381
|
config.whatsapp = parsed.data;
|
|
7305
7382
|
writeConfig(accountDir, config);
|
|
7306
|
-
console.error(`${
|
|
7383
|
+
console.error(`${TAG18} cleared account manager phone=${normalized}`);
|
|
7307
7384
|
reloadManagerConfig(accountDir);
|
|
7308
7385
|
return { ok: true, message: `Cleared the account-manager binding for ${normalized}.` };
|
|
7309
7386
|
} catch (err) {
|
|
7310
7387
|
const msg = err instanceof Error ? err.message : String(err);
|
|
7311
|
-
console.error(`${
|
|
7388
|
+
console.error(`${TAG18} clearAccountManager failed: ${msg}`);
|
|
7312
7389
|
return { ok: false, error: msg };
|
|
7313
7390
|
}
|
|
7314
7391
|
}
|
|
@@ -7355,12 +7432,12 @@ function setPublicAgent(accountDir, slug) {
|
|
|
7355
7432
|
}
|
|
7356
7433
|
config.whatsapp = parsed.data;
|
|
7357
7434
|
writeConfig(accountDir, config);
|
|
7358
|
-
console.error(`${
|
|
7435
|
+
console.error(`${TAG18} publicAgent set to ${trimmed}`);
|
|
7359
7436
|
reloadManagerConfig(accountDir);
|
|
7360
7437
|
return { ok: true, message: `Public agent set to "${trimmed}". WhatsApp messages from non-admin phones will be handled by this agent.` };
|
|
7361
7438
|
} catch (err) {
|
|
7362
7439
|
const msg = err instanceof Error ? err.message : String(err);
|
|
7363
|
-
console.error(`${
|
|
7440
|
+
console.error(`${TAG18} setPublicAgent failed: ${msg}`);
|
|
7364
7441
|
return { ok: false, error: msg };
|
|
7365
7442
|
}
|
|
7366
7443
|
}
|
|
@@ -7401,17 +7478,17 @@ function updateConfig(accountDir, fields) {
|
|
|
7401
7478
|
const parsed = WhatsAppConfigSchema.safeParse(wa);
|
|
7402
7479
|
if (!parsed.success) {
|
|
7403
7480
|
const msg = parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ");
|
|
7404
|
-
console.error(`${
|
|
7481
|
+
console.error(`${TAG18} update validation failed: ${msg}`);
|
|
7405
7482
|
return { ok: false, error: `Validation failed: ${msg}` };
|
|
7406
7483
|
}
|
|
7407
7484
|
config.whatsapp = parsed.data;
|
|
7408
7485
|
writeConfig(accountDir, config);
|
|
7409
|
-
console.error(`${
|
|
7486
|
+
console.error(`${TAG18} updated fields=[${fieldNames.join(",")}]`);
|
|
7410
7487
|
reloadManagerConfig(accountDir);
|
|
7411
7488
|
return { ok: true, message: `Updated WhatsApp config: ${fieldNames.join(", ")}.` };
|
|
7412
7489
|
} catch (err) {
|
|
7413
7490
|
const msg = err instanceof Error ? err.message : String(err);
|
|
7414
|
-
console.error(`${
|
|
7491
|
+
console.error(`${TAG18} updateConfig failed: ${msg}`);
|
|
7415
7492
|
return { ok: false, error: msg };
|
|
7416
7493
|
}
|
|
7417
7494
|
}
|
|
@@ -7448,17 +7525,17 @@ function migrateRemovedConfigKeys(accountDir) {
|
|
|
7448
7525
|
}
|
|
7449
7526
|
writeConfig(accountDir, config);
|
|
7450
7527
|
console.error(
|
|
7451
|
-
`${
|
|
7528
|
+
`${TAG18} migration: stripped unknown keys=[${result.droppedKeys.join(",")}] from account.json`
|
|
7452
7529
|
);
|
|
7453
7530
|
return { dropped: result.droppedKeys };
|
|
7454
7531
|
} catch (err) {
|
|
7455
|
-
console.error(`${
|
|
7532
|
+
console.error(`${TAG18} migration failed: ${String(err)}`);
|
|
7456
7533
|
return { dropped: [] };
|
|
7457
7534
|
}
|
|
7458
7535
|
}
|
|
7459
7536
|
|
|
7460
7537
|
// app/lib/whatsapp/login.ts
|
|
7461
|
-
var
|
|
7538
|
+
var TAG19 = "[whatsapp:login]";
|
|
7462
7539
|
function maskPhone(digits) {
|
|
7463
7540
|
if (digits.length <= 4) return "*".repeat(digits.length);
|
|
7464
7541
|
return digits.slice(0, 2) + "*".repeat(digits.length - 4) + digits.slice(-2);
|
|
@@ -7469,7 +7546,7 @@ function closeSocket(sock) {
|
|
|
7469
7546
|
try {
|
|
7470
7547
|
sock.ws?.close?.();
|
|
7471
7548
|
} catch (err) {
|
|
7472
|
-
console.warn(`${
|
|
7549
|
+
console.warn(`${TAG19} socket close error during cleanup: ${String(err)}`);
|
|
7473
7550
|
}
|
|
7474
7551
|
}
|
|
7475
7552
|
function resetActiveLogin(accountId) {
|
|
@@ -7496,18 +7573,18 @@ async function runPostPairing(login) {
|
|
|
7496
7573
|
const masked = selfPhone ? `${selfPhone.slice(0, 4)}***` : "null";
|
|
7497
7574
|
console.error(`[whatsapp-persist] wa-persist-on-connect account=${login.accountId} phone=${masked}`);
|
|
7498
7575
|
} else {
|
|
7499
|
-
console.error(`${
|
|
7576
|
+
console.error(`${TAG19} wa-persist-on-connect FAILED account=${login.accountId}: ${result.error}`);
|
|
7500
7577
|
}
|
|
7501
7578
|
} catch (err) {
|
|
7502
|
-
console.error(`${
|
|
7579
|
+
console.error(`${TAG19} wa-persist-on-connect error account=${login.accountId}: ${String(err)}`);
|
|
7503
7580
|
}
|
|
7504
7581
|
} else {
|
|
7505
|
-
console.error(`${
|
|
7582
|
+
console.error(`${TAG19} wa-persist-on-connect skipped \u2014 no accountDir account=${login.accountId}`);
|
|
7506
7583
|
}
|
|
7507
7584
|
try {
|
|
7508
7585
|
await registerLoginSocket(login.accountId, login.sock, login.authDir);
|
|
7509
7586
|
} catch (err) {
|
|
7510
|
-
console.error(`${
|
|
7587
|
+
console.error(`${TAG19} registerLoginSocket failed account=${login.accountId}: ${String(err)}`);
|
|
7511
7588
|
}
|
|
7512
7589
|
}
|
|
7513
7590
|
async function loginConnectionLoop(accountId, login) {
|
|
@@ -7516,7 +7593,7 @@ async function loginConnectionLoop(accountId, login) {
|
|
|
7516
7593
|
const logTerminal = (outcome, errMsg) => {
|
|
7517
7594
|
const fc = credsCensus(login.authDir);
|
|
7518
7595
|
console.error(
|
|
7519
|
-
`${
|
|
7596
|
+
`${TAG19} op=terminal cid=${cidShort} outcome=${outcome} credsFinal={registered=${fc.registered},account=${fc.hasAccountSignature},me=${fc.e164 ?? "null"}}` + (errMsg ? ` error="${errMsg}"` : "")
|
|
7520
7597
|
);
|
|
7521
7598
|
};
|
|
7522
7599
|
while (true) {
|
|
@@ -7526,7 +7603,7 @@ async function loginConnectionLoop(accountId, login) {
|
|
|
7526
7603
|
if (current?.id === login.id) {
|
|
7527
7604
|
await runPostPairing(current);
|
|
7528
7605
|
current.connected = true;
|
|
7529
|
-
console.error(`${
|
|
7606
|
+
console.error(`${TAG19} loginConnectionLoop: connected account=${accountId} attempt=${attempt} configPersisted=${current.configPersisted}`);
|
|
7530
7607
|
logTerminal("connected");
|
|
7531
7608
|
}
|
|
7532
7609
|
return;
|
|
@@ -7538,7 +7615,7 @@ async function loginConnectionLoop(accountId, login) {
|
|
|
7538
7615
|
if (!preRetryCensus.hasAccountSignature) {
|
|
7539
7616
|
current.error = "WhatsApp accepted the code but did not finish linking. This usually means the number has reached its linked-device limit or has a stale linked device. On the phone, open WhatsApp, Linked Devices, remove existing devices, then ask for a new code.";
|
|
7540
7617
|
console.error(
|
|
7541
|
-
`${
|
|
7618
|
+
`${TAG19} op=pairing-incomplete cid=${cidShort} closeKind=${closeKind(getStatusCode(err), false)} reason=no-account-signature`
|
|
7542
7619
|
);
|
|
7543
7620
|
return;
|
|
7544
7621
|
}
|
|
@@ -7546,7 +7623,7 @@ async function loginConnectionLoop(accountId, login) {
|
|
|
7546
7623
|
if (!classification.shouldRetry || attempt >= LOGIN_MAX_RECONNECTS) {
|
|
7547
7624
|
if (attempt >= LOGIN_MAX_RECONNECTS) {
|
|
7548
7625
|
console.error(
|
|
7549
|
-
`${
|
|
7626
|
+
`${TAG19} login reconnect attempts exhausted (${attempt}/${LOGIN_MAX_RECONNECTS}) \u2014 surfacing error to agent`
|
|
7550
7627
|
);
|
|
7551
7628
|
current.error = `Login failed after ${attempt} reconnect attempts: ${formatError(err)}`;
|
|
7552
7629
|
} else {
|
|
@@ -7559,7 +7636,7 @@ async function loginConnectionLoop(accountId, login) {
|
|
|
7559
7636
|
attempt++;
|
|
7560
7637
|
const delay = LOGIN_RECONNECT_DELAYS[attempt - 1] ?? 8e3;
|
|
7561
7638
|
console.error(
|
|
7562
|
-
`${
|
|
7639
|
+
`${TAG19} status=${classification.statusCode ?? "unknown"} restart required \u2014 reconnecting with saved creds (attempt ${attempt}/${LOGIN_MAX_RECONNECTS}) delay=${delay}ms`
|
|
7563
7640
|
);
|
|
7564
7641
|
closeSocket(current.sock);
|
|
7565
7642
|
await new Promise((r) => setTimeout(r, delay));
|
|
@@ -7567,14 +7644,14 @@ async function loginConnectionLoop(accountId, login) {
|
|
|
7567
7644
|
if (afterDelay?.id !== login.id) return;
|
|
7568
7645
|
const rc = credsCensus(login.authDir);
|
|
7569
7646
|
console.error(
|
|
7570
|
-
`${
|
|
7647
|
+
`${TAG19} op=reconnect cid=${cidShort} attempt=${attempt}/${LOGIN_MAX_RECONNECTS} withRegistered=${rc.registered} withAccount=${rc.hasAccountSignature}`
|
|
7571
7648
|
);
|
|
7572
7649
|
try {
|
|
7573
7650
|
const newSock = await createWaSocket({ authDir: login.authDir, correlationId: cidShort, account: accountId });
|
|
7574
7651
|
current.sock = newSock;
|
|
7575
7652
|
} catch (sockErr) {
|
|
7576
7653
|
console.error(
|
|
7577
|
-
`${
|
|
7654
|
+
`${TAG19} reconnect socket creation failed (attempt ${attempt}/${LOGIN_MAX_RECONNECTS}): ${String(sockErr)}`
|
|
7578
7655
|
);
|
|
7579
7656
|
current.error = `Reconnection failed: ${String(sockErr)}`;
|
|
7580
7657
|
logTerminal("error", current.error);
|
|
@@ -7591,7 +7668,7 @@ async function startLogin(opts) {
|
|
|
7591
7668
|
const hasAuth = await authExists(authDir);
|
|
7592
7669
|
const selfId = readSelfId(authDir);
|
|
7593
7670
|
console.error(
|
|
7594
|
-
`${
|
|
7671
|
+
`${TAG19} op=start cid=${cid} account=${accountId} force=${!!force} hasAuth=${hasAuth}` + (existing0 ? ` activeLogin={id=${existing0.id.slice(0, 8)},age=${Math.round((Date.now() - existing0.startedAt) / 1e3)}s,hasCode=${!!existing0.pairingCode}}` : " activeLogin=none")
|
|
7595
7672
|
);
|
|
7596
7673
|
if (hasAuth && !force) {
|
|
7597
7674
|
const who = selfId.e164 ?? selfId.jid ?? "unknown";
|
|
@@ -7606,7 +7683,7 @@ async function startLogin(opts) {
|
|
|
7606
7683
|
}
|
|
7607
7684
|
const existing = activeLogins.get(accountId);
|
|
7608
7685
|
if (existing && isLoginFresh(existing) && existing.pairingCode && !force) {
|
|
7609
|
-
console.error(`${
|
|
7686
|
+
console.error(`${TAG19} startLogin account=${accountId} guard: returning existing pairing code (age=${Math.round((Date.now() - existing.startedAt) / 1e3)}s)`);
|
|
7610
7687
|
return {
|
|
7611
7688
|
pairingCode: existing.pairingCode,
|
|
7612
7689
|
phone: existing.phone,
|
|
@@ -7614,7 +7691,7 @@ async function startLogin(opts) {
|
|
|
7614
7691
|
};
|
|
7615
7692
|
}
|
|
7616
7693
|
if (existing) {
|
|
7617
|
-
console.error(`${
|
|
7694
|
+
console.error(`${TAG19} startLogin account=${accountId} ${force ? "force override" : "stale/no-code"}, resetting active login`);
|
|
7618
7695
|
}
|
|
7619
7696
|
resetActiveLogin(accountId);
|
|
7620
7697
|
await clearAuth(authDir);
|
|
@@ -7645,7 +7722,7 @@ async function startLogin(opts) {
|
|
|
7645
7722
|
if (requested) return;
|
|
7646
7723
|
requested = true;
|
|
7647
7724
|
console.error(
|
|
7648
|
-
`${
|
|
7725
|
+
`${TAG19} op=pairing-request cid=${cid} account=${accountId} qrRef=#1 sinceOpenMs=${socketOpenTs ? Date.now() - socketOpenTs : 0}`
|
|
7649
7726
|
);
|
|
7650
7727
|
void sock.requestPairingCode(digits).then((code) => {
|
|
7651
7728
|
clearTimeout(codeTimer);
|
|
@@ -7654,7 +7731,7 @@ async function startLogin(opts) {
|
|
|
7654
7731
|
if (current?.id !== login.id) return;
|
|
7655
7732
|
if (!current.pairingCode) current.pairingCode = code;
|
|
7656
7733
|
console.error(
|
|
7657
|
-
`${
|
|
7734
|
+
`${TAG19} op=code-issued cid=${cid} account=${accountId} phone=${maskPhone(digits)} codeLen=${code.length} sinceOpenMs=${socketOpenTs ? codeIssuedTs - socketOpenTs : 0}`
|
|
7658
7735
|
);
|
|
7659
7736
|
resolveCode?.(code);
|
|
7660
7737
|
}).catch((err) => {
|
|
@@ -7685,7 +7762,7 @@ async function startLogin(opts) {
|
|
|
7685
7762
|
const ttlTimer = setTimeout(() => {
|
|
7686
7763
|
const current = activeLogins.get(accountId);
|
|
7687
7764
|
if (current?.id === login.id && !current.connected) {
|
|
7688
|
-
console.error(`${
|
|
7765
|
+
console.error(`${TAG19} pairing-window-elapsed account=${accountId} (ttl sweep)`);
|
|
7689
7766
|
resetActiveLogin(accountId);
|
|
7690
7767
|
}
|
|
7691
7768
|
}, ACTIVE_LOGIN_TTL_MS);
|
|
@@ -7693,7 +7770,7 @@ async function startLogin(opts) {
|
|
|
7693
7770
|
ttlTimer.unref();
|
|
7694
7771
|
}
|
|
7695
7772
|
loginConnectionLoop(accountId, login).catch((err) => {
|
|
7696
|
-
console.error(`${
|
|
7773
|
+
console.error(`${TAG19} loginConnectionLoop unexpected error: ${String(err)}`);
|
|
7697
7774
|
const current = activeLogins.get(accountId);
|
|
7698
7775
|
if (current?.id === login.id) {
|
|
7699
7776
|
current.error = `Unexpected login error: ${String(err)}`;
|
|
@@ -7718,13 +7795,13 @@ async function waitForLogin(opts) {
|
|
|
7718
7795
|
const { accountId, timeoutMs = 6e4 } = opts;
|
|
7719
7796
|
const login = activeLogins.get(accountId);
|
|
7720
7797
|
console.error(
|
|
7721
|
-
`${
|
|
7798
|
+
`${TAG19} 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},hasCode=${!!login.pairingCode}}` : " login=none")
|
|
7722
7799
|
);
|
|
7723
7800
|
if (!login) {
|
|
7724
7801
|
return { connected: false, message: "No active WhatsApp login in progress.", configPersisted: false };
|
|
7725
7802
|
}
|
|
7726
7803
|
if (!isLoginFresh(login)) {
|
|
7727
|
-
console.error(`${
|
|
7804
|
+
console.error(`${TAG19} pairing-window-elapsed account=${accountId}`);
|
|
7728
7805
|
resetActiveLogin(accountId);
|
|
7729
7806
|
return { connected: false, message: "The pairing window expired. Ask me to generate a new code.", configPersisted: false };
|
|
7730
7807
|
}
|
|
@@ -7732,8 +7809,8 @@ async function waitForLogin(opts) {
|
|
|
7732
7809
|
while (Date.now() < deadline) {
|
|
7733
7810
|
if (login.connected) {
|
|
7734
7811
|
const selfId = readSelfId(login.authDir);
|
|
7735
|
-
console.error(`${
|
|
7736
|
-
console.error(`${
|
|
7812
|
+
console.error(`${TAG19} pairing-connected account=${accountId} phone=${selfId.e164 ?? "unknown"}`);
|
|
7813
|
+
console.error(`${TAG19} login complete for account=${accountId} phone=${selfId.e164 ?? "unknown"} configPersisted=${login.configPersisted}`);
|
|
7737
7814
|
const configPersisted = login.configPersisted;
|
|
7738
7815
|
activeLogins.delete(accountId);
|
|
7739
7816
|
return {
|
|
@@ -7751,7 +7828,7 @@ async function waitForLogin(opts) {
|
|
|
7751
7828
|
await new Promise((r) => setTimeout(r, 1e3));
|
|
7752
7829
|
}
|
|
7753
7830
|
const elapsed = Math.round((Date.now() - (deadline - timeoutMs)) / 1e3);
|
|
7754
|
-
console.error(`${
|
|
7831
|
+
console.error(`${TAG19} waitForLogin poll timeout account=${accountId} elapsed=${elapsed}s \u2014 login kept alive (pairing code still valid)`);
|
|
7755
7832
|
return { connected: false, message: "Still waiting for you to enter the pairing code. The code is still valid \u2014 ask me to keep waiting, or request a new code.", configPersisted: false };
|
|
7756
7833
|
}
|
|
7757
7834
|
function listActiveLoginAccountIds() {
|
|
@@ -7773,8 +7850,7 @@ function reconcileMessageStoreAccountKeys(input) {
|
|
|
7773
7850
|
import { realpathSync as realpathSync3 } from "fs";
|
|
7774
7851
|
import { readFile, stat as stat2 } from "fs/promises";
|
|
7775
7852
|
import { resolve as resolve9, basename as basename3 } from "path";
|
|
7776
|
-
var
|
|
7777
|
-
var WHATSAPP_DOCUMENT_MAX_BYTES = 100 * 1024 * 1024;
|
|
7853
|
+
var TAG20 = "[whatsapp:outbound]";
|
|
7778
7854
|
var lastDocumentOutboundAt = /* @__PURE__ */ new Map();
|
|
7779
7855
|
var lastRouteDocumentOutboundAt = /* @__PURE__ */ new Map();
|
|
7780
7856
|
function normalizeJid2(to) {
|
|
@@ -7807,16 +7883,16 @@ async function sendWhatsAppDocument(input) {
|
|
|
7807
7883
|
const accountResolved = realpathSync3(accountDir);
|
|
7808
7884
|
if (!resolvedPath.startsWith(accountResolved + "/")) {
|
|
7809
7885
|
const sanitised = filePath.replace(accountDir, "<account>/");
|
|
7810
|
-
console.error(`${
|
|
7886
|
+
console.error(`${TAG20} document REJECTED path=${sanitised} reason=outside_account_directory maxyAccountId=${maxyAccountId}`);
|
|
7811
7887
|
return { ok: false, status: 403, error: "Access denied: file is outside the account directory" };
|
|
7812
7888
|
}
|
|
7813
7889
|
} catch (err) {
|
|
7814
7890
|
const code = err.code;
|
|
7815
7891
|
if (code === "ENOENT") {
|
|
7816
|
-
console.error(`${
|
|
7892
|
+
console.error(`${TAG20} document ENOENT path=${filePath}`);
|
|
7817
7893
|
return { ok: false, status: 404, error: `File not found: ${filePath}` };
|
|
7818
7894
|
}
|
|
7819
|
-
console.error(`${
|
|
7895
|
+
console.error(`${TAG20} document path error: ${String(err)}`);
|
|
7820
7896
|
return { ok: false, status: 500, error: String(err) };
|
|
7821
7897
|
}
|
|
7822
7898
|
const fileStat = await stat2(resolvedPath);
|
|
@@ -7832,7 +7908,7 @@ async function sendWhatsAppDocument(input) {
|
|
|
7832
7908
|
const res = resolveSocket(accountId);
|
|
7833
7909
|
if (!res.ok) {
|
|
7834
7910
|
const reason = res.reason === "key-miss" ? `key-miss presentKeys=${res.presentKeys.join(",") || "none"}` : "disconnected";
|
|
7835
|
-
console.error(`${
|
|
7911
|
+
console.error(`${TAG20} sent document to=${jid} file=${filename} bytes=${fileStat.size} ok=false reason=${reason}`);
|
|
7836
7912
|
if (res.reason === "key-miss") {
|
|
7837
7913
|
return {
|
|
7838
7914
|
ok: false,
|
|
@@ -7852,7 +7928,7 @@ async function sendWhatsAppDocument(input) {
|
|
|
7852
7928
|
{ accountId: res.accountId }
|
|
7853
7929
|
);
|
|
7854
7930
|
console.error(
|
|
7855
|
-
`${
|
|
7931
|
+
`${TAG20} sent document to=${jid} file=${filename} bytes=${fileStat.size} ok=${result.success}` + (result.messageId ? ` id=${result.messageId}` : "")
|
|
7856
7932
|
);
|
|
7857
7933
|
if (result.success) {
|
|
7858
7934
|
recordDocumentOutbound(to);
|
|
@@ -7972,7 +8048,7 @@ function serializeWhatsAppSchema() {
|
|
|
7972
8048
|
// app/lib/whatsapp/status-reconcile.ts
|
|
7973
8049
|
import { readdirSync as readdirSync4 } from "fs";
|
|
7974
8050
|
import { join as join9 } from "path";
|
|
7975
|
-
var
|
|
8051
|
+
var TAG21 = "[whatsapp:reconcile]";
|
|
7976
8052
|
var HALF_REGISTERED_MIN_AGE_MS = 5 * 6e4;
|
|
7977
8053
|
function listCredsAccountIds(credsRoot) {
|
|
7978
8054
|
try {
|
|
@@ -7998,14 +8074,14 @@ function reconcileCredsOnDisk(opts) {
|
|
|
7998
8074
|
const ageMs = getAuthAgeMs(authDir);
|
|
7999
8075
|
const abandoned = !inFlight && ageMs !== null && ageMs >= HALF_REGISTERED_MIN_AGE_MS;
|
|
8000
8076
|
if (!abandoned) {
|
|
8001
|
-
console.error(`${
|
|
8077
|
+
console.error(`${TAG21} op=half-registered account=${accountId} registered=true hasAccount=false inFlight=${inFlight} ageMs=${ageMs ?? "null"}`);
|
|
8002
8078
|
entries.push({ accountId, connected: false, linkedUnconfigured: true });
|
|
8003
8079
|
continue;
|
|
8004
8080
|
}
|
|
8005
|
-
console.error(`${
|
|
8081
|
+
console.error(`${TAG21} op=discard-half-registered account=${accountId} registered=true hasAccount=false ageMs=${ageMs}`);
|
|
8006
8082
|
const removed = discardAuthDirSync(authDir);
|
|
8007
8083
|
if (!removed) {
|
|
8008
|
-
console.error(`${
|
|
8084
|
+
console.error(`${TAG21} op=discard-half-registered-failed account=${accountId} \u2014 residue persists`);
|
|
8009
8085
|
entries.push({ accountId, connected: false, linkedUnconfigured: true });
|
|
8010
8086
|
}
|
|
8011
8087
|
continue;
|
|
@@ -8013,14 +8089,14 @@ function reconcileCredsOnDisk(opts) {
|
|
|
8013
8089
|
const selfPhone = readSelfId(authDir).e164 ?? void 0;
|
|
8014
8090
|
const configured = accountDir ? isAccountConfigured(accountDir, accountId) : false;
|
|
8015
8091
|
console.error(
|
|
8016
|
-
`${
|
|
8092
|
+
`${TAG21} wa-link-unconfigured account=${accountId} creds=present config=${configured ? "present" : "absent"}`
|
|
8017
8093
|
);
|
|
8018
8094
|
if (!configured && accountDir) {
|
|
8019
8095
|
const result = persistAfterPairing(accountDir, accountId, selfPhone ?? null);
|
|
8020
8096
|
if (result.ok) {
|
|
8021
|
-
console.error(`${
|
|
8097
|
+
console.error(`${TAG21} self-healed account=${accountId}`);
|
|
8022
8098
|
} else {
|
|
8023
|
-
console.error(`${
|
|
8099
|
+
console.error(`${TAG21} self-heal FAILED account=${accountId}: ${result.error}`);
|
|
8024
8100
|
}
|
|
8025
8101
|
}
|
|
8026
8102
|
entries.push({ accountId, selfPhone, connected: false, linkedUnconfigured: !configured });
|
|
@@ -8121,7 +8197,7 @@ function authorizeRecallRead(input) {
|
|
|
8121
8197
|
}
|
|
8122
8198
|
|
|
8123
8199
|
// server/routes/whatsapp.ts
|
|
8124
|
-
var
|
|
8200
|
+
var TAG22 = "[whatsapp:api]";
|
|
8125
8201
|
var PLATFORM_ROOT5 = process.env.MAXY_PLATFORM_ROOT || "";
|
|
8126
8202
|
function recallAttachment(storeAccountId, attachmentId) {
|
|
8127
8203
|
if (!attachmentId) return void 0;
|
|
@@ -8166,7 +8242,7 @@ function houseSocketScopeDenial(c, op) {
|
|
|
8166
8242
|
const houseAccountId = resolveAccount()?.accountId ?? "";
|
|
8167
8243
|
const { decision, callerAccountId, houseAdminScope } = evaluateCallerScope(c, houseAccountId);
|
|
8168
8244
|
if (decision.authorized) return null;
|
|
8169
|
-
console.error(`${
|
|
8245
|
+
console.error(`${TAG22} op=authz-deny route=${op} caller=${callerAccountId ?? "none"} houseAdmin=${houseAdminScope ? "y" : "n"}`);
|
|
8170
8246
|
return c.json({ ok: false, error: "Not authorized. The WhatsApp connection and its settings are house-scoped; this session may only act on its own account." }, 403);
|
|
8171
8247
|
}
|
|
8172
8248
|
var app2 = new Hono();
|
|
@@ -8182,7 +8258,7 @@ app2.get("/status", (c) => {
|
|
|
8182
8258
|
const reconciled = reconcileCredsOnDisk({ credsRoot, accountDir, liveAccountIds: liveIds, activeLoginAccountIds: activeLoginIds });
|
|
8183
8259
|
const accounts = [...live, ...reconciled];
|
|
8184
8260
|
const summary = accounts.map((a) => `${a.accountId}:${a.connected ? "up" : a.linkedUnconfigured ? "linked-unconfigured" : "down"}`).join(", ");
|
|
8185
|
-
console.error(`${
|
|
8261
|
+
console.error(`${TAG22} status accounts=${accounts.length} [${summary}]`);
|
|
8186
8262
|
const reachableAccountIds = [
|
|
8187
8263
|
.../* @__PURE__ */ new Set([...listStoredAccountIds(), ...listConnectionPlatformAccountIds()])
|
|
8188
8264
|
];
|
|
@@ -8191,11 +8267,11 @@ app2.get("/status", (c) => {
|
|
|
8191
8267
|
reachableAccountIds
|
|
8192
8268
|
});
|
|
8193
8269
|
if (!storeKeyReconcile.ok) {
|
|
8194
|
-
console.warn(`${
|
|
8270
|
+
console.warn(`${TAG22} store-key-drift orphanLiveKeys=[${storeKeyReconcile.orphanLiveKeys.join(",")}] \u2014 in-memory cache keyed by a value with no disk account; read tools will miss`);
|
|
8195
8271
|
}
|
|
8196
8272
|
return c.json({ accounts, storeKeyReconcile });
|
|
8197
8273
|
} catch (err) {
|
|
8198
|
-
console.error(`${
|
|
8274
|
+
console.error(`${TAG22} status error: ${String(err)}`);
|
|
8199
8275
|
return c.json({ error: String(err) }, 500);
|
|
8200
8276
|
}
|
|
8201
8277
|
});
|
|
@@ -8213,10 +8289,10 @@ app2.post("/login/start", async (c) => {
|
|
|
8213
8289
|
const authDir = join10(MAXY_DIR, "credentials", "whatsapp", accountId);
|
|
8214
8290
|
const accountDir = resolveAccount()?.accountDir ?? null;
|
|
8215
8291
|
const result = await startLogin({ accountId, authDir, phone, accountDir, force });
|
|
8216
|
-
console.error(`${
|
|
8292
|
+
console.error(`${TAG22} login/start result account=${accountId} hasCode=${!!result.pairingCode}${result.selfPhone ? ` phone=${result.selfPhone}` : ""}`);
|
|
8217
8293
|
return c.json(result);
|
|
8218
8294
|
} catch (err) {
|
|
8219
|
-
console.error(`${
|
|
8295
|
+
console.error(`${TAG22} login/start error: ${String(err)}`);
|
|
8220
8296
|
return c.json({ error: String(err) }, 500);
|
|
8221
8297
|
}
|
|
8222
8298
|
});
|
|
@@ -8228,7 +8304,7 @@ app2.post("/login/wait", async (c) => {
|
|
|
8228
8304
|
const accountId = validateAccountId(body.accountId);
|
|
8229
8305
|
const timeoutMs = body.timeoutMs ?? 6e4;
|
|
8230
8306
|
const result = await waitForLogin({ accountId, timeoutMs });
|
|
8231
|
-
console.error(`${
|
|
8307
|
+
console.error(`${TAG22} login/wait result account=${accountId} connected=${result.connected}${result.selfPhone ? ` phone=${result.selfPhone}` : ""} configPersisted=${result.configPersisted}`);
|
|
8232
8308
|
return c.json({
|
|
8233
8309
|
connected: result.connected,
|
|
8234
8310
|
message: result.message,
|
|
@@ -8236,7 +8312,7 @@ app2.post("/login/wait", async (c) => {
|
|
|
8236
8312
|
configPersisted: result.configPersisted
|
|
8237
8313
|
});
|
|
8238
8314
|
} catch (err) {
|
|
8239
|
-
console.error(`${
|
|
8315
|
+
console.error(`${TAG22} login/wait error: ${String(err)}`);
|
|
8240
8316
|
return c.json({ error: String(err) }, 500);
|
|
8241
8317
|
}
|
|
8242
8318
|
});
|
|
@@ -8249,7 +8325,7 @@ app2.post("/disconnect", async (c) => {
|
|
|
8249
8325
|
await stopConnection(accountId);
|
|
8250
8326
|
return c.json({ disconnected: true, accountId });
|
|
8251
8327
|
} catch (err) {
|
|
8252
|
-
console.error(`${
|
|
8328
|
+
console.error(`${TAG22} disconnect error: ${String(err)}`);
|
|
8253
8329
|
return c.json({ error: String(err) }, 500);
|
|
8254
8330
|
}
|
|
8255
8331
|
});
|
|
@@ -8262,7 +8338,7 @@ app2.post("/reconnect", async (c) => {
|
|
|
8262
8338
|
await startConnection(accountId);
|
|
8263
8339
|
return c.json({ reconnecting: true, accountId });
|
|
8264
8340
|
} catch (err) {
|
|
8265
|
-
console.error(`${
|
|
8341
|
+
console.error(`${TAG22} reconnect error: ${String(err)}`);
|
|
8266
8342
|
return c.json({ error: String(err) }, 500);
|
|
8267
8343
|
}
|
|
8268
8344
|
});
|
|
@@ -8286,7 +8362,7 @@ app2.post("/config", async (c) => {
|
|
|
8286
8362
|
}
|
|
8287
8363
|
const result = addAdminPhone(account.accountDir, phone);
|
|
8288
8364
|
const reasonTail = !result.ok && result.reason ? ` reason=${result.reason}` : "";
|
|
8289
|
-
console.error(`${
|
|
8365
|
+
console.error(`${TAG22} config action=add-admin-phone accountId=${account.accountId} phone=${phone} ok=${result.ok}${reasonTail}`);
|
|
8290
8366
|
return c.json(result, result.ok ? 200 : 400);
|
|
8291
8367
|
}
|
|
8292
8368
|
case "remove-admin-phone": {
|
|
@@ -8294,12 +8370,12 @@ app2.post("/config", async (c) => {
|
|
|
8294
8370
|
return c.json({ ok: false, error: 'Missing required field "phone".' }, 400);
|
|
8295
8371
|
}
|
|
8296
8372
|
const result = removeAdminPhone(account.accountDir, phone);
|
|
8297
|
-
console.error(`${
|
|
8373
|
+
console.error(`${TAG22} config action=remove-admin-phone phone=${phone} ok=${result.ok}`);
|
|
8298
8374
|
return c.json(result, result.ok ? 200 : 400);
|
|
8299
8375
|
}
|
|
8300
8376
|
case "list-admin-phones": {
|
|
8301
8377
|
const phones = readAdminPhones(account.accountDir);
|
|
8302
|
-
console.error(`${
|
|
8378
|
+
console.error(`${TAG22} config action=list-admin-phones count=${phones.length}`);
|
|
8303
8379
|
return c.json({ ok: true, phones });
|
|
8304
8380
|
}
|
|
8305
8381
|
case "add-account-manager": {
|
|
@@ -8311,7 +8387,7 @@ app2.post("/config", async (c) => {
|
|
|
8311
8387
|
}
|
|
8312
8388
|
const managerMode = mode === "passive" ? "passive" : "active";
|
|
8313
8389
|
const result = setAccountManager(account.accountDir, phone, managesAccount, managerMode);
|
|
8314
|
-
console.error(`${
|
|
8390
|
+
console.error(`${TAG22} config action=add-account-manager phone=${phone} managesAccount=${managesAccount} mode=${managerMode} ok=${result.ok}`);
|
|
8315
8391
|
return c.json(result, result.ok ? 200 : 400);
|
|
8316
8392
|
}
|
|
8317
8393
|
case "remove-account-manager": {
|
|
@@ -8319,12 +8395,12 @@ app2.post("/config", async (c) => {
|
|
|
8319
8395
|
return c.json({ ok: false, error: 'Missing required field "phone".' }, 400);
|
|
8320
8396
|
}
|
|
8321
8397
|
const result = clearAccountManager(account.accountDir, phone);
|
|
8322
|
-
console.error(`${
|
|
8398
|
+
console.error(`${TAG22} config action=remove-account-manager phone=${phone} ok=${result.ok}`);
|
|
8323
8399
|
return c.json(result, result.ok ? 200 : 400);
|
|
8324
8400
|
}
|
|
8325
8401
|
case "list-account-managers": {
|
|
8326
8402
|
const accountManagers = readAccountManagers(account.accountDir);
|
|
8327
|
-
console.error(`${
|
|
8403
|
+
console.error(`${TAG22} config action=list-account-managers count=${Object.keys(accountManagers).length}`);
|
|
8328
8404
|
return c.json({ ok: true, accountManagers });
|
|
8329
8405
|
}
|
|
8330
8406
|
case "set-public-agent": {
|
|
@@ -8332,13 +8408,13 @@ app2.post("/config", async (c) => {
|
|
|
8332
8408
|
return c.json({ ok: false, error: 'Missing required field "slug" (the agent directory name, e.g. "my-agent").' }, 400);
|
|
8333
8409
|
}
|
|
8334
8410
|
const result = setPublicAgent(account.accountDir, slug);
|
|
8335
|
-
console.error(`${
|
|
8411
|
+
console.error(`${TAG22} config action=set-public-agent slug=${slug} ok=${result.ok}`);
|
|
8336
8412
|
return c.json(result, result.ok ? 200 : 400);
|
|
8337
8413
|
}
|
|
8338
8414
|
case "get-public-agent": {
|
|
8339
8415
|
const targetAccount = typeof accountId === "string" && accountId.trim() ? accountId.trim() : "default";
|
|
8340
8416
|
const resolved = resolvePublicAgent(account.accountDir, { accountId: targetAccount });
|
|
8341
|
-
console.error(`${
|
|
8417
|
+
console.error(`${TAG22} config action=get-public-agent accountId=${targetAccount} slug=${resolved?.slug ?? "none"} source=${resolved?.source ?? "none"}`);
|
|
8342
8418
|
return c.json({ ok: true, slug: resolved?.slug ?? null, source: resolved?.source ?? null });
|
|
8343
8419
|
}
|
|
8344
8420
|
case "list-public-agents": {
|
|
@@ -8355,26 +8431,26 @@ app2.post("/config", async (c) => {
|
|
|
8355
8431
|
const config = JSON.parse(readFileSync12(configPath2, "utf-8"));
|
|
8356
8432
|
agents.push({ slug: entry.name, displayName: config.displayName ?? entry.name });
|
|
8357
8433
|
} catch {
|
|
8358
|
-
console.error(`${
|
|
8434
|
+
console.error(`${TAG22} config action=list-public-agents error="failed to parse config.json for agent ${entry.name}" \u2014 skipping`);
|
|
8359
8435
|
}
|
|
8360
8436
|
}
|
|
8361
8437
|
} catch (err) {
|
|
8362
|
-
console.error(`${
|
|
8438
|
+
console.error(`${TAG22} config action=list-public-agents error="failed to scan agents directory: ${String(err)}"`);
|
|
8363
8439
|
}
|
|
8364
8440
|
}
|
|
8365
|
-
console.error(`${
|
|
8441
|
+
console.error(`${TAG22} config action=list-public-agents count=${agents.length}`);
|
|
8366
8442
|
return c.json({ ok: true, agents });
|
|
8367
8443
|
}
|
|
8368
8444
|
case "schema": {
|
|
8369
8445
|
const text = serializeWhatsAppSchema();
|
|
8370
|
-
console.error(`${
|
|
8446
|
+
console.error(`${TAG22} config action=schema`);
|
|
8371
8447
|
return c.json({ ok: true, text });
|
|
8372
8448
|
}
|
|
8373
8449
|
case "list-groups": {
|
|
8374
8450
|
const groupAccountId = accountId ?? "default";
|
|
8375
8451
|
const sock = getSocket(groupAccountId);
|
|
8376
8452
|
if (!sock) {
|
|
8377
|
-
console.error(`${
|
|
8453
|
+
console.error(`${TAG22} config action=list-groups error="not connected" accountId=${groupAccountId}`);
|
|
8378
8454
|
return c.json({ ok: false, error: `WhatsApp account "${groupAccountId}" is not connected. Connect first, then retry.` });
|
|
8379
8455
|
}
|
|
8380
8456
|
try {
|
|
@@ -8384,10 +8460,10 @@ app2.post("/config", async (c) => {
|
|
|
8384
8460
|
name: g.subject ?? g.id,
|
|
8385
8461
|
participantCount: Array.isArray(g.participants) ? g.participants.length : 0
|
|
8386
8462
|
}));
|
|
8387
|
-
console.error(`${
|
|
8463
|
+
console.error(`${TAG22} config action=list-groups count=${groups.length} accountId=${groupAccountId}`);
|
|
8388
8464
|
return c.json({ ok: true, groups });
|
|
8389
8465
|
} catch (err) {
|
|
8390
|
-
console.error(`${
|
|
8466
|
+
console.error(`${TAG22} config action=list-groups error="${String(err)}" accountId=${groupAccountId}`);
|
|
8391
8467
|
return c.json({ ok: false, error: `Failed to fetch groups: ${String(err)}` });
|
|
8392
8468
|
}
|
|
8393
8469
|
}
|
|
@@ -8397,12 +8473,12 @@ app2.post("/config", async (c) => {
|
|
|
8397
8473
|
}
|
|
8398
8474
|
const result = updateConfig(account.accountDir, fields);
|
|
8399
8475
|
const fieldNames = Object.keys(fields);
|
|
8400
|
-
console.error(`${
|
|
8476
|
+
console.error(`${TAG22} config action=update-config fields=[${fieldNames.join(",")}] ok=${result.ok}`);
|
|
8401
8477
|
return c.json(result, result.ok ? 200 : 400);
|
|
8402
8478
|
}
|
|
8403
8479
|
case "get-config": {
|
|
8404
8480
|
const waConfig = getConfig(account.accountDir);
|
|
8405
|
-
console.error(`${
|
|
8481
|
+
console.error(`${TAG22} config action=get-config`);
|
|
8406
8482
|
return c.json({ ok: true, config: waConfig });
|
|
8407
8483
|
}
|
|
8408
8484
|
default:
|
|
@@ -8412,7 +8488,7 @@ app2.post("/config", async (c) => {
|
|
|
8412
8488
|
);
|
|
8413
8489
|
}
|
|
8414
8490
|
} catch (err) {
|
|
8415
|
-
console.error(`${
|
|
8491
|
+
console.error(`${TAG22} config error: ${String(err)}`);
|
|
8416
8492
|
return c.json({ ok: false, error: String(err) }, 500);
|
|
8417
8493
|
}
|
|
8418
8494
|
});
|
|
@@ -8433,7 +8509,7 @@ app2.post("/send-document", async (c) => {
|
|
|
8433
8509
|
recordRouteDocumentOutbound(to, filePath);
|
|
8434
8510
|
return c.json({ success: true, messageId: result.messageId });
|
|
8435
8511
|
} catch (err) {
|
|
8436
|
-
console.error(`${
|
|
8512
|
+
console.error(`${TAG22} send-document error: ${String(err)}`);
|
|
8437
8513
|
return c.json({ error: String(err) }, 500);
|
|
8438
8514
|
}
|
|
8439
8515
|
});
|
|
@@ -8478,7 +8554,7 @@ app2.post("/send-admin", async (c) => {
|
|
|
8478
8554
|
console.error(`[whatsapp:outbound] op=send-admin-ok via=${via} to=${phone}`);
|
|
8479
8555
|
return c.json({ success: true, messageId: result.messageId });
|
|
8480
8556
|
} catch (err) {
|
|
8481
|
-
console.error(`${
|
|
8557
|
+
console.error(`${TAG22} send-admin error: ${String(err)}`);
|
|
8482
8558
|
return c.json({ error: String(err) }, 500);
|
|
8483
8559
|
}
|
|
8484
8560
|
});
|
|
@@ -8488,11 +8564,11 @@ app2.get("/activity", (c) => {
|
|
|
8488
8564
|
const result = getChannelActivity(accountId);
|
|
8489
8565
|
const total = result.accounts.reduce((sum, a) => sum + a.total, 0);
|
|
8490
8566
|
console.error(
|
|
8491
|
-
`${
|
|
8567
|
+
`${TAG22} activity accounts=${result.accounts.length} total=${total} recentEvents=${result.recentEvents.length}` + (accountId ? ` filter=${accountId}` : "")
|
|
8492
8568
|
);
|
|
8493
8569
|
return c.json(result);
|
|
8494
8570
|
} catch (err) {
|
|
8495
|
-
console.error(`${
|
|
8571
|
+
console.error(`${TAG22} activity error: ${String(err)}`);
|
|
8496
8572
|
return c.json({ error: String(err) }, 500);
|
|
8497
8573
|
}
|
|
8498
8574
|
});
|
|
@@ -8517,10 +8593,10 @@ app2.get("/conversations", (c) => {
|
|
|
8517
8593
|
};
|
|
8518
8594
|
});
|
|
8519
8595
|
conversations.sort((a, b) => (b.lastMessageTimestamp ?? 0) - (a.lastMessageTimestamp ?? 0));
|
|
8520
|
-
console.error(`${
|
|
8596
|
+
console.error(`${TAG22} conversations requested=${requestedAccountId} account=${storeAccountId} projected=${projecting} storeAccount=${projecting ? storeAccountId : "self"} managerPhonesBound=${managerPhonesBound} storeKeyFound=${allConversations.size > 0} count=${conversations.length}`);
|
|
8521
8597
|
return c.json({ conversations });
|
|
8522
8598
|
} catch (err) {
|
|
8523
|
-
console.error(`${
|
|
8599
|
+
console.error(`${TAG22} conversations error: ${String(err)}`);
|
|
8524
8600
|
return c.json({ error: String(err) }, 500);
|
|
8525
8601
|
}
|
|
8526
8602
|
});
|
|
@@ -8565,10 +8641,10 @@ app2.get("/messages", (c) => {
|
|
|
8565
8641
|
...reactions && reactions.length > 0 ? { reactions } : {}
|
|
8566
8642
|
};
|
|
8567
8643
|
});
|
|
8568
|
-
console.error(`${
|
|
8644
|
+
console.error(`${TAG22} messages requested=${requestedAccountId} account=${storeAccountId} jid=${jid} projected=${projecting} storeAccount=${projecting ? storeAccountId : "self"} managerPhonesBound=${managerPhonesBound} storeKeyFound=${storeMessages.length > 0} limit=${effectiveLimit ?? "all"} returned=${messages.length} withAttachments=${withAttachments}`);
|
|
8569
8645
|
return c.json({ messages });
|
|
8570
8646
|
} catch (err) {
|
|
8571
|
-
console.error(`${
|
|
8647
|
+
console.error(`${TAG22} messages error: ${String(err)}`);
|
|
8572
8648
|
return c.json({ error: String(err) }, 500);
|
|
8573
8649
|
}
|
|
8574
8650
|
});
|
|
@@ -8670,7 +8746,7 @@ app2.get("/conversation-graph-state", async (c) => {
|
|
|
8670
8746
|
ms
|
|
8671
8747
|
});
|
|
8672
8748
|
} catch (err) {
|
|
8673
|
-
console.error(`${
|
|
8749
|
+
console.error(`${TAG22} conversation-graph-state error: ${String(err)}`);
|
|
8674
8750
|
return c.json({ error: String(err) }, 500);
|
|
8675
8751
|
}
|
|
8676
8752
|
});
|
|
@@ -8682,12 +8758,12 @@ app2.get("/group-info", async (c) => {
|
|
|
8682
8758
|
return c.json({ error: "Missing required parameter: jid" }, 400);
|
|
8683
8759
|
}
|
|
8684
8760
|
if (!isGroupJid(jid)) {
|
|
8685
|
-
console.error(`${
|
|
8761
|
+
console.error(`${TAG22} group-info error="not a group JID" jid=${jid} account=${accountId}`);
|
|
8686
8762
|
return c.json({ error: `"${jid}" is not a group JID. Group JIDs end with @g.us.` }, 400);
|
|
8687
8763
|
}
|
|
8688
8764
|
const sock = getSocket(accountId);
|
|
8689
8765
|
if (!sock) {
|
|
8690
|
-
console.error(`${
|
|
8766
|
+
console.error(`${TAG22} group-info error="not connected" account=${accountId}`);
|
|
8691
8767
|
return c.json({ error: `WhatsApp account "${accountId}" is not connected. Connect first, then retry.` }, 400);
|
|
8692
8768
|
}
|
|
8693
8769
|
const meta = await sock.groupMetadata(jid);
|
|
@@ -8700,10 +8776,10 @@ app2.get("/group-info", async (c) => {
|
|
|
8700
8776
|
participantCount: meta.participants.length,
|
|
8701
8777
|
participants: meta.participants.map((p) => ({ jid: p.id, admin: p.admin ?? null }))
|
|
8702
8778
|
};
|
|
8703
|
-
console.error(`${
|
|
8779
|
+
console.error(`${TAG22} group-info jid=${jid} subject="${meta.subject}" participants=${meta.participants.length} account=${accountId}`);
|
|
8704
8780
|
return c.json(result);
|
|
8705
8781
|
} catch (err) {
|
|
8706
|
-
console.error(`${
|
|
8782
|
+
console.error(`${TAG22} group-info error="${String(err)}" jid=${jid} account=${accountId}`);
|
|
8707
8783
|
return c.json({ error: `Failed to fetch group info: ${String(err)}` }, 500);
|
|
8708
8784
|
}
|
|
8709
8785
|
});
|
|
@@ -9510,10 +9586,10 @@ async function fetchLiveSessions() {
|
|
|
9510
9586
|
function loadUserTitles(accountDir) {
|
|
9511
9587
|
const out = /* @__PURE__ */ new Map();
|
|
9512
9588
|
if (!accountDir) return out;
|
|
9513
|
-
const
|
|
9589
|
+
const path3 = join14(accountDir, "session-titles.json");
|
|
9514
9590
|
let raw;
|
|
9515
9591
|
try {
|
|
9516
|
-
raw = readFileSync15(
|
|
9592
|
+
raw = readFileSync15(path3, "utf8");
|
|
9517
9593
|
} catch {
|
|
9518
9594
|
return out;
|
|
9519
9595
|
}
|
|
@@ -9668,21 +9744,21 @@ app4.get("/", requireAdminSession, async (c) => {
|
|
|
9668
9744
|
const adminUserIdBySession = /* @__PURE__ */ new Map();
|
|
9669
9745
|
const channelSenderBySession = /* @__PURE__ */ new Map();
|
|
9670
9746
|
const channelMissNoSender = [];
|
|
9671
|
-
for (const { path:
|
|
9672
|
-
const lastSlash =
|
|
9673
|
-
const base =
|
|
9674
|
-
const projectDir =
|
|
9747
|
+
for (const { path: path3, isSubagent, archived } of jsonls) {
|
|
9748
|
+
const lastSlash = path3.lastIndexOf("/");
|
|
9749
|
+
const base = path3.slice(lastSlash + 1);
|
|
9750
|
+
const projectDir = path3.slice(0, lastSlash);
|
|
9675
9751
|
const sessionId = base.slice(0, -".jsonl".length);
|
|
9676
9752
|
if (seenIds.has(sessionId)) {
|
|
9677
|
-
console.error(`[admin-sessions-list] duplicate-sessionId sessionId=${sessionId} path=${
|
|
9753
|
+
console.error(`[admin-sessions-list] duplicate-sessionId sessionId=${sessionId} path=${path3}`);
|
|
9678
9754
|
continue;
|
|
9679
9755
|
}
|
|
9680
9756
|
seenIds.add(sessionId);
|
|
9681
9757
|
let body;
|
|
9682
9758
|
let mtimeMs;
|
|
9683
9759
|
try {
|
|
9684
|
-
body = readFileSync15(
|
|
9685
|
-
mtimeMs = statSync4(
|
|
9760
|
+
body = readFileSync15(path3, "utf8");
|
|
9761
|
+
mtimeMs = statSync4(path3).mtimeMs;
|
|
9686
9762
|
} catch {
|
|
9687
9763
|
continue;
|
|
9688
9764
|
}
|
|
@@ -10301,9 +10377,9 @@ app5.get("/conversations", requireAdminSession, async (c) => {
|
|
|
10301
10377
|
const userTitles = loadUserTitles(ACCOUNTS_DIR ?? null);
|
|
10302
10378
|
const rows = [];
|
|
10303
10379
|
let sessionRowsExcludedUntagged = 0;
|
|
10304
|
-
for (const { path:
|
|
10305
|
-
const sessionId = basename4(
|
|
10306
|
-
const projectDir = dirname5(
|
|
10380
|
+
for (const { path: path3 } of enumerateJsonls(projectsRoot)) {
|
|
10381
|
+
const sessionId = basename4(path3).replace(/\.jsonl$/, "");
|
|
10382
|
+
const projectDir = dirname5(path3);
|
|
10307
10383
|
const meta = readSidecarMeta(join16(projectDir, `${sessionId}.meta.json`));
|
|
10308
10384
|
if (!isReaderChannelSession(meta.role, meta.channel)) continue;
|
|
10309
10385
|
if (multiAccount) {
|
|
@@ -10312,7 +10388,7 @@ app5.get("/conversations", requireAdminSession, async (c) => {
|
|
|
10312
10388
|
}
|
|
10313
10389
|
let body = "";
|
|
10314
10390
|
try {
|
|
10315
|
-
body = readFileSync17(
|
|
10391
|
+
body = readFileSync17(path3, "utf8");
|
|
10316
10392
|
} catch {
|
|
10317
10393
|
}
|
|
10318
10394
|
const { title } = resolveTitle(body, sessionId, userTitles);
|
|
@@ -10395,10 +10471,10 @@ app5.get("/conversations", requireAdminSession, async (c) => {
|
|
|
10395
10471
|
);
|
|
10396
10472
|
return c.json({ conversations: [...sessionConversations, ...allStoreRows] });
|
|
10397
10473
|
});
|
|
10398
|
-
function readFrom(
|
|
10399
|
-
const end = statSync5(
|
|
10474
|
+
function readFrom(path3, from) {
|
|
10475
|
+
const end = statSync5(path3).size;
|
|
10400
10476
|
if (end <= from) return { buf: Buffer.alloc(0), end: from };
|
|
10401
|
-
const fd = openSync(
|
|
10477
|
+
const fd = openSync(path3, "r");
|
|
10402
10478
|
try {
|
|
10403
10479
|
const buf = Buffer.alloc(end - from);
|
|
10404
10480
|
readSync(fd, buf, 0, buf.length, from);
|
|
@@ -10733,11 +10809,11 @@ app5.get("/directive", requireAdminSession, (c) => {
|
|
|
10733
10809
|
}
|
|
10734
10810
|
const dir = directiveStoreDir(sessionId);
|
|
10735
10811
|
if (!dir) return c.json({ error: "no account" }, 404);
|
|
10736
|
-
const
|
|
10737
|
-
if (!
|
|
10812
|
+
const path3 = resolve13(join16(dir, name));
|
|
10813
|
+
if (!path3.startsWith(dir + sep3)) return c.json({ error: "bad reference" }, 400);
|
|
10738
10814
|
let body;
|
|
10739
10815
|
try {
|
|
10740
|
-
body = readFileSync17(
|
|
10816
|
+
body = readFileSync17(path3, "utf8");
|
|
10741
10817
|
} catch {
|
|
10742
10818
|
return c.json({ error: "not found" }, 404);
|
|
10743
10819
|
}
|
|
@@ -11214,7 +11290,7 @@ function resolveOpenVisitorSession(rows, visitorId, agentSlug) {
|
|
|
11214
11290
|
|
|
11215
11291
|
// server/routes/public-reader.ts
|
|
11216
11292
|
var app6 = new Hono();
|
|
11217
|
-
var
|
|
11293
|
+
var TAG23 = "[public-webchat]";
|
|
11218
11294
|
function parseAccessSessionId(cookieHeader) {
|
|
11219
11295
|
if (!cookieHeader) return null;
|
|
11220
11296
|
const part = cookieHeader.split(";").map((p) => p.trim()).find((p) => p.startsWith("__access_session="));
|
|
@@ -11241,9 +11317,9 @@ function enumeratePublicRows() {
|
|
|
11241
11317
|
const cfg = claudeConfigDir();
|
|
11242
11318
|
if (!cfg) return [];
|
|
11243
11319
|
const rows = [];
|
|
11244
|
-
for (const { path:
|
|
11245
|
-
const sessionId = basename5(
|
|
11246
|
-
const projectDir = dirname6(
|
|
11320
|
+
for (const { path: path3 } of enumerateJsonls(join17(cfg, "projects"))) {
|
|
11321
|
+
const sessionId = basename5(path3).replace(/\.jsonl$/, "");
|
|
11322
|
+
const projectDir = dirname6(path3);
|
|
11247
11323
|
const meta = readSidecarMeta(join17(projectDir, `${sessionId}.meta.json`));
|
|
11248
11324
|
rows.push({
|
|
11249
11325
|
sessionId,
|
|
@@ -11262,7 +11338,7 @@ function enumeratePublicRows() {
|
|
|
11262
11338
|
app6.get("/session", (c) => {
|
|
11263
11339
|
const visitor = resolveVisitor(c);
|
|
11264
11340
|
if (!visitor) {
|
|
11265
|
-
console.error(`${
|
|
11341
|
+
console.error(`${TAG23} op=reader-refused reason=no-anchor path=/session`);
|
|
11266
11342
|
return c.json({ error: "gate-required" }, 401);
|
|
11267
11343
|
}
|
|
11268
11344
|
const rows = enumeratePublicRows();
|
|
@@ -11281,11 +11357,11 @@ app6.get("/session", (c) => {
|
|
|
11281
11357
|
found = effectiveSlug ? resolveOpenVisitorSession(rows, visitor.visitorId, effectiveSlug) : null;
|
|
11282
11358
|
}
|
|
11283
11359
|
if (found) {
|
|
11284
|
-
console.log(`${
|
|
11360
|
+
console.log(`${TAG23} op=session-resume anchor=${visitor.kind} key=${(found.senderId ?? "").slice(0, 8)} sessionId=${found.sessionId.slice(0, 8)}`);
|
|
11285
11361
|
return c.json({ sessionId: found.sessionId, projectDir: found.projectDir, sessionKey: found.senderId });
|
|
11286
11362
|
}
|
|
11287
11363
|
const sessionKey = crypto.randomUUID();
|
|
11288
|
-
console.log(`${
|
|
11364
|
+
console.log(`${TAG23} op=session-new anchor=${visitor.kind} key=${sessionKey.slice(0, 8)}`);
|
|
11289
11365
|
return c.json({ sessionId: null, projectDir: null, sessionKey });
|
|
11290
11366
|
});
|
|
11291
11367
|
function publicUploadsDir(accountId, sessionId) {
|
|
@@ -11299,10 +11375,10 @@ function enrichPublicAttachments(turns, sessionAttachments, cursor) {
|
|
|
11299
11375
|
if (slice.length > 0) turn.attachments = slice;
|
|
11300
11376
|
}
|
|
11301
11377
|
}
|
|
11302
|
-
function readFrom2(
|
|
11303
|
-
const end = statSync6(
|
|
11378
|
+
function readFrom2(path3, from) {
|
|
11379
|
+
const end = statSync6(path3).size;
|
|
11304
11380
|
if (end <= from) return { buf: Buffer.alloc(0), end: from };
|
|
11305
|
-
const fd = openSync2(
|
|
11381
|
+
const fd = openSync2(path3, "r");
|
|
11306
11382
|
try {
|
|
11307
11383
|
const buf = Buffer.alloc(end - from);
|
|
11308
11384
|
readSync2(fd, buf, 0, buf.length, from);
|
|
@@ -11314,7 +11390,7 @@ function readFrom2(path2, from) {
|
|
|
11314
11390
|
app6.get("/stream", (c) => {
|
|
11315
11391
|
const visitor = resolveVisitor(c);
|
|
11316
11392
|
if (!visitor) {
|
|
11317
|
-
console.error(`${
|
|
11393
|
+
console.error(`${TAG23} op=reader-refused reason=no-anchor path=/stream`);
|
|
11318
11394
|
return c.json({ error: "gate-required" }, 401);
|
|
11319
11395
|
}
|
|
11320
11396
|
const sessionId = c.req.query("sessionId") ?? "";
|
|
@@ -11332,12 +11408,12 @@ app6.get("/stream", (c) => {
|
|
|
11332
11408
|
const isPublicWebchat = meta.role === "public" && meta.channel === "webchat";
|
|
11333
11409
|
const owns = visitor.kind === "person" ? meta.personId === visitor.personId : meta.personId === null && meta.visitorId === visitor.visitorId;
|
|
11334
11410
|
if (!(isPublicWebchat && owns)) {
|
|
11335
|
-
console.error(`${
|
|
11411
|
+
console.error(`${TAG23} op=reader-refused reason=not-owner anchor=${visitor.kind} sessionId=${sessionId.slice(0, 8)}`);
|
|
11336
11412
|
return c.json({ error: "forbidden" }, 403);
|
|
11337
11413
|
}
|
|
11338
11414
|
const senderShort = (meta.senderId ?? "").slice(0, 8);
|
|
11339
11415
|
const encoder = new TextEncoder();
|
|
11340
|
-
console.log(`${
|
|
11416
|
+
console.log(`${TAG23} op=reader-open anchor=${visitor.kind} key=${senderShort} mode=delivered-only sessionId=${sessionId.slice(0, 8)}`);
|
|
11341
11417
|
const send = (controller, turn, id) => controller.enqueue(encoder.encode(`id: ${id}
|
|
11342
11418
|
data: ${JSON.stringify(turn)}
|
|
11343
11419
|
|
|
@@ -11352,7 +11428,7 @@ data: ${JSON.stringify(turn)}
|
|
|
11352
11428
|
const { turns, dropped } = filterDeliveredFrames(lines);
|
|
11353
11429
|
if (uploadsDir) enrichPublicAttachments(turns, listSessionAttachmentsInDir(uploadsDir), attachmentCursor);
|
|
11354
11430
|
for (const turn of turns) send(controller, turn, offset);
|
|
11355
|
-
if (dropped > 0) console.log(`${
|
|
11431
|
+
if (dropped > 0) console.log(`${TAG23} op=reader-filtered key=${senderShort} dropped=${dropped}`);
|
|
11356
11432
|
};
|
|
11357
11433
|
try {
|
|
11358
11434
|
const { buf, end } = readFrom2(jsonlPath, 0);
|
|
@@ -11387,7 +11463,7 @@ data: ${JSON.stringify(turn)}
|
|
|
11387
11463
|
watcher?.close();
|
|
11388
11464
|
clearInterval(poll);
|
|
11389
11465
|
clearInterval(heartbeat);
|
|
11390
|
-
console.log(`${
|
|
11466
|
+
console.log(`${TAG23} op=reader-close key=${senderShort}`);
|
|
11391
11467
|
try {
|
|
11392
11468
|
controller.close();
|
|
11393
11469
|
} catch {
|
|
@@ -11504,10 +11580,10 @@ function writeCanonicalOverrideId(accountDir, userId, sessionId) {
|
|
|
11504
11580
|
}
|
|
11505
11581
|
}
|
|
11506
11582
|
byAdmin[userId] = sessionId;
|
|
11507
|
-
const
|
|
11508
|
-
const tmp = `${
|
|
11583
|
+
const path3 = canonicalOverridePath(accountDir);
|
|
11584
|
+
const tmp = `${path3}.${process.pid}.tmp`;
|
|
11509
11585
|
writeFileSync5(tmp, JSON.stringify({ byAdmin }, null, 2), "utf8");
|
|
11510
|
-
renameSync2(tmp,
|
|
11586
|
+
renameSync2(tmp, path3);
|
|
11511
11587
|
}
|
|
11512
11588
|
|
|
11513
11589
|
// ../lib/models/src/index.ts
|
|
@@ -11516,7 +11592,7 @@ var SONNET_MODEL = "claude-sonnet-5";
|
|
|
11516
11592
|
var HAIKU_MODEL = "claude-haiku-4-5";
|
|
11517
11593
|
var MODEL_CONTEXT_WINDOW = {
|
|
11518
11594
|
[OPUS_MODEL]: 1e6,
|
|
11519
|
-
[SONNET_MODEL]:
|
|
11595
|
+
[SONNET_MODEL]: 1e6,
|
|
11520
11596
|
[HAIKU_MODEL]: 2e5
|
|
11521
11597
|
};
|
|
11522
11598
|
function contextWindow(model) {
|
|
@@ -11566,11 +11642,11 @@ function createSidecarStore(config) {
|
|
|
11566
11642
|
}
|
|
11567
11643
|
function write(sessionsDir, id, record) {
|
|
11568
11644
|
mkdirSync3(sessionsDir, { recursive: true });
|
|
11569
|
-
const
|
|
11570
|
-
const tmp = `${
|
|
11645
|
+
const path3 = pathFor(sessionsDir, id);
|
|
11646
|
+
const tmp = `${path3}.tmp.${process.pid}`;
|
|
11571
11647
|
try {
|
|
11572
11648
|
writeFileSync6(tmp, JSON.stringify(record), "utf8");
|
|
11573
|
-
renameSync3(tmp,
|
|
11649
|
+
renameSync3(tmp, path3);
|
|
11574
11650
|
return { ok: true };
|
|
11575
11651
|
} catch (error) {
|
|
11576
11652
|
try {
|
|
@@ -11580,10 +11656,10 @@ function createSidecarStore(config) {
|
|
|
11580
11656
|
return { ok: false, error };
|
|
11581
11657
|
}
|
|
11582
11658
|
}
|
|
11583
|
-
function readFileAt(
|
|
11659
|
+
function readFileAt(path3) {
|
|
11584
11660
|
let raw;
|
|
11585
11661
|
try {
|
|
11586
|
-
raw = readFileSync20(
|
|
11662
|
+
raw = readFileSync20(path3, "utf8");
|
|
11587
11663
|
} catch {
|
|
11588
11664
|
return null;
|
|
11589
11665
|
}
|
|
@@ -11606,10 +11682,10 @@ function createSidecarStore(config) {
|
|
|
11606
11682
|
const out = [];
|
|
11607
11683
|
for (const name of names) {
|
|
11608
11684
|
if (!suffixRe.test(name)) continue;
|
|
11609
|
-
const
|
|
11685
|
+
const path3 = join19(sessionsDir, name);
|
|
11610
11686
|
let raw;
|
|
11611
11687
|
try {
|
|
11612
|
-
raw = readFileSync20(
|
|
11688
|
+
raw = readFileSync20(path3, "utf8");
|
|
11613
11689
|
} catch {
|
|
11614
11690
|
onSkip?.(name, "unreadable");
|
|
11615
11691
|
continue;
|
|
@@ -11628,10 +11704,10 @@ function createSidecarStore(config) {
|
|
|
11628
11704
|
return out;
|
|
11629
11705
|
}
|
|
11630
11706
|
function clear(sessionsDir, id) {
|
|
11631
|
-
const
|
|
11707
|
+
const path3 = pathFor(sessionsDir, id);
|
|
11632
11708
|
try {
|
|
11633
|
-
if (existsSync12(
|
|
11634
|
-
unlinkSync2(
|
|
11709
|
+
if (existsSync12(path3)) {
|
|
11710
|
+
unlinkSync2(path3);
|
|
11635
11711
|
return { ok: true, removed: true };
|
|
11636
11712
|
}
|
|
11637
11713
|
return { ok: true, removed: false };
|
|
@@ -11860,15 +11936,15 @@ function latestAdminWebchatSessionId(requesterUserId, primaryUserId, scopeAccoun
|
|
|
11860
11936
|
const cfg = claudeConfigDir();
|
|
11861
11937
|
if (!cfg) return null;
|
|
11862
11938
|
let best = null;
|
|
11863
|
-
for (const { path:
|
|
11939
|
+
for (const { path: path3, isSubagent, archived } of enumerateJsonls(join20(cfg, "projects"))) {
|
|
11864
11940
|
if (isSubagent || archived) continue;
|
|
11865
|
-
const id = basename6(
|
|
11866
|
-
const meta = readSidecarMeta(join20(dirname7(
|
|
11941
|
+
const id = basename6(path3).slice(0, -".jsonl".length);
|
|
11942
|
+
const meta = readSidecarMeta(join20(dirname7(path3), `${id}.meta.json`));
|
|
11867
11943
|
if (meta.role !== "admin" || meta.channel !== "webchat") continue;
|
|
11868
11944
|
if (multiAccount && meta.accountId !== scopeAccountId) continue;
|
|
11869
11945
|
let mtimeMs;
|
|
11870
11946
|
try {
|
|
11871
|
-
mtimeMs = statSync7(
|
|
11947
|
+
mtimeMs = statSync7(path3).mtimeMs;
|
|
11872
11948
|
} catch {
|
|
11873
11949
|
continue;
|
|
11874
11950
|
}
|
|
@@ -12265,9 +12341,9 @@ ${note}` : note;
|
|
|
12265
12341
|
const cfg = claudeConfigDir();
|
|
12266
12342
|
let projectDir = null;
|
|
12267
12343
|
if (cfg) {
|
|
12268
|
-
for (const { path:
|
|
12269
|
-
if (basename6(
|
|
12270
|
-
projectDir = dirname7(
|
|
12344
|
+
for (const { path: path3 } of enumerateJsonls(join20(cfg, "projects"))) {
|
|
12345
|
+
if (basename6(path3) === `${sessionId}.jsonl`) {
|
|
12346
|
+
projectDir = dirname7(path3);
|
|
12271
12347
|
break;
|
|
12272
12348
|
}
|
|
12273
12349
|
}
|
|
@@ -12499,7 +12575,7 @@ function routeTelegramUpdate(input) {
|
|
|
12499
12575
|
}
|
|
12500
12576
|
|
|
12501
12577
|
// server/routes/telegram.ts
|
|
12502
|
-
var
|
|
12578
|
+
var TAG24 = "[telegram-inbound]";
|
|
12503
12579
|
function configDirName() {
|
|
12504
12580
|
const platformRoot5 = process.env.MAXY_PLATFORM_ROOT ?? resolve16(process.cwd(), "..");
|
|
12505
12581
|
const brandPath = join22(platformRoot5, "config", "brand.json");
|
|
@@ -12520,18 +12596,18 @@ app8.post("/", async (c) => {
|
|
|
12520
12596
|
const botParam = c.req.query("bot");
|
|
12521
12597
|
const botType = botParam === "admin" ? "admin" : botParam === "public" ? "public" : null;
|
|
12522
12598
|
if (!botType) {
|
|
12523
|
-
console.error(`${
|
|
12599
|
+
console.error(`${TAG24} op=reject reason=missing-bot-param`);
|
|
12524
12600
|
return c.json({ ok: false }, 400);
|
|
12525
12601
|
}
|
|
12526
12602
|
const sp = secretPath(botType);
|
|
12527
12603
|
if (!existsSync15(sp)) {
|
|
12528
|
-
console.error(`${
|
|
12604
|
+
console.error(`${TAG24} op=reject reason=no-secret-file botType=${botType}`);
|
|
12529
12605
|
return c.json({ ok: false }, 401);
|
|
12530
12606
|
}
|
|
12531
12607
|
const expected = readFileSync23(sp, "utf-8").trim();
|
|
12532
12608
|
const got = c.req.header("x-telegram-bot-api-secret-token") ?? "";
|
|
12533
12609
|
if (got !== expected) {
|
|
12534
|
-
console.error(`${
|
|
12610
|
+
console.error(`${TAG24} op=reject reason=bad-secret botType=${botType}`);
|
|
12535
12611
|
return c.json({ ok: false }, 401);
|
|
12536
12612
|
}
|
|
12537
12613
|
let update;
|
|
@@ -12542,40 +12618,40 @@ app8.post("/", async (c) => {
|
|
|
12542
12618
|
}
|
|
12543
12619
|
const account = resolveAccount();
|
|
12544
12620
|
if (!account) {
|
|
12545
|
-
console.error(`${
|
|
12621
|
+
console.error(`${TAG24} op=reject reason=no-account`);
|
|
12546
12622
|
return c.json({ ok: true }, 200);
|
|
12547
12623
|
}
|
|
12548
12624
|
const decision = routeTelegramUpdate({ update, botType, config: account.config.telegram ?? {} });
|
|
12549
12625
|
if (decision.kind === "ignore") {
|
|
12550
|
-
console.error(`${
|
|
12626
|
+
console.error(`${TAG24} op=ignore reason=${decision.reason}`);
|
|
12551
12627
|
return c.json({ ok: true }, 200);
|
|
12552
12628
|
}
|
|
12553
12629
|
if (decision.kind === "denied") {
|
|
12554
|
-
console.error(`${
|
|
12630
|
+
console.error(`${TAG24} op=denied reason=${decision.reason} agentType=${decision.agentType}`);
|
|
12555
12631
|
return c.json({ ok: true }, 200);
|
|
12556
12632
|
}
|
|
12557
12633
|
const role = decision.agentType === "admin" ? "admin" : "public";
|
|
12558
12634
|
const agentSlug = role === "admin" ? "admin" : resolveDefaultAgentSlug(account.accountDir);
|
|
12559
12635
|
if (!agentSlug) {
|
|
12560
|
-
console.error(`${
|
|
12636
|
+
console.error(`${TAG24} op=reject reason=no-default-agent`);
|
|
12561
12637
|
return c.json({ ok: true }, 200);
|
|
12562
12638
|
}
|
|
12563
|
-
console.error(`${
|
|
12639
|
+
console.error(`${TAG24} op=update accountId=${account.accountId} from=${decision.senderId}`);
|
|
12564
12640
|
const botToken = botType === "admin" ? account.config.telegram?.adminBotToken : account.config.telegram?.publicBotToken;
|
|
12565
12641
|
const { senderId, chatId, text } = decision;
|
|
12566
12642
|
const gateway = getTelegramGateway();
|
|
12567
12643
|
if (!gateway) {
|
|
12568
|
-
console.error(`${
|
|
12644
|
+
console.error(`${TAG24} op=reject reason=gateway-not-ready from=${senderId}`);
|
|
12569
12645
|
return c.json({ ok: true }, 200);
|
|
12570
12646
|
}
|
|
12571
12647
|
const reply = async (replyText) => {
|
|
12572
12648
|
if (!botToken) {
|
|
12573
12649
|
const reason = account.config.telegram ? "no-bot-token" : "no-telegram-config";
|
|
12574
|
-
console.error(`${
|
|
12650
|
+
console.error(`${TAG24} op=reply-dropped reason=${reason} botType=${botType} from=${senderId}`);
|
|
12575
12651
|
return;
|
|
12576
12652
|
}
|
|
12577
12653
|
const sent = await sendTelegramText(botToken, chatId, replyText);
|
|
12578
|
-
console.error(`${
|
|
12654
|
+
console.error(`${TAG24} op=reply-sent from=${senderId} ok=${sent.ok}${sent.ok ? "" : ` error=${sent.error}`}`);
|
|
12579
12655
|
};
|
|
12580
12656
|
void gateway.handleInbound({
|
|
12581
12657
|
accountId: account.accountId,
|
|
@@ -12593,7 +12669,7 @@ var telegram_default = app8;
|
|
|
12593
12669
|
// server/routes/quickbooks.ts
|
|
12594
12670
|
import { join as join23 } from "path";
|
|
12595
12671
|
import { existsSync as existsSync16, readFileSync as readFileSync24, writeFileSync as writeFileSync8, mkdirSync as mkdirSync4, rmSync, renameSync as renameSync5 } from "fs";
|
|
12596
|
-
var
|
|
12672
|
+
var TAG25 = "[quickbooks]";
|
|
12597
12673
|
var INTUIT_TOKEN_URL = "https://oauth.platform.intuit.com/oauth2/v1/tokens/bearer";
|
|
12598
12674
|
var STATE_RE = /^[A-Za-z0-9_-]+$/;
|
|
12599
12675
|
function pendingPath(accountDir, state) {
|
|
@@ -12606,14 +12682,14 @@ async function completeConsent(args) {
|
|
|
12606
12682
|
const { accountDir, code, realmId } = args;
|
|
12607
12683
|
const state = args.state;
|
|
12608
12684
|
if (!state || !STATE_RE.test(state) || !existsSync16(pendingPath(accountDir, state))) {
|
|
12609
|
-
console.error(`${
|
|
12685
|
+
console.error(`${TAG25} op=callback state=${state ?? ""} stateValid=false realmId=${realmId ?? ""}`);
|
|
12610
12686
|
return { ok: false, status: 400, message: "Invalid or unknown authorization state.", stateValid: false };
|
|
12611
12687
|
}
|
|
12612
12688
|
const claimFile = `${pendingPath(accountDir, state)}.claimed`;
|
|
12613
12689
|
try {
|
|
12614
12690
|
renameSync5(pendingPath(accountDir, state), claimFile);
|
|
12615
12691
|
} catch {
|
|
12616
|
-
console.error(`${
|
|
12692
|
+
console.error(`${TAG25} op=callback state=${state} stateValid=false realmId=${realmId ?? ""}`);
|
|
12617
12693
|
return { ok: false, status: 400, message: "Invalid or unknown authorization state.", stateValid: false };
|
|
12618
12694
|
}
|
|
12619
12695
|
try {
|
|
@@ -12621,14 +12697,14 @@ async function completeConsent(args) {
|
|
|
12621
12697
|
try {
|
|
12622
12698
|
pending = JSON.parse(readFileSync24(claimFile, "utf-8"));
|
|
12623
12699
|
} catch {
|
|
12624
|
-
console.error(`${
|
|
12700
|
+
console.error(`${TAG25} op=callback state=${state} stateValid=false realmId=${realmId ?? ""}`);
|
|
12625
12701
|
return { ok: false, status: 400, message: "Authorization state could not be read.", stateValid: false };
|
|
12626
12702
|
}
|
|
12627
12703
|
if (Date.now() > pending.expiresAt) {
|
|
12628
|
-
console.error(`${
|
|
12704
|
+
console.error(`${TAG25} op=callback state=${state} stateValid=false realmId=${realmId ?? ""}`);
|
|
12629
12705
|
return { ok: false, status: 400, message: "Authorization request expired. Generate a new consent link.", stateValid: false };
|
|
12630
12706
|
}
|
|
12631
|
-
console.error(`${
|
|
12707
|
+
console.error(`${TAG25} op=callback state=${state} stateValid=true realmId=${realmId ?? ""}`);
|
|
12632
12708
|
if (!code || !realmId) {
|
|
12633
12709
|
return { ok: false, status: 400, message: "Missing code or realmId in the callback.", stateValid: true };
|
|
12634
12710
|
}
|
|
@@ -12652,12 +12728,12 @@ async function completeConsent(args) {
|
|
|
12652
12728
|
});
|
|
12653
12729
|
const text = await res.text();
|
|
12654
12730
|
if (!res.ok) {
|
|
12655
|
-
console.error(`${
|
|
12731
|
+
console.error(`${TAG25} op=token-exchange realmId=${realmId} persisted=false`);
|
|
12656
12732
|
return { ok: false, status: 502, message: `Token exchange failed (${res.status}). Generate a new consent link to retry.`, stateValid: true };
|
|
12657
12733
|
}
|
|
12658
12734
|
const body = JSON.parse(text);
|
|
12659
12735
|
if (!body.refresh_token) {
|
|
12660
|
-
console.error(`${
|
|
12736
|
+
console.error(`${TAG25} op=token-exchange realmId=${realmId} persisted=false`);
|
|
12661
12737
|
return { ok: false, status: 502, message: "Token exchange returned no refresh token.", stateValid: true };
|
|
12662
12738
|
}
|
|
12663
12739
|
const now = Date.now();
|
|
@@ -12670,7 +12746,7 @@ async function completeConsent(args) {
|
|
|
12670
12746
|
mkdirSync4(join23(accountDir, "secrets"), { recursive: true });
|
|
12671
12747
|
writeFileSync8(storePath(accountDir), JSON.stringify(store2, null, 2), { mode: 384 });
|
|
12672
12748
|
const persisted = JSON.parse(readFileSync24(storePath(accountDir), "utf-8")).connections?.[realmId]?.refreshToken === body.refresh_token;
|
|
12673
|
-
console.error(`${
|
|
12749
|
+
console.error(`${TAG25} op=token-exchange realmId=${realmId} persisted=${persisted}`);
|
|
12674
12750
|
return { ok: persisted, status: persisted ? 200 : 500, message: persisted ? `Connected company ${realmId}.` : "Failed to persist the connection.", stateValid: true };
|
|
12675
12751
|
} finally {
|
|
12676
12752
|
rmSync(claimFile, { force: true });
|
|
@@ -12683,7 +12759,7 @@ var app9 = new Hono();
|
|
|
12683
12759
|
app9.get("/callback", async (c) => {
|
|
12684
12760
|
const account = resolveAccount();
|
|
12685
12761
|
if (!account) {
|
|
12686
|
-
console.error(`${
|
|
12762
|
+
console.error(`${TAG25} op=callback stateValid=false realmId= reason=no-account`);
|
|
12687
12763
|
return c.html(page("QuickBooks", "This install has no account configured."), 500);
|
|
12688
12764
|
}
|
|
12689
12765
|
const result = await completeConsent({
|
|
@@ -14875,10 +14951,10 @@ function extractPluginToolDescriptions(pluginDir) {
|
|
|
14875
14951
|
join28(pluginDir, "mcp/src/index.ts")
|
|
14876
14952
|
];
|
|
14877
14953
|
let raw = null;
|
|
14878
|
-
for (const
|
|
14879
|
-
if (!existsSync25(
|
|
14954
|
+
for (const path3 of candidates) {
|
|
14955
|
+
if (!existsSync25(path3)) continue;
|
|
14880
14956
|
try {
|
|
14881
|
-
raw = readFileSync29(
|
|
14957
|
+
raw = readFileSync29(path3, "utf-8");
|
|
14882
14958
|
break;
|
|
14883
14959
|
} catch {
|
|
14884
14960
|
}
|
|
@@ -14968,11 +15044,11 @@ function readEnabledPlugins(accountId) {
|
|
|
14968
15044
|
return /* @__PURE__ */ new Set();
|
|
14969
15045
|
}
|
|
14970
15046
|
}
|
|
14971
|
-
function readFrontmatter(
|
|
14972
|
-
if (!existsSync25(
|
|
15047
|
+
function readFrontmatter(path3) {
|
|
15048
|
+
if (!existsSync25(path3)) return null;
|
|
14973
15049
|
let raw;
|
|
14974
15050
|
try {
|
|
14975
|
-
raw = readFileSync29(
|
|
15051
|
+
raw = readFileSync29(path3, "utf-8");
|
|
14976
15052
|
} catch {
|
|
14977
15053
|
return null;
|
|
14978
15054
|
}
|
|
@@ -15151,7 +15227,7 @@ function resolveTunnelUrl() {
|
|
|
15151
15227
|
if (!state) return null;
|
|
15152
15228
|
return `https://${hostname2}.${state.domain}`;
|
|
15153
15229
|
}
|
|
15154
|
-
var
|
|
15230
|
+
var TAG26 = "[claude-session-manager:wrapper]";
|
|
15155
15231
|
async function refuseIfClaudeAuthDead(c, route, sessionId) {
|
|
15156
15232
|
const auth = await ensureAuth();
|
|
15157
15233
|
if (auth.status !== "dead" && auth.status !== "missing") return null;
|
|
@@ -15169,12 +15245,12 @@ async function performSpawnWithInitialMessage(args) {
|
|
|
15169
15245
|
const aboutOwner = await resolveOwnerProfileBlock(args.senderId, args.userId);
|
|
15170
15246
|
const ownerMs = Date.now() - ownerStart;
|
|
15171
15247
|
const aboutOwnerStatus = aboutOwner == null ? "absent" : "ok" in aboutOwner && aboutOwner.ok === false ? `unresolved:${aboutOwner.reason}` : "ok";
|
|
15172
|
-
console.log(`${
|
|
15248
|
+
console.log(`${TAG26} about-owner-resolved status=${aboutOwnerStatus} ms=${ownerMs}`);
|
|
15173
15249
|
const dormantPlugins = computeDormantPlugins(args.senderId);
|
|
15174
15250
|
const activePlugins = computeActivePlugins(args.senderId);
|
|
15175
15251
|
const specialistDomains = computeSpecialistDomains(args.senderId);
|
|
15176
15252
|
const tunnelUrl = resolveTunnelUrl();
|
|
15177
|
-
console.log(`${
|
|
15253
|
+
console.log(`${TAG26} tunnel-url-resolved value=${tunnelUrl ?? "null"}`);
|
|
15178
15254
|
const upstreamPayload = JSON.stringify({
|
|
15179
15255
|
senderId: args.senderId,
|
|
15180
15256
|
// Task 205 — pass userId through to the manager so it lands as
|
|
@@ -15201,24 +15277,24 @@ async function performSpawnWithInitialMessage(args) {
|
|
|
15201
15277
|
// unshapely values.
|
|
15202
15278
|
conversationNodeId: args.conversationNodeId
|
|
15203
15279
|
});
|
|
15204
|
-
console.log(`${
|
|
15280
|
+
console.log(`${TAG26} forward-spawn-start managerBase=${managerBase("claude-session-manager:wrapper")} bytes=${upstreamPayload.length} hidden=${args.hidden} specialist=${args.specialist ?? "none"}`);
|
|
15205
15281
|
const forwardStart = Date.now();
|
|
15206
15282
|
const upstream = await fetch(`${managerBase("claude-session-manager:wrapper")}/public-spawn`, {
|
|
15207
15283
|
method: "POST",
|
|
15208
15284
|
headers: { "content-type": "application/json" },
|
|
15209
15285
|
body: upstreamPayload
|
|
15210
15286
|
}).catch((err) => {
|
|
15211
|
-
console.error(`${
|
|
15287
|
+
console.error(`${TAG26} fetch-failed op=spawn message=${err instanceof Error ? err.message : String(err)} ms=${Date.now() - forwardStart}`);
|
|
15212
15288
|
return null;
|
|
15213
15289
|
});
|
|
15214
15290
|
if (!upstream) return {
|
|
15215
15291
|
response: new Response(JSON.stringify({ error: "manager-unreachable" }), { status: 503, headers: { "content-type": "application/json" } }),
|
|
15216
15292
|
claudeSessionId: null
|
|
15217
15293
|
};
|
|
15218
|
-
console.log(`${
|
|
15294
|
+
console.log(`${TAG26} forward-spawn-done status=${upstream.status} ms=${Date.now() - forwardStart}`);
|
|
15219
15295
|
if (args.initialMessage) {
|
|
15220
15296
|
const inputBytes = Buffer.byteLength(args.initialMessage, "utf8");
|
|
15221
|
-
console.log(`${
|
|
15297
|
+
console.log(`${TAG26} initial-message-inlined bytes=${inputBytes}`);
|
|
15222
15298
|
}
|
|
15223
15299
|
const bodyText = await upstream.text().catch(() => "");
|
|
15224
15300
|
let claudeSessionId = null;
|
|
@@ -15253,7 +15329,7 @@ app19.post("/", async (c) => {
|
|
|
15253
15329
|
if (refusal) return refusal;
|
|
15254
15330
|
const senderId = getAccountIdForSession(cacheKey) ?? "";
|
|
15255
15331
|
if (!senderId) {
|
|
15256
|
-
console.error(`${
|
|
15332
|
+
console.error(`${TAG26} reject reason=no-account-id cacheKey-prefix=${cacheKey.slice(0, 8)}`);
|
|
15257
15333
|
return c.json({ error: "admin-account-not-resolved" }, 500);
|
|
15258
15334
|
}
|
|
15259
15335
|
const userId = getUserIdForSession(cacheKey) ?? void 0;
|
|
@@ -15262,7 +15338,7 @@ app19.post("/", async (c) => {
|
|
|
15262
15338
|
const permissionMode = typeof body.permissionMode === "string" ? body.permissionMode : void 0;
|
|
15263
15339
|
const specialist = typeof body.specialist === "string" && /^[A-Za-z0-9_-]{1,64}$/.test(body.specialist) ? body.specialist : void 0;
|
|
15264
15340
|
const model = typeof body.model === "string" && /^[A-Za-z0-9._-]{1,64}$/.test(body.model) ? body.model : void 0;
|
|
15265
|
-
console.log(`${
|
|
15341
|
+
console.log(`${TAG26} 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"}`);
|
|
15266
15342
|
const conversationNodeId = cacheKey ? getSessionIdForSession(cacheKey) : void 0;
|
|
15267
15343
|
const { response, claudeSessionId } = await performSpawnWithInitialMessage({
|
|
15268
15344
|
senderId,
|
|
@@ -15281,13 +15357,13 @@ app19.post("/", async (c) => {
|
|
|
15281
15357
|
claudeSessionId,
|
|
15282
15358
|
senderId
|
|
15283
15359
|
);
|
|
15284
|
-
console.log(`${
|
|
15360
|
+
console.log(`${TAG26} route-done surface=cookie status=${response.status} route-ms=${Date.now() - routeStart}`);
|
|
15285
15361
|
return response;
|
|
15286
15362
|
});
|
|
15287
15363
|
var claude_sessions_default = app19;
|
|
15288
15364
|
|
|
15289
15365
|
// server/routes/admin/log-ingest.ts
|
|
15290
|
-
var
|
|
15366
|
+
var TAG27 = "[log-ingest]";
|
|
15291
15367
|
var TAG_PATTERN = /^[A-Za-z0-9_:-]{1,32}$/;
|
|
15292
15368
|
var LEVELS = /* @__PURE__ */ new Set(["debug", "info", "warn", "error"]);
|
|
15293
15369
|
var MAX_LINE_BYTES = 4096;
|
|
@@ -15298,7 +15374,7 @@ function isLoopbackAddr2(addr) {
|
|
|
15298
15374
|
app20.post("/", async (c) => {
|
|
15299
15375
|
const remoteAddr = c.env?.incoming?.socket?.remoteAddress ?? "";
|
|
15300
15376
|
if (!isLoopbackAddr2(remoteAddr)) {
|
|
15301
|
-
console.error(`${
|
|
15377
|
+
console.error(`${TAG27} reject reason=non-loopback remoteAddr=${remoteAddr}`);
|
|
15302
15378
|
return c.json({ error: "log-ingest-loopback-only" }, 403);
|
|
15303
15379
|
}
|
|
15304
15380
|
const xffHeader = c.env?.incoming?.headers?.["x-forwarded-for"];
|
|
@@ -15307,7 +15383,7 @@ app20.post("/", async (c) => {
|
|
|
15307
15383
|
const tokens = xffRaw.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
|
|
15308
15384
|
const offender = tokens.find((t) => !isLoopbackAddr2(t));
|
|
15309
15385
|
if (offender !== void 0) {
|
|
15310
|
-
console.error(`${
|
|
15386
|
+
console.error(`${TAG27} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
|
|
15311
15387
|
return c.json({ error: "log-ingest-loopback-only" }, 403);
|
|
15312
15388
|
}
|
|
15313
15389
|
}
|
|
@@ -15349,18 +15425,18 @@ var ALLOWED_EVENTS = /* @__PURE__ */ new Set([
|
|
|
15349
15425
|
]);
|
|
15350
15426
|
var app21 = new Hono();
|
|
15351
15427
|
app21.post("/", async (c) => {
|
|
15352
|
-
const
|
|
15428
|
+
const TAG47 = "[admin:events]";
|
|
15353
15429
|
let body;
|
|
15354
15430
|
try {
|
|
15355
15431
|
body = await c.req.json();
|
|
15356
15432
|
} catch (err) {
|
|
15357
15433
|
const detail = err instanceof Error ? err.message : String(err);
|
|
15358
|
-
console.error(`${
|
|
15434
|
+
console.error(`${TAG47} reject reason=body-not-json detail=${detail}`);
|
|
15359
15435
|
return c.json({ ok: false, detail: "Request body was not valid JSON" }, 400);
|
|
15360
15436
|
}
|
|
15361
15437
|
const event = typeof body.event === "string" ? body.event : "";
|
|
15362
15438
|
if (!ALLOWED_EVENTS.has(event)) {
|
|
15363
|
-
console.error(`${
|
|
15439
|
+
console.error(`${TAG47} reject reason=event-not-allowed event=${JSON.stringify(event)}`);
|
|
15364
15440
|
return c.json({ ok: false, detail: `Event "${event}" is not allowed` }, 400);
|
|
15365
15441
|
}
|
|
15366
15442
|
const rawFields = body.fields && typeof body.fields === "object" ? body.fields : {};
|
|
@@ -20412,7 +20488,7 @@ function managerLogFollowUrl(sessionId, opts) {
|
|
|
20412
20488
|
}
|
|
20413
20489
|
|
|
20414
20490
|
// server/routes/admin/linkedin-ingest.ts
|
|
20415
|
-
var
|
|
20491
|
+
var TAG28 = "[linkedin-ingest-route]";
|
|
20416
20492
|
var UUID2 = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;
|
|
20417
20493
|
var ISO = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,3})?(?:Z|[+-]\d{2}:\d{2})$/;
|
|
20418
20494
|
var LINKEDIN_URL = /^https:\/\/www\.linkedin\.com\//;
|
|
@@ -20516,29 +20592,29 @@ app39.post("/", requireAdminSession, async (c) => {
|
|
|
20516
20592
|
try {
|
|
20517
20593
|
body = await c.req.json();
|
|
20518
20594
|
} catch {
|
|
20519
|
-
console.error(
|
|
20595
|
+
console.error(TAG28 + " rejected status=400 reason=schema:body-not-json");
|
|
20520
20596
|
return c.json({ ok: false, error: "schema", reason: "body-not-json" }, 400);
|
|
20521
20597
|
}
|
|
20522
20598
|
const v = validate(body);
|
|
20523
20599
|
if (!v.ok) {
|
|
20524
|
-
console.error(
|
|
20600
|
+
console.error(TAG28 + " rejected status=" + v.status + " reason=" + v.reason + " missing=" + v.missing.join(","));
|
|
20525
20601
|
return c.json({ ok: false, error: v.error, reason: v.reason, missing: v.missing }, v.status);
|
|
20526
20602
|
}
|
|
20527
20603
|
const envelope = v.envelope;
|
|
20528
20604
|
const cacheKey = c.var.cacheKey ?? "";
|
|
20529
20605
|
const senderId = getAccountIdForSession(cacheKey) ?? "";
|
|
20530
20606
|
if (!senderId) {
|
|
20531
|
-
console.error(
|
|
20607
|
+
console.error(TAG28 + " rejected status=500 reason=admin-account-not-resolved");
|
|
20532
20608
|
return c.json({ ok: false, error: "admin-account-not-resolved" }, 500);
|
|
20533
20609
|
}
|
|
20534
20610
|
const payloadBytes = JSON.stringify(envelope).length;
|
|
20535
20611
|
console.log(
|
|
20536
|
-
|
|
20612
|
+
TAG28 + " received kind=" + envelope.kind + " account=" + senderId.slice(0, 8) + " pageUrl=" + envelope.pageUrl + " dispatchId=" + envelope.dispatchId + " payloadBytes=" + payloadBytes
|
|
20537
20613
|
);
|
|
20538
20614
|
const initialMessage = buildInitialMessage(envelope);
|
|
20539
20615
|
const spawnStart = Date.now();
|
|
20540
20616
|
const sessionId = randomUUID13();
|
|
20541
|
-
console.log(
|
|
20617
|
+
console.log(TAG28 + " route target=rc-spawn dispatchId=" + envelope.dispatchId + " sessionId=" + sessionId.slice(0, 8));
|
|
20542
20618
|
const spawned = await managerRcSpawn({
|
|
20543
20619
|
sessionId,
|
|
20544
20620
|
initialMessage,
|
|
@@ -20547,12 +20623,12 @@ app39.post("/", requireAdminSession, async (c) => {
|
|
|
20547
20623
|
});
|
|
20548
20624
|
if ("error" in spawned) {
|
|
20549
20625
|
console.error(
|
|
20550
|
-
|
|
20626
|
+
TAG28 + " dispatch-failed dispatchId=" + envelope.dispatchId + " status=" + spawned.status + " ms=" + (Date.now() - spawnStart) + " message=" + spawned.error
|
|
20551
20627
|
);
|
|
20552
20628
|
return c.json({ ok: false, error: "dispatch-failed", upstreamStatus: spawned.status, detail: spawned.error }, 502);
|
|
20553
20629
|
}
|
|
20554
20630
|
console.log(
|
|
20555
|
-
|
|
20631
|
+
TAG28 + " dispatched dispatchId=" + envelope.dispatchId + " taskId=" + spawned.sessionId + " ms=" + (Date.now() - spawnStart)
|
|
20556
20632
|
);
|
|
20557
20633
|
return c.json({ ok: true, dispatchId: envelope.dispatchId, taskId: spawned.sessionId }, 202);
|
|
20558
20634
|
});
|
|
@@ -20560,7 +20636,7 @@ var linkedin_ingest_default = app39;
|
|
|
20560
20636
|
|
|
20561
20637
|
// server/routes/admin/post-turn-context.ts
|
|
20562
20638
|
import neo4j3 from "neo4j-driver";
|
|
20563
|
-
var
|
|
20639
|
+
var TAG29 = "[post-turn-context]";
|
|
20564
20640
|
var STRIPPED_PROPERTIES2 = /* @__PURE__ */ new Set([
|
|
20565
20641
|
"embedding",
|
|
20566
20642
|
"passwordHash",
|
|
@@ -20602,7 +20678,7 @@ var app40 = new Hono();
|
|
|
20602
20678
|
app40.get("/", async (c) => {
|
|
20603
20679
|
const remoteAddr = c.env?.incoming?.socket?.remoteAddress ?? "";
|
|
20604
20680
|
if (!isLoopbackAddr3(remoteAddr)) {
|
|
20605
|
-
console.error(`${
|
|
20681
|
+
console.error(`${TAG29} reject reason=non-loopback remoteAddr=${remoteAddr}`);
|
|
20606
20682
|
return c.json({ error: "post-turn-context-loopback-only" }, 403);
|
|
20607
20683
|
}
|
|
20608
20684
|
const xffHeader = c.env?.incoming?.headers?.["x-forwarded-for"];
|
|
@@ -20611,7 +20687,7 @@ app40.get("/", async (c) => {
|
|
|
20611
20687
|
const tokens = xffRaw.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
|
|
20612
20688
|
const offender = tokens.find((t) => !isLoopbackAddr3(t));
|
|
20613
20689
|
if (offender !== void 0) {
|
|
20614
|
-
console.error(`${
|
|
20690
|
+
console.error(`${TAG29} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
|
|
20615
20691
|
return c.json({ error: "post-turn-context-loopback-only" }, 403);
|
|
20616
20692
|
}
|
|
20617
20693
|
}
|
|
@@ -20643,7 +20719,7 @@ app40.get("/", async (c) => {
|
|
|
20643
20719
|
writes.sort((a, b) => a.sortKey.localeCompare(b.sortKey));
|
|
20644
20720
|
const total = Date.now() - started;
|
|
20645
20721
|
console.log(
|
|
20646
|
-
`${
|
|
20722
|
+
`${TAG29} sessionId=${sessionId} accountId=${accountId} writes=${writes.length} ms=${total}`
|
|
20647
20723
|
);
|
|
20648
20724
|
return c.json({
|
|
20649
20725
|
writes: writes.map(({ elementId, labels, properties }) => ({ elementId, labels, properties }))
|
|
@@ -20652,7 +20728,7 @@ app40.get("/", async (c) => {
|
|
|
20652
20728
|
const elapsed = Date.now() - started;
|
|
20653
20729
|
const message = err instanceof Error ? err.message : String(err);
|
|
20654
20730
|
console.error(
|
|
20655
|
-
`${
|
|
20731
|
+
`${TAG29} neo4j-unreachable sessionId=${sessionId} ms=${elapsed} err="${message}"`
|
|
20656
20732
|
);
|
|
20657
20733
|
return c.json({ error: `post-turn-context unavailable: ${message}` }, 503);
|
|
20658
20734
|
} finally {
|
|
@@ -20765,7 +20841,7 @@ function formatPreviousContext(writes) {
|
|
|
20765
20841
|
}
|
|
20766
20842
|
|
|
20767
20843
|
// server/routes/admin/public-session-context.ts
|
|
20768
|
-
var
|
|
20844
|
+
var TAG30 = "[public-session-context]";
|
|
20769
20845
|
function isLoopbackAddr4(addr) {
|
|
20770
20846
|
return addr === "127.0.0.1" || addr === "::1" || addr === "::ffff:127.0.0.1";
|
|
20771
20847
|
}
|
|
@@ -20773,7 +20849,7 @@ var app41 = new Hono();
|
|
|
20773
20849
|
app41.get("/", async (c) => {
|
|
20774
20850
|
const remoteAddr = c.env?.incoming?.socket?.remoteAddress ?? "";
|
|
20775
20851
|
if (!isLoopbackAddr4(remoteAddr)) {
|
|
20776
|
-
console.error(`${
|
|
20852
|
+
console.error(`${TAG30} reject reason=non-loopback remoteAddr=${remoteAddr}`);
|
|
20777
20853
|
return c.json({ error: "public-session-context-loopback-only" }, 403);
|
|
20778
20854
|
}
|
|
20779
20855
|
const xffHeader = c.env?.incoming?.headers?.["x-forwarded-for"];
|
|
@@ -20782,7 +20858,7 @@ app41.get("/", async (c) => {
|
|
|
20782
20858
|
const tokens = xffRaw.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
|
|
20783
20859
|
const offender = tokens.find((t) => !isLoopbackAddr4(t));
|
|
20784
20860
|
if (offender !== void 0) {
|
|
20785
|
-
console.error(`${
|
|
20861
|
+
console.error(`${TAG30} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
|
|
20786
20862
|
return c.json({ error: "public-session-context-loopback-only" }, 403);
|
|
20787
20863
|
}
|
|
20788
20864
|
}
|
|
@@ -20796,14 +20872,14 @@ app41.get("/", async (c) => {
|
|
|
20796
20872
|
const writes = await fetchSliceWrites(session, sliceToken, accountId);
|
|
20797
20873
|
const total = Date.now() - started;
|
|
20798
20874
|
console.log(
|
|
20799
|
-
`${
|
|
20875
|
+
`${TAG30} sliceToken=${sliceToken.slice(0, 8)} writes=${writes.length} ms=${total}`
|
|
20800
20876
|
);
|
|
20801
20877
|
return c.json({ writes });
|
|
20802
20878
|
} catch (err) {
|
|
20803
20879
|
const elapsed = Date.now() - started;
|
|
20804
20880
|
const message = err instanceof Error ? err.message : String(err);
|
|
20805
20881
|
console.error(
|
|
20806
|
-
`${
|
|
20882
|
+
`${TAG30} neo4j-unreachable sliceToken=${sliceToken.slice(0, 8)} ms=${elapsed} err="${message}"`
|
|
20807
20883
|
);
|
|
20808
20884
|
return c.json({ error: `public-session-context unavailable: ${message}` }, 503);
|
|
20809
20885
|
} finally {
|
|
@@ -20822,7 +20898,7 @@ function getWebchatGateway() {
|
|
|
20822
20898
|
}
|
|
20823
20899
|
|
|
20824
20900
|
// server/routes/admin/public-session-exit.ts
|
|
20825
|
-
var
|
|
20901
|
+
var TAG31 = "[public-session-exit-route]";
|
|
20826
20902
|
function isLoopbackAddr5(addr) {
|
|
20827
20903
|
return addr === "127.0.0.1" || addr === "::1" || addr === "::ffff:127.0.0.1";
|
|
20828
20904
|
}
|
|
@@ -20830,7 +20906,7 @@ var app42 = new Hono();
|
|
|
20830
20906
|
app42.post("/", async (c) => {
|
|
20831
20907
|
const remoteAddr = c.env?.incoming?.socket?.remoteAddress ?? "";
|
|
20832
20908
|
if (!isLoopbackAddr5(remoteAddr)) {
|
|
20833
|
-
console.error(`${
|
|
20909
|
+
console.error(`${TAG31} reject reason=non-loopback remoteAddr=${remoteAddr}`);
|
|
20834
20910
|
return c.json({ error: "public-session-exit-loopback-only" }, 403);
|
|
20835
20911
|
}
|
|
20836
20912
|
const xffHeader = c.env?.incoming?.headers?.["x-forwarded-for"];
|
|
@@ -20839,7 +20915,7 @@ app42.post("/", async (c) => {
|
|
|
20839
20915
|
const tokens = xffRaw.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
|
|
20840
20916
|
const offender = tokens.find((t) => !isLoopbackAddr5(t));
|
|
20841
20917
|
if (offender !== void 0) {
|
|
20842
|
-
console.error(`${
|
|
20918
|
+
console.error(`${TAG31} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
|
|
20843
20919
|
return c.json({ error: "public-session-exit-loopback-only" }, 403);
|
|
20844
20920
|
}
|
|
20845
20921
|
}
|
|
@@ -20853,7 +20929,7 @@ app42.post("/", async (c) => {
|
|
|
20853
20929
|
if (!sessionId) return c.json({ error: "sessionId required" }, 400);
|
|
20854
20930
|
const gateway = getWebchatGateway();
|
|
20855
20931
|
if (!gateway) {
|
|
20856
|
-
console.error(`${
|
|
20932
|
+
console.error(`${TAG31} reject reason=gateway-unset sessionId=${sessionId.slice(0, 8)}`);
|
|
20857
20933
|
return c.json({ error: "webchat-gateway-unset" }, 503);
|
|
20858
20934
|
}
|
|
20859
20935
|
gateway.handlePublicSessionExit(sessionId);
|
|
@@ -20862,7 +20938,7 @@ app42.post("/", async (c) => {
|
|
|
20862
20938
|
var public_session_exit_default = app42;
|
|
20863
20939
|
|
|
20864
20940
|
// server/routes/admin/access-session-evict.ts
|
|
20865
|
-
var
|
|
20941
|
+
var TAG32 = "[access-session-evict]";
|
|
20866
20942
|
function isLoopbackAddr6(addr) {
|
|
20867
20943
|
return addr === "127.0.0.1" || addr === "::1" || addr === "::ffff:127.0.0.1";
|
|
20868
20944
|
}
|
|
@@ -20870,7 +20946,7 @@ var app43 = new Hono();
|
|
|
20870
20946
|
app43.post("/", async (c) => {
|
|
20871
20947
|
const remoteAddr = c.env?.incoming?.socket?.remoteAddress ?? "";
|
|
20872
20948
|
if (!isLoopbackAddr6(remoteAddr)) {
|
|
20873
|
-
console.error(`${
|
|
20949
|
+
console.error(`${TAG32} reject reason=non-loopback remoteAddr=${remoteAddr}`);
|
|
20874
20950
|
return c.json({ error: "access-session-evict-loopback-only" }, 403);
|
|
20875
20951
|
}
|
|
20876
20952
|
const xffHeader = c.env?.incoming?.headers?.["x-forwarded-for"];
|
|
@@ -20879,7 +20955,7 @@ app43.post("/", async (c) => {
|
|
|
20879
20955
|
const tokens = xffRaw.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
|
|
20880
20956
|
const offender = tokens.find((t) => !isLoopbackAddr6(t));
|
|
20881
20957
|
if (offender !== void 0) {
|
|
20882
|
-
console.error(`${
|
|
20958
|
+
console.error(`${TAG32} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
|
|
20883
20959
|
return c.json({ error: "access-session-evict-loopback-only" }, 403);
|
|
20884
20960
|
}
|
|
20885
20961
|
}
|
|
@@ -20892,13 +20968,13 @@ app43.post("/", async (c) => {
|
|
|
20892
20968
|
const grantId = typeof body.grantId === "string" ? body.grantId.trim() : "";
|
|
20893
20969
|
if (!grantId) return c.json({ error: "grantId required" }, 400);
|
|
20894
20970
|
const dropped = evictAccessSessionsByGrant(grantId);
|
|
20895
|
-
console.log(`${
|
|
20971
|
+
console.log(`${TAG32} grantId=${grantId} dropped=${dropped}`);
|
|
20896
20972
|
return c.json({ ok: true, dropped });
|
|
20897
20973
|
});
|
|
20898
20974
|
var access_session_evict_default = app43;
|
|
20899
20975
|
|
|
20900
20976
|
// server/routes/admin/enrol-person.ts
|
|
20901
|
-
var
|
|
20977
|
+
var TAG33 = "[enrol]";
|
|
20902
20978
|
function isLoopbackAddr7(addr) {
|
|
20903
20979
|
return addr === "127.0.0.1" || addr === "::1" || addr === "::ffff:127.0.0.1";
|
|
20904
20980
|
}
|
|
@@ -20907,7 +20983,7 @@ var app44 = new Hono();
|
|
|
20907
20983
|
app44.post("/", async (c) => {
|
|
20908
20984
|
const remoteAddr = c.env?.incoming?.socket?.remoteAddress ?? "";
|
|
20909
20985
|
if (!isLoopbackAddr7(remoteAddr)) {
|
|
20910
|
-
console.error(`${
|
|
20986
|
+
console.error(`${TAG33} reject reason=non-loopback remoteAddr=${remoteAddr}`);
|
|
20911
20987
|
return c.json({ error: "enrol-person-loopback-only" }, 403);
|
|
20912
20988
|
}
|
|
20913
20989
|
const xffHeader = c.env?.incoming?.headers?.["x-forwarded-for"];
|
|
@@ -20916,7 +20992,7 @@ app44.post("/", async (c) => {
|
|
|
20916
20992
|
const tokens = xffRaw.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
|
|
20917
20993
|
const offender = tokens.find((t) => !isLoopbackAddr7(t));
|
|
20918
20994
|
if (offender !== void 0) {
|
|
20919
|
-
console.error(`${
|
|
20995
|
+
console.error(`${TAG33} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
|
|
20920
20996
|
return c.json({ error: "enrol-person-loopback-only" }, 403);
|
|
20921
20997
|
}
|
|
20922
20998
|
}
|
|
@@ -20941,7 +21017,7 @@ app44.post("/", async (c) => {
|
|
|
20941
21017
|
}
|
|
20942
21018
|
const accountId = process.env.ACCOUNT_ID ?? "";
|
|
20943
21019
|
if (!accountId) {
|
|
20944
|
-
console.error(`${
|
|
21020
|
+
console.error(`${TAG33} op=person result=no-account-id`);
|
|
20945
21021
|
return c.json({ error: "no-account-id" }, 500);
|
|
20946
21022
|
}
|
|
20947
21023
|
let personId;
|
|
@@ -20949,7 +21025,7 @@ app44.post("/", async (c) => {
|
|
|
20949
21025
|
personId = await enrolPerson({ accountId, phone, email, agentSlug });
|
|
20950
21026
|
} catch (err) {
|
|
20951
21027
|
console.error(
|
|
20952
|
-
`${
|
|
21028
|
+
`${TAG33} op=person result=write-failed agentSlug=${agentSlug} contact=${maskContact(email)} err="${err instanceof Error ? err.message : String(err)}"`
|
|
20953
21029
|
);
|
|
20954
21030
|
return c.json({ error: "enrol-failed" }, 500);
|
|
20955
21031
|
}
|
|
@@ -20959,11 +21035,11 @@ app44.post("/", async (c) => {
|
|
|
20959
21035
|
if (g) grant = { grantId: g.grantId, status: g.status, contactValue: g.contactValue };
|
|
20960
21036
|
} catch (err) {
|
|
20961
21037
|
console.error(
|
|
20962
|
-
`${
|
|
21038
|
+
`${TAG33} op=person result=grant-lookup-failed contact=${maskContact(email)} err="${err instanceof Error ? err.message : String(err)}"`
|
|
20963
21039
|
);
|
|
20964
21040
|
}
|
|
20965
21041
|
console.log(
|
|
20966
|
-
`${
|
|
21042
|
+
`${TAG33} op=person personId=${personId} account=${accountId} agentSlug=${agentSlug} contact=${maskContact(email)}`
|
|
20967
21043
|
);
|
|
20968
21044
|
return c.json({ personId, grant }, 200);
|
|
20969
21045
|
});
|
|
@@ -21119,33 +21195,33 @@ var REQUIRED_KEYS = [
|
|
|
21119
21195
|
"weekly"
|
|
21120
21196
|
];
|
|
21121
21197
|
function readAvailabilityConfig(accountDir) {
|
|
21122
|
-
const
|
|
21198
|
+
const path3 = join33(accountDir, AVAILABILITY_FILENAME);
|
|
21123
21199
|
let raw;
|
|
21124
21200
|
try {
|
|
21125
|
-
raw = readFileSync33(
|
|
21201
|
+
raw = readFileSync33(path3, "utf-8");
|
|
21126
21202
|
} catch {
|
|
21127
|
-
throw new Error(`availability not configured: ${
|
|
21203
|
+
throw new Error(`availability not configured: ${path3} not found`);
|
|
21128
21204
|
}
|
|
21129
21205
|
let parsed;
|
|
21130
21206
|
try {
|
|
21131
21207
|
parsed = JSON.parse(raw);
|
|
21132
21208
|
} catch (err) {
|
|
21133
|
-
throw new Error(`invalid JSON in ${
|
|
21209
|
+
throw new Error(`invalid JSON in ${path3}: ${err.message}`);
|
|
21134
21210
|
}
|
|
21135
21211
|
const cfg = parsed;
|
|
21136
21212
|
for (const key of REQUIRED_KEYS) {
|
|
21137
21213
|
if (cfg[key] === void 0 || cfg[key] === null) {
|
|
21138
|
-
throw new Error(`availability config ${
|
|
21214
|
+
throw new Error(`availability config ${path3} missing required key: ${key}`);
|
|
21139
21215
|
}
|
|
21140
21216
|
}
|
|
21141
21217
|
if (cfg.daysAhead !== void 0 && cfg.daysAhead !== null) {
|
|
21142
21218
|
if (typeof cfg.daysAhead !== "number" || !Number.isInteger(cfg.daysAhead) || cfg.daysAhead < 1) {
|
|
21143
|
-
throw new Error(`availability config ${
|
|
21219
|
+
throw new Error(`availability config ${path3} invalid daysAhead: must be a positive integer`);
|
|
21144
21220
|
}
|
|
21145
21221
|
}
|
|
21146
21222
|
if (cfg.allowMultiSlot !== void 0 && cfg.allowMultiSlot !== null) {
|
|
21147
21223
|
if (typeof cfg.allowMultiSlot !== "boolean") {
|
|
21148
|
-
throw new Error(`availability config ${
|
|
21224
|
+
throw new Error(`availability config ${path3} invalid allowMultiSlot: must be a boolean`);
|
|
21149
21225
|
}
|
|
21150
21226
|
}
|
|
21151
21227
|
return cfg;
|
|
@@ -21517,7 +21593,7 @@ function toNum(v) {
|
|
|
21517
21593
|
}
|
|
21518
21594
|
|
|
21519
21595
|
// server/routes/admin/tasks-list.ts
|
|
21520
|
-
var
|
|
21596
|
+
var TAG34 = "[task-timer]";
|
|
21521
21597
|
var COMPLETED = /* @__PURE__ */ new Set(["completed"]);
|
|
21522
21598
|
var HIDDEN_FROM_OPEN = /* @__PURE__ */ new Set(["completed", "cancelled"]);
|
|
21523
21599
|
var app47 = new Hono();
|
|
@@ -21525,7 +21601,7 @@ app47.get("/", requireAdminSession, async (c) => {
|
|
|
21525
21601
|
const cacheKey = c.var.cacheKey;
|
|
21526
21602
|
const accountId = getAccountIdForSession(cacheKey);
|
|
21527
21603
|
if (!accountId) {
|
|
21528
|
-
console.error(`${
|
|
21604
|
+
console.error(`${TAG34} op=list auth-rejected reason="no account for session"`);
|
|
21529
21605
|
return c.json({ error: "Account not found for session" }, 401);
|
|
21530
21606
|
}
|
|
21531
21607
|
const session = getSession();
|
|
@@ -21563,10 +21639,10 @@ app47.get("/", requireAdminSession, async (c) => {
|
|
|
21563
21639
|
if (COMPLETED.has(status)) completed.push(row);
|
|
21564
21640
|
else if (!HIDDEN_FROM_OPEN.has(status)) open.push(row);
|
|
21565
21641
|
}
|
|
21566
|
-
console.log(`${
|
|
21642
|
+
console.log(`${TAG34} op=list account=${accountId} open=${open.length} completed=${completed.length}`);
|
|
21567
21643
|
return c.json({ open, completed });
|
|
21568
21644
|
} catch (err) {
|
|
21569
|
-
console.error(`${
|
|
21645
|
+
console.error(`${TAG34} op=list error="${err instanceof Error ? err.message : String(err)}"`);
|
|
21570
21646
|
return c.json({ error: "tasks-list-failed" }, 500);
|
|
21571
21647
|
} finally {
|
|
21572
21648
|
await session.close();
|
|
@@ -21576,7 +21652,7 @@ var tasks_list_default = app47;
|
|
|
21576
21652
|
|
|
21577
21653
|
// server/routes/admin/task-timer-start.ts
|
|
21578
21654
|
import { randomUUID as randomUUID14 } from "crypto";
|
|
21579
|
-
var
|
|
21655
|
+
var TAG35 = "[task-timer]";
|
|
21580
21656
|
var app48 = new Hono();
|
|
21581
21657
|
app48.post("/", requireAdminSession, async (c) => {
|
|
21582
21658
|
const cacheKey = c.var.cacheKey;
|
|
@@ -21598,7 +21674,7 @@ app48.post("/", requireAdminSession, async (c) => {
|
|
|
21598
21674
|
{ taskId, accountId }
|
|
21599
21675
|
);
|
|
21600
21676
|
if (target.records.length === 0) {
|
|
21601
|
-
console.error(`${
|
|
21677
|
+
console.error(`${TAG35} op=start taskId=${taskId} accountId=${accountId} result=task-not-found`);
|
|
21602
21678
|
return c.json({ error: "task-not-found" }, 404);
|
|
21603
21679
|
}
|
|
21604
21680
|
const open = await session.run(
|
|
@@ -21610,7 +21686,7 @@ app48.post("/", requireAdminSession, async (c) => {
|
|
|
21610
21686
|
const priorEntryId = open.records[0]?.get("entryId");
|
|
21611
21687
|
const priorStartedAt = open.records[0]?.get("startedAt");
|
|
21612
21688
|
if (priorEntryId) {
|
|
21613
|
-
console.log(`${
|
|
21689
|
+
console.log(`${TAG35} op=start taskId=${taskId} accountId=${accountId} result=already-running`);
|
|
21614
21690
|
return c.json({ ok: true, running: true, entryId: priorEntryId, startedAt: priorStartedAt });
|
|
21615
21691
|
}
|
|
21616
21692
|
const entryId = randomUUID14();
|
|
@@ -21620,10 +21696,10 @@ app48.post("/", requireAdminSession, async (c) => {
|
|
|
21620
21696
|
CREATE (te)-[:LOGGED_AGAINST]->(t)`,
|
|
21621
21697
|
{ taskId, accountId, entryId, now }
|
|
21622
21698
|
);
|
|
21623
|
-
console.log(`${
|
|
21699
|
+
console.log(`${TAG35} op=opened taskId=${taskId} entryId=${entryId} startedAt=${now}`);
|
|
21624
21700
|
return c.json({ ok: true, running: true, entryId, startedAt: now });
|
|
21625
21701
|
} catch (err) {
|
|
21626
|
-
console.error(`${
|
|
21702
|
+
console.error(`${TAG35} op=start error="${err instanceof Error ? err.message : String(err)}"`);
|
|
21627
21703
|
return c.json({ error: "task-timer-start-failed" }, 500);
|
|
21628
21704
|
} finally {
|
|
21629
21705
|
await session.close();
|
|
@@ -21633,7 +21709,7 @@ var task_timer_start_default = app48;
|
|
|
21633
21709
|
|
|
21634
21710
|
// server/routes/admin/task-timer-stop.ts
|
|
21635
21711
|
import neo4j5 from "neo4j-driver";
|
|
21636
|
-
var
|
|
21712
|
+
var TAG36 = "[task-timer]";
|
|
21637
21713
|
var app49 = new Hono();
|
|
21638
21714
|
app49.post("/", requireAdminSession, async (c) => {
|
|
21639
21715
|
const cacheKey = c.var.cacheKey;
|
|
@@ -21665,7 +21741,7 @@ app49.post("/", requireAdminSession, async (c) => {
|
|
|
21665
21741
|
{ taskId, accountId }
|
|
21666
21742
|
);
|
|
21667
21743
|
const secondsLogged2 = toNum(cur.records[0]?.get("secondsLogged"));
|
|
21668
|
-
console.log(`${
|
|
21744
|
+
console.log(`${TAG36} op=stop taskId=${taskId} entryId=none seconds=0 totalAfter=${secondsLogged2}`);
|
|
21669
21745
|
return c.json({ ok: true, running: false, seconds: 0, secondsLogged: secondsLogged2 });
|
|
21670
21746
|
}
|
|
21671
21747
|
const seconds = secondsBetween(startedAt, now);
|
|
@@ -21688,11 +21764,11 @@ app49.post("/", requireAdminSession, async (c) => {
|
|
|
21688
21764
|
const sumEntries = toNum(verify.records[0]?.get("sumEntries"));
|
|
21689
21765
|
const sumAdjustments = toNum(verify.records[0]?.get("sumAdjustments"));
|
|
21690
21766
|
const secondsLogged = toNum(verify.records[0]?.get("secondsLogged"));
|
|
21691
|
-
console.log(`${
|
|
21692
|
-
console.log(`${
|
|
21767
|
+
console.log(`${TAG36} op=stop taskId=${taskId} entryId=${entryId} seconds=${seconds} totalAfter=${secondsLogged}`);
|
|
21768
|
+
console.log(`${TAG36} op=persisted taskId=${taskId} entryId=${entryId} sumEntries=${sumEntries} sumAdjustments=${sumAdjustments} secondsLogged=${secondsLogged}`);
|
|
21693
21769
|
return c.json({ ok: true, running: false, seconds, secondsLogged });
|
|
21694
21770
|
} catch (err) {
|
|
21695
|
-
console.error(`${
|
|
21771
|
+
console.error(`${TAG36} op=stop error="${err instanceof Error ? err.message : String(err)}"`);
|
|
21696
21772
|
return c.json({ error: "task-timer-stop-failed" }, 500);
|
|
21697
21773
|
} finally {
|
|
21698
21774
|
await session.close();
|
|
@@ -21701,7 +21777,7 @@ app49.post("/", requireAdminSession, async (c) => {
|
|
|
21701
21777
|
var task_timer_stop_default = app49;
|
|
21702
21778
|
|
|
21703
21779
|
// server/routes/admin/task-complete.ts
|
|
21704
|
-
var
|
|
21780
|
+
var TAG37 = "[task-timer]";
|
|
21705
21781
|
var app50 = new Hono();
|
|
21706
21782
|
app50.post("/", requireAdminSession, async (c) => {
|
|
21707
21783
|
const cacheKey = c.var.cacheKey;
|
|
@@ -21728,10 +21804,10 @@ app50.post("/", requireAdminSession, async (c) => {
|
|
|
21728
21804
|
return c.json({ error: "task-not-found" }, 404);
|
|
21729
21805
|
}
|
|
21730
21806
|
const secondsLogged = toNum(result.records[0].get("secondsLogged"));
|
|
21731
|
-
console.log(`${
|
|
21807
|
+
console.log(`${TAG37} op=complete taskId=${taskId} status=completed secondsLogged=${secondsLogged}`);
|
|
21732
21808
|
return c.json({ ok: true, status: "completed", secondsLogged });
|
|
21733
21809
|
} catch (err) {
|
|
21734
|
-
console.error(`${
|
|
21810
|
+
console.error(`${TAG37} op=complete error="${err instanceof Error ? err.message : String(err)}"`);
|
|
21735
21811
|
return c.json({ error: "task-complete-failed" }, 500);
|
|
21736
21812
|
} finally {
|
|
21737
21813
|
await session.close();
|
|
@@ -21742,7 +21818,7 @@ var task_complete_default = app50;
|
|
|
21742
21818
|
// server/routes/admin/task-time-adjust.ts
|
|
21743
21819
|
import { randomUUID as randomUUID15 } from "crypto";
|
|
21744
21820
|
import neo4j6 from "neo4j-driver";
|
|
21745
|
-
var
|
|
21821
|
+
var TAG38 = "[task-time-adjust]";
|
|
21746
21822
|
var app51 = new Hono();
|
|
21747
21823
|
app51.post("/", requireAdminSession, async (c) => {
|
|
21748
21824
|
const cacheKey = c.var.cacheKey;
|
|
@@ -21752,22 +21828,22 @@ app51.post("/", requireAdminSession, async (c) => {
|
|
|
21752
21828
|
try {
|
|
21753
21829
|
body = await c.req.json();
|
|
21754
21830
|
} catch {
|
|
21755
|
-
console.error(`${
|
|
21831
|
+
console.error(`${TAG38} op=reject reason=bad-json`);
|
|
21756
21832
|
return c.json({ error: "invalid-json" }, 400);
|
|
21757
21833
|
}
|
|
21758
21834
|
const taskId = typeof body.taskId === "string" ? body.taskId : "";
|
|
21759
21835
|
if (!taskId) {
|
|
21760
|
-
console.error(`${
|
|
21836
|
+
console.error(`${TAG38} op=reject reason=no-task`);
|
|
21761
21837
|
return c.json({ error: "taskId required" }, 400);
|
|
21762
21838
|
}
|
|
21763
21839
|
const newSeconds = body.newSeconds;
|
|
21764
21840
|
if (typeof newSeconds !== "number" || !Number.isInteger(newSeconds) || newSeconds < 0) {
|
|
21765
|
-
console.error(`${
|
|
21841
|
+
console.error(`${TAG38} op=reject reason=invalid-seconds taskId=${taskId} accountId=${accountId}`);
|
|
21766
21842
|
return c.json({ error: "newSeconds must be a non-negative integer" }, 400);
|
|
21767
21843
|
}
|
|
21768
21844
|
const adjustmentId = randomUUID15();
|
|
21769
21845
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
21770
|
-
console.log(`${
|
|
21846
|
+
console.log(`${TAG38} op=request adjustmentId=${adjustmentId} taskId=${taskId} accountId=${accountId} newSeconds=${newSeconds}`);
|
|
21771
21847
|
const session = getSession();
|
|
21772
21848
|
try {
|
|
21773
21849
|
const guard = await session.run(
|
|
@@ -21777,12 +21853,12 @@ app51.post("/", requireAdminSession, async (c) => {
|
|
|
21777
21853
|
{ taskId, accountId }
|
|
21778
21854
|
);
|
|
21779
21855
|
if (guard.records.length === 0) {
|
|
21780
|
-
console.error(`${
|
|
21856
|
+
console.error(`${TAG38} op=reject reason=no-task adjustmentId=${adjustmentId} taskId=${taskId} accountId=${accountId}`);
|
|
21781
21857
|
return c.json({ error: "task-not-found" }, 404);
|
|
21782
21858
|
}
|
|
21783
21859
|
const openCount = toNum(guard.records[0].get("openCount"));
|
|
21784
21860
|
if (openCount > 0) {
|
|
21785
|
-
console.error(`${
|
|
21861
|
+
console.error(`${TAG38} op=reject reason=running adjustmentId=${adjustmentId} taskId=${taskId} accountId=${accountId}`);
|
|
21786
21862
|
return c.json({ error: "task-timer-running" }, 409);
|
|
21787
21863
|
}
|
|
21788
21864
|
const previousSeconds = toNum(guard.records[0].get("previousSeconds"));
|
|
@@ -21808,10 +21884,10 @@ app51.post("/", requireAdminSession, async (c) => {
|
|
|
21808
21884
|
}
|
|
21809
21885
|
);
|
|
21810
21886
|
const secondsLoggedAfter = toNum(write.records[0]?.get("secondsLoggedAfter"));
|
|
21811
|
-
console.log(`${
|
|
21887
|
+
console.log(`${TAG38} op=persisted adjustmentId=${adjustmentId} previousSeconds=${previousSeconds} newSeconds=${newSeconds} delta=${delta} secondsLoggedAfter=${secondsLoggedAfter}`);
|
|
21812
21888
|
return c.json({ ok: true, secondsLogged: secondsLoggedAfter });
|
|
21813
21889
|
} catch (err) {
|
|
21814
|
-
console.error(`${
|
|
21890
|
+
console.error(`${TAG38} op=error adjustmentId=${adjustmentId} taskId=${taskId} error="${err instanceof Error ? err.message : String(err)}"`);
|
|
21815
21891
|
return c.json({ error: "task-time-adjust-failed" }, 500);
|
|
21816
21892
|
} finally {
|
|
21817
21893
|
await session.close();
|
|
@@ -21865,7 +21941,7 @@ app52.route("/task-time-adjust", task_time_adjust_default);
|
|
|
21865
21941
|
var admin_default = app52;
|
|
21866
21942
|
|
|
21867
21943
|
// server/routes/access/verify-token.ts
|
|
21868
|
-
var
|
|
21944
|
+
var TAG39 = "[access-verify]";
|
|
21869
21945
|
var MINT_TAG = "[access-session-mint]";
|
|
21870
21946
|
var COOKIE_NAME = "__access_session";
|
|
21871
21947
|
var app53 = new Hono();
|
|
@@ -21884,39 +21960,39 @@ app53.post("/", async (c) => {
|
|
|
21884
21960
|
}
|
|
21885
21961
|
const rateMsg = checkAccessRateLimit(ip, agentSlug);
|
|
21886
21962
|
if (rateMsg) {
|
|
21887
|
-
console.error(`${
|
|
21963
|
+
console.error(`${TAG39} grantId=- agentSlug=${agentSlug} result=rate-limited ip=${ip}`);
|
|
21888
21964
|
return c.json({ error: rateMsg }, 429);
|
|
21889
21965
|
}
|
|
21890
21966
|
const grant = await findGrantByMagicToken(token);
|
|
21891
21967
|
if (!grant) {
|
|
21892
21968
|
recordAccessFailedAttempt(ip, agentSlug);
|
|
21893
|
-
console.error(`${
|
|
21969
|
+
console.error(`${TAG39} grantId=- agentSlug=${agentSlug} result=notfound ip=${ip}`);
|
|
21894
21970
|
return c.json({ error: "invalid-or-expired-link" }, 401);
|
|
21895
21971
|
}
|
|
21896
21972
|
if (grant.agentSlug !== agentSlug) {
|
|
21897
21973
|
recordAccessFailedAttempt(ip, agentSlug);
|
|
21898
21974
|
console.error(
|
|
21899
|
-
`${
|
|
21975
|
+
`${TAG39} grantId=${grant.grantId} agentSlug=${agentSlug} result=agent-mismatch grantAgent=${grant.agentSlug} ip=${ip}`
|
|
21900
21976
|
);
|
|
21901
21977
|
return c.json({ error: "invalid-or-expired-link" }, 401);
|
|
21902
21978
|
}
|
|
21903
21979
|
if (grant.status === "expired" || grant.status === "revoked") {
|
|
21904
21980
|
recordAccessFailedAttempt(ip, agentSlug);
|
|
21905
21981
|
console.error(
|
|
21906
|
-
`${
|
|
21982
|
+
`${TAG39} grantId=${grant.grantId} agentSlug=${agentSlug} result=expired status=${grant.status} ip=${ip}`
|
|
21907
21983
|
);
|
|
21908
21984
|
return c.json({ error: "access-no-longer-valid" }, 401);
|
|
21909
21985
|
}
|
|
21910
21986
|
if (grant.expiresAt !== null && grant.expiresAt < Date.now()) {
|
|
21911
21987
|
recordAccessFailedAttempt(ip, agentSlug);
|
|
21912
21988
|
console.error(
|
|
21913
|
-
`${
|
|
21989
|
+
`${TAG39} grantId=${grant.grantId} agentSlug=${agentSlug} result=expired reason=expiresAt-past ip=${ip}`
|
|
21914
21990
|
);
|
|
21915
21991
|
return c.json({ error: "access-no-longer-valid" }, 401);
|
|
21916
21992
|
}
|
|
21917
21993
|
if (!grant.sliceToken) {
|
|
21918
21994
|
console.error(
|
|
21919
|
-
`${
|
|
21995
|
+
`${TAG39} grantId=${grant.grantId} agentSlug=${agentSlug} result=no-slice-token reason=schema-violation`
|
|
21920
21996
|
);
|
|
21921
21997
|
return c.json({ error: "grant-misconfigured" }, 500);
|
|
21922
21998
|
}
|
|
@@ -21932,12 +22008,12 @@ app53.post("/", async (c) => {
|
|
|
21932
22008
|
await consumeMagicTokenAndActivate(grant.grantId);
|
|
21933
22009
|
} catch (err) {
|
|
21934
22010
|
console.error(
|
|
21935
|
-
`${
|
|
22011
|
+
`${TAG39} grantId=${grant.grantId} agentSlug=${agentSlug} result=consume-failed err="${err instanceof Error ? err.message : String(err)}"`
|
|
21936
22012
|
);
|
|
21937
22013
|
return c.json({ error: "verification-failed" }, 500);
|
|
21938
22014
|
}
|
|
21939
22015
|
clearAccessRateLimit(ip, agentSlug);
|
|
21940
|
-
console.log(`${
|
|
22016
|
+
console.log(`${TAG39} grantId=${grant.grantId} agentSlug=${agentSlug} result=ok ip=${ip}`);
|
|
21941
22017
|
console.log(
|
|
21942
22018
|
`${MINT_TAG} grantId=${grant.grantId} sliceToken=${grant.sliceToken.slice(0, 8)} agentSlug=${agentSlug} personId=${grant.personId ?? "none"}`
|
|
21943
22019
|
);
|
|
@@ -22013,7 +22089,7 @@ async function sendMagicLinkEmail(payload) {
|
|
|
22013
22089
|
}
|
|
22014
22090
|
|
|
22015
22091
|
// server/routes/access/request-magic-link.ts
|
|
22016
|
-
var
|
|
22092
|
+
var TAG40 = "[access-request-link]";
|
|
22017
22093
|
var app54 = new Hono();
|
|
22018
22094
|
var VISITOR_MESSAGE = "If that email is on the invite list, a fresh link is on the way.";
|
|
22019
22095
|
app54.post("/", async (c) => {
|
|
@@ -22030,18 +22106,18 @@ app54.post("/", async (c) => {
|
|
|
22030
22106
|
}
|
|
22031
22107
|
const rateMsg = checkRequestLinkRateLimit(contactValue);
|
|
22032
22108
|
if (rateMsg) {
|
|
22033
|
-
console.error(`${
|
|
22109
|
+
console.error(`${TAG40} contactValue=${maskContact(contactValue)} result=rate-limited`);
|
|
22034
22110
|
return c.json({ error: rateMsg }, 429);
|
|
22035
22111
|
}
|
|
22036
22112
|
recordRequestLinkAttempt(contactValue);
|
|
22037
22113
|
const accountId = process.env.ACCOUNT_ID ?? "";
|
|
22038
22114
|
if (!accountId) {
|
|
22039
|
-
console.error(`${
|
|
22115
|
+
console.error(`${TAG40} contactValue=${maskContact(contactValue)} result=no-account-id`);
|
|
22040
22116
|
return c.json({ message: VISITOR_MESSAGE }, 200);
|
|
22041
22117
|
}
|
|
22042
22118
|
const grant = await findActiveGrantByContact(contactValue, agentSlug, accountId);
|
|
22043
22119
|
if (!grant) {
|
|
22044
|
-
console.log(`${
|
|
22120
|
+
console.log(`${TAG40} contactValue=${maskContact(contactValue)} result=notfound`);
|
|
22045
22121
|
return c.json({ message: VISITOR_MESSAGE }, 200);
|
|
22046
22122
|
}
|
|
22047
22123
|
let token;
|
|
@@ -22049,7 +22125,7 @@ app54.post("/", async (c) => {
|
|
|
22049
22125
|
token = await generateNewMagicToken(grant.grantId);
|
|
22050
22126
|
} catch (err) {
|
|
22051
22127
|
console.error(
|
|
22052
|
-
`${
|
|
22128
|
+
`${TAG40} contactValue=${maskContact(contactValue)} result=mint-failed err="${err instanceof Error ? err.message : String(err)}"`
|
|
22053
22129
|
);
|
|
22054
22130
|
return c.json({ message: VISITOR_MESSAGE }, 200);
|
|
22055
22131
|
}
|
|
@@ -22081,12 +22157,12 @@ app54.post("/", async (c) => {
|
|
|
22081
22157
|
});
|
|
22082
22158
|
if (!sendResult.ok) {
|
|
22083
22159
|
console.error(
|
|
22084
|
-
`${
|
|
22160
|
+
`${TAG40} contactValue=${maskContact(contactValue)} result=send-failed err="${sendResult.error}"`
|
|
22085
22161
|
);
|
|
22086
22162
|
return c.json({ message: VISITOR_MESSAGE }, 200);
|
|
22087
22163
|
}
|
|
22088
22164
|
console.log(
|
|
22089
|
-
`${
|
|
22165
|
+
`${TAG40} contactValue=${maskContact(contactValue)} result=ok messageId=${sendResult.messageId}`
|
|
22090
22166
|
);
|
|
22091
22167
|
return c.json({ message: VISITOR_MESSAGE }, 200);
|
|
22092
22168
|
});
|
|
@@ -23148,7 +23224,7 @@ app61.get("/free-busy", async (c) => {
|
|
|
23148
23224
|
var calendar_public_default = app61;
|
|
23149
23225
|
|
|
23150
23226
|
// app/lib/timeentry-census.ts
|
|
23151
|
-
var
|
|
23227
|
+
var TAG41 = "[timeentry-census]";
|
|
23152
23228
|
function reconcileTimeEntries(rows, now, horizonMs) {
|
|
23153
23229
|
const openEntries = rows.openEntries.length;
|
|
23154
23230
|
let oldestOpenAgeMs = 0;
|
|
@@ -23188,12 +23264,12 @@ async function runTimeEntryCensusOnce(session, now, horizonMs) {
|
|
|
23188
23264
|
sumAdjustments: toNum(r.get("sumAdjustments"))
|
|
23189
23265
|
}));
|
|
23190
23266
|
const finding = reconcileTimeEntries({ openEntries, taskSums }, now, horizonMs);
|
|
23191
|
-
const line = `${
|
|
23267
|
+
const line = `${TAG41} openEntries=${finding.openEntries} oldestOpenAgeMin=${finding.oldestOpenAgeMin} maxOpenPerAccount=${finding.maxOpenPerAccount} driftTasks=${finding.driftTasks}`;
|
|
23192
23268
|
if (finding.alarm) console.error(`${line} alarm=true`);
|
|
23193
23269
|
else console.log(line);
|
|
23194
23270
|
return finding;
|
|
23195
23271
|
} catch (err) {
|
|
23196
|
-
console.error(`${
|
|
23272
|
+
console.error(`${TAG41} error="${err instanceof Error ? err.message : String(err)}"`);
|
|
23197
23273
|
return null;
|
|
23198
23274
|
}
|
|
23199
23275
|
}
|
|
@@ -23207,13 +23283,13 @@ function startTimeEntryCensus(openSession, opts = {}) {
|
|
|
23207
23283
|
try {
|
|
23208
23284
|
session = openSession();
|
|
23209
23285
|
} catch (err) {
|
|
23210
|
-
console.error(`${
|
|
23286
|
+
console.error(`${TAG41} open-session-failed error="${err instanceof Error ? err.message : String(err)}"`);
|
|
23211
23287
|
return;
|
|
23212
23288
|
}
|
|
23213
23289
|
try {
|
|
23214
23290
|
await runTimeEntryCensusOnce(session, Date.now(), horizonMs);
|
|
23215
23291
|
} catch (err) {
|
|
23216
|
-
console.error(`${
|
|
23292
|
+
console.error(`${TAG41} tick-failed error="${err instanceof Error ? err.message : String(err)}"`);
|
|
23217
23293
|
} finally {
|
|
23218
23294
|
try {
|
|
23219
23295
|
await session.close();
|
|
@@ -23585,10 +23661,10 @@ var defaultResolveName = async (_accountId, userId) => {
|
|
|
23585
23661
|
const res = await loadAdminUserName(userId);
|
|
23586
23662
|
return res.source === "neo4j" && res.joined.length > 0 ? res.joined : null;
|
|
23587
23663
|
};
|
|
23588
|
-
async function readRaw2(
|
|
23664
|
+
async function readRaw2(path3) {
|
|
23589
23665
|
let raw;
|
|
23590
23666
|
try {
|
|
23591
|
-
raw = await readFile6(
|
|
23667
|
+
raw = await readFile6(path3, "utf8");
|
|
23592
23668
|
} catch {
|
|
23593
23669
|
return null;
|
|
23594
23670
|
}
|
|
@@ -23599,11 +23675,11 @@ async function readRaw2(path2) {
|
|
|
23599
23675
|
return null;
|
|
23600
23676
|
}
|
|
23601
23677
|
}
|
|
23602
|
-
async function writeRaw(
|
|
23603
|
-
const tmp = `${
|
|
23678
|
+
async function writeRaw(path3, obj) {
|
|
23679
|
+
const tmp = `${path3}.tmp.${process.pid}.${Date.now()}`;
|
|
23604
23680
|
try {
|
|
23605
23681
|
await writeFile4(tmp, JSON.stringify(obj, null, 2), "utf8");
|
|
23606
|
-
await rename3(tmp,
|
|
23682
|
+
await rename3(tmp, path3);
|
|
23607
23683
|
} catch (err) {
|
|
23608
23684
|
try {
|
|
23609
23685
|
if (existsSync34(tmp)) await unlink3(tmp);
|
|
@@ -23648,8 +23724,8 @@ async function collectLiveSidecars(projectsRoot) {
|
|
|
23648
23724
|
}
|
|
23649
23725
|
return out;
|
|
23650
23726
|
}
|
|
23651
|
-
function shortId(
|
|
23652
|
-
const base =
|
|
23727
|
+
function shortId(path3) {
|
|
23728
|
+
const base = path3.slice(path3.lastIndexOf("/") + 1).replace(/\.meta\.json$/, "");
|
|
23653
23729
|
return base.slice(0, 8);
|
|
23654
23730
|
}
|
|
23655
23731
|
async function migrateAdminWebchatSidecars(opts = {}) {
|
|
@@ -23692,8 +23768,8 @@ async function migrateAdminWebchatSidecars(opts = {}) {
|
|
|
23692
23768
|
}
|
|
23693
23769
|
};
|
|
23694
23770
|
const sidecars = await collectLiveSidecars(projectsRoot);
|
|
23695
|
-
for (const
|
|
23696
|
-
const raw = await readRaw2(
|
|
23771
|
+
for (const path3 of sidecars) {
|
|
23772
|
+
const raw = await readRaw2(path3);
|
|
23697
23773
|
if (!raw) continue;
|
|
23698
23774
|
if (raw.role !== ADMIN_ROLE2 || raw.channel !== WEBCHAT_CHANNEL2) continue;
|
|
23699
23775
|
result.scanned++;
|
|
@@ -23704,21 +23780,21 @@ async function migrateAdminWebchatSidecars(opts = {}) {
|
|
|
23704
23780
|
}
|
|
23705
23781
|
if (!singleAdmin) {
|
|
23706
23782
|
result.skippedAmbiguous++;
|
|
23707
|
-
console.error(`[sidecar-backfill] skip sessionId=${shortId(
|
|
23783
|
+
console.error(`[sidecar-backfill] skip sessionId=${shortId(path3)} reason=admin-count-not-one count=${adminCount < 0 ? "unparseable" : adminCount}`);
|
|
23708
23784
|
continue;
|
|
23709
23785
|
}
|
|
23710
23786
|
raw.adminUserId = soleAdminUserId;
|
|
23711
|
-
await writeRaw(
|
|
23787
|
+
await writeRaw(path3, raw);
|
|
23712
23788
|
result.backfilled++;
|
|
23713
23789
|
await resolveAdminName();
|
|
23714
|
-
const back = await readRaw2(
|
|
23790
|
+
const back = await readRaw2(path3);
|
|
23715
23791
|
const wrote = back && back.adminUserId === soleAdminUserId;
|
|
23716
23792
|
if (wrote && resolvedNameForAdmin) {
|
|
23717
23793
|
result.resolvedName++;
|
|
23718
23794
|
} else if (wrote) {
|
|
23719
|
-
console.error(`[sidecar-backfill] backfilled-no-name sessionId=${shortId(
|
|
23795
|
+
console.error(`[sidecar-backfill] backfilled-no-name sessionId=${shortId(path3)} userId=${soleAdminUserId.slice(0, 8)}`);
|
|
23720
23796
|
} else {
|
|
23721
|
-
console.error(`[sidecar-backfill] post-write-verify-failed sessionId=${shortId(
|
|
23797
|
+
console.error(`[sidecar-backfill] post-write-verify-failed sessionId=${shortId(path3)}`);
|
|
23722
23798
|
}
|
|
23723
23799
|
}
|
|
23724
23800
|
console.error(
|
|
@@ -25321,14 +25397,14 @@ function makeFileDelivery(opts) {
|
|
|
25321
25397
|
}
|
|
25322
25398
|
|
|
25323
25399
|
// app/lib/webchat/file-delivery.ts
|
|
25324
|
-
var
|
|
25400
|
+
var TAG42 = "[webchat-adaptor]";
|
|
25325
25401
|
function platformRoot2() {
|
|
25326
25402
|
return process.env.MAXY_PLATFORM_ROOT || "";
|
|
25327
25403
|
}
|
|
25328
25404
|
function makeWebchatSendFile(entry) {
|
|
25329
25405
|
return async (filePath) => {
|
|
25330
25406
|
if (!entry.accountId) {
|
|
25331
|
-
console.error(`${
|
|
25407
|
+
console.error(`${TAG42} file-delivery reject reason=no-account sender=${entry.senderId}`);
|
|
25332
25408
|
return { ok: false, error: "no-account" };
|
|
25333
25409
|
}
|
|
25334
25410
|
const accountDir = resolve32(platformRoot2(), "..", "data/accounts", entry.accountId);
|
|
@@ -25336,14 +25412,14 @@ function makeWebchatSendFile(entry) {
|
|
|
25336
25412
|
const resolved = realpathSync8(filePath);
|
|
25337
25413
|
const accountResolved = realpathSync8(accountDir);
|
|
25338
25414
|
if (!resolved.startsWith(accountResolved + "/")) {
|
|
25339
|
-
console.error(`${
|
|
25415
|
+
console.error(`${TAG42} file-delivery reject reason=outside_account_directory sender=${entry.senderId}`);
|
|
25340
25416
|
return { ok: false, error: "outside-account" };
|
|
25341
25417
|
}
|
|
25342
25418
|
return { ok: true };
|
|
25343
25419
|
} catch (err) {
|
|
25344
25420
|
const code = err.code;
|
|
25345
25421
|
console.error(
|
|
25346
|
-
`${
|
|
25422
|
+
`${TAG42} file-delivery reject reason=${code === "ENOENT" ? "not-found" : "path-error"} sender=${entry.senderId}`
|
|
25347
25423
|
);
|
|
25348
25424
|
return { ok: false, error: code === "ENOENT" ? "not-found" : "path-error" };
|
|
25349
25425
|
}
|
|
@@ -25352,7 +25428,7 @@ function makeWebchatSendFile(entry) {
|
|
|
25352
25428
|
function makeWebchatFileDelivery(entry) {
|
|
25353
25429
|
return makeFileDelivery({
|
|
25354
25430
|
entry,
|
|
25355
|
-
tag:
|
|
25431
|
+
tag: TAG42,
|
|
25356
25432
|
channel: "webchat",
|
|
25357
25433
|
sendFile: makeWebchatSendFile(entry),
|
|
25358
25434
|
deferUntilVerdict: true
|
|
@@ -25414,7 +25490,7 @@ function buildPublicWebchatSpawnRequest(input) {
|
|
|
25414
25490
|
}
|
|
25415
25491
|
|
|
25416
25492
|
// app/lib/whatsapp/inbound/file-delivery-bridge.ts
|
|
25417
|
-
var
|
|
25493
|
+
var TAG43 = "[whatsapp-adaptor]";
|
|
25418
25494
|
var SEND_USER_FILE2 = "SendUserFile";
|
|
25419
25495
|
var WHATSAPP_SEND_DOCUMENT = "whatsapp-send-document";
|
|
25420
25496
|
function platformRoot3() {
|
|
@@ -25432,7 +25508,7 @@ function makeWhatsAppSendFile(entry, maxyAccountId) {
|
|
|
25432
25508
|
});
|
|
25433
25509
|
if (result.ok) return { ok: true };
|
|
25434
25510
|
console.error(
|
|
25435
|
-
`${
|
|
25511
|
+
`${TAG43} file-delivery reject reason=send-failed sender=${entry.senderId} status=${result.status} message=${result.error}`
|
|
25436
25512
|
);
|
|
25437
25513
|
return { ok: false, error: result.error };
|
|
25438
25514
|
};
|
|
@@ -25440,7 +25516,7 @@ function makeWhatsAppSendFile(entry, maxyAccountId) {
|
|
|
25440
25516
|
function makeWhatsAppFileDelivery(entry, maxyAccountId) {
|
|
25441
25517
|
const shared = makeFileDelivery({
|
|
25442
25518
|
entry,
|
|
25443
|
-
tag:
|
|
25519
|
+
tag: TAG43,
|
|
25444
25520
|
channel: "whatsapp",
|
|
25445
25521
|
sendFile: makeWhatsAppSendFile(entry, maxyAccountId)
|
|
25446
25522
|
});
|
|
@@ -25475,7 +25551,7 @@ function makeWhatsAppFileDelivery(entry, maxyAccountId) {
|
|
|
25475
25551
|
if (!delivered) {
|
|
25476
25552
|
const fileField = call.filePath !== void 0 ? ` file=${call.filePath}` : "";
|
|
25477
25553
|
console.error(
|
|
25478
|
-
`${
|
|
25554
|
+
`${TAG43} file-delivery-unreconciled sender=${entry.senderId} sessionId=${sid} tool=${WHATSAPP_SEND_DOCUMENT}${fileField}`
|
|
25479
25555
|
);
|
|
25480
25556
|
}
|
|
25481
25557
|
}
|
|
@@ -25870,7 +25946,7 @@ function buildTelegramSpawnRequest(input) {
|
|
|
25870
25946
|
import { realpathSync as realpathSync9 } from "fs";
|
|
25871
25947
|
import { readFile as readFile7, stat as stat7 } from "fs/promises";
|
|
25872
25948
|
import { resolve as resolve33, basename as basename11 } from "path";
|
|
25873
|
-
var
|
|
25949
|
+
var TAG44 = "[telegram:outbound]";
|
|
25874
25950
|
var TELEGRAM_DOCUMENT_MAX_BYTES = 50 * 1024 * 1024;
|
|
25875
25951
|
async function sendTelegramDocument(input) {
|
|
25876
25952
|
const { botToken, chatId, filePath, caption, maxyAccountId, platformRoot: platformRoot5 } = input;
|
|
@@ -25886,16 +25962,16 @@ async function sendTelegramDocument(input) {
|
|
|
25886
25962
|
resolvedPath = realpathSync9(filePath);
|
|
25887
25963
|
const accountResolved = realpathSync9(accountDir);
|
|
25888
25964
|
if (!resolvedPath.startsWith(accountResolved + "/")) {
|
|
25889
|
-
console.error(`${
|
|
25965
|
+
console.error(`${TAG44} document REJECTED reason=outside_account_directory maxyAccountId=${maxyAccountId}`);
|
|
25890
25966
|
return { ok: false, status: 403, error: "Access denied: file is outside the account directory" };
|
|
25891
25967
|
}
|
|
25892
25968
|
} catch (err) {
|
|
25893
25969
|
const code = err.code;
|
|
25894
25970
|
if (code === "ENOENT") {
|
|
25895
|
-
console.error(`${
|
|
25971
|
+
console.error(`${TAG44} document ENOENT path=${filePath}`);
|
|
25896
25972
|
return { ok: false, status: 404, error: `File not found: ${filePath}` };
|
|
25897
25973
|
}
|
|
25898
|
-
console.error(`${
|
|
25974
|
+
console.error(`${TAG44} document path error: ${String(err)}`);
|
|
25899
25975
|
return { ok: false, status: 500, error: String(err) };
|
|
25900
25976
|
}
|
|
25901
25977
|
const fileStat = await stat7(resolvedPath);
|
|
@@ -25928,13 +26004,13 @@ async function sendTelegramDocument(input) {
|
|
|
25928
26004
|
error = e instanceof Error ? e.message : String(e);
|
|
25929
26005
|
}
|
|
25930
26006
|
console.error(
|
|
25931
|
-
`${
|
|
26007
|
+
`${TAG44} sent document to=${chatId} file=${filename} bytes=${fileStat.size} ok=${ok}` + (messageId !== void 0 ? ` id=${messageId}` : "")
|
|
25932
26008
|
);
|
|
25933
26009
|
return ok ? { ok: true, messageId } : { ok: false, status: 500, error };
|
|
25934
26010
|
}
|
|
25935
26011
|
|
|
25936
26012
|
// app/lib/telegram/outbound/file-delivery.ts
|
|
25937
|
-
var
|
|
26013
|
+
var TAG45 = "[telegram:outbound]";
|
|
25938
26014
|
function platformRoot4() {
|
|
25939
26015
|
return process.env.MAXY_PLATFORM_ROOT || "";
|
|
25940
26016
|
}
|
|
@@ -25946,11 +26022,11 @@ function makeTelegramSendFile(entry) {
|
|
|
25946
26022
|
botToken = (entry.role === "admin" ? tg?.adminBotToken : tg?.publicBotToken) ?? null;
|
|
25947
26023
|
}
|
|
25948
26024
|
if (!botToken) {
|
|
25949
|
-
console.error(`${
|
|
26025
|
+
console.error(`${TAG45} file-delivery reject reason=no-bot-token sender=${entry.senderId} role=${entry.role}`);
|
|
25950
26026
|
return { ok: false, error: "no-bot-token" };
|
|
25951
26027
|
}
|
|
25952
26028
|
if (entry.replyTarget == null) {
|
|
25953
|
-
console.error(`${
|
|
26029
|
+
console.error(`${TAG45} file-delivery reject reason=no-reply-target sender=${entry.senderId} role=${entry.role}`);
|
|
25954
26030
|
return { ok: false, error: "no-reply-target" };
|
|
25955
26031
|
}
|
|
25956
26032
|
const result = await sendTelegramDocument({
|
|
@@ -25967,7 +26043,7 @@ function makeTelegramSendFile(entry) {
|
|
|
25967
26043
|
function makeTelegramFileDelivery(entry) {
|
|
25968
26044
|
return makeFileDelivery({
|
|
25969
26045
|
entry,
|
|
25970
|
-
tag:
|
|
26046
|
+
tag: TAG45,
|
|
25971
26047
|
channel: "telegram",
|
|
25972
26048
|
sendFile: makeTelegramSendFile(entry)
|
|
25973
26049
|
});
|
|
@@ -26017,7 +26093,7 @@ function telegramServerPath() {
|
|
|
26017
26093
|
|
|
26018
26094
|
// app/lib/channel-pty-bridge/public-session-end-review.ts
|
|
26019
26095
|
import { randomUUID as randomUUID16 } from "crypto";
|
|
26020
|
-
var
|
|
26096
|
+
var TAG46 = "[public-session-review]";
|
|
26021
26097
|
var CONSUMED_CAP = 500;
|
|
26022
26098
|
var consumed = /* @__PURE__ */ new Set();
|
|
26023
26099
|
function consumeOnce(sessionId) {
|
|
@@ -26044,7 +26120,7 @@ async function fetchJsonl(sessionId) {
|
|
|
26044
26120
|
return await res.text();
|
|
26045
26121
|
} catch (err) {
|
|
26046
26122
|
console.error(
|
|
26047
|
-
`${
|
|
26123
|
+
`${TAG46} jsonl-fetch-failed sessionId=${sessionId} err="${err instanceof Error ? err.message : String(err)}"`
|
|
26048
26124
|
);
|
|
26049
26125
|
return null;
|
|
26050
26126
|
}
|
|
@@ -26068,7 +26144,7 @@ async function fetchPriorWrites(sliceToken, accountId) {
|
|
|
26068
26144
|
const res = await fetch(url);
|
|
26069
26145
|
if (!res.ok) {
|
|
26070
26146
|
console.error(
|
|
26071
|
-
`${
|
|
26147
|
+
`${TAG46} prior-writes-fetch-failed sliceToken=${sliceToken.slice(0, 8)} status=${res.status}`
|
|
26072
26148
|
);
|
|
26073
26149
|
return [];
|
|
26074
26150
|
}
|
|
@@ -26076,7 +26152,7 @@ async function fetchPriorWrites(sliceToken, accountId) {
|
|
|
26076
26152
|
return Array.isArray(body.writes) ? body.writes : [];
|
|
26077
26153
|
} catch (err) {
|
|
26078
26154
|
console.error(
|
|
26079
|
-
`${
|
|
26155
|
+
`${TAG46} prior-writes-fetch-threw sliceToken=${sliceToken.slice(0, 8)} err="${err instanceof Error ? err.message : String(err)}"`
|
|
26080
26156
|
);
|
|
26081
26157
|
return [];
|
|
26082
26158
|
}
|
|
@@ -26105,7 +26181,7 @@ function composeInitialMessage(input) {
|
|
|
26105
26181
|
}
|
|
26106
26182
|
async function dispatchReviewer(input, initialMessage) {
|
|
26107
26183
|
const sessionId = randomUUID16();
|
|
26108
|
-
console.log(`${
|
|
26184
|
+
console.log(`${TAG46} route target=rc-spawn sessionId=${sessionId.slice(0, 8)} sourceSession=${input.sessionId.slice(0, 8)}`);
|
|
26109
26185
|
const spawned = await managerRcSpawn({
|
|
26110
26186
|
sessionId,
|
|
26111
26187
|
initialMessage,
|
|
@@ -26125,21 +26201,21 @@ async function firePublicSessionEndReview(input) {
|
|
|
26125
26201
|
const dispatchedAt = Date.now();
|
|
26126
26202
|
if (consumed.has(input.sessionId)) {
|
|
26127
26203
|
console.log(
|
|
26128
|
-
`${
|
|
26204
|
+
`${TAG46} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-already-reviewed`
|
|
26129
26205
|
);
|
|
26130
26206
|
return;
|
|
26131
26207
|
}
|
|
26132
26208
|
const jsonl = await fetchJsonl(input.sessionId);
|
|
26133
26209
|
if (!jsonl) {
|
|
26134
26210
|
console.log(
|
|
26135
|
-
`${
|
|
26211
|
+
`${TAG46} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-jsonl-missing`
|
|
26136
26212
|
);
|
|
26137
26213
|
return;
|
|
26138
26214
|
}
|
|
26139
26215
|
const operatorTurns = countOperatorTurns(jsonl);
|
|
26140
26216
|
if (operatorTurns === 0) {
|
|
26141
26217
|
console.log(
|
|
26142
|
-
`${
|
|
26218
|
+
`${TAG46} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-no-turns`
|
|
26143
26219
|
);
|
|
26144
26220
|
return;
|
|
26145
26221
|
}
|
|
@@ -26153,19 +26229,19 @@ async function firePublicSessionEndReview(input) {
|
|
|
26153
26229
|
});
|
|
26154
26230
|
if (!consumeOnce(input.sessionId)) {
|
|
26155
26231
|
console.log(
|
|
26156
|
-
`${
|
|
26232
|
+
`${TAG46} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-already-reviewed`
|
|
26157
26233
|
);
|
|
26158
26234
|
return;
|
|
26159
26235
|
}
|
|
26160
26236
|
const dispatched = await dispatchReviewer(input, initialMessage);
|
|
26161
26237
|
if (!dispatched.ok) {
|
|
26162
26238
|
console.error(
|
|
26163
|
-
`${
|
|
26239
|
+
`${TAG46} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-spawn-failed reason=${dispatched.reason}`
|
|
26164
26240
|
);
|
|
26165
26241
|
return;
|
|
26166
26242
|
}
|
|
26167
26243
|
console.log(
|
|
26168
|
-
`${
|
|
26244
|
+
`${TAG46} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=dispatched managerSessionId=${dispatched.managerSessionId} operatorTurns=${operatorTurns} priorWrites=${priorWrites.length} ms=${Date.now() - dispatchedAt}`
|
|
26169
26245
|
);
|
|
26170
26246
|
}
|
|
26171
26247
|
|
|
@@ -26938,8 +27014,8 @@ var scheduleInjectRoutes = createScheduleInjectRoutes({
|
|
|
26938
27014
|
});
|
|
26939
27015
|
app62.route("/api/channel/schedule-inject", scheduleInjectRoutes);
|
|
26940
27016
|
app62.use("*", clientIpMiddleware);
|
|
26941
|
-
function allowsSameOriginFraming(
|
|
26942
|
-
return
|
|
27017
|
+
function allowsSameOriginFraming(path3) {
|
|
27018
|
+
return path3 === "/vnc-viewer.html" || path3.startsWith("/api/admin/attachment/") || path3.startsWith("/api/public-reader/attachment/") || path3 === "/api/admin/files/download";
|
|
26943
27019
|
}
|
|
26944
27020
|
app62.use("*", async (c, next) => {
|
|
26945
27021
|
await next();
|
|
@@ -26972,12 +27048,12 @@ app62.use("*", async (c, next) => {
|
|
|
26972
27048
|
await next();
|
|
26973
27049
|
return;
|
|
26974
27050
|
}
|
|
26975
|
-
const
|
|
26976
|
-
if (isPublicPathAllowed(
|
|
27051
|
+
const path3 = c.req.path;
|
|
27052
|
+
if (isPublicPathAllowed(path3)) {
|
|
26977
27053
|
await next();
|
|
26978
27054
|
return;
|
|
26979
27055
|
}
|
|
26980
|
-
console.error(`[public-host] blocked path=${
|
|
27056
|
+
console.error(`[public-host] blocked path=${path3}`);
|
|
26981
27057
|
return c.text("Not found", 404);
|
|
26982
27058
|
});
|
|
26983
27059
|
var lastRemoteAuthDisplayName = "";
|
|
@@ -27212,12 +27288,12 @@ console.log("[client-error-route] mounted");
|
|
|
27212
27288
|
var PWA_PUBLIC_PATHS = /* @__PURE__ */ new Set(["/sw.js", ...PWA_SURFACES.map((s) => s.manifestPath)]);
|
|
27213
27289
|
app62.use("*", async (c, next) => {
|
|
27214
27290
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
27215
|
-
const
|
|
27216
|
-
if (
|
|
27291
|
+
const path3 = c.req.path;
|
|
27292
|
+
if (path3 === "/favicon.ico" || path3.startsWith("/assets/") || path3.startsWith("/brand/") || // Public free/busy is read by the booking page (a separate Cloudflare Pages
|
|
27217
27293
|
// origin) against the brand host with no admin cookie. It is public by
|
|
27218
27294
|
// design — open slots only, no :Meeting detail — so it bypasses the gate
|
|
27219
27295
|
// exactly like /brand/. The detail-leak guard lives in the route + its test.
|
|
27220
|
-
|
|
27296
|
+
path3 === "/api/calendar/free-busy" || PWA_PUBLIC_PATHS.has(path3)) {
|
|
27221
27297
|
await next();
|
|
27222
27298
|
return;
|
|
27223
27299
|
}
|
|
@@ -27244,15 +27320,15 @@ app62.use("*", async (c, next) => {
|
|
|
27244
27320
|
}
|
|
27245
27321
|
const disambig = `remoteAddress="${rawRemote ?? ""}" xff="${rawXff ?? ""}" resolvedKind=${decision.client.kind}`;
|
|
27246
27322
|
if (decision.reason === "remote-unconfigured") {
|
|
27247
|
-
console.error(`[remote-auth] not configured, redirecting to setup ip=${clientIp} path=${
|
|
27323
|
+
console.error(`[remote-auth] not configured, redirecting to setup ip=${clientIp} path=${path3} ${disambig}`);
|
|
27248
27324
|
return c.redirect("/__remote-auth/setup");
|
|
27249
27325
|
}
|
|
27250
|
-
const respond = isApiRequestPath(
|
|
27251
|
-
console.error(`[remote-auth] login required ip=${clientIp} path=${
|
|
27326
|
+
const respond = isApiRequestPath(path3) ? "401" : "html";
|
|
27327
|
+
console.error(`[remote-auth] login required ip=${clientIp} path=${path3} respond=${respond} ${disambig}`);
|
|
27252
27328
|
if (respond === "401") {
|
|
27253
27329
|
return c.json({ code: "remote-auth-required" }, 401);
|
|
27254
27330
|
}
|
|
27255
|
-
return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(c), redirect:
|
|
27331
|
+
return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(c), redirect: path3 }), 200);
|
|
27256
27332
|
});
|
|
27257
27333
|
app62.route("/api/health", health_default);
|
|
27258
27334
|
app62.route("/api/chat", chatRoutes);
|
|
@@ -27677,11 +27753,11 @@ if (brandFaviconPath !== "/favicon.ico") {
|
|
|
27677
27753
|
app62.use("/*", serveStatic({ root: "./public" }));
|
|
27678
27754
|
app62.all("*", (c) => {
|
|
27679
27755
|
const host = (c.req.header("host") ?? "").split(":")[0];
|
|
27680
|
-
const
|
|
27756
|
+
const path3 = c.req.path;
|
|
27681
27757
|
if (isPublicHost(host)) {
|
|
27682
|
-
const inAllowedList = PUBLIC_ALLOWED_EXACT.includes(
|
|
27758
|
+
const inAllowedList = PUBLIC_ALLOWED_EXACT.includes(path3) || PUBLIC_ALLOWED_PREFIXES.some((prefix) => path3.startsWith(prefix));
|
|
27683
27759
|
if (inAllowedList) {
|
|
27684
|
-
console.error(`[public-host] unmatched-allowed path=${
|
|
27760
|
+
console.error(`[public-host] unmatched-allowed path=${path3}`);
|
|
27685
27761
|
return c.json({ error: "not-found" }, 404);
|
|
27686
27762
|
}
|
|
27687
27763
|
}
|